Mercurial > hg > octave-lyh
changeset 14242:637675470c58 gui
Added OctaveDEs konsole sources.
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/gui/OctaveDE_Qt-README @@ -0,0 +1,36 @@ +This is a first step toward migrating octavede from GTK+ with the VTE terminal emulator widget to a QT-based solutions. There was a previous developer who had taken the KDE Konsole application and remove all KDE dependencies and created a QT-only widget on top of a subset of the Konsole sources. It was called qtermwidget (http://qtermwidget.sourceforge.net). I would like to make a set of patches for Konsole so that Konsole can be separated into a QT-only part and a KDE app on top of it and proposed it to the Konsole maintainers to make this process easier for all future versions of Konsole. + +Here are a few notes about the current sources + +Directories +================================================================================ +konsole - contains all the modifies source code for Konsole, plus some sources from KDElibs +kb-layouts - taken from the qtermwidget project to handle different keyboards +src - modified from the qtermwidget project to create a terminal running octave. This program runs + octave_main() in the same thread as the UI so that the UI can interact with octave through + the same octave_server class that was used for the GTK+/VTE version of octavede. + +BUILD +================================================================================ +I didn't want to try and figure out how to compile QT stuff (with all the weird MOC stuff) +using autotools, so I used a .pro file for compiling the src directory and a CMakeLists.txt +file for compiling the konsole library. + +Steps: +1) cd konsole +2) ccmake . +3) c c g (for configure, configure, generate) +4) make +5) cd ../src +6) qmake +7) make + +You will definitely need to modify src.pro to point to your octave installation. I was pointing to my own recent build, but I don't think at this point that anything depends +on a specific version of octave. + +Please let me know if you have any problems. + +jpswensen at gmail.com + + +
--- a/gui/all.pro +++ b/gui/all.pro @@ -16,5 +16,4 @@ TEMPLATE = subdirs -SUBDIRS += octave-gui\ - qtermwidget +SUBDIRS += octave-gui
new file mode 100644 --- /dev/null +++ b/gui/konsole/Application.cpp @@ -0,0 +1,462 @@ +/* + 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> + +#include "kdebug.h" + +// Qt +#include <QHashIterator> +#include <QFileInfo> +#include <QDir> + +// KDE +#include <KAction> +#include <KCmdLineArgs> +#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() +{ + _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,const QString&,ViewManager*)), + this , SLOT(createSession(Profile::Ptr,const QString&,ViewManager*))); + connect( window , SIGNAL(newSSHSessionRequest(Profile::Ptr,const KUrl&,ViewManager*)), + this , SLOT(createSSHSession(Profile::Ptr,const KUrl&,ViewManager*))); + connect( window , SIGNAL(newWindowRequest(Profile::Ptr,const QString&)), + this , SLOT(createWindow(Profile::Ptr,const 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.baseName().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<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; + } + 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<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); + 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<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 + view->createView(session); + session->run(); + + 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" +
new file mode 100644 --- /dev/null +++ b/gui/konsole/Application.h @@ -0,0 +1,98 @@ +/* + Copyright 2007-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. +*/ + +#ifndef APPLICATION_H +#define APPLICATION_H + +// KDE +#include <KUniqueApplication> + +// Konsole +#include "Profile.h" + +class KCmdLineArgs; + +namespace Konsole +{ +class ProfileList; +class ViewManager; +class MainWindow; +class Session; + +/** + * The Konsole Application. + * + * The application consists of one or more main windows and a set of factories to create + * new sessions and views. + * + * To create a new main window with a default terminal session, call the newInstance() method. + * Empty main windows can be created using newMainWindow(). + * + * The factory used to create new terminal sessions can be retrieved using the sessionManager() accessor. + */ +class Application : public KUniqueApplication +{ +Q_OBJECT + +public: + /** Constructs a new Konsole application. */ + Application(); + + virtual ~Application(); + + /** Creates a new main window and opens a default terminal session */ + virtual int newInstance(); + + /** + * Creates a new, empty main window and connects to its newSessionRequest() + * and newWindowRequest() signals to trigger creation of new sessions or + * windows when then they are emitted. + */ + MainWindow* newMainWindow(); + + /** Returns the Application instance */ + static Application* self(); + +private slots: + Session* createSession(Profile::Ptr profile, const QString& directory , ViewManager* view); + Session* createSSHSession(Profile::Ptr profile, const KUrl& url, ViewManager* view); + void createWindow(Profile::Ptr profile , const QString& directory); + void detachView(Session* session); + + void toggleBackgroundInstance(); + +private: + void init(); + void listAvailableProfiles(); + void startBackgroundMode(MainWindow* window); + bool processHelpArgs(KCmdLineArgs* args); + MainWindow* processWindowArgs(KCmdLineArgs* args); + void processProfileSelectArgs(KCmdLineArgs* args,MainWindow* window); + void processProfileChangeArgs(KCmdLineArgs* args,MainWindow* window); + void processTabsFromFileArgs(KCmdLineArgs* args, MainWindow* window); + void createTabFromArgs(KCmdLineArgs* args, MainWindow* window, const QHash<QString, QString>&); + + KCmdLineArgs* _arguments; + ProfileList* _sessionList; + + MainWindow* _backgroundInstance; +}; + +} +#endif //APPLICATION_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/BlockArray.cpp @@ -0,0 +1,340 @@ +/* + This file is part of Konsole, an X terminal. + Copyright 2000 by Stephan Kulow <coolo@kde.org> + + 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 "BlockArray.h" + +// System +#include <assert.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <unistd.h> +#include <stdio.h> + +// KDE +//#include <kde_file.h> +//#include <kdebug.h> + +#define KDE_fseek ::fseek +#define KDE_lseek ::lseek + + +using namespace Konsole; + +static int blocksize = 0; + +BlockArray::BlockArray() + : size(0), + current(size_t(-1)), + index(size_t(-1)), + lastmap(0), + lastmap_index(size_t(-1)), + lastblock(0), ion(-1), + length(0) +{ + // lastmap_index = index = current = size_t(-1); + if (blocksize == 0) + blocksize = ((sizeof(Block) / getpagesize()) + 1) * getpagesize(); + +} + +BlockArray::~BlockArray() +{ + setHistorySize(0); + assert(!lastblock); +} + +size_t BlockArray::append(Block *block) +{ + if (!size) + return size_t(-1); + + ++current; + if (current >= size) current = 0; + + int rc; + rc = KDE_lseek(ion, current * blocksize, SEEK_SET); if (rc < 0) { perror("HistoryBuffer::add.seek"); setHistorySize(0); return size_t(-1); } + rc = write(ion, block, blocksize); if (rc < 0) { perror("HistoryBuffer::add.write"); setHistorySize(0); return size_t(-1); } + + length++; + if (length > size) length = size; + + ++index; + + delete block; + return current; +} + +size_t BlockArray::newBlock() +{ + if (!size) + return size_t(-1); + append(lastblock); + + lastblock = new Block(); + return index + 1; +} + +Block *BlockArray::lastBlock() const +{ + return lastblock; +} + +bool BlockArray::has(size_t i) const +{ + if (i == index + 1) + return true; + + if (i > index) + return false; + if (index - i >= length) + return false; + return true; +} + +const Block* BlockArray::at(size_t i) +{ + if (i == index + 1) + return lastblock; + + if (i == lastmap_index) + return lastmap; + + if (i > index) { + //kDebug(1211) << "BlockArray::at() i > index\n"; + return 0; + } + +// if (index - i >= length) { +// kDebug(1211) << "BlockArray::at() index - i >= length\n"; +// return 0; +// } + + size_t j = i; // (current - (index - i) + (index/size+1)*size) % size ; + + assert(j < size); + unmap(); + + Block *block = (Block*)mmap(0, blocksize, PROT_READ, MAP_PRIVATE, ion, j * blocksize); + + if (block == (Block*)-1) { perror("mmap"); return 0; } + + lastmap = block; + lastmap_index = i; + + return block; +} + +void BlockArray::unmap() +{ + if (lastmap) { + int res = munmap((char*)lastmap, blocksize); + if (res < 0) perror("munmap"); + } + lastmap = 0; + lastmap_index = size_t(-1); +} + +bool BlockArray::setSize(size_t newsize) +{ + return setHistorySize(newsize * 1024 / blocksize); +} + +bool BlockArray::setHistorySize(size_t newsize) +{ +// kDebug(1211) << "setHistorySize " << size << " " << newsize; + + if (size == newsize) + return false; + + unmap(); + + if (!newsize) { + delete lastblock; + lastblock = 0; + if (ion >= 0) close(ion); + ion = -1; + current = size_t(-1); + return true; + } + + if (!size) { + FILE* tmp = tmpfile(); + if (!tmp) { + perror("konsole: cannot open temp file.\n"); + } else { + ion = dup(fileno(tmp)); + if (ion<0) { + perror("konsole: cannot dup temp file.\n"); + fclose(tmp); + } + } + if (ion < 0) + return false; + + assert(!lastblock); + + lastblock = new Block(); + size = newsize; + return false; + } + + if (newsize > size) { + increaseBuffer(); + size = newsize; + return false; + } else { + decreaseBuffer(newsize); + if (ftruncate(ion, length*blocksize) == -1) + perror("ftruncate"); + size = newsize; + + return true; + } +} + +void moveBlock(FILE *fion, int cursor, int newpos, char *buffer2) +{ + int res = KDE_fseek(fion, cursor * blocksize, SEEK_SET); + if (res) + perror("fseek"); + res = fread(buffer2, blocksize, 1, fion); + if (res != 1) + perror("fread"); + + res = KDE_fseek(fion, newpos * blocksize, SEEK_SET); + if (res) + perror("fseek"); + res = fwrite(buffer2, blocksize, 1, fion); + if (res != 1) + perror("fwrite"); + // printf("moving block %d to %d\n", cursor, newpos); +} + +void BlockArray::decreaseBuffer(size_t newsize) +{ + if (index < newsize) // still fits in whole + return; + + int offset = (current - (newsize - 1) + size) % size; + + if (!offset) + return; + + // The Block constructor could do somthing in future... + char *buffer1 = new char[blocksize]; + + FILE *fion = fdopen(dup(ion), "w+b"); + if (!fion) { + delete [] buffer1; + perror("fdopen/dup"); + return; + } + + int firstblock; + if (current <= newsize) { + firstblock = current + 1; + } else { + firstblock = 0; + } + + size_t oldpos; + for (size_t i = 0, cursor=firstblock; i < newsize; i++) { + oldpos = (size + cursor + offset) % size; + moveBlock(fion, oldpos, cursor, buffer1); + if (oldpos < newsize) { + cursor = oldpos; + } else + cursor++; + } + + current = newsize - 1; + length = newsize; + + delete [] buffer1; + + fclose(fion); + +} + +void BlockArray::increaseBuffer() +{ + if (index < size) // not even wrapped once + return; + + int offset = (current + size + 1) % size; + if (!offset) // no moving needed + return; + + // The Block constructor could do somthing in future... + char *buffer1 = new char[blocksize]; + char *buffer2 = new char[blocksize]; + + int runs = 1; + int bpr = size; // blocks per run + + if (size % offset == 0) { + bpr = size / offset; + runs = offset; + } + + FILE *fion = fdopen(dup(ion), "w+b"); + if (!fion) { + perror("fdopen/dup"); + delete [] buffer1; + delete [] buffer2; + return; + } + + int res; + for (int i = 0; i < runs; i++) + { + // free one block in chain + int firstblock = (offset + i) % size; + res = KDE_fseek(fion, firstblock * blocksize, SEEK_SET); + if (res) + perror("fseek"); + res = fread(buffer1, blocksize, 1, fion); + if (res != 1) + perror("fread"); + int newpos = 0; + for (int j = 1, cursor=firstblock; j < bpr; j++) + { + cursor = (cursor + offset) % size; + newpos = (cursor - offset + size) % size; + moveBlock(fion, cursor, newpos, buffer2); + } + res = KDE_fseek(fion, i * blocksize, SEEK_SET); + if (res) + perror("fseek"); + res = fwrite(buffer1, blocksize, 1, fion); + if (res != 1) + perror("fwrite"); + } + current = size - 1; + length = size; + + delete [] buffer1; + delete [] buffer2; + + fclose(fion); + +} +
new file mode 100644 --- /dev/null +++ b/gui/konsole/BlockArray.h @@ -0,0 +1,123 @@ +/* + This file is part of Konsole, an X terminal. + Copyright 2000 by Stephan Kulow <coolo@kde.org> + + 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 BLOCKARRAY_H +#define BLOCKARRAY_H + +#include <unistd.h> + +//#error Do not use in KDE 2.1 + +#define BlockSize (1 << 12) +#define ENTRIES ((BlockSize - sizeof(size_t) ) / sizeof(unsigned char)) + +namespace Konsole +{ + +struct Block { + Block() { size = 0; } + unsigned char data[ENTRIES]; + size_t size; +}; + +// /////////////////////////////////////////////////////// + +class BlockArray { +public: + /** + * Creates a history file for holding + * maximal size blocks. If more blocks + * are requested, then it drops earlier + * added ones. + */ + BlockArray(); + + /// destructor + ~BlockArray(); + + /** + * adds the Block at the end of history. + * This may drop other blocks. + * + * The ownership on the block is transferred. + * An unique index number is returned for accessing + * it later (if not yet dropped then) + * + * Note, that the block may be dropped completely + * if history is turned off. + */ + size_t append(Block *block); + + /** + * gets the block at the index. Function may return + * 0 if the block isn't available any more. + * + * The returned block is strictly readonly as only + * maped in memory - and will be invalid on the next + * operation on this class. + */ + const Block *at(size_t index); + + /** + * reorders blocks as needed. If newsize is null, + * the history is emptied completely. The indices + * returned on append won't change their semantic, + * but they may not be valid after this call. + */ + bool setHistorySize(size_t newsize); + + size_t newBlock(); + + Block *lastBlock() const; + + /** + * Convenient function to set the size in KBytes + * instead of blocks + */ + bool setSize(size_t newsize); + + size_t len() const { return length; } + + bool has(size_t index) const; + + size_t getCurrent() const { return current; } + +private: + void unmap(); + void increaseBuffer(); + void decreaseBuffer(size_t newsize); + + size_t size; + // current always shows to the last inserted block + size_t current; + size_t index; + + Block *lastmap; + size_t lastmap_index; + Block *lastblock; + + int ion; + size_t length; + +}; + +} + +#endif
new file mode 100644 --- /dev/null +++ b/gui/konsole/BookmarkHandler.cpp @@ -0,0 +1,178 @@ +/* This file was part of the KDE libraries + + Copyright 2002 Carsten Pfeiffer <pfeiffer@kde.org> + Copyright 2007-2008 Robert Knight <robertknight@gmail.com> + + library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation, version 2 + or ( at your option ), any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Born as kdelibs/kio/kfile/kfilebookmarkhandler.characterpp + +// Own +#include "BookmarkHandler.h" + +// Qt +#include <QtCore/QFile> +#include <QtCore/QFileInfo> + +// KDE +#include <kshell.h> + +#include <KBookmarkMenu> +#include <KDebug> +#include <KMenu> +#include <KStandardDirs> + +// Konsole +#include "ViewProperties.h" + +using namespace Konsole; + +BookmarkHandler::BookmarkHandler( KActionCollection* collection, + KMenu* menu, + bool toplevel , + QObject* parent ) + : QObject( parent ), + KBookmarkOwner(), + m_toplevel(toplevel), + m_activeView(0) +{ + setObjectName( QLatin1String( "BookmarkHandler" ) ); + + m_menu = menu; + + QString new_bm_file = KStandardDirs::locateLocal( "data", "konsole/bookmarks.xml" ); + + m_file = KStandardDirs::locate( "data", "konsole/bookmarks.xml" ); + if ( m_file.isEmpty() ) + m_file = KStandardDirs::locateLocal( "data", "konsole/bookmarks.xml" ); + + KBookmarkManager *manager = KBookmarkManager::managerForFile( m_file, "konsole" ); + + manager->setUpdate( true ); + + if (toplevel) { + m_bookmarkMenu = new KBookmarkMenu( manager, this, m_menu, + collection ); + } else { + m_bookmarkMenu = new KBookmarkMenu( manager, this, m_menu, + NULL); + } +} + +BookmarkHandler::~BookmarkHandler() +{ + delete m_bookmarkMenu; +} + +void BookmarkHandler::openBookmark( const KBookmark & bm, Qt::MouseButtons, Qt::KeyboardModifiers ) +{ + emit openUrl( bm.url() ); +} +void BookmarkHandler::openFolderinTabs( const KBookmarkGroup& group ) +{ + emit openUrls(group.groupUrlList()); +} +bool BookmarkHandler::enableOption(BookmarkOption option ) const +{ + if(option == ShowAddBookmark || option == ShowEditBookmark) + return m_toplevel; + else + return KBookmarkOwner::enableOption(option); +} + +QString BookmarkHandler::currentUrl() const +{ + return urlForView(m_activeView); +} + +QString BookmarkHandler::urlForView(ViewProperties* view) const +{ + if ( view ) + { + return view->url().prettyUrl(); + } + else + { + return QString(); + } +} + +QString BookmarkHandler::currentTitle() const +{ + return titleForView(m_activeView); +} + +QString BookmarkHandler::titleForView(ViewProperties* view) const +{ + const KUrl &u = view ? view->url() : KUrl(); + if (u.isLocalFile()) + { + QString path = u.path(); + path = KShell::tildeExpand(path); + + path = QFileInfo(path).baseName(); + + return path; + } + else if ( u.hasHost() ) + { + if ( u.hasUser() ) + return i18nc("@item:inmenu The user's name and host they are connected to via ssh", "%1 on %2",u.user(),u.host()); + else + return i18nc("@item:inmenu The host the user is connected to via ssh", "%1",u.host()); + } + return u.prettyUrl(); +} + +bool BookmarkHandler::supportsTabs() const +{ + return true; +} + +QList<QPair<QString,QString> > BookmarkHandler::currentBookmarkList() const +{ + QList<QPair<QString,QString> > list; + + QListIterator<ViewProperties*> iter( m_views ); + + while ( iter.hasNext() ) + { + ViewProperties* next = iter.next(); + list << QPair<QString,QString>(titleForView(next) , urlForView(next)); + } + + return list; +} + +void BookmarkHandler::setViews(const QList<ViewProperties*>& views) +{ + m_views = views; +} +QList<ViewProperties*> BookmarkHandler::views() const +{ + return m_views; +} +void BookmarkHandler::setActiveView( ViewProperties* view ) +{ + m_activeView = view; +} +ViewProperties* BookmarkHandler::activeView() const +{ + return m_activeView; +} + +#include "BookmarkHandler.moc"
new file mode 100644 --- /dev/null +++ b/gui/konsole/BookmarkHandler.h @@ -0,0 +1,133 @@ +/* This file was part of the KDE libraries + + Copyright 2002 Carsten Pfeiffer <pfeiffer@kde.org> + Copyright 2007-2008 Robert Knight <robertknight@gmail.com> + + library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation, version 2 + or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Born as kdelibs/kio/kfile/kfilebookmarkhandler.h + +#ifndef KONSOLEBOOKMARKHANDLER_H +#define KONSOLEBOOKMARKHANDLER_H + +// Qt +#include <QtGui/QMenu> + +// KDE +#include <KBookmarkManager> + +// Konsole +#include "konsole_export.h" + +class KMenu; +class KBookmarkMenu; +class KBookmarkManager; +class KActionCollection; + +namespace Konsole +{ + +class ViewProperties; + +/** + * This class handles the communication between the bookmark menu and the active session, + * providing a suggested title and URL when the user clicks the "Add Bookmark" item in + * the bookmarks menu. + * + * The bookmark handler is associated with a session controller, which is used to + * determine the working URL of the current session. When the user changes the active + * view, the bookmark handler's controller should be changed using setController() + * + * When the user selects a bookmark, the openUrl() signal is emitted. + */ +class KONSOLEPRIVATE_EXPORT BookmarkHandler : public QObject, public KBookmarkOwner +{ + Q_OBJECT + +public: + + /** + * Constructs a new bookmark handler for Konsole bookmarks. + * + * @param collection The collection which the boomark menu's actions should be added to + * @param menu The menu which the bookmark actions should be added to + * @param toplevel TODO: Document me + * @param parent TODO: Document me + */ + BookmarkHandler( KActionCollection* collection , KMenu* menu, bool toplevel , QObject* parent ); + ~BookmarkHandler(); + + QMenu * popupMenu(); + + virtual QString currentUrl() const; + virtual QString currentTitle() const; + virtual bool enableOption(BookmarkOption option) const; + virtual bool supportsTabs() const; + virtual QList<QPair<QString,QString> > currentBookmarkList() const; + virtual void openFolderinTabs(const KBookmarkGroup& group); + + /** + * Returns the menu which this bookmark handler inserts its actions into. + */ + KMenu *menu() const { return m_menu; } + + QList<ViewProperties*> views() const; + ViewProperties* activeView() const; + +public slots: + /** + * + */ + void setViews( const QList<ViewProperties*>& views ); + + void setActiveView( ViewProperties* view ); + +signals: + /** + * Emitted when the user selects a bookmark from the bookmark menu. + * + * @param url The url of the bookmark which was selected by the user. + */ + void openUrl( const KUrl& url ); + + /** + * Emitted when the user selects 'Open Folder in Tabs' + * from the bookmark menu. + * + * @param urls The urls of the bookmarks in the folder whoose + * 'Open Folder in Tabs' action was triggered + */ + void openUrls( const QList<KUrl>& urls ); + +private Q_SLOTS: + void openBookmark( const KBookmark & bm, Qt::MouseButtons, Qt::KeyboardModifiers ); + +private: + QString titleForView( ViewProperties* view ) const; + QString urlForView( ViewProperties* view ) const; + + KMenu* m_menu; + KBookmarkMenu* m_bookmarkMenu; + QString m_file; + bool m_toplevel; + ViewProperties* m_activeView; + QList<ViewProperties*> m_views; +}; + +} + +#endif // KONSOLEBOOKMARKHANDLER_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/CMakeLists.txt @@ -0,0 +1,92 @@ +project(qtermwidget) +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +FIND_PACKAGE(Qt4 COMPONENTS QtCore QtGui REQUIRED) + +include_directories( ${QT_INCLUDES} ) + +set(kpty_LIB_SRCS + kpty.cpp + kptydevice.cpp + kprocess.cpp + kptyprocess.cpp +) + +SET( kpty_MOC_HDR + ./kptydevice.h + ./kprocess.h +) + +set(qtermwidget_LIB_SRCS + Pty.cpp + Session.cpp + TerminalDisplay.cpp + ScreenWindow.cpp + Filter.cpp + Vt102Emulation.cpp + Emulation.cpp + Screen.cpp + TerminalCharacterDecoder.cpp + History.cpp + BlockArray.cpp + ShellCommand.cpp + ProcessInfo.cpp + KeyboardTranslator.cpp + konsole_wcwidth.cpp + qtermwidget.cpp +) + +set(qtermwidget_MOC_HDR + ./Pty.h + ./Session.h + ./TerminalDisplay.h + ./ScreenWindow.h + ./Filter.h + ./Vt102Emulation.h + ./Emulation.h + ./qtermwidget.h +) + +# Generate the KPty moc files +QT4_GENERATE_MOC(kptydevice.h kptydevice.moc) +SET_SOURCE_FILES_PROPERTIES(kptydevice.cpp PROPERTIES OBJECT_DEPENDS kptydevice.moc) +QT4_GENERATE_MOC(kprocess.h kprocess.moc) +SET_SOURCE_FILES_PROPERTIES(kprocess.cpp PROPERTIES OBJECT_DEPENDS kprocess.moc) +QT4_GENERATE_MOC(kptyprocess.h kptyprocess.moc) +SET_SOURCE_FILES_PROPERTIES(kptyprocess.cpp PROPERTIES OBJECT_DEPENDS kptyprocess.moc) + +# Generate the qtermwidget moc file +QT4_GENERATE_MOC(Pty.h Pty.moc) +SET_SOURCE_FILES_PROPERTIES(Pty.cpp PROPERTIES OBJECT_DEPENDS Pty.moc) +QT4_GENERATE_MOC(Session.h Session.moc) +SET_SOURCE_FILES_PROPERTIES(Session.cpp PROPERTIES OBJECT_DEPENDS Session.moc) +QT4_GENERATE_MOC(TerminalDisplay.h TerminalDisplay.moc) +SET_SOURCE_FILES_PROPERTIES(TerminalDisplay.cpp PROPERTIES OBJECT_DEPENDS TerminalDisplay.moc) +QT4_GENERATE_MOC(ScreenWindow.h ScreenWindow.moc) +SET_SOURCE_FILES_PROPERTIES(ScreenWindow.cpp PROPERTIES OBJECT_DEPENDS ScreenWindow.moc) +QT4_GENERATE_MOC(Filter.h Filter.moc) +SET_SOURCE_FILES_PROPERTIES(Filter.cpp PROPERTIES OBJECT_DEPENDS Filter.moc) +QT4_GENERATE_MOC(Vt102Emulation.h Vt102Emulation.moc) +SET_SOURCE_FILES_PROPERTIES(Vt102Emulation.cpp PROPERTIES OBJECT_DEPENDS Vt102Emulation.moc) +QT4_GENERATE_MOC(Emulation.h Emulation.moc) +SET_SOURCE_FILES_PROPERTIES(Emulation.cpp PROPERTIES OBJECT_DEPENDS Emulation.moc) +QT4_GENERATE_MOC(qtermwidget.h qtermwidget.moc) +SET_SOURCE_FILES_PROPERTIES(qtermwidget.cpp PROPERTIES OBJECT_DEPENDS qtermwidget.moc) + +#QT4_GENERATE_MOC(.h .moc) +#SET_SOURCE_FILES_PROPERTIES(.cpp PROPERTIES OBJECT_DEPENDS .moc) + + +add_library(qtermwidget ${LIBRARY_TYPE} ${kpty_LIB_SRCS} ${qtermwidget_LIB_SRCS}) + +target_link_libraries(qtermwidget ${UTIL_LIBRARY} ${UTEMPTER_LIBRARY}) +target_link_libraries(qtermwidget LINK_INTERFACE_LIBRARIES ${QT_QTCORE_LIBRARY} ) + +set_target_properties(qtermwidget PROPERTIES + VERSION ${GENERIC_LIB_VERSION} + SOVERSION ${GENERIC_LIB_SOVERSION} +) + + +########### next target ############### + +
new file mode 100644 --- /dev/null +++ b/gui/konsole/Character.h @@ -0,0 +1,224 @@ +/* + This file is part of Konsole, KDE's terminal. + + Copyright 2007-2008 by Robert Knight <robertknight@gmail.com> + Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de> + + 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 CHARACTER_H +#define CHARACTER_H + +// Qt +#include <QtCore/QHash> + +// Local +#include "CharacterColor.h" + +namespace Konsole +{ + +typedef unsigned char LineProperty; + +static const int LINE_DEFAULT = 0; +static const int LINE_WRAPPED = (1 << 0); +static const int LINE_DOUBLEWIDTH = (1 << 1); +static const int LINE_DOUBLEHEIGHT = (1 << 2); + +#define DEFAULT_RENDITION 0 +#define RE_BOLD (1 << 0) +#define RE_BLINK (1 << 1) +#define RE_UNDERLINE (1 << 2) +#define RE_REVERSE (1 << 3) // Screen only +#define RE_INTENSIVE (1 << 3) // Widget only +#define RE_CURSOR (1 << 4) +#define RE_EXTENDED_CHAR (1 << 5) + +/** + * A single character in the terminal which consists of a unicode character + * value, foreground and background colors and a set of rendition attributes + * which specify how it should be drawn. + */ +class Character +{ +public: + /** + * Constructs a new character. + * + * @param _c The unicode character value of this character. + * @param _f The foreground color used to draw the character. + * @param _b The color used to draw the character's background. + * @param _r A set of rendition flags which specify how this character is to be drawn. + */ + inline Character(quint16 _c = ' ', + CharacterColor _f = CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR), + CharacterColor _b = CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR), + quint8 _r = DEFAULT_RENDITION) + : character(_c), rendition(_r), foregroundColor(_f), backgroundColor(_b) {} + + union + { + /** The unicode character value for this character. */ + quint16 character; + /** + * Experimental addition which allows a single Character instance to contain more than + * one unicode character. + * + * charSequence is a hash code which can be used to look up the unicode + * character sequence in the ExtendedCharTable used to create the sequence. + */ + quint16 charSequence; + }; + + /** A combination of RENDITION flags which specify options for drawing the character. */ + quint8 rendition; + + /** The foreground color used to draw this character. */ + CharacterColor foregroundColor; + /** The color used to draw this character's background. */ + CharacterColor backgroundColor; + + /** + * Returns true if this character has a transparent background when + * it is drawn with the specified @p palette. + */ + bool isTransparent(const ColorEntry* palette) const; + /** + * Returns true if this character should always be drawn in bold when + * it is drawn with the specified @p palette, independent of whether + * or not the character has the RE_BOLD rendition flag. + */ + ColorEntry::FontWeight fontWeight(const ColorEntry* base) const; + + /** + * returns true if the format (color, rendition flag) of the compared characters is equal + */ + bool equalsFormat(const Character &other) const; + + /** + * Compares two characters and returns true if they have the same unicode character value, + * rendition and colors. + */ + friend bool operator == (const Character& a, const Character& b); + /** + * Compares two characters and returns true if they have different unicode character values, + * renditions or colors. + */ + friend bool operator != (const Character& a, const Character& b); +}; + +inline bool operator == (const Character& a, const Character& b) +{ + return a.character == b.character && + a.rendition == b.rendition && + a.foregroundColor == b.foregroundColor && + a.backgroundColor == b.backgroundColor; +} + +inline bool operator != (const Character& a, const Character& b) +{ + return a.character != b.character || + a.rendition != b.rendition || + a.foregroundColor != b.foregroundColor || + a.backgroundColor != b.backgroundColor; +} + +inline bool Character::isTransparent(const ColorEntry* base) const +{ + return ((backgroundColor._colorSpace == COLOR_SPACE_DEFAULT) && + base[backgroundColor._u+0+(backgroundColor._v?BASE_COLORS:0)].transparent) + || ((backgroundColor._colorSpace == COLOR_SPACE_SYSTEM) && + base[backgroundColor._u+2+(backgroundColor._v?BASE_COLORS:0)].transparent); +} + +inline bool Character::equalsFormat(const Character& other) const +{ + return + backgroundColor==other.backgroundColor && + foregroundColor==other.foregroundColor && + rendition==other.rendition; +} + +inline ColorEntry::FontWeight Character::fontWeight(const ColorEntry* base) const +{ + if (backgroundColor._colorSpace == COLOR_SPACE_DEFAULT) + return base[backgroundColor._u+0+(backgroundColor._v?BASE_COLORS:0)].fontWeight; + else if (backgroundColor._colorSpace == COLOR_SPACE_SYSTEM) + return base[backgroundColor._u+2+(backgroundColor._v?BASE_COLORS:0)].fontWeight; + else + return ColorEntry::UseCurrentFormat; +} + +extern unsigned short vt100_graphics[32]; + + +/** + * A table which stores sequences of unicode characters, referenced + * by hash keys. The hash key itself is the same size as a unicode + * character ( ushort ) so that it can occupy the same space in + * a structure. + */ +class ExtendedCharTable +{ +public: + /** Constructs a new character table. */ + ExtendedCharTable(); + ~ExtendedCharTable(); + + /** + * Adds a sequences of unicode characters to the table and returns + * a hash code which can be used later to look up the sequence + * using lookupExtendedChar() + * + * If the same sequence already exists in the table, the hash + * of the existing sequence will be returned. + * + * @param unicodePoints An array of unicode character points + * @param length Length of @p unicodePoints + */ + ushort createExtendedChar(ushort* unicodePoints , ushort length); + /** + * Looks up and returns a pointer to a sequence of unicode characters + * which was added to the table using createExtendedChar(). + * + * @param hash The hash key returned by createExtendedChar() + * @param length This variable is set to the length of the + * character sequence. + * + * @return A unicode character sequence of size @p length. + */ + ushort* lookupExtendedChar(ushort hash , ushort& length) const; + + /** The global ExtendedCharTable instance. */ + static ExtendedCharTable instance; +private: + // calculates the hash key of a sequence of unicode points of size 'length' + ushort extendedCharHash(ushort* unicodePoints , ushort length) const; + // tests whether the entry in the table specified by 'hash' matches the + // character sequence 'unicodePoints' of size 'length' + bool extendedCharMatch(ushort hash , ushort* unicodePoints , ushort length) const; + // internal, maps hash keys to character sequence buffers. The first ushort + // in each value is the length of the buffer, followed by the ushorts in the buffer + // themselves. + QHash<ushort,ushort*> extendedCharTable; +}; + +} +Q_DECLARE_TYPEINFO(Konsole::Character, Q_MOVABLE_TYPE); + +#endif // CHARACTER_H +
new file mode 100644 --- /dev/null +++ b/gui/konsole/CharacterColor.h @@ -0,0 +1,299 @@ +/* + This file is part of Konsole, KDE's terminal. + + Copyright 2007-2008 by Robert Knight <robertknight@gmail.com> + Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de> + + 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 CHARACTERCOLOR_H +#define CHARACTERCOLOR_H + +// Qt +#include <QtGui/QColor> + +//#include <kdemacros.h> +#define KDE_NO_EXPORT + +namespace Konsole +{ + +/** + * An entry in a terminal display's color palette. + * + * A color palette is an array of 16 ColorEntry instances which map + * system color indexes (from 0 to 15) into actual colors. + * + * Each entry can be set as bold, in which case any text + * drawn using the color should be drawn in bold. + * + * Each entry can also be transparent, in which case the terminal + * display should avoid drawing the background for any characters + * using the entry as a background. + */ +class ColorEntry +{ +public: + /** Specifies the weight to use when drawing text with this color. */ + enum FontWeight + { + /** Always draw text in this color with a bold weight. */ + Bold, + /** Always draw text in this color with a normal weight. */ + Normal, + /** + * Use the current font weight set by the terminal application. + * This is the default behavior. + */ + UseCurrentFormat + }; + + /** + * Constructs a new color palette entry. + * + * @param c The color value for this entry. + * @param tr Specifies that the color should be transparent when used as a background color. + * @param weight Specifies the font weight to use when drawing text with this color. + */ + ColorEntry(QColor c, bool tr, FontWeight weight = UseCurrentFormat) + : color(c), transparent(tr), fontWeight(weight) {} + + /** + * Constructs a new color palette entry with an undefined color, and + * with the transparent and bold flags set to false. + */ + ColorEntry() : transparent(false), fontWeight(UseCurrentFormat) {} + + /** + * Sets the color, transparency and boldness of this color to those of @p rhs. + */ + void operator=(const ColorEntry& rhs) + { + color = rhs.color; + transparent = rhs.transparent; + fontWeight = rhs.fontWeight; + } + + /** The color value of this entry for display. */ + QColor color; + + /** + * If true character backgrounds using this color should be transparent. + * This is not applicable when the color is used to render text. + */ + bool transparent; + /** + * Specifies the font weight to use when drawing text with this color. + * This is not applicable when the color is used to draw a character's background. + */ + FontWeight fontWeight; +}; + + +// Attributed Character Representations /////////////////////////////// + +// Colors + +#define BASE_COLORS (2+8) +#define INTENSITIES 2 +#define TABLE_COLORS (INTENSITIES*BASE_COLORS) + +#define DEFAULT_FORE_COLOR 0 +#define DEFAULT_BACK_COLOR 1 + +//a standard set of colors using black text on a white background. +//defined in TerminalDisplay.cpp + +extern const ColorEntry base_color_table[TABLE_COLORS] KDE_NO_EXPORT; + +/* CharacterColor is a union of the various color spaces. + + Assignment is as follows: + + Type - Space - Values + + 0 - Undefined - u: 0, v:0 w:0 + 1 - Default - u: 0..1 v:intense w:0 + 2 - System - u: 0..7 v:intense w:0 + 3 - Index(256) - u: 16..255 v:0 w:0 + 4 - RGB - u: 0..255 v:0..256 w:0..256 + + Default colour space has two separate colours, namely + default foreground and default background colour. +*/ + +#define COLOR_SPACE_UNDEFINED 0 +#define COLOR_SPACE_DEFAULT 1 +#define COLOR_SPACE_SYSTEM 2 +#define COLOR_SPACE_256 3 +#define COLOR_SPACE_RGB 4 + +/** + * Describes the color of a single character in the terminal. + */ +class CharacterColor +{ + friend class Character; + +public: + /** Constructs a new CharacterColor whoose color and color space are undefined. */ + CharacterColor() + : _colorSpace(COLOR_SPACE_UNDEFINED), + _u(0), + _v(0), + _w(0) + {} + + /** + * Constructs a new CharacterColor using the specified @p colorSpace and with + * color value @p co + * + * The meaning of @p co depends on the @p colorSpace used. + * + * TODO : Document how @p co relates to @p colorSpace + * + * TODO : Add documentation about available color spaces. + */ + CharacterColor(quint8 colorSpace, int co) + : _colorSpace(colorSpace), + _u(0), + _v(0), + _w(0) + { + switch (colorSpace) + { + case COLOR_SPACE_DEFAULT: + _u = co & 1; + break; + case COLOR_SPACE_SYSTEM: + _u = co & 7; + _v = (co >> 3) & 1; + break; + case COLOR_SPACE_256: + _u = co & 255; + break; + case COLOR_SPACE_RGB: + _u = co >> 16; + _v = co >> 8; + _w = co; + break; + default: + _colorSpace = COLOR_SPACE_UNDEFINED; + } + } + + /** + * Returns true if this character color entry is valid. + */ + bool isValid() + { + return _colorSpace != COLOR_SPACE_UNDEFINED; + } + + /** + * Toggles the value of this color between a normal system color and the corresponding intensive + * system color. + * + * This is only applicable if the color is using the COLOR_SPACE_DEFAULT or COLOR_SPACE_SYSTEM + * color spaces. + */ + void toggleIntensive(); + + /** + * Returns the color within the specified color @p palette + * + * The @p palette is only used if this color is one of the 16 system colors, otherwise + * it is ignored. + */ + QColor color(const ColorEntry* palette) const; + + /** + * Compares two colors and returns true if they represent the same color value and + * use the same color space. + */ + friend bool operator == (const CharacterColor& a, const CharacterColor& b); + /** + * Compares two colors and returns true if they represent different color values + * or use different color spaces. + */ + friend bool operator != (const CharacterColor& a, const CharacterColor& b); + +private: + quint8 _colorSpace; + + // bytes storing the character color + quint8 _u; + quint8 _v; + quint8 _w; +}; + +inline bool operator == (const CharacterColor& a, const CharacterColor& b) +{ + return a._colorSpace == b._colorSpace && + a._u == b._u && + a._v == b._v && + a._w == b._w; +} +inline bool operator != (const CharacterColor& a, const CharacterColor& b) +{ + return !operator==(a,b); +} + +inline const QColor color256(quint8 u, const ColorEntry* base) +{ + // 0.. 16: system colors + if (u < 8) return base[u+2 ].color; u -= 8; + if (u < 8) return base[u+2+BASE_COLORS].color; u -= 8; + + // 16..231: 6x6x6 rgb color cube + if (u < 216) return QColor(((u/36)%6) ? (40*((u/36)%6)+55) : 0, + ((u/ 6)%6) ? (40*((u/ 6)%6)+55) : 0, + ((u/ 1)%6) ? (40*((u/ 1)%6)+55) : 0); u -= 216; + + // 232..255: gray, leaving out black and white + int gray = u*10+8; return QColor(gray,gray,gray); +} + +inline QColor CharacterColor::color(const ColorEntry* base) const +{ + switch (_colorSpace) + { + case COLOR_SPACE_DEFAULT: return base[_u+0+(_v?BASE_COLORS:0)].color; + case COLOR_SPACE_SYSTEM: return base[_u+2+(_v?BASE_COLORS:0)].color; + case COLOR_SPACE_256: return color256(_u,base); + case COLOR_SPACE_RGB: return QColor(_u,_v,_w); + case COLOR_SPACE_UNDEFINED: return QColor(); + } + + Q_ASSERT(false); // invalid color space + + return QColor(); +} + +inline void CharacterColor::toggleIntensive() +{ + if (_colorSpace == COLOR_SPACE_SYSTEM || _colorSpace == COLOR_SPACE_DEFAULT) + { + _v = !_v; + } +} + + +} + +#endif // CHARACTERCOLOR_H +
new file mode 100644 --- /dev/null +++ b/gui/konsole/ColorScheme.cpp @@ -0,0 +1,706 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2007-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 "ColorScheme.h" + +// Qt +#include <QtGui/QBrush> +#include <QtCore/QFile> +#include <QtCore/QFileInfo> + +// KDE +#include <KColorScheme> +#include <KConfig> +#include <KLocale> +#include <KDebug> +#include <KConfigGroup> +#include <KStandardDirs> + +using namespace Konsole; + +const ColorEntry ColorScheme::defaultTable[TABLE_COLORS] = + // The following are almost IBM standard color codes, with some slight + // gamma correction for the dim colors to compensate for bright X screens. + // It contains the 8 ansiterm/xterm colors in 2 intensities. +{ + ColorEntry( QColor(0x00,0x00,0x00), 0), ColorEntry( +QColor(0xFF,0xFF,0xFF), 1), // Dfore, Dback + ColorEntry( QColor(0x00,0x00,0x00), 0), ColorEntry( +QColor(0xB2,0x18,0x18), 0), // Black, Red + ColorEntry( QColor(0x18,0xB2,0x18), 0), ColorEntry( +QColor(0xB2,0x68,0x18), 0), // Green, Yellow + ColorEntry( QColor(0x18,0x18,0xB2), 0), ColorEntry( +QColor(0xB2,0x18,0xB2), 0), // Blue, Magenta + ColorEntry( QColor(0x18,0xB2,0xB2), 0), ColorEntry( +QColor(0xB2,0xB2,0xB2), 0), // Cyan, White + // intensive + ColorEntry( QColor(0x00,0x00,0x00), 0), ColorEntry( +QColor(0xFF,0xFF,0xFF), 1), + ColorEntry( QColor(0x68,0x68,0x68), 0), ColorEntry( +QColor(0xFF,0x54,0x54), 0), + ColorEntry( QColor(0x54,0xFF,0x54), 0), ColorEntry( +QColor(0xFF,0xFF,0x54), 0), + ColorEntry( QColor(0x54,0x54,0xFF), 0), ColorEntry( +QColor(0xFF,0x54,0xFF), 0), + ColorEntry( QColor(0x54,0xFF,0xFF), 0), ColorEntry( +QColor(0xFF,0xFF,0xFF), 0) +}; + +const char* const ColorScheme::colorNames[TABLE_COLORS] = +{ + "Foreground", + "Background", + "Color0", + "Color1", + "Color2", + "Color3", + "Color4", + "Color5", + "Color6", + "Color7", + "ForegroundIntense", + "BackgroundIntense", + "Color0Intense", + "Color1Intense", + "Color2Intense", + "Color3Intense", + "Color4Intense", + "Color5Intense", + "Color6Intense", + "Color7Intense" +}; +const char* const ColorScheme::translatedColorNames[TABLE_COLORS] = +{ + I18N_NOOP("Foreground"), + I18N_NOOP("Background"), + I18N_NOOP("Color 1"), + I18N_NOOP("Color 2"), + I18N_NOOP("Color 3"), + I18N_NOOP("Color 4"), + I18N_NOOP("Color 5"), + I18N_NOOP("Color 6"), + I18N_NOOP("Color 7"), + I18N_NOOP("Color 8"), + I18N_NOOP("Foreground (Intense)"), + I18N_NOOP("Background (Intense)"), + I18N_NOOP("Color 1 (Intense)"), + I18N_NOOP("Color 2 (Intense)"), + I18N_NOOP("Color 3 (Intense)"), + I18N_NOOP("Color 4 (Intense)"), + I18N_NOOP("Color 5 (Intense)"), + I18N_NOOP("Color 6 (Intense)"), + I18N_NOOP("Color 7 (Intense)"), + I18N_NOOP("Color 8 (Intense)") +}; + +ColorScheme::ColorScheme() +{ + _table = 0; + _randomTable = 0; + _opacity = 1.0; +} +ColorScheme::ColorScheme(const ColorScheme& other) + : _opacity(other._opacity) + ,_table(0) + ,_randomTable(0) +{ + setName(other.name()); + setDescription(other.description()); + + if ( other._table != 0 ) + { + for ( int i = 0 ; i < TABLE_COLORS ; i++ ) + setColorTableEntry(i,other._table[i]); + } + + if ( other._randomTable != 0 ) + { + for ( int i = 0 ; i < TABLE_COLORS ; i++ ) + { + const RandomizationRange& range = other._randomTable[i]; + setRandomizationRange(i,range.hue,range.saturation,range.value); + } + } +} +ColorScheme::~ColorScheme() +{ + delete[] _table; + delete[] _randomTable; +} + +void ColorScheme::setDescription(const QString& description) { _description = description; } +QString ColorScheme::description() const { return _description; } + +void ColorScheme::setName(const QString& name) { _name = name; } +QString ColorScheme::name() const { return _name; } + +void ColorScheme::setColorTableEntry(int index , const ColorEntry& entry) +{ + Q_ASSERT( index >= 0 && index < TABLE_COLORS ); + + if ( !_table ) + { + _table = new ColorEntry[TABLE_COLORS]; + + for (int i=0;i<TABLE_COLORS;i++) + _table[i] = defaultTable[i]; + } + + _table[index] = entry; +} +ColorEntry ColorScheme::colorEntry(int index , uint randomSeed) const +{ + Q_ASSERT( index >= 0 && index < TABLE_COLORS ); + + if ( randomSeed != 0 ) + qsrand(randomSeed); + + ColorEntry entry = colorTable()[index]; + + if ( randomSeed != 0 && + _randomTable != 0 && + !_randomTable[index].isNull() ) + { + const RandomizationRange& range = _randomTable[index]; + + + int hueDifference = range.hue ? (qrand() % range.hue) - range.hue/2 : 0; + int saturationDifference = range.saturation ? (qrand() % range.saturation) - range.saturation/2 : 0; + int valueDifference = range.value ? (qrand() % range.value) - range.value/2 : 0; + + QColor& color = entry.color; + + int newHue = qAbs( (color.hue() + hueDifference) % MAX_HUE ); + int newValue = qMin( qAbs(color.value() + valueDifference) , 255 ); + int newSaturation = qMin( qAbs(color.saturation() + saturationDifference) , 255 ); + + color.setHsv(newHue,newSaturation,newValue); + } + + return entry; +} +void ColorScheme::getColorTable(ColorEntry* table , uint randomSeed) const +{ + for ( int i = 0 ; i < TABLE_COLORS ; i++ ) + table[i] = colorEntry(i,randomSeed); +} +bool ColorScheme::randomizedBackgroundColor() const +{ + return _randomTable == 0 ? false : !_randomTable[1].isNull(); +} +void ColorScheme::setRandomizedBackgroundColor(bool randomize) +{ + // the hue of the background colour is allowed to be randomly + // adjusted as much as possible. + // + // the value and saturation are left alone to maintain read-ability + if ( randomize ) + { + setRandomizationRange( 1 /* background color index */ , MAX_HUE , 255 , 0 ); + } + else + { + if ( _randomTable ) + setRandomizationRange( 1 /* background color index */ , 0 , 0 , 0 ); + } +} + +void ColorScheme::setRandomizationRange( int index , quint16 hue , quint8 saturation , + quint8 value ) +{ + Q_ASSERT( hue <= MAX_HUE ); + Q_ASSERT( index >= 0 && index < TABLE_COLORS ); + + if ( _randomTable == 0 ) + _randomTable = new RandomizationRange[TABLE_COLORS]; + + _randomTable[index].hue = hue; + _randomTable[index].value = value; + _randomTable[index].saturation = saturation; +} + +const ColorEntry* ColorScheme::colorTable() const +{ + if ( _table ) + return _table; + else + return defaultTable; +} +QColor ColorScheme::foregroundColor() const +{ + return colorTable()[0].color; +} +QColor ColorScheme::backgroundColor() const +{ + return colorTable()[1].color; +} +bool ColorScheme::hasDarkBackground() const +{ + // value can range from 0 - 255, with larger values indicating higher brightness. + // so 127 is in the middle, anything less is deemed 'dark' + return backgroundColor().value() < 127; +} +void ColorScheme::setOpacity(qreal opacity) { _opacity = opacity; } +qreal ColorScheme::opacity() const { return _opacity; } + +void ColorScheme::read(KConfig& config) +{ + KConfigGroup configGroup = config.group("General"); + + QString description = configGroup.readEntry("Description", I18N_NOOP("Un-named Color Scheme")); + + _description = i18n(description.toUtf8()); + _opacity = configGroup.readEntry("Opacity",qreal(1.0)); + + for (int i=0 ; i < TABLE_COLORS ; i++) + { + readColorEntry(config,i); + } +} +void ColorScheme::write(KConfig& config) const +{ + KConfigGroup configGroup = config.group("General"); + + configGroup.writeEntry("Description",_description); + configGroup.writeEntry("Opacity",_opacity); + + for (int i=0 ; i < TABLE_COLORS ; i++) + { + RandomizationRange random = _randomTable != 0 ? _randomTable[i] : RandomizationRange(); + writeColorEntry(config,colorNameForIndex(i),colorTable()[i],random); + } +} + +QString ColorScheme::colorNameForIndex(int index) +{ + Q_ASSERT( index >= 0 && index < TABLE_COLORS ); + + return QString(colorNames[index]); +} +QString ColorScheme::translatedColorNameForIndex(int index) +{ + Q_ASSERT( index >= 0 && index < TABLE_COLORS ); + + return i18n(translatedColorNames[index]); +} +void ColorScheme::readColorEntry(KConfig& config , int index) +{ + KConfigGroup configGroup(&config,colorNameForIndex(index)); + + ColorEntry entry; + + entry.color = configGroup.readEntry("Color",QColor()); + entry.transparent = configGroup.readEntry("Transparent",false); + + // Deprecated key from KDE 4.0 which set 'Bold' to true to force + // a color to be bold or false to use the current format + // + // TODO - Add a new tri-state key which allows for bold, normal or + // current format + if (configGroup.hasKey("Bold")) + entry.fontWeight = configGroup.readEntry("Bold",false) ? ColorEntry::Bold : + ColorEntry::UseCurrentFormat; + + quint16 hue = configGroup.readEntry("MaxRandomHue",0); + quint8 value = configGroup.readEntry("MaxRandomValue",0); + quint8 saturation = configGroup.readEntry("MaxRandomSaturation",0); + + setColorTableEntry( index , entry ); + + if ( hue != 0 || value != 0 || saturation != 0 ) + setRandomizationRange( index , hue , saturation , value ); +} +void ColorScheme::writeColorEntry(KConfig& config , const QString& colorName, const ColorEntry& entry , const RandomizationRange& random) const +{ + KConfigGroup configGroup(&config,colorName); + + configGroup.writeEntry("Color",entry.color); + configGroup.writeEntry("Transparency",(bool)entry.transparent); + if (entry.fontWeight != ColorEntry::UseCurrentFormat) + { + configGroup.writeEntry("Bold",entry.fontWeight == ColorEntry::Bold); + } + + // record randomization if this color has randomization or + // if one of the keys already exists + if ( !random.isNull() || configGroup.hasKey("MaxRandomHue") ) + { + configGroup.writeEntry("MaxRandomHue",(int)random.hue); + configGroup.writeEntry("MaxRandomValue",(int)random.value); + configGroup.writeEntry("MaxRandomSaturation",(int)random.saturation); + } +} + + +// +// Work In Progress - A color scheme for use on KDE setups for users +// with visual disabilities which means that they may have trouble +// reading text with the supplied color schemes. +// +// This color scheme uses only the 'safe' colors defined by the +// KColorScheme class. +// +// A complication this introduces is that each color provided by +// KColorScheme is defined as a 'background' or 'foreground' color. +// Only foreground colors are allowed to be used to render text and +// only background colors are allowed to be used for backgrounds. +// +// The ColorEntry and TerminalDisplay classes do not currently +// support this restriction. +// +// Requirements: +// - A color scheme which uses only colors from the KColorScheme class +// - Ability to restrict which colors the TerminalDisplay widget +// uses as foreground and background color +// - Make use of KGlobalSettings::allowDefaultBackgroundImages() as +// a hint to determine whether this accessible color scheme should +// be used by default. +// +// +// -- Robert Knight <robertknight@gmail.com> 21/07/2007 +// +AccessibleColorScheme::AccessibleColorScheme() + : ColorScheme() +{ + // basic attributes + setName("accessible"); + setDescription(i18n("Accessible Color Scheme")); + + // setup colors + const int ColorRoleCount = 8; + + const KColorScheme colorScheme(QPalette::Active); + + QBrush colors[ColorRoleCount] = + { + colorScheme.foreground( colorScheme.NormalText ), + colorScheme.background( colorScheme.NormalBackground ), + + colorScheme.foreground( colorScheme.InactiveText ), + colorScheme.foreground( colorScheme.ActiveText ), + colorScheme.foreground( colorScheme.LinkText ), + colorScheme.foreground( colorScheme.VisitedText ), + colorScheme.foreground( colorScheme.NegativeText ), + colorScheme.foreground( colorScheme.NeutralText ) + }; + + for ( int i = 0 ; i < TABLE_COLORS ; i++ ) + { + ColorEntry entry; + entry.color = colors[ i % ColorRoleCount ].color(); + + setColorTableEntry( i , entry ); + } +} + +KDE3ColorSchemeReader::KDE3ColorSchemeReader( QIODevice* device ) : + _device(device) +{ +} +ColorScheme* KDE3ColorSchemeReader::read() +{ + Q_ASSERT( _device->openMode() == QIODevice::ReadOnly || + _device->openMode() == QIODevice::ReadWrite ); + + ColorScheme* scheme = new ColorScheme(); + + QRegExp comment("#.*$"); + while ( !_device->atEnd() ) + { + QString line(_device->readLine()); + line.remove(comment); + line = line.simplified(); + + if ( line.isEmpty() ) + continue; + + if ( line.startsWith(QLatin1String("color")) ) + { + if (!readColorLine(line,scheme)) + kWarning() << "Failed to read KDE 3 color scheme line" << line; + } + else if ( line.startsWith(QLatin1String("title")) ) + { + if (!readTitleLine(line,scheme)) + kWarning() << "Failed to read KDE 3 color scheme title line" << line; + } + else + { + kWarning() << "KDE 3 color scheme contains an unsupported feature, '" << + line << "'"; + } + } + + return scheme; +} +bool KDE3ColorSchemeReader::readColorLine(const QString& line,ColorScheme* scheme) +{ + QStringList list = line.split(QChar(' ')); + + if (list.count() != 7) + return false; + if (list.first() != "color") + return false; + + int index = list[1].toInt(); + int red = list[2].toInt(); + int green = list[3].toInt(); + int blue = list[4].toInt(); + int transparent = list[5].toInt(); + int bold = list[6].toInt(); + + const int MAX_COLOR_VALUE = 255; + + if( (index < 0 || index >= TABLE_COLORS ) + || (red < 0 || red > MAX_COLOR_VALUE ) + || (blue < 0 || blue > MAX_COLOR_VALUE ) + || (green < 0 || green > MAX_COLOR_VALUE ) + || (transparent != 0 && transparent != 1 ) + || (bold != 0 && bold != 1) ) + return false; + + ColorEntry entry; + entry.color = QColor(red,green,blue); + entry.transparent = ( transparent != 0 ); + entry.fontWeight = ( bold != 0 ) ? ColorEntry::Bold : ColorEntry::UseCurrentFormat; + + scheme->setColorTableEntry(index,entry); + return true; +} +bool KDE3ColorSchemeReader::readTitleLine(const QString& line,ColorScheme* scheme) +{ + if( !line.startsWith(QLatin1String("title")) ) + return false; + + int spacePos = line.indexOf(' '); + if( spacePos == -1 ) + return false; + + QString description = line.mid(spacePos+1); + + scheme->setDescription(i18n(description.toUtf8())); + return true; +} +ColorSchemeManager::ColorSchemeManager() + : _haveLoadedAll(false) +{ +} +ColorSchemeManager::~ColorSchemeManager() +{ + QHashIterator<QString,const ColorScheme*> iter(_colorSchemes); + while (iter.hasNext()) + { + iter.next(); + delete iter.value(); + } +} +void ColorSchemeManager::loadAllColorSchemes() +{ + int success = 0; + int failed = 0; + + QList<QString> nativeColorSchemes = listColorSchemes(); + + QListIterator<QString> nativeIter(nativeColorSchemes); + while ( nativeIter.hasNext() ) + { + if ( loadColorScheme( nativeIter.next() ) ) + success++; + else + failed++; + } + + QList<QString> kde3ColorSchemes = listKDE3ColorSchemes(); + QListIterator<QString> kde3Iter(kde3ColorSchemes); + while ( kde3Iter.hasNext() ) + { + if ( loadKDE3ColorScheme( kde3Iter.next() ) ) + success++; + else + failed++; + } + + if ( failed > 0 ) + kWarning() << "failed to load " << failed << " color schemes."; + + _haveLoadedAll = true; +} +QList<const ColorScheme*> ColorSchemeManager::allColorSchemes() +{ + if ( !_haveLoadedAll ) + { + loadAllColorSchemes(); + } + + return _colorSchemes.values(); +} +bool ColorSchemeManager::loadKDE3ColorScheme(const QString& filePath) +{ + QFile file(filePath); + if (!filePath.endsWith(QLatin1String(".schema")) || !file.open(QIODevice::ReadOnly)) + return false; + + KDE3ColorSchemeReader reader(&file); + ColorScheme* scheme = reader.read(); + scheme->setName(QFileInfo(file).baseName()); + file.close(); + + if (scheme->name().isEmpty()) + { + kWarning() << "color scheme name is not valid."; + delete scheme; + return false; + } + + QFileInfo info(filePath); + + if ( !_colorSchemes.contains(info.baseName()) ) + _colorSchemes.insert(scheme->name(),scheme); + else + { + kWarning() << "color scheme with name" << scheme->name() << "has already been" << + "found, ignoring."; + delete scheme; + } + + return true; +} +void ColorSchemeManager::addColorScheme(ColorScheme* scheme) +{ + _colorSchemes.insert(scheme->name(),scheme); + + // save changes to disk + QString path = KGlobal::dirs()->saveLocation("data","konsole/") + scheme->name() + ".colorscheme"; + KConfig config(path , KConfig::NoGlobals); + + scheme->write(config); +} +bool ColorSchemeManager::loadColorScheme(const QString& filePath) +{ + if ( !filePath.endsWith(QLatin1String(".colorscheme")) || !QFile::exists(filePath) ) + return false; + + QFileInfo info(filePath); + + KConfig config(filePath , KConfig::NoGlobals); + ColorScheme* scheme = new ColorScheme(); + scheme->setName(info.baseName()); + scheme->read(config); + + if (scheme->name().isEmpty()) + { + kWarning() << "Color scheme in" << filePath << "does not have a valid name and was not loaded."; + delete scheme; + return false; + } + + if ( !_colorSchemes.contains(info.baseName()) ) + { + _colorSchemes.insert(scheme->name(),scheme); + } + else + { + kWarning() << "color scheme with name" << scheme->name() << "has already been" << + "found, ignoring."; + + delete scheme; + } + + return true; +} +QList<QString> ColorSchemeManager::listKDE3ColorSchemes() +{ + return KGlobal::dirs()->findAllResources("data", + "konsole/*.schema", + KStandardDirs::NoDuplicates); + +} +QList<QString> ColorSchemeManager::listColorSchemes() +{ + return KGlobal::dirs()->findAllResources("data", + "konsole/*.colorscheme", + KStandardDirs::NoDuplicates); +} +const ColorScheme ColorSchemeManager::_defaultColorScheme; +const ColorScheme* ColorSchemeManager::defaultColorScheme() const +{ + return &_defaultColorScheme; +} +bool ColorSchemeManager::deleteColorScheme(const QString& name) +{ + Q_ASSERT( _colorSchemes.contains(name) ); + + // lookup the path and delete + QString path = findColorSchemePath(name); + if ( QFile::remove(path) ) + { + _colorSchemes.remove(name); + return true; + } + else + { + kWarning() << "Failed to remove color scheme -" << path; + return false; + } +} +QString ColorSchemeManager::findColorSchemePath(const QString& name) const +{ + QString path = KStandardDirs::locate("data","konsole/"+name+".colorscheme"); + + if ( !path.isEmpty() ) + return path; + + path = KStandardDirs::locate("data","konsole/"+name+".schema"); + + return path; +} +const ColorScheme* ColorSchemeManager::findColorScheme(const QString& name) +{ + if ( name.isEmpty() ) + return defaultColorScheme(); + + if ( _colorSchemes.contains(name) ) + return _colorSchemes[name]; + else + { + // look for this color scheme + QString path = findColorSchemePath(name); + if ( !path.isEmpty() && loadColorScheme(path) ) + { + return findColorScheme(name); + } + else + { + if (!path.isEmpty() && loadKDE3ColorScheme(path)) + return findColorScheme(name); + } + + kWarning() << "Could not find color scheme - " << name; + + return 0; + } +} +K_GLOBAL_STATIC( ColorSchemeManager , theColorSchemeManager ) +ColorSchemeManager* ColorSchemeManager::instance() +{ + return theColorSchemeManager; +}
new file mode 100644 --- /dev/null +++ b/gui/konsole/ColorScheme.h @@ -0,0 +1,334 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2007-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. +*/ + +#ifndef COLORSCHEME_H +#define COLORSCHEME_H + +// Qt +#include <QtCore/QHash> +#include <QtCore/QList> +#include <QtCore/QMetaType> +#include <QtCore/QIODevice> +#include <QtCore/QSet> + +// Konsole +#include "CharacterColor.h" + +class QIODevice; +class KConfig; + +namespace Konsole +{ + +/** + * Represents a color scheme for a terminal display. + * + * The color scheme includes the palette of colors used to draw the text and character backgrounds + * in the display and the opacity level of the display background. + */ +class ColorScheme +{ +public: + /** + * Constructs a new color scheme which is initialised to the default color set + * for Konsole. + */ + ColorScheme(); + ColorScheme(const ColorScheme& other); + ~ColorScheme(); + + /** Sets the descriptive name of the color scheme. */ + void setDescription(const QString& description); + /** Returns the descriptive name of the color scheme. */ + QString description() const; + + /** Sets the name of the color scheme */ + void setName(const QString& name); + /** Returns the name of the color scheme */ + QString name() const; + + /** Reads the color scheme from the specified configuration source */ + void read(KConfig& config); + /** Writes the color scheme to the specified configuration source */ + void write(KConfig& config) const; + + /** Sets a single entry within the color palette. */ + void setColorTableEntry(int index , const ColorEntry& entry); + + /** + * Copies the color entries which form the palette for this color scheme + * into @p table. @p table should be an array with TABLE_COLORS entries. + * + * @param table Array into which the color entries for this color scheme + * are copied. + * @param randomSeed Color schemes may allow certain colors in their + * palette to be randomized. The seed is used to pick the random color. + */ + void getColorTable(ColorEntry* table, uint randomSeed = 0) const; + + /** + * Retrieves a single color entry from the table. + * + * See getColorTable() + */ + ColorEntry colorEntry(int index , uint randomSeed = 0) const; + + /** + * Convenience method. Returns the + * foreground color for this scheme, + * this is the primary color used to draw the + * text in this scheme. + */ + QColor foregroundColor() const; + /** + * Convenience method. Returns the background color for + * this scheme, this is the primary color used to + * draw the terminal background in this scheme. + */ + QColor backgroundColor() const; + + /** + * Returns true if this color scheme has a dark background. + * The background color is said to be dark if it has a value of less than 127 + * in the HSV color space. + */ + bool hasDarkBackground() const; + + /** + * Sets the opacity level of the display background. @p opacity ranges + * between 0 (completely transparent background) and 1 (completely + * opaque background). + * + * Defaults to 1. + * + * TODO: More documentation + */ + void setOpacity(qreal opacity); + /** + * Returns the opacity level for this color scheme, see setOpacity() + * TODO: More documentation + */ + qreal opacity() const; + + /** + * Enables randomization of the background color. This will cause + * the palette returned by getColorTable() and colorEntry() to + * be adjusted depending on the value of the random seed argument + * to them. + */ + void setRandomizedBackgroundColor(bool randomize); + + /** Returns true if the background color is randomized. */ + bool randomizedBackgroundColor() const; + + static QString colorNameForIndex(int index); + static QString translatedColorNameForIndex(int index); + +private: + // specifies how much a particular color can be randomized by + class RandomizationRange + { + public: + RandomizationRange() : hue(0) , saturation(0) , value(0) {} + + bool isNull() const + { + return ( hue == 0 && saturation == 0 && value == 0 ); + } + + quint16 hue; + quint8 saturation; + quint8 value; + }; + + // returns the active color table. if none has been set specifically, + // this is the default color table. + const ColorEntry* colorTable() const; + + // reads a single colour entry from a KConfig source + // and sets the palette entry at 'index' to the entry read. + void readColorEntry(KConfig& config , int index); + // writes a single colour entry to a KConfig source + void writeColorEntry(KConfig& config , const QString& colorName, const ColorEntry& entry,const RandomizationRange& range) const; + + // sets the amount of randomization allowed for a particular color + // in the palette. creates the randomization table if + // it does not already exist + void setRandomizationRange( int index , quint16 hue , quint8 saturation , quint8 value ); + + QString _description; + QString _name; + qreal _opacity; + ColorEntry* _table; // pointer to custom color table or 0 if the default + // color scheme is being used + + + static const quint16 MAX_HUE = 340; + + RandomizationRange* _randomTable; // pointer to randomization table or 0 + // if no colors in the color scheme support + // randomization + + static const char* const colorNames[TABLE_COLORS]; + static const char* const translatedColorNames[TABLE_COLORS]; + + static const ColorEntry defaultTable[]; // table of default color entries +}; + +/** + * A color scheme which uses colors from the standard KDE color palette. + * + * This is designed primarily for the benefit of users who are using specially + * designed colors. + * + * TODO Implement and make it the default on systems with specialized KDE + * color schemes. + */ +class AccessibleColorScheme : public ColorScheme +{ +public: + AccessibleColorScheme(); +}; + +/** + * Reads a color scheme stored in the .schema format used in the KDE 3 incarnation + * of Konsole + * + * Only the basic essentials ( title and color palette entries ) are currently + * supported. Additional options such as background image and background + * blend colors are ignored. + */ +class KDE3ColorSchemeReader +{ +public: + /** + * Constructs a new reader which reads from the specified device. + * The device should be open in read-only mode. + */ + KDE3ColorSchemeReader( QIODevice* device ); + + /** + * Reads and parses the contents of the .schema file from the input + * device and returns the ColorScheme defined within it. + * + * Returns a null pointer if an error occurs whilst parsing + * the contents of the file. + */ + ColorScheme* read(); + +private: + // reads a line from the file specifying a colour palette entry + // format is: color [index] [red] [green] [blue] [transparent] [bold] + bool readColorLine(const QString& line , ColorScheme* scheme); + bool readTitleLine(const QString& line , ColorScheme* scheme); + + QIODevice* _device; +}; + +/** + * Manages the color schemes available for use by terminal displays. + * See ColorScheme + */ +class ColorSchemeManager +{ +public: + + /** + * Constructs a new ColorSchemeManager and loads the list + * of available color schemes. + * + * The color schemes themselves are not loaded until they are first + * requested via a call to findColorScheme() + */ + ColorSchemeManager(); + /** + * Destroys the ColorSchemeManager and saves any modified color schemes to disk. + */ + ~ColorSchemeManager(); + + /** + * Returns the default color scheme for Konsole + */ + const ColorScheme* defaultColorScheme() const; + + /** + * Returns the color scheme with the given name or 0 if no + * scheme with that name exists. If @p name is empty, the + * default color scheme is returned. + * + * The first time that a color scheme with a particular name is + * requested, the configuration information is loaded from disk. + */ + const ColorScheme* findColorScheme(const QString& name); + + /** + * Adds a new color scheme to the manager. If @p scheme has the same name as + * an existing color scheme, it replaces the existing scheme. + * + * TODO - Ensure the old color scheme gets deleted + */ + void addColorScheme(ColorScheme* scheme); + + /** + * Deletes a color scheme. Returns true on successful deletion or false otherwise. + */ + bool deleteColorScheme(const QString& name); + + /** + * Returns a list of the all the available color schemes. + * This may be slow when first called because all of the color + * scheme resources on disk must be located, read and parsed. + * + * Subsequent calls will be inexpensive. + */ + QList<const ColorScheme*> allColorSchemes(); + + /** Returns the global color scheme manager instance. */ + static ColorSchemeManager* instance(); + +private: + // loads a color scheme from a KDE 4+ .colorscheme file + bool loadColorScheme(const QString& path); + // loads a color scheme from a KDE 3 .schema file + bool loadKDE3ColorScheme(const QString& path); + // returns a list of paths of color schemes in the KDE 4+ .colorscheme file format + QList<QString> listColorSchemes(); + // returns a list of paths of color schemes in the .schema file format + // used in KDE 3 + QList<QString> listKDE3ColorSchemes(); + // loads all of the color schemes + void loadAllColorSchemes(); + // finds the path of a color scheme + QString findColorSchemePath(const QString& name) const; + + QHash<QString,const ColorScheme*> _colorSchemes; + QSet<ColorScheme*> _modifiedSchemes; + + bool _haveLoadedAll; + + static const ColorScheme _defaultColorScheme; +}; + +} + +Q_DECLARE_METATYPE(const Konsole::ColorScheme*) + +#endif //COLORSCHEME_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/ColorSchemeEditor.cpp @@ -0,0 +1,198 @@ +/* + Copyright 2007-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 "ColorSchemeEditor.h" + +// Qt +#include <QtGui/QBrush> +#include <QtGui/QFontMetrics> +#include <QtGui/QHeaderView> +#include <QtGui/QItemDelegate> +#include <QtGui/QItemEditorCreator> + +// KDE +#include <KColorDialog> +#include <KDebug> +#include <KWindowSystem> + +// Konsole +#include "ui_ColorSchemeEditor.h" +#include "CharacterColor.h" + +using namespace Konsole; + +#if 0 +class ColorEditorCreator : public QItemEditorCreatorBase +{ + virtual QWidget* createWidget(QWidget* parent) const + { + return new KColorButton(parent); + } + + virtual QByteArray valuePropertyName() const + { + return QByteArray("color"); + } +}; +#endif + +ColorSchemeEditor::ColorSchemeEditor(QWidget* parent) + : QWidget(parent) + , _colors(0) +{ + _ui = new Ui::ColorSchemeEditor(); + _ui->setupUi(this); + + // description edit + _ui->descriptionEdit->setClearButtonShown(true); + connect( _ui->descriptionEdit , SIGNAL(textChanged(const QString&)) , this , + SLOT(setDescription(const QString&)) ); + + // transparency slider + QFontMetrics metrics(font()); + _ui->transparencyPercentLabel->setMinimumWidth( metrics.width("100%") ); + + connect( _ui->transparencySlider , SIGNAL(valueChanged(int)) , this , SLOT(setTransparencyPercentLabel(int)) ); + + // randomized background + connect( _ui->randomizedBackgroundCheck , SIGNAL(toggled(bool)) , this , + SLOT(setRandomizedBackgroundColor(bool)) ); + + // color table + _ui->colorTable->setColumnCount(2); + _ui->colorTable->setRowCount(TABLE_COLORS); + + QStringList labels; + labels << i18nc("@label:listbox Column header text for color names", "Name") << i18nc("@label:listbox Column header text for the actual colors", "Color"); + _ui->colorTable->setHorizontalHeaderLabels(labels); + + _ui->colorTable->horizontalHeader()->setStretchLastSection(true); + + QTableWidgetItem* item = new QTableWidgetItem("Test"); + _ui->colorTable->setItem(0,0,item); + + _ui->colorTable->verticalHeader()->hide(); + + connect( _ui->colorTable , SIGNAL(itemClicked(QTableWidgetItem*)) , this , + SLOT(editColorItem(QTableWidgetItem*)) ); + + // warning label when transparency is not available + if ( KWindowSystem::compositingActive() ) + { + _ui->transparencyWarningWidget->setVisible(false); + } + else + { + _ui->transparencyWarningWidget->setText(i18nc("@info:status", + "The background transparency setting will not" + " be used because your desktop does not appear to support" + " transparent windows.")); + } +} +void ColorSchemeEditor::setRandomizedBackgroundColor( bool randomize ) +{ + _colors->setRandomizedBackgroundColor(randomize); +} +ColorSchemeEditor::~ColorSchemeEditor() +{ + delete _colors; + delete _ui; +} +void ColorSchemeEditor::editColorItem( QTableWidgetItem* item ) +{ + // ignore if this is not a color column + if ( item->column() != 1 ) + return; + + QColor color = item->background().color(); + int result = KColorDialog::getColor( color ); + if ( result == KColorDialog::Accepted ) { + item->setBackground( color ); + + ColorEntry entry( _colors->colorEntry(item->row()) ); + entry.color = color; + _colors->setColorTableEntry( item->row(), entry ); + + emit colorsChanged( _colors ); + + } +} +void ColorSchemeEditor::setDescription(const QString& text) +{ + if ( _colors ) + _colors->setDescription(text); + + if ( _ui->descriptionEdit->text() != text ) + _ui->descriptionEdit->setText(text); +} +void ColorSchemeEditor::setTransparencyPercentLabel(int percent) +{ + _ui->transparencyPercentLabel->setText( QString("%1%").arg(percent) ); + + qreal opacity = ( 100.0 - percent ) / 100.0; + _colors->setOpacity(opacity); +} +void ColorSchemeEditor::setup(const ColorScheme* scheme) +{ + if ( _colors ) + delete _colors; + + _colors = new ColorScheme(*scheme); + + // setup description edit + _ui->descriptionEdit->setText(_colors->description()); + + // setup color table + setupColorTable(_colors); + + // setup transparency slider + const int transparencyPercent = qRound( (1-_colors->opacity())*100 ); + + _ui->transparencySlider->setValue(transparencyPercent); + setTransparencyPercentLabel(transparencyPercent); + + // randomized background color checkbox + _ui->randomizedBackgroundCheck->setChecked( scheme->randomizedBackgroundColor() ); +} +void ColorSchemeEditor::setupColorTable(const ColorScheme* colors) +{ + ColorEntry table[TABLE_COLORS]; + colors->getColorTable(table); + + for ( int row = 0 ; row < TABLE_COLORS ; row++ ) + { + QTableWidgetItem* nameItem = new QTableWidgetItem( ColorScheme::translatedColorNameForIndex(row) ); + QTableWidgetItem* colorItem = new QTableWidgetItem(); + colorItem->setBackground( table[row].color ); + colorItem->setFlags( colorItem->flags() & ~Qt::ItemIsEditable & ~Qt::ItemIsSelectable ); + + _ui->colorTable->setItem(row,0,nameItem); + _ui->colorTable->setItem(row,1,colorItem); + } + + // ensure that color names are as fully visible as possible + _ui->colorTable->resizeColumnToContents(0); +} +ColorScheme* ColorSchemeEditor::colorScheme() const +{ + return _colors; +} + +#include "ColorSchemeEditor.moc"
new file mode 100644 --- /dev/null +++ b/gui/konsole/ColorSchemeEditor.h @@ -0,0 +1,89 @@ +/* + Copyright 2007-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. +*/ + +#ifndef COLORSCHEMEEDITOR_H +#define COLORSCHEMEEDITOR_H + +// Qt +#include <QtGui/QWidget> + +// Konsole +#include "ColorScheme.h" + +class QTableWidgetItem; + +namespace Ui +{ + class ColorSchemeEditor; +} + +namespace Konsole +{ + +class ColorScheme; + +/** + * A dialog for editing color schemes. + * + * After creation, the dialog can be initialised with the settings + * of a color scheme using the setup() method. + * + * The dialog creates a copy of the supplied color scheme to which + * any changes made are applied. The modified color scheme + * can be retrieved using the colorScheme() method. + * + * When changes are made the colorsChanged() signal is emitted. + */ +class ColorSchemeEditor : public QWidget +{ +Q_OBJECT + +public: + /** Constructs a new color scheme editor with the specified parent. */ + ColorSchemeEditor(QWidget* parent = 0); + virtual ~ColorSchemeEditor(); + + /** Initialises the dialog with the properties of the specified color scheme. */ + void setup(const ColorScheme* scheme); + /** Returns the modified color scheme. */ + ColorScheme* colorScheme() const; + +signals: + /** Emitted when the colors in the color scheme change. */ + void colorsChanged(ColorScheme* scheme); + +public slots: + /** Sets the text displayed in the description edit field. */ + void setDescription(const QString& description); + +private slots: + void setTransparencyPercentLabel(int percent); + void setRandomizedBackgroundColor(bool randomized); + void editColorItem(QTableWidgetItem* item); + +private: + void setupColorTable(const ColorScheme* table); + + Ui::ColorSchemeEditor* _ui; + ColorScheme* _colors; +}; + +} + +#endif // COLORSCHEMEEDITOR_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/CopyInputDialog.cpp @@ -0,0 +1,200 @@ +/* + Copyright 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 "CopyInputDialog.h" + +// Qt +#include <QtGui/QSortFilterProxyModel> +#include <QtGui/QHeaderView> + +// Konsole +#include "ui_CopyInputDialog.h" + +using namespace Konsole; + +CopyInputDialog::CopyInputDialog(QWidget* parent) +: KDialog(parent) +{ + setCaption(i18n("Copy Input")); + setButtons( KDialog::Ok | KDialog::Cancel ); + + _ui = new Ui::CopyInputDialog(); + _ui->setupUi(mainWidget()); + + connect(_ui->selectAllButton,SIGNAL(clicked()),this,SLOT(selectAll())); + connect(_ui->deselectAllButton,SIGNAL(clicked()),this,SLOT(deselectAll())); + + _ui->filterEdit->setClearButtonShown(true); + _ui->filterEdit->setFocus(); + + _model = new CheckableSessionModel(parent); + _model->setCheckColumn(1); + _model->setSessions(SessionManager::instance()->sessions()); + + QSortFilterProxyModel* filterProxyModel = new QSortFilterProxyModel(this); + filterProxyModel->setDynamicSortFilter(true); + filterProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + filterProxyModel->setSourceModel(_model); + filterProxyModel->setFilterKeyColumn(-1); + + connect(_ui->filterEdit,SIGNAL(textChanged(QString)),filterProxyModel, + SLOT(setFilterFixedString(QString))); + + _ui->sessionList->setModel(filterProxyModel); + _ui->sessionList->setColumnHidden(0,true); // Hide number column + _ui->sessionList->header()->hide(); +} + +CopyInputDialog::~CopyInputDialog() +{ + delete _ui; +} + +void CopyInputDialog::setChosenSessions(const QSet<Session*>& sessions) +{ + QSet<Session*> checked = sessions; + if (_masterSession) + checked.insert(_masterSession); + + _model->setCheckedSessions(checked); +} +QSet<Session*> CopyInputDialog::chosenSessions() const +{ + return _model->checkedSessions(); +} +void CopyInputDialog::setMasterSession(Session* session) +{ + if (_masterSession) + _model->setCheckable(_masterSession,true); + + _model->setCheckable(session,false); + QSet<Session*> checked = _model->checkedSessions(); + checked.insert(session); + _model->setCheckedSessions(checked); + + _masterSession = session; +} +void CopyInputDialog::setSelectionChecked(bool checked) +{ + QAbstractItemModel* model = _ui->sessionList->model(); + int rows = model->rowCount(); + + QModelIndexList selected = _ui->sessionList->selectionModel()->selectedIndexes(); + + if (selected.count() > 1) + { + foreach(const QModelIndex &index,selected) + setRowChecked(index.row(),checked); + } + else + { + for (int i=0;i<rows;i++) + setRowChecked(i,checked); + } +} +void CopyInputDialog::setRowChecked(int row, bool checked) +{ + QAbstractItemModel* model = _ui->sessionList->model(); + QModelIndex index = model->index(row,_model->checkColumn()); + if (checked) + model->setData(index,(int)Qt::Checked,Qt::CheckStateRole); + else + model->setData(index,(int)Qt::Unchecked,Qt::CheckStateRole); +} +CheckableSessionModel::CheckableSessionModel(QObject* parent) +: SessionListModel(parent) +, _checkColumn(0) +{ +} +void CheckableSessionModel::setCheckColumn(int column) +{ + _checkColumn = column; + reset(); +} +Qt::ItemFlags CheckableSessionModel::flags(const QModelIndex& index) const +{ + Session* session = (Session*)index.internalPointer(); + + if (_fixedSessions.contains(session)) + return SessionListModel::flags(index) & ~Qt::ItemIsEnabled; + else + return SessionListModel::flags(index) | Qt::ItemIsUserCheckable; +} +QVariant CheckableSessionModel::data(const QModelIndex& index, int role) const +{ + if (role == Qt::CheckStateRole && index.column() == _checkColumn) + { + Session* session = (Session*)index.internalPointer(); + + if (_checkedSessions.contains(session)) + return QVariant::fromValue((int)Qt::Checked); + else + return QVariant::fromValue((int)Qt::Unchecked); + } + else + return SessionListModel::data(index,role); +} +bool CheckableSessionModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + if (role == Qt::CheckStateRole && index.column() == _checkColumn) + { + Session* session = (Session*)index.internalPointer(); + + if (_fixedSessions.contains(session)) + return false; + + if (value.value<int>() == Qt::Checked) + _checkedSessions.insert(session); + else + _checkedSessions.remove(session); + + emit dataChanged(index,index); + return true; + } + else + return SessionListModel::setData(index,value,role); +} +void CheckableSessionModel::setCheckedSessions(const QSet<Session*> sessions) +{ + _checkedSessions = sessions; + reset(); +} +QSet<Session*> CheckableSessionModel::checkedSessions() const +{ + return _checkedSessions; +} +void CheckableSessionModel::setCheckable(Session* session, bool checkable) +{ + if (!checkable) + _fixedSessions.insert(session); + else + _fixedSessions.remove(session); + + reset(); +} +void CheckableSessionModel::sessionRemoved(Session* session) +{ + _checkedSessions.remove(session); + _fixedSessions.remove(session); +} + + + +
new file mode 100644 --- /dev/null +++ b/gui/konsole/CopyInputDialog.h @@ -0,0 +1,129 @@ +/* + Copyright 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. +*/ + +#ifndef COPYINPUTDIALOG +#define COPYINPUTDIALOG + +// Qt +#include <QPointer> + +// KDE +#include <KDialog> + +// Konsole +#include "SessionManager.h" +#include "Session.h" + +namespace Ui +{ + class CopyInputDialog; +} + +namespace Konsole +{ +class CheckableSessionModel; + +/** + * Dialog which allows the user to mark a list of sessions to copy + * the input from the current session to. The current session is + * set using setMasterSession(). After the dialog has been executed, + * the set of chosen sessions can be retrieved using chosenSessions() + */ +class CopyInputDialog : public KDialog +{ +Q_OBJECT + +public: + CopyInputDialog(QWidget* parent = 0); + ~CopyInputDialog(); + /** + * Sets the 'source' session whoose input will be copied to + * other sessions. This session is displayed grayed out in the list + * and cannot be unchecked. + */ + void setMasterSession(Session* master); + /** See setMasterSession() */ + Session* masterSession() const; + + /** Sets the sessions in the list which are checked. */ + void setChosenSessions(const QSet<Session*>& sessions); + /** Set setChosenSessions() */ + QSet<Session*> chosenSessions() const; + +private slots: + void selectAll() { setSelectionChecked(true); }; + void deselectAll() { setSelectionChecked(false); }; + +private: + // Checks or unchecks selected sessions. If there are no + // selected items then all sessions are checked or unchecked + void setSelectionChecked(bool checked); + void setRowChecked(int row, bool checked); + + Ui::CopyInputDialog* _ui; + CheckableSessionModel* _model; + QPointer<Session> _masterSession; +}; + +/** + * A list of sessions with a checkbox next to each one which allows the + * user to select a subset of the available sessions to perform + * some action on them. + */ +class CheckableSessionModel : public SessionListModel +{ +Q_OBJECT + +public: + CheckableSessionModel(QObject* parent); + + void setCheckColumn(int column); + int checkColumn() const; + + /** + * Sets whether a session can be checked or un-checked. + * Non-checkable items have the Qt::ItemIsEnabled flag unset. + */ + void setCheckable(Session* session, bool checkable); + + /** Sets the list of sessions which are currently checked. */ + void setCheckedSessions(const QSet<Session*> sessions); + /** Returns the set of checked sessions. */ + QSet<Session*> checkedSessions() const; + + // reimplemented from QAbstractItemModel + virtual Qt::ItemFlags flags(const QModelIndex& index) const; + virtual QVariant data(const QModelIndex& index, int role) const; + virtual bool setData(const QModelIndex& index, const QVariant& value, int role); + +protected: + virtual void sessionRemoved(Session*); + +private: + QSet<Session*> _checkedSessions; + QSet<Session*> _fixedSessions; + int _checkColumn; +}; +inline int CheckableSessionModel::checkColumn() const +{ return _checkColumn; } + +} + +#endif // COPYINPUTDIALOG +
new file mode 100644 --- /dev/null +++ b/gui/konsole/EditProfileDialog.cpp @@ -0,0 +1,1289 @@ +/* + Copyright 2007-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 "EditProfileDialog.h" + +// Qt +#include <QtGui/QKeyEvent> +#include <QtGui/QBrush> +#include <QtGui/QPainter> +#include <QtGui/QStandardItem> +#include <QtCore/QTextCodec> +#include <QtGui/QLinearGradient> +#include <QtGui/QRadialGradient> + +#include <QtCore/QTimer> +#include <QtCore/QTimeLine> + +// KDE +#include <kcodecaction.h> +#include <KDebug> +#include <KFontDialog> +#include <KIcon> +#include <KIconDialog> +#include <KFileDialog> +#include <KUrlCompletion> +#include <KWindowSystem> +#include <KTextEdit> + +#include <cmath> + +// Konsole +#include "ColorScheme.h" +#include "ColorSchemeEditor.h" +#include "ui_EditProfileDialog.h" +#include "KeyBindingEditor.h" +#include "KeyboardTranslator.h" +#include "SessionManager.h" +#include "ShellCommand.h" +#include "TabTitleFormatAction.h" + +using namespace Konsole; + +EditProfileDialog::EditProfileDialog(QWidget* parent) + : KDialog(parent) + , _colorSchemeAnimationTimeLine(0) + , _delayedPreviewTimer(new QTimer(this)) +{ + setCaption(i18n("Edit Profile")); + setButtons( KDialog::Ok | KDialog::Cancel | KDialog::Apply ); + + connect( this , SIGNAL(applyClicked()) , this , SLOT(save()) ); + connect( _delayedPreviewTimer , SIGNAL(timeout()) , this , SLOT(delayedPreviewActivate()) ); + _ui = new Ui::EditProfileDialog(); + _ui->setupUi(mainWidget()); + + // - Renable in a later KDE 4.x release when this feature works again + _ui->enableResizeWindowButton->setVisible(false); + + // there are various setupXYZPage() methods to load the items + // for each page and update their states to match the profile + // being edited. + // + // these are only called when needed ( ie. when the user clicks + // the tab to move to that page ). + // + // the _pageNeedsUpdate vector keeps track of the pages that have + // not been updated since the last profile change and will need + // to be refreshed when the user switches to them + _pageNeedsUpdate.resize( _ui->tabWidget->count() ); + connect( _ui->tabWidget , SIGNAL(currentChanged(int)) , this , + SLOT(preparePage(int)) ); + + _tempProfile = new Profile; + _tempProfile->setHidden(true); +} +EditProfileDialog::~EditProfileDialog() +{ + delete _ui; +} +void EditProfileDialog::save() +{ + if ( _tempProfile->isEmpty() ) + return; + + SessionManager::instance()->changeProfile(_profile,_tempProfile->setProperties()); + + // ensure that these settings are not undone by a call + // to unpreview() + QHashIterator<Profile::Property,QVariant> iter(_tempProfile->setProperties()); + while ( iter.hasNext() ) + { + iter.next(); + _previewedProperties.remove(iter.key()); + } +} +void EditProfileDialog::reject() +{ + unpreviewAll(); + KDialog::reject(); +} +void EditProfileDialog::accept() +{ + save(); + unpreviewAll(); + KDialog::accept(); +} +QString EditProfileDialog::groupProfileNames(const ProfileGroup::Ptr group, int maxLength) +{ + QString caption; + int count = group->profiles().count(); + for (int i=0;i < count;i++) + { + caption += group->profiles()[i]->name(); + if (i < (count-1)) + { + caption += ','; + // limit caption length to prevent very long window titles + if (maxLength > 0 && caption.length() > maxLength) + { + caption += "..."; + break; + } + } + } + return caption; +} +void EditProfileDialog::updateCaption(const Profile::Ptr profile) +{ + const int MAX_GROUP_CAPTION_LENGTH = 25; + ProfileGroup::Ptr group = profile->asGroup(); + if (group && group->profiles().count() > 1) + { + QString caption = groupProfileNames(group,MAX_GROUP_CAPTION_LENGTH); + setCaption( i18np("Editing profile: %2","Editing %1 profiles: %2",group->profiles().count(), caption) ); + } + else + setCaption( i18n("Edit Profile \"%1\"",profile->name()) ); +} +void EditProfileDialog::setProfile(Profile::Ptr profile) +{ + _profile = profile; + + Q_ASSERT( profile ); + + // update caption + updateCaption(profile); + + // mark each page of the dialog as out of date + // and force an update of the currently visible page + // + // the other pages will be updated as necessary + _pageNeedsUpdate.fill(true); + preparePage( _ui->tabWidget->currentIndex() ); + + if ( _tempProfile ) + { + _tempProfile = new Profile; + } +} +const Profile::Ptr EditProfileDialog::lookupProfile() const +{ + return _profile; +} +void EditProfileDialog::preparePage(int page) +{ + const Profile::Ptr info = lookupProfile(); + + Q_ASSERT( _pageNeedsUpdate.count() > page ); + Q_ASSERT( info ); + + QWidget* pageWidget = _ui->tabWidget->widget(page); + + if ( _pageNeedsUpdate[page] ) + { + if ( pageWidget == _ui->generalTab ) + setupGeneralPage(info); + else if ( pageWidget == _ui->tabsTab ) + setupTabsPage(info); + else if ( pageWidget == _ui->appearanceTab ) + setupAppearancePage(info); + else if ( pageWidget == _ui->scrollingTab ) + setupScrollingPage(info); + else if ( pageWidget == _ui->keyboardTab ) + setupKeyboardPage(info); + else if ( pageWidget == _ui->advancedTab ) + setupAdvancedPage(info); + else + Q_ASSERT(false); + + _pageNeedsUpdate[page] = false; + } + + // start page entry animation for color schemes + if ( pageWidget == _ui->appearanceTab ) + _colorSchemeAnimationTimeLine->start(); +} +void EditProfileDialog::selectProfileName() +{ + _ui->profileNameEdit->selectAll(); + _ui->profileNameEdit->setFocus(); +} +void EditProfileDialog::setupGeneralPage(const Profile::Ptr info) +{ + // basic profile options + { + ProfileGroup::Ptr group = info->asGroup(); + if (!group || group->profiles().count() < 2) + { + _ui->profileNameEdit->setClearButtonShown(true); + _ui->profileNameEdit->setText( info->name() ); + } + else + { + _ui->profileNameEdit->setText( groupProfileNames(group,-1) ); + _ui->profileNameLabel->setEnabled(false); + _ui->profileNameEdit->setEnabled(false); + } + } + + ShellCommand command( info->command() , info->arguments() ); + _ui->commandEdit->setText( command.fullCommand() ); + + KUrlCompletion* exeCompletion = new KUrlCompletion(KUrlCompletion::ExeCompletion); + exeCompletion->setParent(this); + exeCompletion->setDir(QString()); + _ui->commandEdit->setCompletionObject( exeCompletion ); + _ui->initialDirEdit->setText( info->defaultWorkingDirectory() ); + + KUrlCompletion* dirCompletion = new KUrlCompletion(KUrlCompletion::DirCompletion); + dirCompletion->setParent(this); + _ui->initialDirEdit->setCompletionObject( dirCompletion ); + _ui->initialDirEdit->setClearButtonShown(true); + _ui->dirSelectButton->setIcon( KIcon("folder-open") ); + _ui->iconSelectButton->setIcon( KIcon(info->icon()) ); + _ui->startInSameDirButton->setChecked(info->property<bool>(Profile::StartInCurrentSessionDir)); + + // window options + _ui->showMenuBarButton->setChecked( info->property<bool>(Profile::ShowMenuBar) ); + _ui->saveGeometryOnExitButton->setChecked( info->property<bool>(Profile::SaveGeometryOnExit) ); + + // signals and slots + connect( _ui->dirSelectButton , SIGNAL(clicked()) , this , SLOT(selectInitialDir()) ); + connect( _ui->iconSelectButton , SIGNAL(clicked()) , this , SLOT(selectIcon()) ); + connect( _ui->startInSameDirButton , SIGNAL(toggled(bool)) , this , + SLOT(startInSameDir(bool))); + connect( _ui->profileNameEdit , SIGNAL(textChanged(const QString&)) , this , + SLOT(profileNameChanged(const QString&)) ); + connect( _ui->initialDirEdit , SIGNAL(textChanged(const QString&)) , this , + SLOT(initialDirChanged(const QString&)) ); + connect(_ui->commandEdit , SIGNAL(textChanged(const QString&)) , this , + SLOT(commandChanged(const QString&)) ); + + connect(_ui->showMenuBarButton , SIGNAL(toggled(bool)) , this , + SLOT(showMenuBar(bool)) ); + connect(_ui->saveGeometryOnExitButton , SIGNAL(toggled(bool)) , this , + SLOT(saveGeometryOnExit(bool)) ); + + connect(_ui->environmentEditButton , SIGNAL(clicked()) , this , + SLOT(showEnvironmentEditor()) ); +} +void EditProfileDialog::showEnvironmentEditor() +{ + const Profile::Ptr info = lookupProfile(); + + KDialog* dialog = new KDialog(this); + KTextEdit* edit = new KTextEdit(dialog); + + QStringList currentEnvironment = info->property<QStringList>(Profile::Environment); + + edit->setPlainText( currentEnvironment.join("\n") ); + dialog->setPlainCaption(i18n("Edit Environment")); + dialog->setMainWidget(edit); + + if ( dialog->exec() == QDialog::Accepted ) + { + QStringList newEnvironment = edit->toPlainText().split('\n'); + _tempProfile->setProperty(Profile::Environment,newEnvironment); + } + + dialog->deleteLater(); +} +void EditProfileDialog::setupTabsPage(const Profile::Ptr info) +{ + // tab title format + _ui->tabTitleEdit->setClearButtonShown(true); + _ui->remoteTabTitleEdit->setClearButtonShown(true); + _ui->tabTitleEdit->setText( info->property<QString>(Profile::LocalTabTitleFormat) ); + _ui->remoteTabTitleEdit->setText( + info->property<QString>(Profile::RemoteTabTitleFormat)); + + // tab options + int tabMode = info->property<int>(Profile::TabBarMode); + int tabPosition = info->property<int>(Profile::TabBarPosition); + + // note: Items should be in the same order as the + // Profile::TabBarModeEnum enum + _ui->tabBarVisibilityCombo->addItems( QStringList() << i18n("Always Hide Tab Bar") + << i18n("Show Tab Bar When Needed") + << i18n("Always Show Tab Bar") ); + _ui->tabBarVisibilityCombo->setCurrentIndex(tabMode); + + // note: Items should be in the same order as the + // Profile::TabBarPositionEnum enum + _ui->tabBarPositionCombo->addItems( QStringList() << i18n("Below Terminal Displays") + << i18n("Above Terminal Displays") ); + + _ui->tabBarPositionCombo->setCurrentIndex(tabPosition); + _ui->newTabButton->setChecked(info->property<bool>(Profile::ShowNewAndCloseTabButtons)); + + // signals and slots + connect( _ui->tabBarVisibilityCombo , SIGNAL(activated(int)) , this , + SLOT(tabBarVisibilityChanged(int)) ); + connect( _ui->tabBarPositionCombo , SIGNAL(activated(int)) , this , + SLOT(tabBarPositionChanged(int)) ); + connect( _ui->newTabButton , SIGNAL(toggled(bool)) , this , + SLOT(showNewTabButton(bool)) ); + + connect(_ui->tabTitleEdit , SIGNAL(textChanged(const QString&)) , this , + SLOT(tabTitleFormatChanged(const QString&)) ); + connect(_ui->remoteTabTitleEdit , SIGNAL(textChanged(const QString&)) , this , + SLOT(remoteTabTitleFormatChanged(const QString&))); + + // menus for local and remote tab title dynamic elements + TabTitleFormatAction* localTabTitleAction = new TabTitleFormatAction(this); + localTabTitleAction->setContext(Session::LocalTabTitle); + _ui->tabTitleEditButton->setMenu(localTabTitleAction->menu()); + connect( localTabTitleAction , SIGNAL(dynamicElementSelected(const QString&)) , + this , SLOT(insertTabTitleText(const QString&)) ); + + TabTitleFormatAction* remoteTabTitleAction = new TabTitleFormatAction(this); + remoteTabTitleAction->setContext(Session::RemoteTabTitle); + _ui->remoteTabTitleEditButton->setMenu(remoteTabTitleAction->menu()); + connect( remoteTabTitleAction , SIGNAL(dynamicElementSelected(const QString&)) , + this , SLOT(insertRemoteTabTitleText(const QString&)) ); +} +void EditProfileDialog::showNewTabButton(bool show) +{ _tempProfile->setProperty(Profile::ShowNewAndCloseTabButtons,show); } +void EditProfileDialog::tabBarVisibilityChanged(int newValue) +{ + _tempProfile->setProperty( Profile::TabBarMode , newValue ); +} +void EditProfileDialog::tabBarPositionChanged(int newValue) +{ + _tempProfile->setProperty( Profile::TabBarPosition , newValue ); +} +void EditProfileDialog::insertTabTitleText(const QString& text) +{ + _ui->tabTitleEdit->insert(text); +} +void EditProfileDialog::insertRemoteTabTitleText(const QString& text) +{ + _ui->remoteTabTitleEdit->insert(text); +} +void EditProfileDialog::showMenuBar(bool show) +{ + _tempProfile->setProperty(Profile::ShowMenuBar,show); +} +void EditProfileDialog::saveGeometryOnExit(bool save) +{ + _tempProfile->setProperty(Profile::SaveGeometryOnExit,save); +} +void EditProfileDialog::tabTitleFormatChanged(const QString& format) +{ + _tempProfile->setProperty(Profile::LocalTabTitleFormat,format); +} +void EditProfileDialog::remoteTabTitleFormatChanged(const QString& format) +{ + _tempProfile->setProperty(Profile::RemoteTabTitleFormat,format); +} + +void EditProfileDialog::selectIcon() +{ + const QString& icon = KIconDialog::getIcon(KIconLoader::Desktop, KIconLoader::Application, + false, 0, false, this); + if (!icon.isEmpty()) + { + _ui->iconSelectButton->setIcon( KIcon(icon) ); + _tempProfile->setProperty(Profile::Icon,icon); + } +} +void EditProfileDialog::profileNameChanged(const QString& text) +{ + _tempProfile->setProperty(Profile::Name,text); + updateCaption(_tempProfile); +} +void EditProfileDialog::startInSameDir(bool sameDir) +{ + _tempProfile->setProperty(Profile::StartInCurrentSessionDir,sameDir); +} +void EditProfileDialog::initialDirChanged(const QString& dir) +{ + _tempProfile->setProperty(Profile::Directory,dir); +} +void EditProfileDialog::commandChanged(const QString& command) +{ + ShellCommand shellCommand(command); + + _tempProfile->setProperty(Profile::Command,shellCommand.command()); + _tempProfile->setProperty(Profile::Arguments,shellCommand.arguments()); +} +void EditProfileDialog::selectInitialDir() +{ + const KUrl url = KFileDialog::getExistingDirectoryUrl(_ui->initialDirEdit->text(), + this, + i18n("Select Initial Directory")); + + if ( !url.isEmpty() ) + _ui->initialDirEdit->setText(url.path()); +} +void EditProfileDialog::setupAppearancePage(const Profile::Ptr info) +{ + ColorSchemeViewDelegate* delegate = new ColorSchemeViewDelegate(this); + _ui->colorSchemeList->setItemDelegate(delegate); + + _colorSchemeAnimationTimeLine = new QTimeLine( 500 , this ); + delegate->setEntryTimeLine(_colorSchemeAnimationTimeLine); + + connect( _colorSchemeAnimationTimeLine , SIGNAL(valueChanged(qreal)) , this , + SLOT(colorSchemeAnimationUpdate()) ); + + _ui->transparencyWarningWidget->setVisible(false); + _ui->transparencyWarningWidget->setText(i18n("This color scheme uses a transparent background" + " which does not appear to be supported on your" + " desktop")); + _ui->editColorSchemeButton->setEnabled(false); + _ui->removeColorSchemeButton->setEnabled(false); + + // setup color list + updateColorSchemeList(true); + + _ui->colorSchemeList->setMouseTracking(true); + _ui->colorSchemeList->installEventFilter(this); + _ui->colorSchemeList->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); + + connect( _ui->colorSchemeList->selectionModel() , + SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)) + , this , SLOT(colorSchemeSelected()) ); + connect( _ui->colorSchemeList , SIGNAL(entered(const QModelIndex&)) , this , + SLOT(previewColorScheme(const QModelIndex&)) ); + + updateColorSchemeButtons(); + + connect( _ui->editColorSchemeButton , SIGNAL(clicked()) , this , + SLOT(editColorScheme()) ); + connect( _ui->removeColorSchemeButton , SIGNAL(clicked()) , this , + SLOT(removeColorScheme()) ); + connect( _ui->newColorSchemeButton , SIGNAL(clicked()) , this , + SLOT(newColorScheme()) ); + + // setup font preview + bool antialias = info->property<bool>(Profile::AntiAliasFonts); + + QFont font = info->font(); + font.setStyleStrategy(antialias ? QFont::PreferAntialias : QFont::NoAntialias); + + _ui->fontPreviewLabel->installEventFilter(this); + _ui->fontPreviewLabel->setFont(font); + setFontSliderRange(font); + setFontSliderValue(font); + + connect( _ui->fontSizeSlider , SIGNAL(valueChanged(int)) , this , + SLOT(setFontSize(int)) ); + connect( _ui->editFontButton , SIGNAL(clicked()) , this , + SLOT(showFontDialog()) ); + + // setup font smoothing + _ui->antialiasTextButton->setChecked(antialias); + connect( _ui->antialiasTextButton , SIGNAL(toggled(bool)) , this , + SLOT(setAntialiasText(bool)) ); + + bool boldIntense = info->property<bool>(Profile::BoldIntense); + _ui->boldIntenseButton->setChecked(boldIntense); + connect( _ui->boldIntenseButton , SIGNAL(toggled(bool)) , this , + SLOT(setBoldIntense(bool))); +} +void EditProfileDialog::setAntialiasText(bool enable) +{ + _tempProfile->setProperty(Profile::AntiAliasFonts,enable); + + QFont font = _ui->fontPreviewLabel->font(); + font.setStyleStrategy(enable ? QFont::PreferAntialias : QFont::NoAntialias); + + // update preview to reflect text smoothing state + fontSelected(font); +} +void EditProfileDialog::setBoldIntense(bool enable) +{ + _tempProfile->setProperty(Profile::BoldIntense,enable); + preview(Profile::BoldIntense,enable); +} +void EditProfileDialog::colorSchemeAnimationUpdate() +{ + QAbstractItemModel* model = _ui->colorSchemeList->model(); + + for ( int i = model->rowCount() ; i >= 0 ; i-- ) + _ui->colorSchemeList->update( model->index(i,0) ); +} +void EditProfileDialog::updateColorSchemeList(bool selectCurrentScheme) +{ + if (!_ui->colorSchemeList->model()) + _ui->colorSchemeList->setModel(new QStandardItemModel(this)); + + const QString& name = lookupProfile()->colorScheme(); + const ColorScheme* currentScheme = ColorSchemeManager::instance()->findColorScheme(name); + + QStandardItemModel* model = qobject_cast<QStandardItemModel*>(_ui->colorSchemeList->model()); + + Q_ASSERT(model); + + model->clear(); + + QList<const ColorScheme*> schemeList = ColorSchemeManager::instance()->allColorSchemes(); + QListIterator<const ColorScheme*> schemeIter(schemeList); + + QStandardItem* selectedItem = 0; + + while (schemeIter.hasNext()) + { + const ColorScheme* colors = schemeIter.next(); + QStandardItem* item = new QStandardItem(colors->description()); + item->setData( QVariant::fromValue(colors) , Qt::UserRole + 1); + item->setFlags( item->flags() ); + + if ( currentScheme == colors ) + selectedItem = item; + + model->appendRow(item); + } + + model->sort(0); + + if ( selectCurrentScheme && selectedItem ) + { + _ui->colorSchemeList->updateGeometry(); + _ui->colorSchemeList->selectionModel()->setCurrentIndex( selectedItem->index() , + QItemSelectionModel::Select ); + + // update transparency warning label + updateTransparencyWarning(); + } +} +void EditProfileDialog::updateKeyBindingsList(bool selectCurrentTranslator) +{ + if (!_ui->keyBindingList->model()) + _ui->keyBindingList->setModel(new QStandardItemModel(this)); + + KeyboardTranslatorManager* keyManager = KeyboardTranslatorManager::instance(); + + const QString& name = lookupProfile() + ->property<QString>(Profile::KeyBindings); + + const KeyboardTranslator* currentTranslator = keyManager->findTranslator(name); + + QStandardItemModel* model = qobject_cast<QStandardItemModel*>(_ui->keyBindingList->model()); + + Q_ASSERT(model); + + model->clear(); + + QStandardItem* selectedItem = 0; + + QList<QString> translatorNames = keyManager->allTranslators(); + QListIterator<QString> iter(translatorNames); + while (iter.hasNext()) + { + const QString& name = iter.next(); + + const KeyboardTranslator* translator = keyManager->findTranslator(name); + + QStandardItem* item = new QStandardItem(translator->description()); + item->setData(QVariant::fromValue(translator),Qt::UserRole+1); + item->setIcon( KIcon("preferences-desktop-keyboard") ); + + if ( translator == currentTranslator ) + selectedItem = item; + + model->appendRow(item); + } + + model->sort(0); + + if ( selectCurrentTranslator && selectedItem ) + { + _ui->keyBindingList->selectionModel()->setCurrentIndex( selectedItem->index() , + QItemSelectionModel::Select ); + } +} +bool EditProfileDialog::eventFilter( QObject* watched , QEvent* event ) +{ + if ( watched == _ui->colorSchemeList && event->type() == QEvent::Leave ) + { + if ( _tempProfile->isPropertySet(Profile::ColorScheme) ) + preview(Profile::ColorScheme,_tempProfile->colorScheme()); + else + unpreview(Profile::ColorScheme); + } + if ( watched == _ui->fontPreviewLabel && event->type() == QEvent::FontChange ) + { + const QFont& labelFont = _ui->fontPreviewLabel->font(); + qreal size = labelFont.pointSizeF(); + QString fontSize = KGlobal::locale()->formatNumber(size, size == floor(size) ? 0 : 1); + _ui->fontPreviewLabel->setText(i18n("%1, size %2", labelFont.family(), fontSize)); + } + + return KDialog::eventFilter(watched,event); +} +void EditProfileDialog::unpreviewAll() +{ + _delayedPreviewTimer->stop(); + _delayedPreviewProperties.clear(); + + QHash<Profile::Property,QVariant> map; + QHashIterator<int,QVariant> iter(_previewedProperties); + while ( iter.hasNext() ) + { + iter.next(); + map.insert((Profile::Property)iter.key(),iter.value()); + } + + // undo any preview changes + if ( !map.isEmpty() ) + SessionManager::instance()->changeProfile(_profile,map,false); +} +void EditProfileDialog::unpreview(int property) +{ + _delayedPreviewProperties.remove(property); + + if (!_previewedProperties.contains(property)) + return; + + QHash<Profile::Property,QVariant> map; + map.insert((Profile::Property)property,_previewedProperties[property]); + SessionManager::instance()->changeProfile(_profile,map,false); + + _previewedProperties.remove(property); +} +void EditProfileDialog::delayedPreview(int property , const QVariant& value) +{ + _delayedPreviewProperties.insert(property,value); + + _delayedPreviewTimer->stop(); + _delayedPreviewTimer->start(300); +} +void EditProfileDialog::delayedPreviewActivate() +{ + Q_ASSERT( qobject_cast<QTimer*>(sender()) ); + + QMutableHashIterator<int,QVariant> iter(_delayedPreviewProperties); + if ( iter.hasNext() ) + { + iter.next(); + preview(iter.key(),iter.value()); + } +} +void EditProfileDialog::preview(int property , const QVariant& value) +{ + QHash<Profile::Property,QVariant> map; + map.insert((Profile::Property)property,value); + + _delayedPreviewProperties.remove(property); + + const Profile::Ptr original = lookupProfile(); + + // skip previews for profile groups if the profiles in the group + // have conflicting original values for the property + // + // TODO - Save the original values for each profile and use to unpreview properties + ProfileGroup::Ptr group = original->asGroup(); + if (group && group->profiles().count() > 1 && + original->property<QVariant>((Profile::Property)property).isNull()) + return; + + if (!_previewedProperties.contains(property)) + { + _previewedProperties.insert(property , original->property<QVariant>((Profile::Property)property) ); + } + + // temporary change to color scheme + SessionManager::instance()->changeProfile( _profile , map , false); +} +void EditProfileDialog::previewColorScheme(const QModelIndex& index) +{ + const QString& name = index.data(Qt::UserRole+1).value<const ColorScheme*>()->name(); + + delayedPreview( Profile::ColorScheme , name ); +} +void EditProfileDialog::removeColorScheme() +{ + QModelIndexList selected = _ui->colorSchemeList->selectionModel()->selectedIndexes(); + + if ( !selected.isEmpty() ) + { + const QString& name = selected.first().data(Qt::UserRole+1).value<const ColorScheme*>()->name(); + + if (ColorSchemeManager::instance()->deleteColorScheme(name)) + _ui->colorSchemeList->model()->removeRow(selected.first().row()); + } +} +void EditProfileDialog::showColorSchemeEditor(bool isNewScheme) +{ + QModelIndexList selected = _ui->colorSchemeList->selectionModel()->selectedIndexes(); + + QAbstractItemModel* model = _ui->colorSchemeList->model(); + const ColorScheme* colors = 0; + if ( !selected.isEmpty() ) + colors = model->data(selected.first(),Qt::UserRole+1).value<const ColorScheme*>(); + else + colors = ColorSchemeManager::instance()->defaultColorScheme(); + + Q_ASSERT(colors); + + KDialog* dialog = new KDialog(this); + + if ( isNewScheme ) + dialog->setCaption(i18n("New Color Scheme")); + else + dialog->setCaption(i18n("Edit Color Scheme")); + + ColorSchemeEditor* editor = new ColorSchemeEditor; + dialog->setMainWidget(editor); + editor->setup(colors); + + if ( isNewScheme ) + editor->setDescription(i18n("New Color Scheme")); + + if ( dialog->exec() == QDialog::Accepted ) + { + ColorScheme* newScheme = new ColorScheme(*editor->colorScheme()); + + // if this is a new color scheme, pick a name based on the description + if ( isNewScheme ) + newScheme->setName(newScheme->description()); + + ColorSchemeManager::instance()->addColorScheme( newScheme ); + + updateColorSchemeList(true); + + preview(Profile::ColorScheme,newScheme->name()); + } +} +void EditProfileDialog::newColorScheme() +{ + showColorSchemeEditor(true); +} +void EditProfileDialog::editColorScheme() +{ + showColorSchemeEditor(false); +} +void EditProfileDialog::colorSchemeSelected() +{ + QModelIndexList selected = _ui->colorSchemeList->selectionModel()->selectedIndexes(); + + if ( !selected.isEmpty() ) + { + QAbstractItemModel* model = _ui->colorSchemeList->model(); + const ColorScheme* colors = model->data(selected.first(),Qt::UserRole+1).value<const ColorScheme*>(); + + //kDebug() << "Setting temp profile color to" << colors->name(); + + previewColorScheme(selected.first()); + _tempProfile->setProperty(Profile::ColorScheme,colors->name()); + + updateTransparencyWarning(); + } + + updateColorSchemeButtons(); +} +void EditProfileDialog::updateColorSchemeButtons() +{ + enableIfNonEmptySelection(_ui->editColorSchemeButton,_ui->colorSchemeList->selectionModel()); + enableIfNonEmptySelection(_ui->removeColorSchemeButton,_ui->colorSchemeList->selectionModel()); +} +void EditProfileDialog::updateKeyBindingsButtons() +{ + enableIfNonEmptySelection(_ui->editKeyBindingsButton,_ui->keyBindingList->selectionModel()); + enableIfNonEmptySelection(_ui->removeKeyBindingsButton,_ui->keyBindingList->selectionModel()); +} +void EditProfileDialog::enableIfNonEmptySelection(QWidget* widget,QItemSelectionModel* selectionModel) +{ + widget->setEnabled(selectionModel->hasSelection()); +} +void EditProfileDialog::updateTransparencyWarning() +{ + // zero or one indexes can be selected + foreach( const QModelIndex& index , _ui->colorSchemeList->selectionModel()->selectedIndexes() ) + { + bool hasTransparency = index.data(Qt::UserRole+1).value<const ColorScheme*>()->opacity() < 1.0; + _ui->transparencyWarningWidget->setHidden(KWindowSystem::compositingActive() || !hasTransparency); + } +} +void EditProfileDialog::setupKeyboardPage(const Profile::Ptr /* info */) +{ + // setup translator list + updateKeyBindingsList(true); + + connect( _ui->keyBindingList->selectionModel() , + SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)), + SLOT(keyBindingSelected()) ); + connect( _ui->newKeyBindingsButton , SIGNAL(clicked()) , this , + SLOT(newKeyBinding()) ); + + updateKeyBindingsButtons(); + + connect( _ui->editKeyBindingsButton , SIGNAL(clicked()) , this , + SLOT(editKeyBinding()) ); + connect( _ui->removeKeyBindingsButton , SIGNAL(clicked()) , this , + SLOT(removeKeyBinding()) ); +} +void EditProfileDialog::keyBindingSelected() +{ + QModelIndexList selected = _ui->keyBindingList->selectionModel()->selectedIndexes(); + + if ( !selected.isEmpty() ) + { + QAbstractItemModel* model = _ui->keyBindingList->model(); + const KeyboardTranslator* translator = model->data(selected.first(),Qt::UserRole+1) + .value<const KeyboardTranslator*>(); + _tempProfile->setProperty(Profile::KeyBindings,translator->name()); + } + + updateKeyBindingsButtons(); +} +void EditProfileDialog::removeKeyBinding() +{ + QModelIndexList selected = _ui->keyBindingList->selectionModel()->selectedIndexes(); + + if ( !selected.isEmpty() ) + { + const QString& name = selected.first().data(Qt::UserRole+1).value<const KeyboardTranslator*>()->name(); + if (KeyboardTranslatorManager::instance()->deleteTranslator(name)) + _ui->keyBindingList->model()->removeRow(selected.first().row()); + } +} +void EditProfileDialog::showKeyBindingEditor(bool isNewTranslator) +{ + QModelIndexList selected = _ui->keyBindingList->selectionModel()->selectedIndexes(); + QAbstractItemModel* model = _ui->keyBindingList->model(); + + const KeyboardTranslator* translator = 0; + if ( !selected.isEmpty() ) + translator = model->data(selected.first(),Qt::UserRole+1).value<const KeyboardTranslator*>(); + else + translator = KeyboardTranslatorManager::instance()->defaultTranslator(); + + Q_ASSERT(translator); + + KDialog* dialog = new KDialog(this); + + if ( isNewTranslator ) + dialog->setCaption(i18n("New Key Binding List")); + else + dialog->setCaption(i18n("Edit Key Binding List")); + + KeyBindingEditor* editor = new KeyBindingEditor; + dialog->setMainWidget(editor); + + if ( translator ) + editor->setup(translator); + + if ( isNewTranslator ) + editor->setDescription(i18n("New Key Binding List")); + + if ( dialog->exec() == QDialog::Accepted ) + { + KeyboardTranslator* newTranslator = new KeyboardTranslator(*editor->translator()); + + if ( isNewTranslator ) + newTranslator->setName(newTranslator->description()); + + KeyboardTranslatorManager::instance()->addTranslator( newTranslator ); + + updateKeyBindingsList(); + + const QString& currentTranslator = lookupProfile() + ->property<QString>(Profile::KeyBindings); + + if ( newTranslator->name() == currentTranslator ) + { + _tempProfile->setProperty(Profile::KeyBindings,newTranslator->name()); + } + } +} +void EditProfileDialog::newKeyBinding() +{ + showKeyBindingEditor(true); +} +void EditProfileDialog::editKeyBinding() +{ + showKeyBindingEditor(false); +} +void EditProfileDialog::setupCombo( ComboOption* options , const Profile::Ptr profile ) +{ + while ( options->button != 0 ) + { + options->button->setChecked(profile->property<bool>((Profile::Property)options->property)); + connect( options->button , SIGNAL(toggled(bool)) , this , options->slot ); + + ++options; + } +} +void EditProfileDialog::setupRadio( RadioOption* possible , int actual ) +{ + while (possible->button != 0) + { + if ( possible->property == actual ) + possible->button->setChecked(true); + else + possible->button->setChecked(false); + + connect( possible->button , SIGNAL(clicked()) , this , possible->slot ); + + ++possible; + } +} + +void EditProfileDialog::setupScrollingPage(const Profile::Ptr profile) +{ + // setup scrollbar radio + int scrollBarPosition = profile->property<int>(Profile::ScrollBarPosition); + + RadioOption positions[] = { {_ui->scrollBarHiddenButton,Profile::ScrollBarHidden,SLOT(hideScrollBar())}, + {_ui->scrollBarLeftButton,Profile::ScrollBarLeft,SLOT(showScrollBarLeft())}, + {_ui->scrollBarRightButton,Profile::ScrollBarRight,SLOT(showScrollBarRight())}, + {0,0,0} + }; + + setupRadio( positions , scrollBarPosition ); + + // setup scrollback type radio + int scrollBackType = profile->property<int>(Profile::HistoryMode); + + RadioOption types[] = { {_ui->disableScrollbackButton,Profile::DisableHistory,SLOT(noScrollBack())}, + {_ui->fixedScrollbackButton,Profile::FixedSizeHistory,SLOT(fixedScrollBack())}, + {_ui->unlimitedScrollbackButton,Profile::UnlimitedHistory,SLOT(unlimitedScrollBack())}, + {0,0,0} }; + setupRadio( types , scrollBackType ); + + // setup scrollback line count spinner + _ui->scrollBackLinesSpinner->setValue( profile->property<int>(Profile::HistorySize) ); + + // signals and slots + connect( _ui->scrollBackLinesSpinner , SIGNAL(valueChanged(int)) , this , + SLOT(scrollBackLinesChanged(int)) ); +} + +void EditProfileDialog::scrollBackLinesChanged(int lineCount) +{ + _tempProfile->setProperty(Profile::HistorySize , lineCount); +} +void EditProfileDialog::noScrollBack() +{ + _tempProfile->setProperty(Profile::HistoryMode , Profile::DisableHistory); +} +void EditProfileDialog::fixedScrollBack() +{ + _tempProfile->setProperty(Profile::HistoryMode , Profile::FixedSizeHistory); +} +void EditProfileDialog::unlimitedScrollBack() +{ + _tempProfile->setProperty(Profile::HistoryMode , Profile::UnlimitedHistory ); +} +void EditProfileDialog::hideScrollBar() +{ + _tempProfile->setProperty(Profile::ScrollBarPosition , Profile::ScrollBarHidden ); +} +void EditProfileDialog::showScrollBarLeft() +{ + _tempProfile->setProperty(Profile::ScrollBarPosition , Profile::ScrollBarLeft ); +} +void EditProfileDialog::showScrollBarRight() +{ + _tempProfile->setProperty(Profile::ScrollBarPosition , Profile::ScrollBarRight ); +} +void EditProfileDialog::setupAdvancedPage(const Profile::Ptr profile) +{ + ComboOption options[] = { { _ui->enableBlinkingTextButton , Profile::BlinkingTextEnabled , + SLOT(toggleBlinkingText(bool)) }, + { _ui->enableFlowControlButton , Profile::FlowControlEnabled , + SLOT(toggleFlowControl(bool)) }, + { _ui->enableResizeWindowButton , Profile::AllowProgramsToResizeWindow , + SLOT(toggleResizeWindow(bool)) }, + { _ui->enableBlinkingCursorButton , Profile::BlinkingCursorEnabled , + SLOT(toggleBlinkingCursor(bool)) }, + { _ui->tripleClickMode , Profile::TripleClickMode , + SLOT(toggleTripleClickMode(bool)) }, + { _ui->enableBidiRenderingButton , Profile::BidiRenderingEnabled , + SLOT(togglebidiRendering(bool)) }, + { 0 , 0 , 0 } + }; + setupCombo( options , profile ); + + // interaction options + _ui->wordCharacterEdit->setText( profile->property<QString>(Profile::WordCharacters) ); + + connect( _ui->wordCharacterEdit , SIGNAL(textChanged(const QString&)) , this , + SLOT(wordCharactersChanged(const QString&)) ); + + // cursor options + if ( profile->property<bool>(Profile::UseCustomCursorColor) ) + _ui->customCursorColorButton->setChecked(true); + else + _ui->autoCursorColorButton->setChecked(true); + + _ui->customColorSelectButton->setColor( profile->property<QColor>(Profile::CustomCursorColor) ); + + connect( _ui->customCursorColorButton , SIGNAL(clicked()) , this , SLOT(customCursorColor()) ); + connect( _ui->autoCursorColorButton , SIGNAL(clicked()) , this , SLOT(autoCursorColor()) ); + connect( _ui->customColorSelectButton , SIGNAL(changed(const QColor&)) , + SLOT(customCursorColorChanged(const QColor&)) ); + + int shape = profile->property<int>(Profile::CursorShape); + _ui->cursorShapeCombo->setCurrentIndex(shape); + + connect( _ui->cursorShapeCombo , SIGNAL(activated(int)) , this , SLOT(setCursorShape(int)) ); + + // encoding options + QAction* codecAction = new KCodecAction(this); + _ui->selectEncodingButton->setMenu( codecAction->menu() ); + connect( codecAction , SIGNAL(triggered(QTextCodec*)) , this , SLOT(setDefaultCodec(QTextCodec*)) ); + + _ui->characterEncodingLabel->setText( profile->property<QString>(Profile::DefaultEncoding) ); + +} +void EditProfileDialog::setDefaultCodec(QTextCodec* codec) +{ + QString name = QString(codec->name()); + + _tempProfile->setProperty(Profile::DefaultEncoding,name); + _ui->characterEncodingLabel->setText(codec->name()); +} +void EditProfileDialog::customCursorColorChanged(const QColor& color) +{ + _tempProfile->setProperty(Profile::CustomCursorColor,color); + + // ensure that custom cursor colors are enabled + _ui->customCursorColorButton->click(); +} +void EditProfileDialog::wordCharactersChanged(const QString& text) +{ + _tempProfile->setProperty(Profile::WordCharacters,text); +} +void EditProfileDialog::autoCursorColor() +{ + _tempProfile->setProperty(Profile::UseCustomCursorColor,false); +} +void EditProfileDialog::customCursorColor() +{ + _tempProfile->setProperty(Profile::UseCustomCursorColor,true); +} +void EditProfileDialog::setCursorShape(int index) +{ + _tempProfile->setProperty(Profile::CursorShape,index); +} +void EditProfileDialog::togglebidiRendering(bool enable) +{ + _tempProfile->setProperty(Profile::BidiRenderingEnabled,enable); +} +void EditProfileDialog::toggleBlinkingCursor(bool enable) +{ + _tempProfile->setProperty(Profile::BlinkingCursorEnabled,enable); +} +void EditProfileDialog::toggleTripleClickMode(bool enable) +{ + _tempProfile->setProperty(Profile::TripleClickMode,enable); +} +void EditProfileDialog::toggleBlinkingText(bool enable) +{ + _tempProfile->setProperty(Profile::BlinkingTextEnabled,enable); +} +void EditProfileDialog::toggleFlowControl(bool enable) +{ + _tempProfile->setProperty(Profile::FlowControlEnabled,enable); +} +void EditProfileDialog::toggleResizeWindow(bool enable) +{ + _tempProfile->setProperty(Profile::AllowProgramsToResizeWindow,enable); +} +void EditProfileDialog::fontSelected(const QFont& font) +{ + QFont previewFont = font; + + setFontSliderRange(font); + setFontSliderValue(font); + + _ui->fontPreviewLabel->setFont(previewFont); + + _tempProfile->setProperty(Profile::Font,font); + + preview(Profile::Font,font); +} +void EditProfileDialog::showFontDialog() +{ + QFont currentFont = _ui->fontPreviewLabel->font(); + + KFontDialog* dialog = new KFontDialog(this, KFontChooser::FixedFontsOnly); + dialog->setFont(currentFont, true); + + connect( dialog , SIGNAL(fontSelected(const QFont&)) , this , SLOT(fontSelected(const QFont&)) ); + + if (dialog->exec() == QDialog::Rejected) + fontSelected(currentFont); +} +void EditProfileDialog::setFontSize(int pointSize) +{ + QFont newFont = _ui->fontPreviewLabel->font(); + newFont.setPointSizeF(pointSize / 10.0); + _ui->fontPreviewLabel->setFont(newFont); + + _tempProfile->setProperty(Profile::Font,newFont); + + preview(Profile::Font,newFont); +} + +void EditProfileDialog::setFontSliderRange(const QFont& font) +{ + QSlider* slider = _ui->fontSizeSlider; + // Minimum on the slider is 4, + // Maximum is the greater of 2 times the current size and 14 + slider->setRange( qMin(4 * 10, qRound(font.pointSizeF() * 10)), + qMax(14 * 10, 2 * qRound(font.pointSize() * 10)) ); + +} + +void EditProfileDialog::setFontSliderValue(const QFont& font) +{ + _ui->fontSizeSlider->setValue(qRound(font.pointSize() * 10)); +} + +ColorSchemeViewDelegate::ColorSchemeViewDelegate(QObject* parent) + : QAbstractItemDelegate(parent) +{ + +} + +void ColorSchemeViewDelegate::setEntryTimeLine(QTimeLine* timeLine) +{ + _entryTimeLine = timeLine; +} + +void ColorSchemeViewDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + // entry animation + // + // note that the translation occurs for each item drawn, but the + // painter is not reset between painting items. this means that when + // the items are painted in order ( as occurs when the list is first + // shown ), there is a visually pleasing staggering of items as they + // enter. + if ( _entryTimeLine != 0 ) + { + qreal value = 1.0-_entryTimeLine->currentValue(); + painter->translate( value * + option.rect.width() , 0 ); + + painter->setOpacity( _entryTimeLine->currentValue() ); + } + + const ColorScheme* scheme = index.data(Qt::UserRole + 1).value<const ColorScheme*>(); + + Q_ASSERT(scheme); + + bool transparencyAvailable = KWindowSystem::compositingActive(); + + painter->setRenderHint( QPainter::Antialiasing ); + + // draw background + painter->setPen( QPen(scheme->foregroundColor() , 1) ); + + // radial gradient for background + // from a lightened version of the scheme's background color in the center to + // a darker version at the outer edge + QColor color = scheme->backgroundColor(); + QRectF backgroundRect = QRectF(option.rect).adjusted(1.5,1.5,-1.5,-1.5); + + QRadialGradient backgroundGradient(backgroundRect.center() , backgroundRect.width() / 2); + backgroundGradient.setColorAt( 0 , color.lighter(105) ); + backgroundGradient.setColorAt( 1 , color.darker(115) ); + + const int backgroundRectXRoundness = 4; + const int backgroundRectYRoundness = 30; + + QPainterPath backgroundRectPath(backgroundRect.topLeft()); + backgroundRectPath.addRoundRect( backgroundRect , backgroundRectXRoundness , backgroundRectYRoundness ); + + if ( transparencyAvailable ) + { + painter->save(); + color.setAlphaF(scheme->opacity()); + painter->setCompositionMode( QPainter::CompositionMode_Source ); + painter->setBrush(backgroundGradient); + + painter->drawPath(backgroundRectPath); + painter->restore(); + } + else + { + painter->setBrush(backgroundGradient); + painter->drawPath(backgroundRectPath); + } + + // draw stripe at the side using scheme's foreground color + painter->setPen( QPen(Qt::NoPen) ); + QPainterPath path( option.rect.topLeft() ); + path.lineTo( option.rect.width() / 10.0 , option.rect.top() ); + path.lineTo( option.rect.bottomLeft() ); + path.lineTo( option.rect.topLeft() ); + painter->setBrush( scheme->foregroundColor() ); + painter->drawPath(path.intersected(backgroundRectPath)); + + // draw highlight + // with a linear gradient going from translucent white to transparent + QLinearGradient gradient( option.rect.topLeft() , option.rect.bottomLeft() ); + gradient.setColorAt( 0 , QColor(255,255,255,90) ); + gradient.setColorAt( 1 , Qt::transparent ); + painter->setBrush(gradient); + painter->drawRoundRect( backgroundRect , 4 , 30 ); + + //const bool isChecked = index.data(Qt::CheckStateRole) == Qt::Checked; + const bool isSelected = option.state & QStyle::State_Selected; + + // draw border on selected items + if ( isSelected ) //|| isChecked ) + { + static const int selectedBorderWidth = 6; + + + painter->setBrush( QBrush(Qt::NoBrush) ); + QPen pen; + + QColor highlightColor = option.palette.highlight().color(); + + if ( isSelected ) + highlightColor.setAlphaF(1.0); + else + highlightColor.setAlphaF(0.7); + + pen.setBrush(highlightColor); + pen.setWidth(selectedBorderWidth); + pen.setJoinStyle(Qt::MiterJoin); + + painter->setPen(pen); + + + painter->drawRect( option.rect.adjusted(selectedBorderWidth/2, + selectedBorderWidth/2, + -selectedBorderWidth/2, + -selectedBorderWidth/2) ); + } + + // draw color scheme name using scheme's foreground color + QPen pen(scheme->foregroundColor()); + painter->setPen(pen); + + painter->drawText( option.rect , Qt::AlignCenter , + index.data(Qt::DisplayRole).value<QString>() ); + +} + +QSize ColorSchemeViewDelegate::sizeHint( const QStyleOptionViewItem& option, + const QModelIndex& /*index*/) const +{ + const int width = 200; + qreal colorWidth = (qreal)width / TABLE_COLORS; + int margin = 5; + qreal heightForWidth = ( colorWidth * 2 ) + option.fontMetrics.height() + margin; + + // temporary + return QSize(width,(int)heightForWidth); +} + +#include "EditProfileDialog.moc"
new file mode 100644 --- /dev/null +++ b/gui/konsole/EditProfileDialog.h @@ -0,0 +1,277 @@ +/* + Copyright 2007-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. +*/ + +#ifndef EDITPROFILEDIALOG_H +#define EDITPROFILEDIALOG_H + +// Qt +#include <QtGui/QAbstractItemDelegate> +#include <QtCore/QPair> +#include <QtCore/QHash> +#include <QtCore/QSet> +#include <QtCore/QPointer> + +// KDE +#include <KDialog> + +// Local +#include "Profile.h" + +class QAbstractButton; +class QItemSelectionModel; +class QTextCodec; +class QTimeLine; + +namespace Ui +{ + class EditProfileDialog; +} + +namespace Konsole +{ + +class Profile; + +/** + * A dialog which allows the user to edit a profile. + * After the dialog is created, it can be initialised with the settings + * for a profile using setProfile(). When the user makes changes to the + * dialog and accepts the changes, the dialog will update the + * profile in the SessionManager by calling the SessionManager's + * changeProfile() method. + * + * Some changes made in the dialog are preview-only changes which cause + * the SessionManager's changeProfile() method to be called with + * the persistant argument set to false. These changes are then + * un-done when the dialog is closed. + */ +class KONSOLEPRIVATE_EXPORT EditProfileDialog : public KDialog +{ +Q_OBJECT + +public: + /** Constructs a new dialog with the specified parent. */ + EditProfileDialog(QWidget* parent = 0); + virtual ~EditProfileDialog(); + + /** + * Initialises the dialog with the settings for the specified session + * type. + * + * When the dialog closes, the profile will be updated in the SessionManager + * with the altered settings. + * + * @param profile The profile to be edited + */ + void setProfile(Profile::Ptr profile); + + /** + * Selects the text in the profile name edit area. + * When the dialog is being used to create a new profile, + * this can be used to draw the user's attention to the profile name + * and make it easy for them to change it. + */ + void selectProfileName(); + +public slots: + // reimplemented + virtual void accept(); + // reimplemented + virtual void reject(); + +protected: + virtual bool eventFilter(QObject* watched , QEvent* event); + +private slots: + // sets up the specified tab page if necessary + void preparePage(int); + + // saves changes to profile + void save(); + + // general page + void selectInitialDir(); + void selectIcon(); + + void profileNameChanged(const QString& text); + void initialDirChanged(const QString& text); + void startInSameDir(bool); + void commandChanged(const QString& text); + void tabTitleFormatChanged(const QString& text); + void remoteTabTitleFormatChanged(const QString& text); + + void insertTabTitleText(const QString& text); + void insertRemoteTabTitleText(const QString& text); + + void showMenuBar(bool); + void saveGeometryOnExit(bool); + void showEnvironmentEditor(); + void tabBarVisibilityChanged(int); + void tabBarPositionChanged(int); + void showNewTabButton(bool); + + // appearance page + void setFontSize(int pointSize); + void setFontSliderRange(const QFont&); + void setFontSliderValue(const QFont&); + void setAntialiasText(bool enable); + void setBoldIntense(bool enable); + void showFontDialog(); + void newColorScheme(); + void editColorScheme(); + void removeColorScheme(); + void colorSchemeSelected(); + void previewColorScheme(const QModelIndex& index); + void fontSelected(const QFont&); + + void colorSchemeAnimationUpdate(); + + // scrolling page + void noScrollBack(); + void fixedScrollBack(); + void unlimitedScrollBack(); + + void scrollBackLinesChanged(int); + + void hideScrollBar(); + void showScrollBarLeft(); + void showScrollBarRight(); + + // keyboard page + void editKeyBinding(); + void newKeyBinding(); + void keyBindingSelected(); + void removeKeyBinding(); + + // advanced page + void toggleBlinkingText(bool); + void toggleFlowControl(bool); + void toggleResizeWindow(bool); + void togglebidiRendering(bool); + void toggleBlinkingCursor(bool); + void toggleTripleClickMode(bool); + + void setCursorShape(int); + void autoCursorColor(); + void customCursorColor(); + void customCursorColorChanged(const QColor&); + void wordCharactersChanged(const QString&); + void setDefaultCodec(QTextCodec*); + + // apply the first previewed changes stored up by delayedPreview() + void delayedPreviewActivate(); + +private: + // initialize various pages of the dialog + void setupGeneralPage(const Profile::Ptr info); + void setupTabsPage(const Profile::Ptr info); + void setupAppearancePage(const Profile::Ptr info); + void setupKeyboardPage(const Profile::Ptr info); + void setupScrollingPage(const Profile::Ptr info); + void setupAdvancedPage(const Profile::Ptr info); + + void updateColorSchemeList(bool selectCurrentScheme = false); + void updateColorSchemeButtons(); + void updateKeyBindingsList(bool selectCurrentTranslator = false); + void updateKeyBindingsButtons(); + + void showColorSchemeEditor(bool newScheme); + void showKeyBindingEditor(bool newTranslator); + + void changeCheckedItem( QAbstractItemModel* mode, const QModelIndex& to ); + + void preview(int property , const QVariant& value); + void delayedPreview(int property , const QVariant& value); + void unpreview(int property); + void unpreviewAll(); + void enableIfNonEmptySelection(QWidget* widget,QItemSelectionModel* selectionModel); + + void updateCaption(const Profile::Ptr profile); + void updateTransparencyWarning(); + + static QString groupProfileNames(const ProfileGroup::Ptr group, int maxLength = -1); + + struct RadioOption + { + QAbstractButton* button; + int property; + const char* slot; + }; + void setupRadio(RadioOption* possible,int actual); + struct ComboOption + { + QAbstractButton* button; + int property; + const char* slot; + }; + void setupCombo(ComboOption* options , const Profile::Ptr profile); + + const Profile::Ptr lookupProfile() const; + + Ui::EditProfileDialog* _ui; + Profile::Ptr _tempProfile; + Profile::Ptr _profile; + + // keeps track of pages which need to be updated to match the current + // profile. all elements in this vector are set to true when the + // profile is changed and individual elements are set to false + // after an update by a call to ensurePageLoaded() + QVector<bool> _pageNeedsUpdate; + QHash<int,QVariant> _previewedProperties; + + QTimeLine* _colorSchemeAnimationTimeLine; + + QHash<int,QVariant> _delayedPreviewProperties; + QTimer* _delayedPreviewTimer; +}; + +/** + * A delegate which can display and edit color schemes in a view. + */ +class ColorSchemeViewDelegate : public QAbstractItemDelegate +{ +Q_OBJECT + +public: + ColorSchemeViewDelegate(QObject* parent = 0); + + // reimplemented + virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, + const QModelIndex& index) const; + virtual QSize sizeHint( const QStyleOptionViewItem& option, + const QModelIndex& index) const; + + /** + * Sets the timeline used to control the entry animation + * for this delegate. + * + * During a call to paint(), the value of the timeLine is used to + * determine how to render the item ( with 0 being the beginning + * of the animation and 1.0 being the end ) + */ + void setEntryTimeLine( QTimeLine* timeLine ); + +private: + QPointer<QTimeLine> _entryTimeLine; + +}; + +} + +#endif // EDITPROFILEDIALOG_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/Emulation.cpp @@ -0,0 +1,460 @@ +/* + Copyright 2007-2008 Robert Knight <robertknight@gmail.com> + Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de> + Copyright 1996 by Matthias Ettrich <ettrich@kde.org> + + 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 "Emulation.h" + +// System +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +// Qt +#include <QtGui/QApplication> +#include <QtGui/QClipboard> +#include <QtCore/QHash> +#include <QtGui/QKeyEvent> +#include <QtCore/QRegExp> +#include <QtCore/QTextStream> +#include <QtCore/QThread> + +#include <QtCore/QTime> + +// KDE +//#include <kdebug.h> + +// Konsole +#include "KeyboardTranslator.h" +#include "Screen.h" +#include "TerminalCharacterDecoder.h" +#include "ScreenWindow.h" + +using namespace Konsole; + +Emulation::Emulation() : + _currentScreen(0), + _codec(0), + _decoder(0), + _keyTranslator(0), + _usesMouse(false) +{ + // create screens with a default size + _screen[0] = new Screen(40,80); + _screen[1] = new Screen(40,80); + _currentScreen = _screen[0]; + + QObject::connect(&_bulkTimer1, SIGNAL(timeout()), this, SLOT(showBulk()) ); + QObject::connect(&_bulkTimer2, SIGNAL(timeout()), this, SLOT(showBulk()) ); + + // listen for mouse status changes + connect( this , SIGNAL(programUsesMouseChanged(bool)) , + SLOT(usesMouseChanged(bool)) ); +} + +bool Emulation::programUsesMouse() const +{ + return _usesMouse; +} + +void Emulation::usesMouseChanged(bool usesMouse) +{ + _usesMouse = usesMouse; +} + +ScreenWindow* Emulation::createWindow() +{ + ScreenWindow* window = new ScreenWindow(); + window->setScreen(_currentScreen); + _windows << window; + + connect(window , SIGNAL(selectionChanged()), + this , SLOT(bufferedUpdate())); + + connect(this , SIGNAL(outputChanged()), + window , SLOT(notifyOutputChanged()) ); + return window; +} + +Emulation::~Emulation() +{ + QListIterator<ScreenWindow*> windowIter(_windows); + + while (windowIter.hasNext()) + { + delete windowIter.next(); + } + + delete _screen[0]; + delete _screen[1]; + delete _decoder; +} + +void Emulation::setScreen(int n) +{ + Screen *old = _currentScreen; + _currentScreen = _screen[n & 1]; + if (_currentScreen != old) + { + // tell all windows onto this emulation to switch to the newly active screen + foreach(ScreenWindow* window,_windows) + window->setScreen(_currentScreen); + } +} + +void Emulation::clearHistory() +{ + _screen[0]->setScroll( _screen[0]->getScroll() , false ); +} +void Emulation::setHistory(const HistoryType& t) +{ + _screen[0]->setScroll(t); + + showBulk(); +} + +const HistoryType& Emulation::history() const +{ + return _screen[0]->getScroll(); +} + +void Emulation::setCodec(const QTextCodec * qtc) +{ + if (qtc) + _codec = qtc; + else + setCodec(LocaleCodec); + + delete _decoder; + _decoder = _codec->makeDecoder(); + + emit useUtf8Request(utf8()); +} + +void Emulation::setCodec(EmulationCodec codec) +{ + if ( codec == Utf8Codec ) + setCodec( QTextCodec::codecForName("utf8") ); + else if ( codec == LocaleCodec ) + setCodec( QTextCodec::codecForLocale() ); +} + +void Emulation::setKeyBindings(const QString& name) +{ + _keyTranslator = KeyboardTranslatorManager::instance()->findTranslator(name); + if (!_keyTranslator) + { + _keyTranslator = KeyboardTranslatorManager::instance()->defaultTranslator(); + } +} + +QString Emulation::keyBindings() const +{ + return _keyTranslator->name(); +} + +void Emulation::receiveChar(int c) +// process application unicode input to terminal +// this is a trivial scanner +{ + c &= 0xff; + switch (c) + { + case '\b' : _currentScreen->backspace(); break; + case '\t' : _currentScreen->tab(); break; + case '\n' : _currentScreen->newLine(); break; + case '\r' : _currentScreen->toStartOfLine(); break; + case 0x07 : emit stateSet(NOTIFYBELL); + break; + default : _currentScreen->displayCharacter(c); break; + }; +} + +void Emulation::sendKeyEvent( QKeyEvent* ev ) +{ + emit stateSet(NOTIFYNORMAL); + + if (!ev->text().isEmpty()) + { // A block of text + // Note that the text is proper unicode. + // We should do a conversion here + emit sendData(ev->text().toUtf8(),ev->text().length()); + } +} + +void Emulation::sendString(const char*,int) +{ + // default implementation does nothing +} + +void Emulation::sendMouseEvent(int /*buttons*/, int /*column*/, int /*row*/, int /*eventType*/) +{ + // default implementation does nothing +} + +/* + We are doing code conversion from locale to unicode first. +TODO: Character composition from the old code. See #96536 +*/ + +void Emulation::receiveData(const char* text, int length) +{ + emit stateSet(NOTIFYACTIVITY); + + bufferedUpdate(); + + QString unicodeText = _decoder->toUnicode(text,length); + + //send characters to terminal emulator + for (int i=0;i<unicodeText.length();i++) + receiveChar(unicodeText[i].unicode()); + + //look for z-modem indicator + //-- someone who understands more about z-modems that I do may be able to move + //this check into the above for loop? + for (int i=0;i<length;i++) + { + if (text[i] == '\030') + { + if ((length-i-1 > 3) && (strncmp(text+i+1, "B00", 3) == 0)) + emit zmodemDetected(); + } + } +} + +//OLDER VERSION +//This version of onRcvBlock was commented out because +// a) It decoded incoming characters one-by-one, which is slow in the current version of Qt (4.2 tech preview) +// b) It messed up decoding of non-ASCII characters, with the result that (for example) chinese characters +// were not printed properly. +// +//There is something about stopping the _decoder if "we get a control code halfway a multi-byte sequence" (see below) +//which hasn't been ported into the newer function (above). Hopefully someone who understands this better +//can find an alternative way of handling the check. + + +/*void Emulation::onRcvBlock(const char *s, int len) +{ + emit notifySessionState(NOTIFYACTIVITY); + + bufferedUpdate(); + for (int i = 0; i < len; i++) + { + + QString result = _decoder->toUnicode(&s[i],1); + int reslen = result.length(); + + // If we get a control code halfway a multi-byte sequence + // we flush the _decoder and continue with the control code. + if ((s[i] < 32) && (s[i] > 0)) + { + // Flush _decoder + while(!result.length()) + result = _decoder->toUnicode(&s[i],1); + reslen = 1; + result.resize(reslen); + result[0] = QChar(s[i]); + } + + for (int j = 0; j < reslen; j++) + { + if (result[j].characterategory() == QChar::Mark_NonSpacing) + _currentScreen->compose(result.mid(j,1)); + else + onRcvChar(result[j].unicode()); + } + if (s[i] == '\030') + { + if ((len-i-1 > 3) && (strncmp(s+i+1, "B00", 3) == 0)) + emit zmodemDetected(); + } + } +}*/ + +void Emulation::writeToStream( TerminalCharacterDecoder* _decoder , + int startLine , + int endLine) +{ + _currentScreen->writeLinesToStream(_decoder,startLine,endLine); +} + +int Emulation::lineCount() const +{ + // sum number of lines currently on _screen plus number of lines in history + return _currentScreen->getLines() + _currentScreen->getHistLines(); +} + +#define BULK_TIMEOUT1 10 +#define BULK_TIMEOUT2 40 + +void Emulation::showBulk() +{ + _bulkTimer1.stop(); + _bulkTimer2.stop(); + + emit outputChanged(); + + _currentScreen->resetScrolledLines(); + _currentScreen->resetDroppedLines(); +} + +void Emulation::bufferedUpdate() +{ + _bulkTimer1.setSingleShot(true); + _bulkTimer1.start(BULK_TIMEOUT1); + if (!_bulkTimer2.isActive()) + { + _bulkTimer2.setSingleShot(true); + _bulkTimer2.start(BULK_TIMEOUT2); + } +} + +char Emulation::eraseChar() const +{ + return '\b'; +} + +void Emulation::setImageSize(int lines, int columns) +{ + if ((lines < 1) || (columns < 1)) + return; + + QSize screenSize[2] = { QSize(_screen[0]->getColumns(), + _screen[0]->getLines()), + QSize(_screen[1]->getColumns(), + _screen[1]->getLines()) }; + QSize newSize(columns,lines); + + if (newSize == screenSize[0] && newSize == screenSize[1]) + return; + + _screen[0]->resizeImage(lines,columns); + _screen[1]->resizeImage(lines,columns); + + emit imageSizeChanged(lines,columns); + + bufferedUpdate(); +} + +QSize Emulation::imageSize() const +{ + return QSize(_currentScreen->getColumns(), _currentScreen->getLines()); +} + +ushort ExtendedCharTable::extendedCharHash(ushort* unicodePoints , ushort length) const +{ + ushort hash = 0; + for ( ushort i = 0 ; i < length ; i++ ) + { + hash = 31*hash + unicodePoints[i]; + } + return hash; +} +bool ExtendedCharTable::extendedCharMatch(ushort hash , ushort* unicodePoints , ushort length) const +{ + ushort* entry = extendedCharTable[hash]; + + // compare given length with stored sequence length ( given as the first ushort in the + // stored buffer ) + if ( entry == 0 || entry[0] != length ) + return false; + // if the lengths match, each character must be checked. the stored buffer starts at + // entry[1] + for ( int i = 0 ; i < length ; i++ ) + { + if ( entry[i+1] != unicodePoints[i] ) + return false; + } + return true; +} +ushort ExtendedCharTable::createExtendedChar(ushort* unicodePoints , ushort length) +{ + // look for this sequence of points in the table + ushort hash = extendedCharHash(unicodePoints,length); + + // check existing entry for match + while ( extendedCharTable.contains(hash) ) + { + if ( extendedCharMatch(hash,unicodePoints,length) ) + { + // this sequence already has an entry in the table, + // return its hash + return hash; + } + else + { + // if hash is already used by another, different sequence of unicode character + // points then try next hash + hash++; + } + } + + + // add the new sequence to the table and + // return that index + ushort* buffer = new ushort[length+1]; + buffer[0] = length; + for ( int i = 0 ; i < length ; i++ ) + buffer[i+1] = unicodePoints[i]; + + extendedCharTable.insert(hash,buffer); + + return hash; +} + +ushort* ExtendedCharTable::lookupExtendedChar(ushort hash , ushort& length) const +{ + // lookup index in table and if found, set the length + // argument and return a pointer to the character sequence + + ushort* buffer = extendedCharTable[hash]; + if ( buffer ) + { + length = buffer[0]; + return buffer+1; + } + else + { + length = 0; + return 0; + } +} + +ExtendedCharTable::ExtendedCharTable() +{ +} +ExtendedCharTable::~ExtendedCharTable() +{ + // free all allocated character buffers + QHashIterator<ushort,ushort*> iter(extendedCharTable); + while ( iter.hasNext() ) + { + iter.next(); + delete[] iter.value(); + } +} + +// global instance +ExtendedCharTable ExtendedCharTable::instance; + + +#include "Emulation.moc" +
new file mode 100644 --- /dev/null +++ b/gui/konsole/Emulation.h @@ -0,0 +1,470 @@ +/* + This file is part of Konsole, an X terminal. + + Copyright 2007-2008 by Robert Knight <robertknight@gmail.com> + Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de> + + 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 EMULATION_H +#define EMULATION_H + +// System +#include <stdio.h> + +// Qt +#include <QtGui/QKeyEvent> +//#include <QPointer> +#include <QtCore/QTextCodec> +#include <QtCore/QTextStream> +#include <QtCore/QTimer> + +// Konsole +#include "konsole_export.h" + +namespace Konsole +{ + +class KeyboardTranslator; +class HistoryType; +class Screen; +class ScreenWindow; +class TerminalCharacterDecoder; + +/** + * This enum describes the available states which + * the terminal emulation may be set to. + * + * These are the values used by Emulation::stateChanged() + */ +enum +{ + /** The emulation is currently receiving user input. */ + NOTIFYNORMAL=0, + /** + * The terminal program has triggered a bell event + * to get the user's attention. + */ + NOTIFYBELL=1, + /** + * The emulation is currently receiving data from its + * terminal input. + */ + NOTIFYACTIVITY=2, + + // unused here? + NOTIFYSILENCE=3 +}; + +/** + * Base class for terminal emulation back-ends. + * + * The back-end is responsible for decoding an incoming character stream and + * producing an output image of characters. + * + * When input from the terminal is received, the receiveData() slot should be called with + * the data which has arrived. The emulation will process the data and update the + * screen image accordingly. The codec used to decode the incoming character stream + * into the unicode characters used internally can be specified using setCodec() + * + * The size of the screen image can be specified by calling setImageSize() with the + * desired number of lines and columns. When new lines are added, old content + * is moved into a history store, which can be set by calling setHistory(). + * + * The screen image can be accessed by creating a ScreenWindow onto this emulation + * by calling createWindow(). Screen windows provide access to a section of the + * output. Each screen window covers the same number of lines and columns as the + * image size returned by imageSize(). The screen window can be moved up and down + * and provides transparent access to both the current on-screen image and the + * previous output. The screen windows emit an outputChanged signal + * when the section of the image they are looking at changes. + * Graphical views can then render the contents of a screen window, listening for notifications + * of output changes from the screen window which they are associated with and updating + * accordingly. + * + * The emulation also is also responsible for converting input from the connected views such + * as keypresses and mouse activity into a character string which can be sent + * to the terminal program. Key presses can be processed by calling the sendKeyEvent() slot, + * while mouse events can be processed using the sendMouseEvent() slot. When the character + * stream has been produced, the emulation will emit a sendData() signal with a pointer + * to the character buffer. This data should be fed to the standard input of the terminal + * process. The translation of key presses into an output character stream is performed + * using a lookup in a set of key bindings which map key sequences to output + * character sequences. The name of the key bindings set used can be specified using + * setKeyBindings() + * + * The emulation maintains certain state information which changes depending on the + * input received. The emulation can be reset back to its starting state by calling + * reset(). + * + * The emulation also maintains an activity state, which specifies whether + * terminal is currently active ( when data is received ), normal + * ( when the terminal is idle or receiving user input ) or trying + * to alert the user ( also known as a "Bell" event ). The stateSet() signal + * is emitted whenever the activity state is set. This can be used to determine + * how long the emulation has been active/idle for and also respond to + * a 'bell' event in different ways. + */ +class KONSOLEPRIVATE_EXPORT Emulation : public QObject +{ +Q_OBJECT + +public: + + /** Constructs a new terminal emulation */ + Emulation(); + ~Emulation(); + + /** + * Creates a new window onto the output from this emulation. The contents + * of the window are then rendered by views which are set to use this window using the + * TerminalDisplay::setScreenWindow() method. + */ + ScreenWindow* createWindow(); + + /** Returns the size of the screen image which the emulation produces */ + QSize imageSize() const; + + /** + * Returns the total number of lines, including those stored in the history. + */ + int lineCount() const; + + /** + * Sets the history store used by this emulation. When new lines + * are added to the output, older lines at the top of the screen are transferred to a history + * store. + * + * The number of lines which are kept and the storage location depend on the + * type of store. + */ + void setHistory(const HistoryType&); + /** Returns the history store used by this emulation. See setHistory() */ + const HistoryType& history() const; + /** Clears the history scroll. */ + void clearHistory(); + + /** + * Copies the output history from @p startLine to @p endLine + * into @p stream, using @p decoder to convert the terminal + * characters into text. + * + * @param decoder A decoder which converts lines of terminal characters with + * appearance attributes into output text. PlainTextDecoder is the most commonly + * used decoder. + * @param startLine Index of first line to copy + * @param endLine Index of last line to copy + */ + virtual void writeToStream(TerminalCharacterDecoder* decoder,int startLine,int endLine); + + /** Returns the codec used to decode incoming characters. See setCodec() */ + const QTextCodec* codec() const { return _codec; } + /** Sets the codec used to decode incoming characters. */ + void setCodec(const QTextCodec*); + + /** + * Convenience method. + * Returns true if the current codec used to decode incoming + * characters is UTF-8 + */ + bool utf8() const + { Q_ASSERT(_codec); return _codec->mibEnum() == 106; } + + + /** TODO Document me */ + virtual char eraseChar() const; + + /** + * Sets the key bindings used to key events + * ( received through sendKeyEvent() ) into character + * streams to send to the terminal. + */ + void setKeyBindings(const QString& name); + /** + * Returns the name of the emulation's current key bindings. + * See setKeyBindings() + */ + QString keyBindings() const; + + /** + * Copies the current image into the history and clears the screen. + */ + virtual void clearEntireScreen() =0; + + /** Resets the state of the terminal. */ + virtual void reset() =0; + + /** + * Returns true if the active terminal program wants + * mouse input events. + * + * The programUsesMouseChanged() signal is emitted when this + * changes. + */ + bool programUsesMouse() const; + +public slots: + + /** Change the size of the emulation's image */ + virtual void setImageSize(int lines, int columns); + + /** + * Interprets a sequence of characters and sends the result to the terminal. + * This is equivalent to calling sendKeyEvent() for each character in @p text in succession. + */ + virtual void sendText(const QString& text) = 0; + + /** + * Interprets a key press event and emits the sendData() signal with + * the resulting character stream. + */ + virtual void sendKeyEvent(QKeyEvent*); + + /** + * Converts information about a mouse event into an xterm-compatible escape + * sequence and emits the character sequence via sendData() + */ + virtual void sendMouseEvent(int buttons, int column, int line, int eventType); + + /** + * Sends a string of characters to the foreground terminal process. + * + * @param string The characters to send. + * @param length Length of @p string or if set to a negative value, @p string will + * be treated as a null-terminated string and its length will be determined automatically. + */ + virtual void sendString(const char* string, int length = -1) = 0; + + /** + * Processes an incoming stream of characters. receiveData() decodes the incoming + * character buffer using the current codec(), and then calls receiveChar() for + * each unicode character in the resulting buffer. + * + * receiveData() also starts a timer which causes the outputChanged() signal + * to be emitted when it expires. The timer allows multiple updates in quick + * succession to be buffered into a single outputChanged() signal emission. + * + * @param buffer A string of characters received from the terminal program. + * @param len The length of @p buffer + */ + void receiveData(const char* buffer,int len); + +signals: + + /** + * Emitted when a buffer of data is ready to send to the + * standard input of the terminal. + * + * @param data The buffer of data ready to be sent + * @param len The length of @p data in bytes + */ + void sendData(const char* data,int len); + + /** + * Requests that sending of input to the emulation + * from the terminal process be suspended or resumed. + * + * @param suspend If true, requests that sending of + * input from the terminal process' stdout be + * suspended. Otherwise requests that sending of + * input be resumed. + */ + void lockPtyRequest(bool suspend); + + /** + * Requests that the pty used by the terminal process + * be set to UTF 8 mode. + * + * TODO: More documentation + */ + void useUtf8Request(bool); + + /** + * Emitted when the activity state of the emulation is set. + * + * @param state The new activity state, one of NOTIFYNORMAL, NOTIFYACTIVITY + * or NOTIFYBELL + */ + void stateSet(int state); + + /** TODO Document me */ + void zmodemDetected(); + + + /** + * Requests that the color of the text used + * to represent the tabs associated with this + * emulation be changed. This is a Konsole-specific + * extension from pre-KDE 4 times. + * + * TODO: Document how the parameter works. + */ + void changeTabTextColorRequest(int color); + + /** + * This is emitted when the program running in the shell indicates whether or + * not it is interested in mouse events. + * + * @param usesMouse This will be true if the program wants to be informed about + * mouse events or false otherwise. + */ + void programUsesMouseChanged(bool usesMouse); + + /** + * Emitted when the contents of the screen image change. + * The emulation buffers the updates from successive image changes, + * and only emits outputChanged() at sensible intervals when + * there is a lot of terminal activity. + * + * Normally there is no need for objects other than the screen windows + * created with createWindow() to listen for this signal. + * + * ScreenWindow objects created using createWindow() will emit their + * own outputChanged() signal in response to this signal. + */ + void outputChanged(); + + /** + * Emitted when the program running in the terminal wishes to update the + * session's title. This also allows terminal programs to customize other + * aspects of the terminal emulation display. + * + * This signal is emitted when the escape sequence "\033]ARG;VALUE\007" + * is received in the input string, where ARG is a number specifying what + * should change and VALUE is a string specifying the new value. + * + * TODO: The name of this method is not very accurate since this method + * is used to perform a whole range of tasks besides just setting + * the user-title of the session. + * + * @param title Specifies what to change. + * <ul> + * <li>0 - Set window icon text and session title to @p newTitle</li> + * <li>1 - Set window icon text to @p newTitle</li> + * <li>2 - Set session title to @p newTitle</li> + * <li>11 - Set the session's default background color to @p newTitle, + * where @p newTitle can be an HTML-style string ("#RRGGBB") or a named + * color (eg 'red', 'blue'). + * See http://doc.trolltech.com/4.2/qcolor.html#setNamedColor for more + * details. + * </li> + * <li>31 - Supposedly treats @p newTitle as a URL and opens it (NOT IMPLEMENTED)</li> + * <li>32 - Sets the icon associated with the session. @p newTitle is the name + * of the icon to use, which can be the name of any icon in the current KDE icon + * theme (eg: 'konsole', 'kate', 'folder_home')</li> + * </ul> + * @param newTitle Specifies the new title + */ + + void titleChanged(int title,const QString& newTitle); + + /** + * Emitted when the program running in the terminal changes the + * screen size. + */ + void imageSizeChanged(int lineCount , int columnCount); + + /** + * Emitted when the terminal program requests to change various properties + * of the terminal display. + * + * A profile change command occurs when a special escape sequence, followed + * by a string containing a series of name and value pairs is received. + * This string can be parsed using a ProfileCommandParser instance. + * + * @param text A string expected to contain a series of key and value pairs in + * the form: name=value;name2=value2 ... + */ + void profileChangeCommandReceived(const QString& text); + + /** + * Emitted when a flow control key combination ( Ctrl+S or Ctrl+Q ) is pressed. + * @param suspendKeyPressed True if Ctrl+S was pressed to suspend output or Ctrl+Q to + * resume output. + */ + void flowControlKeyPressed(bool suspendKeyPressed); + +protected: + virtual void setMode(int mode) = 0; + virtual void resetMode(int mode) = 0; + + /** + * Processes an incoming character. See receiveData() + * @p ch A unicode character code. + */ + virtual void receiveChar(int ch); + + /** + * Sets the active screen. The terminal has two screens, primary and alternate. + * The primary screen is used by default. When certain interactive programs such + * as Vim are run, they trigger a switch to the alternate screen. + * + * @param index 0 to switch to the primary screen, or 1 to switch to the alternate screen + */ + void setScreen(int index); + + enum EmulationCodec + { + LocaleCodec = 0, + Utf8Codec = 1 + }; + void setCodec(EmulationCodec codec); // codec number, 0 = locale, 1=utf8 + + + QList<ScreenWindow*> _windows; + + Screen* _currentScreen; // pointer to the screen which is currently active, + // this is one of the elements in the screen[] array + + Screen* _screen[2]; // 0 = primary screen ( used by most programs, including the shell + // scrollbars are enabled in this mode ) + // 1 = alternate ( used by vi , emacs etc. + // scrollbars are not enabled in this mode ) + + + //decodes an incoming C-style character stream into a unicode QString using + //the current text codec. (this allows for rendering of non-ASCII characters in text files etc.) + const QTextCodec* _codec; + QTextDecoder* _decoder; + const KeyboardTranslator* _keyTranslator; // the keyboard layout + +protected slots: + /** + * Schedules an update of attached views. + * Repeated calls to bufferedUpdate() in close succession will result in only a single update, + * much like the Qt buffered update of widgets. + */ + void bufferedUpdate(); + +private slots: + + // triggered by timer, causes the emulation to send an updated screen image to each + // view + void showBulk(); + + void usesMouseChanged(bool usesMouse); + +private: + bool _usesMouse; + QTimer _bulkTimer1; + QTimer _bulkTimer2; + +}; + +} + +#endif // ifndef EMULATION_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/Filter.cpp @@ -0,0 +1,541 @@ +/* + Copyright 2007-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 "Filter.h" + +// System +#include <iostream> + +// Qt +#include <QtGui/QAction> +#include <QtGui/QApplication> +#include <QtGui/QClipboard> +#include <QtCore/QString> +#include <QtCore/QTextStream> +#include <QtCore/QSharedData> +#include <QtCore/QFile> + +// KDE +//#include <KLocale> +//#include <KRun> + +// Konsole +#include "TerminalCharacterDecoder.h" +#include "konsole_wcwidth.h" +#include "konsole_export.h" + +using namespace Konsole; + +FilterChain::~FilterChain() +{ + QMutableListIterator<Filter*> iter(*this); + + while ( iter.hasNext() ) + { + Filter* filter = iter.next(); + iter.remove(); + delete filter; + } +} + +void FilterChain::addFilter(Filter* filter) +{ + append(filter); +} +void FilterChain::removeFilter(Filter* filter) +{ + removeAll(filter); +} +bool FilterChain::containsFilter(Filter* filter) +{ + return contains(filter); +} +void FilterChain::reset() +{ + QListIterator<Filter*> iter(*this); + while (iter.hasNext()) + iter.next()->reset(); +} +void FilterChain::setBuffer(const QString* buffer , const QList<int>* linePositions) +{ + QListIterator<Filter*> iter(*this); + while (iter.hasNext()) + iter.next()->setBuffer(buffer,linePositions); +} +void FilterChain::process() +{ + QListIterator<Filter*> iter(*this); + while (iter.hasNext()) + iter.next()->process(); +} +void FilterChain::clear() +{ + QList<Filter*>::clear(); +} +Filter::HotSpot* FilterChain::hotSpotAt(int line , int column) const +{ + QListIterator<Filter*> iter(*this); + while (iter.hasNext()) + { + Filter* filter = iter.next(); + Filter::HotSpot* spot = filter->hotSpotAt(line,column); + if ( spot != 0 ) + { + return spot; + } + } + + return 0; +} + +QList<Filter::HotSpot*> FilterChain::hotSpots() const +{ + QList<Filter::HotSpot*> list; + QListIterator<Filter*> iter(*this); + while (iter.hasNext()) + { + Filter* filter = iter.next(); + list << filter->hotSpots(); + } + return list; +} +//QList<Filter::HotSpot*> FilterChain::hotSpotsAtLine(int line) const; + +TerminalImageFilterChain::TerminalImageFilterChain() +: _buffer(0) +, _linePositions(0) +{ +} + +TerminalImageFilterChain::~TerminalImageFilterChain() +{ + delete _buffer; + delete _linePositions; +} + +void TerminalImageFilterChain::setImage(const Character* const image , int lines , int columns, const QVector<LineProperty>& lineProperties) +{ + if (empty()) + return; + + // reset all filters and hotspots + reset(); + + PlainTextDecoder decoder; + decoder.setTrailingWhitespace(false); + + // setup new shared buffers for the filters to process on + QString* newBuffer = new QString(); + QList<int>* newLinePositions = new QList<int>(); + setBuffer( newBuffer , newLinePositions ); + + // free the old buffers + delete _buffer; + delete _linePositions; + + _buffer = newBuffer; + _linePositions = newLinePositions; + + QTextStream lineStream(_buffer); + decoder.begin(&lineStream); + + for (int i=0 ; i < lines ; i++) + { + _linePositions->append(_buffer->length()); + decoder.decodeLine(image + i*columns,columns,LINE_DEFAULT); + + // pretend that each line ends with a newline character. + // this prevents a link that occurs at the end of one line + // being treated as part of a link that occurs at the start of the next line + // + // the downside is that links which are spread over more than one line are not + // highlighted. + // + // TODO - Use the "line wrapped" attribute associated with lines in a + // terminal image to avoid adding this imaginary character for wrapped + // lines + if ( !(lineProperties.value(i,LINE_DEFAULT) & LINE_WRAPPED) ) + lineStream << QChar('\n'); + } + decoder.end(); +} + +Filter::Filter() : +_linePositions(0), +_buffer(0) +{ +} + +Filter::~Filter() +{ + QListIterator<HotSpot*> iter(_hotspotList); + while (iter.hasNext()) + { + delete iter.next(); + } +} +void Filter::reset() +{ + _hotspots.clear(); + _hotspotList.clear(); +} + +void Filter::setBuffer(const QString* buffer , const QList<int>* linePositions) +{ + _buffer = buffer; + _linePositions = linePositions; +} + +void Filter::getLineColumn(int position , int& startLine , int& startColumn) +{ + Q_ASSERT( _linePositions ); + Q_ASSERT( _buffer ); + + + for (int i = 0 ; i < _linePositions->count() ; i++) + { + int nextLine = 0; + + if ( i == _linePositions->count()-1 ) + nextLine = _buffer->length() + 1; + else + nextLine = _linePositions->value(i+1); + + if ( _linePositions->value(i) <= position && position < nextLine ) + { + startLine = i; + startColumn = string_width(buffer()->mid(_linePositions->value(i),position - _linePositions->value(i))); + return; + } + } +} + + +/*void Filter::addLine(const QString& text) +{ + _linePositions << _buffer.length(); + _buffer.append(text); +}*/ + +const QString* Filter::buffer() +{ + return _buffer; +} +Filter::HotSpot::~HotSpot() +{ +} +void Filter::addHotSpot(HotSpot* spot) +{ + _hotspotList << spot; + + for (int line = spot->startLine() ; line <= spot->endLine() ; line++) + { + _hotspots.insert(line,spot); + } +} +QList<Filter::HotSpot*> Filter::hotSpots() const +{ + return _hotspotList; +} +QList<Filter::HotSpot*> Filter::hotSpotsAtLine(int line) const +{ + return _hotspots.values(line); +} + +Filter::HotSpot* Filter::hotSpotAt(int line , int column) const +{ + QListIterator<HotSpot*> spotIter(_hotspots.values(line)); + + while (spotIter.hasNext()) + { + HotSpot* spot = spotIter.next(); + + if ( spot->startLine() == line && spot->startColumn() > column ) + continue; + if ( spot->endLine() == line && spot->endColumn() < column ) + continue; + + return spot; + } + + return 0; +} + +Filter::HotSpot::HotSpot(int startLine , int startColumn , int endLine , int endColumn) + : _startLine(startLine) + , _startColumn(startColumn) + , _endLine(endLine) + , _endColumn(endColumn) + , _type(NotSpecified) +{ +} +QString Filter::HotSpot::tooltip() const +{ + return QString(); +} +QList<QAction*> Filter::HotSpot::actions() +{ + return QList<QAction*>(); +} +int Filter::HotSpot::startLine() const +{ + return _startLine; +} +int Filter::HotSpot::endLine() const +{ + return _endLine; +} +int Filter::HotSpot::startColumn() const +{ + return _startColumn; +} +int Filter::HotSpot::endColumn() const +{ + return _endColumn; +} +Filter::HotSpot::Type Filter::HotSpot::type() const +{ + return _type; +} +void Filter::HotSpot::setType(Type type) +{ + _type = type; +} + +RegExpFilter::RegExpFilter() +{ +} + +RegExpFilter::HotSpot::HotSpot(int startLine,int startColumn,int endLine,int endColumn) + : Filter::HotSpot(startLine,startColumn,endLine,endColumn) +{ + setType(Marker); +} + +void RegExpFilter::HotSpot::activate(QObject*) +{ +} + +void RegExpFilter::HotSpot::setCapturedTexts(const QStringList& texts) +{ + _capturedTexts = texts; +} +QStringList RegExpFilter::HotSpot::capturedTexts() const +{ + return _capturedTexts; +} + +void RegExpFilter::setRegExp(const QRegExp& regExp) +{ + _searchText = regExp; +} +QRegExp RegExpFilter::regExp() const +{ + return _searchText; +} +/*void RegExpFilter::reset(int) +{ + _buffer = QString(); +}*/ +void RegExpFilter::process() +{ + int pos = 0; + const QString* text = buffer(); + + Q_ASSERT( text ); + + // ignore any regular expressions which match an empty string. + // otherwise the while loop below will run indefinitely + static const QString emptyString(""); + if ( _searchText.exactMatch(emptyString) ) + return; + + while(pos >= 0) + { + pos = _searchText.indexIn(*text,pos); + + if ( pos >= 0 ) + { + int startLine = 0; + int endLine = 0; + int startColumn = 0; + int endColumn = 0; + + getLineColumn(pos,startLine,startColumn); + getLineColumn(pos + _searchText.matchedLength(),endLine,endColumn); + + RegExpFilter::HotSpot* spot = newHotSpot(startLine,startColumn, + endLine,endColumn); + spot->setCapturedTexts(_searchText.capturedTexts()); + + addHotSpot( spot ); + pos += _searchText.matchedLength(); + + // if matchedLength == 0, the program will get stuck in an infinite loop + if ( _searchText.matchedLength() == 0 ) + pos = -1; + } + } +} + +RegExpFilter::HotSpot* RegExpFilter::newHotSpot(int startLine,int startColumn, + int endLine,int endColumn) +{ + return new RegExpFilter::HotSpot(startLine,startColumn, + endLine,endColumn); +} +RegExpFilter::HotSpot* UrlFilter::newHotSpot(int startLine,int startColumn,int endLine, + int endColumn) +{ + return new UrlFilter::HotSpot(startLine,startColumn, + endLine,endColumn); +} +UrlFilter::HotSpot::HotSpot(int startLine,int startColumn,int endLine,int endColumn) +: RegExpFilter::HotSpot(startLine,startColumn,endLine,endColumn) +, _urlObject(new FilterObject(this)) +{ + setType(Link); +} +QString UrlFilter::HotSpot::tooltip() const +{ + QString url = capturedTexts().first(); + + const UrlType kind = urlType(); + + if ( kind == StandardUrl ) + return QString(); + else if ( kind == Email ) + return QString(); + else + return QString(); +} +UrlFilter::HotSpot::UrlType UrlFilter::HotSpot::urlType() const +{ + QString url = capturedTexts().first(); + + if ( FullUrlRegExp.exactMatch(url) ) + return StandardUrl; + else if ( EmailAddressRegExp.exactMatch(url) ) + return Email; + else + return Unknown; +} + +void UrlFilter::HotSpot::activate(QObject* object) +{ + QString url = capturedTexts().first(); + + const UrlType kind = urlType(); + + const QString& actionName = object ? object->objectName() : QString(); + + if ( actionName == "copy-action" ) + { + QApplication::clipboard()->setText(url); + return; + } + + if ( !object || actionName == "open-action" ) + { + if ( kind == StandardUrl ) + { + // if the URL path does not include the protocol ( eg. "www.kde.org" ) then + // prepend http:// ( eg. "www.kde.org" --> "http://www.kde.org" ) + if (!url.contains("://")) + { + url.prepend("http://"); + } + } + else if ( kind == Email ) + { + url.prepend("mailto:"); + } + + //new KRun(url,QApplication::activeWindow()); + } +} + +// Note: Altering these regular expressions can have a major effect on the performance of the filters +// used for finding URLs in the text, especially if they are very general and could match very long +// pieces of text. +// Please be careful when altering them. + +//regexp matches: +// full url: +// protocolname:// or www. followed by anything other than whitespaces, <, >, ' or ", and ends before whitespaces, <, >, ', ", ], !, comma and dot +const QRegExp UrlFilter::FullUrlRegExp("(www\\.(?!\\.)|[a-z][a-z0-9+.-]*://)[^\\s<>'\"]+[^!,\\.\\s<>'\"\\]]"); +// email address: +// [word chars, dots or dashes]@[word chars, dots or dashes].[word chars] +const QRegExp UrlFilter::EmailAddressRegExp("\\b(\\w|\\.|-)+@(\\w|\\.|-)+\\.\\w+\\b"); + +// matches full url or email address +const QRegExp UrlFilter::CompleteUrlRegExp('('+FullUrlRegExp.pattern()+'|'+ + EmailAddressRegExp.pattern()+')'); + +UrlFilter::UrlFilter() +{ + setRegExp( CompleteUrlRegExp ); +} +UrlFilter::HotSpot::~HotSpot() +{ + delete _urlObject; +} +void FilterObject::activated() +{ + _filter->activate(sender()); +} +QList<QAction*> UrlFilter::HotSpot::actions() +{ + QList<QAction*> list; + + const UrlType kind = urlType(); + + QAction* openAction = new QAction(_urlObject); + QAction* copyAction = new QAction(_urlObject);; + + Q_ASSERT( kind == StandardUrl || kind == Email ); + + if ( kind == StandardUrl ) + { + openAction->setText(i18n("Open Link")); + copyAction->setText(i18n("Copy Link Address")); + } + else if ( kind == Email ) + { + openAction->setText(i18n("Send Email To...")); + copyAction->setText(i18n("Copy Email Address")); + } + + // object names are set here so that the hotspot performs the + // correct action when activated() is called with the triggered + // action passed as a parameter. + openAction->setObjectName( QLatin1String("open-action" )); + copyAction->setObjectName( QLatin1String("copy-action" )); + + QObject::connect( openAction , SIGNAL(triggered()) , _urlObject , SLOT(activated()) ); + QObject::connect( copyAction , SIGNAL(triggered()) , _urlObject , SLOT(activated()) ); + + list << openAction; + list << copyAction; + + return list; +} + +#include "Filter.moc"
new file mode 100644 --- /dev/null +++ b/gui/konsole/Filter.h @@ -0,0 +1,382 @@ +/* + Copyright 2007-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. +*/ + +#ifndef FILTER_H +#define FILTER_H + +// Qt +#include <QtGui/QAction> +#include <QtCore/QList> +#include <QtCore/QObject> +#include <QtCore/QStringList> +#include <QtCore/QHash> +#include <QtCore/QRegExp> + +// Local +#include "Character.h" + +namespace Konsole +{ + +/** + * A filter processes blocks of text looking for certain patterns (such as URLs or keywords from a list) + * and marks the areas which match the filter's patterns as 'hotspots'. + * + * Each hotspot has a type identifier associated with it ( such as a link or a highlighted section ), + * and an action. When the user performs some activity such as a mouse-click in a hotspot area ( the exact + * action will depend on what is displaying the block of text which the filter is processing ), the hotspot's + * activate() method should be called. Depending on the type of hotspot this will trigger a suitable response. + * + * For example, if a hotspot represents a URL then a suitable action would be opening that URL in a web browser. + * Hotspots may have more than one action, in which case the list of actions can be obtained using the + * actions() method. + * + * Different subclasses of filter will return different types of hotspot. + * Subclasses must reimplement the process() method to examine a block of text and identify sections of interest. + * When processing the text they should create instances of Filter::HotSpot subclasses for sections of interest + * and add them to the filter's list of hotspots using addHotSpot() + */ +class Filter +{ +public: + /** + * Represents an area of text which matched the pattern a particular filter has been looking for. + * + * Each hotspot has a type identifier associated with it ( such as a link or a highlighted section ), + * and an action. When the user performs some activity such as a mouse-click in a hotspot area ( the exact + * action will depend on what is displaying the block of text which the filter is processing ), the hotspot's + * activate() method should be called. Depending on the type of hotspot this will trigger a suitable response. + * + * For example, if a hotspot represents a URL then a suitable action would be opening that URL in a web browser. + * Hotspots may have more than one action, in which case the list of actions can be obtained using the + * actions() method. These actions may then be displayed in a popup menu or toolbar for example. + */ + class HotSpot + { + public: + /** + * Constructs a new hotspot which covers the area from (@p startLine,@p startColumn) to (@p endLine,@p endColumn) + * in a block of text. + */ + HotSpot(int startLine , int startColumn , int endLine , int endColumn); + virtual ~HotSpot(); + + enum Type + { + // the type of the hotspot is not specified + NotSpecified, + // this hotspot represents a clickable link + Link, + // this hotspot represents a marker + Marker + }; + + /** Returns the line when the hotspot area starts */ + int startLine() const; + /** Returns the line where the hotspot area ends */ + int endLine() const; + /** Returns the column on startLine() where the hotspot area starts */ + int startColumn() const; + /** Returns the column on endLine() where the hotspot area ends */ + int endColumn() const; + /** + * Returns the type of the hotspot. This is usually used as a hint for views on how to represent + * the hotspot graphically. eg. Link hotspots are typically underlined when the user mouses over them + */ + Type type() const; + /** + * Causes the an action associated with a hotspot to be triggered. + * + * @param object The object which caused the hotspot to be triggered. This is + * typically null ( in which case the default action should be performed ) or + * one of the objects from the actions() list. In which case the associated + * action should be performed. + */ + virtual void activate(QObject* object = 0) = 0; + /** + * Returns a list of actions associated with the hotspot which can be used in a + * menu or toolbar + */ + virtual QList<QAction*> actions(); + + /** + * Returns the text of a tooltip to be shown when the mouse moves over the hotspot, or + * an empty string if there is no tooltip associated with this hotspot. + * + * The default implementation returns an empty string. + */ + virtual QString tooltip() const; + + protected: + /** Sets the type of a hotspot. This should only be set once */ + void setType(Type type); + + private: + int _startLine; + int _startColumn; + int _endLine; + int _endColumn; + Type _type; + + }; + + /** Constructs a new filter. */ + Filter(); + virtual ~Filter(); + + /** Causes the filter to process the block of text currently in its internal buffer */ + virtual void process() = 0; + + /** + * Empties the filters internal buffer and resets the line count back to 0. + * All hotspots are deleted. + */ + void reset(); + + /** Adds a new line of text to the filter and increments the line count */ + //void addLine(const QString& string); + + /** Returns the hotspot which covers the given @p line and @p column, or 0 if no hotspot covers that area */ + HotSpot* hotSpotAt(int line , int column) const; + + /** Returns the list of hotspots identified by the filter */ + QList<HotSpot*> hotSpots() const; + + /** Returns the list of hotspots identified by the filter which occur on a given line */ + QList<HotSpot*> hotSpotsAtLine(int line) const; + + /** + * TODO: Document me + */ + void setBuffer(const QString* buffer , const QList<int>* linePositions); + +protected: + /** Adds a new hotspot to the list */ + void addHotSpot(HotSpot*); + /** Returns the internal buffer */ + const QString* buffer(); + /** Converts a character position within buffer() to a line and column */ + void getLineColumn(int position , int& startLine , int& startColumn); + +private: + QMultiHash<int,HotSpot*> _hotspots; + QList<HotSpot*> _hotspotList; + + const QList<int>* _linePositions; + const QString* _buffer; +}; + +/** + * A filter which searches for sections of text matching a regular expression and creates a new RegExpFilter::HotSpot + * instance for them. + * + * Subclasses can reimplement newHotSpot() to return custom hotspot types when matches for the regular expression + * are found. + */ +class RegExpFilter : public Filter +{ +public: + /** + * Type of hotspot created by RegExpFilter. The capturedTexts() method can be used to find the text + * matched by the filter's regular expression. + */ + class HotSpot : public Filter::HotSpot + { + public: + HotSpot(int startLine, int startColumn, int endLine , int endColumn); + virtual void activate(QObject* object = 0); + + /** Sets the captured texts associated with this hotspot */ + void setCapturedTexts(const QStringList& texts); + /** Returns the texts found by the filter when matching the filter's regular expression */ + QStringList capturedTexts() const; + private: + QStringList _capturedTexts; + }; + + /** Constructs a new regular expression filter */ + RegExpFilter(); + + /** + * Sets the regular expression which the filter searches for in blocks of text. + * + * Regular expressions which match the empty string are treated as not matching + * anything. + */ + void setRegExp(const QRegExp& text); + /** Returns the regular expression which the filter searches for in blocks of text */ + QRegExp regExp() const; + + /** + * Reimplemented to search the filter's text buffer for text matching regExp() + * + * If regexp matches the empty string, then process() will return immediately + * without finding results. + */ + virtual void process(); + +protected: + /** + * Called when a match for the regular expression is encountered. Subclasses should reimplement this + * to return custom hotspot types + */ + virtual RegExpFilter::HotSpot* newHotSpot(int startLine,int startColumn, + int endLine,int endColumn); + +private: + QRegExp _searchText; +}; + +class FilterObject; + +/** A filter which matches URLs in blocks of text */ +class UrlFilter : public RegExpFilter +{ +public: + /** + * Hotspot type created by UrlFilter instances. The activate() method opens a web browser + * at the given URL when called. + */ + class HotSpot : public RegExpFilter::HotSpot + { + public: + HotSpot(int startLine,int startColumn,int endLine,int endColumn); + virtual ~HotSpot(); + + virtual QList<QAction*> actions(); + + /** + * Open a web browser at the current URL. The url itself can be determined using + * the capturedTexts() method. + */ + virtual void activate(QObject* object = 0); + + virtual QString tooltip() const; + private: + enum UrlType + { + StandardUrl, + Email, + Unknown + }; + UrlType urlType() const; + + FilterObject* _urlObject; + }; + + UrlFilter(); + +protected: + virtual RegExpFilter::HotSpot* newHotSpot(int,int,int,int); + +private: + + static const QRegExp FullUrlRegExp; + static const QRegExp EmailAddressRegExp; + + // combined OR of FullUrlRegExp and EmailAddressRegExp + static const QRegExp CompleteUrlRegExp; +}; + +class FilterObject : public QObject +{ +Q_OBJECT +public: + FilterObject(Filter::HotSpot* filter) : _filter(filter) {} +private slots: + void activated(); +private: + Filter::HotSpot* _filter; +}; + +/** + * A chain which allows a group of filters to be processed as one. + * The chain owns the filters added to it and deletes them when the chain itself is destroyed. + * + * Use addFilter() to add a new filter to the chain. + * When new text to be filtered arrives, use addLine() to add each additional + * line of text which needs to be processed and then after adding the last line, use + * process() to cause each filter in the chain to process the text. + * + * After processing a block of text, the reset() method can be used to set the filter chain's + * internal cursor back to the first line. + * + * The hotSpotAt() method will return the first hotspot which covers a given position. + * + * The hotSpots() and hotSpotsAtLine() method return all of the hotspots in the text and on + * a given line respectively. + */ +class FilterChain : protected QList<Filter*> +{ +public: + virtual ~FilterChain(); + + /** Adds a new filter to the chain. The chain will delete this filter when it is destroyed */ + void addFilter(Filter* filter); + /** Removes a filter from the chain. The chain will no longer delete the filter when destroyed */ + void removeFilter(Filter* filter); + /** Returns true if the chain contains @p filter */ + bool containsFilter(Filter* filter); + /** Removes all filters from the chain */ + void clear(); + + /** Resets each filter in the chain */ + void reset(); + /** + * Processes each filter in the chain + */ + void process(); + + /** Sets the buffer for each filter in the chain to process. */ + void setBuffer(const QString* buffer , const QList<int>* linePositions); + + /** Returns the first hotspot which occurs at @p line, @p column or 0 if no hotspot was found */ + Filter::HotSpot* hotSpotAt(int line , int column) const; + /** Returns a list of all the hotspots in all the chain's filters */ + QList<Filter::HotSpot*> hotSpots() const; + /** Returns a list of all hotspots at the given line in all the chain's filters */ + QList<Filter::HotSpot> hotSpotsAtLine(int line) const; + +}; + +/** A filter chain which processes character images from terminal displays */ +class TerminalImageFilterChain : public FilterChain +{ +public: + TerminalImageFilterChain(); + virtual ~TerminalImageFilterChain(); + + /** + * Set the current terminal image to @p image. + * + * @param image The terminal image + * @param lines The number of lines in the terminal image + * @param columns The number of columns in the terminal image + * @param lineProperties The line properties to set for image + */ + void setImage(const Character* const image , int lines , int columns, + const QVector<LineProperty>& lineProperties); + +private: + QString* _buffer; + QList<int>* _linePositions; +}; + +} +#endif //FILTER_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/History.cpp @@ -0,0 +1,987 @@ +/* + This file is part of Konsole, an X terminal. + Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de> + + 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 "History.h" + +// System +#include <iostream> +#include <stdlib.h> +#include <assert.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <unistd.h> +#include <errno.h> + +// KDE +//#include <kde_file.h> +//#include <kdebug.h> + +// Reasonable line size +#define LINE_SIZE 1024 + +using namespace Konsole; + +/* + An arbitrary long scroll. + + One can modify the scroll only by adding either cells + or newlines, but access it randomly. + + The model is that of an arbitrary wide typewriter scroll + in that the scroll is a serie of lines and each line is + a serie of cells with no overwriting permitted. + + The implementation provides arbitrary length and numbers + of cells and line/column indexed read access to the scroll + at constant costs. + +KDE4: Can we use QTemporaryFile here, instead of KTempFile? + +FIXME: some complain about the history buffer comsuming the + memory of their machines. This problem is critical + since the history does not behave gracefully in cases + where the memory is used up completely. + + I put in a workaround that should handle it problem + now gracefully. I'm not satisfied with the solution. + +FIXME: Terminating the history is not properly indicated + in the menu. We should throw a signal. + +FIXME: There is noticeable decrease in speed, also. Perhaps, + there whole feature needs to be revisited therefore. + Disadvantage of a more elaborated, say block-oriented + scheme with wrap around would be it's complexity. +*/ + +//FIXME: tempory replacement for tmpfile +// this is here one for debugging purpose. + +//#define tmpfile xTmpFile + +// History File /////////////////////////////////////////// + +/* + A Row(X) data type which allows adding elements to the end. +*/ + +HistoryFile::HistoryFile() + : ion(-1), + length(0), + fileMap(0) +{ + if (tmpFile.open()) + { + tmpFile.setAutoRemove(true); + ion = tmpFile.handle(); + } +} + +HistoryFile::~HistoryFile() +{ + if (fileMap) + unmap(); +} + +//TODO: Mapping the entire file in will cause problems if the history file becomes exceedingly large, +//(ie. larger than available memory). HistoryFile::map() should only map in sections of the file at a time, +//to avoid this. +void HistoryFile::map() +{ + assert( fileMap == 0 ); + + fileMap = (char*)mmap( 0 , length , PROT_READ , MAP_PRIVATE , ion , 0 ); + + //if mmap'ing fails, fall back to the read-lseek combination + if ( fileMap == MAP_FAILED ) + { + readWriteBalance = 0; + fileMap = 0; + //kWarning() << k_funcinfo << ": mmap'ing history failed. errno = " << errno; + kWarning() << ": mmap'ing history failed. errno = " << errno; + } +} + +void HistoryFile::unmap() +{ + int result = munmap( fileMap , length ); + assert( result == 0 ); Q_UNUSED( result ); + + fileMap = 0; +} + +bool HistoryFile::isMapped() +{ + return (fileMap != 0); +} + +void HistoryFile::add(const unsigned char* bytes, int len) +{ + if ( fileMap ) + unmap(); + + readWriteBalance++; + + int rc = 0; + + //rc = KDE_lseek(ion,length,SEEK_SET); if (rc < 0) { perror("HistoryFile::add.seek"); return; } + rc = ::lseek(ion,length,SEEK_SET); if (rc < 0) { perror("HistoryFile::add.seek"); return; } + rc = write(ion,bytes,len); if (rc < 0) { perror("HistoryFile::add.write"); return; } + length += rc; +} + +void HistoryFile::get(unsigned char* bytes, int len, int loc) +{ + //count number of get() calls vs. number of add() calls. + //If there are many more get() calls compared with add() + //calls (decided by using MAP_THRESHOLD) then mmap the log + //file to improve performance. + readWriteBalance--; + if ( !fileMap && readWriteBalance < MAP_THRESHOLD ) + map(); + + if ( fileMap ) + { + for (int i=0;i<len;i++) + bytes[i]=fileMap[loc+i]; + } + else + { + int rc = 0; + + if (loc < 0 || len < 0 || loc + len > length) + fprintf(stderr,"getHist(...,%d,%d): invalid args.\n",len,loc); + //rc = KDE_lseek(ion,loc,SEEK_SET); if (rc < 0) { perror("HistoryFile::get.seek"); return; } + rc = ::lseek(ion,loc,SEEK_SET); if (rc < 0) { perror("HistoryFile::get.seek"); return; } + rc = read(ion,bytes,len); if (rc < 0) { perror("HistoryFile::get.read"); return; } + } +} + +int HistoryFile::len() +{ + return length; +} + + +// History Scroll abstract base class ////////////////////////////////////// + + +HistoryScroll::HistoryScroll(HistoryType* t) + : m_histType(t) +{ +} + +HistoryScroll::~HistoryScroll() +{ + delete m_histType; +} + +bool HistoryScroll::hasScroll() +{ + return true; +} + +// History Scroll File ////////////////////////////////////// + +/* + The history scroll makes a Row(Row(Cell)) from + two history buffers. The index buffer contains + start of line positions which refere to the cells + buffer. + + Note that index[0] addresses the second line + (line #1), while the first line (line #0) starts + at 0 in cells. +*/ + +HistoryScrollFile::HistoryScrollFile(const QString &logFileName) + : HistoryScroll(new HistoryTypeFile(logFileName)), + m_logFileName(logFileName) +{ +} + +HistoryScrollFile::~HistoryScrollFile() +{ +} + +int HistoryScrollFile::getLines() +{ + return index.len() / sizeof(int); +} + +int HistoryScrollFile::getLineLen(int lineno) +{ + return (startOfLine(lineno+1) - startOfLine(lineno)) / sizeof(Character); +} + +bool HistoryScrollFile::isWrappedLine(int lineno) +{ + if (lineno>=0 && lineno <= getLines()) { + unsigned char flag; + lineflags.get((unsigned char*)&flag,sizeof(unsigned char),(lineno)*sizeof(unsigned char)); + return flag; + } + return false; +} + +int HistoryScrollFile::startOfLine(int lineno) +{ + if (lineno <= 0) return 0; + if (lineno <= getLines()) + { + + if (!index.isMapped()) + index.map(); + + int res; + index.get((unsigned char*)&res,sizeof(int),(lineno-1)*sizeof(int)); + return res; + } + return cells.len(); +} + +void HistoryScrollFile::getCells(int lineno, int colno, int count, Character res[]) +{ + cells.get((unsigned char*)res,count*sizeof(Character),startOfLine(lineno)+colno*sizeof(Character)); +} + +void HistoryScrollFile::addCells(const Character text[], int count) +{ + cells.add((unsigned char*)text,count*sizeof(Character)); +} + +void HistoryScrollFile::addLine(bool previousWrapped) +{ + if (index.isMapped()) + index.unmap(); + + int locn = cells.len(); + index.add((unsigned char*)&locn,sizeof(int)); + unsigned char flags = previousWrapped ? 0x01 : 0x00; + lineflags.add((unsigned char*)&flags,sizeof(unsigned char)); +} + + +// History Scroll Buffer ////////////////////////////////////// +HistoryScrollBuffer::HistoryScrollBuffer(unsigned int maxLineCount) + : HistoryScroll(new HistoryTypeBuffer(maxLineCount)) + ,_historyBuffer() + ,_maxLineCount(0) + ,_usedLines(0) + ,_head(0) +{ + setMaxNbLines(maxLineCount); +} + +HistoryScrollBuffer::~HistoryScrollBuffer() +{ + delete[] _historyBuffer; +} + +void HistoryScrollBuffer::addCellsVector(const QVector<Character>& cells) +{ + _head++; + if ( _usedLines < _maxLineCount ) + _usedLines++; + + if ( _head >= _maxLineCount ) + { + _head = 0; + } + + _historyBuffer[bufferIndex(_usedLines-1)] = cells; + _wrappedLine[bufferIndex(_usedLines-1)] = false; +} +void HistoryScrollBuffer::addCells(const Character a[], int count) +{ + HistoryLine newLine(count); + qCopy(a,a+count,newLine.begin()); + + addCellsVector(newLine); +} + +void HistoryScrollBuffer::addLine(bool previousWrapped) +{ + _wrappedLine[bufferIndex(_usedLines-1)] = previousWrapped; +} + +int HistoryScrollBuffer::getLines() +{ + return _usedLines; +} + +int HistoryScrollBuffer::getLineLen(int lineNumber) +{ + Q_ASSERT( lineNumber >= 0 && lineNumber < _maxLineCount ); + + if ( lineNumber < _usedLines ) + { + return _historyBuffer[bufferIndex(lineNumber)].size(); + } + else + { + return 0; + } +} + +bool HistoryScrollBuffer::isWrappedLine(int lineNumber) +{ + Q_ASSERT( lineNumber >= 0 && lineNumber < _maxLineCount ); + + if (lineNumber < _usedLines) + { + //kDebug() << "Line" << lineNumber << "wrapped is" << _wrappedLine[bufferIndex(lineNumber)]; + return _wrappedLine[bufferIndex(lineNumber)]; + } + else + return false; +} + +void HistoryScrollBuffer::getCells(int lineNumber, int startColumn, int count, Character buffer[]) +{ + if ( count == 0 ) return; + + Q_ASSERT( lineNumber < _maxLineCount ); + + if (lineNumber >= _usedLines) + { + memset(buffer, 0, count * sizeof(Character)); + return; + } + + const HistoryLine& line = _historyBuffer[bufferIndex(lineNumber)]; + + //kDebug() << "startCol " << startColumn; + //kDebug() << "line.size() " << line.size(); + //kDebug() << "count " << count; + + Q_ASSERT( startColumn <= line.size() - count ); + + memcpy(buffer, line.constData() + startColumn , count * sizeof(Character)); +} + +void HistoryScrollBuffer::setMaxNbLines(unsigned int lineCount) +{ + HistoryLine* oldBuffer = _historyBuffer; + HistoryLine* newBuffer = new HistoryLine[lineCount]; + + for ( int i = 0 ; i < qMin(_usedLines,(int)lineCount) ; i++ ) + { + newBuffer[i] = oldBuffer[bufferIndex(i)]; + } + + _usedLines = qMin(_usedLines,(int)lineCount); + _maxLineCount = lineCount; + _head = ( _usedLines == _maxLineCount ) ? 0 : _usedLines-1; + + _historyBuffer = newBuffer; + delete[] oldBuffer; + + _wrappedLine.resize(lineCount); + dynamic_cast<HistoryTypeBuffer*>(m_histType)->m_nbLines = lineCount; +} + +int HistoryScrollBuffer::bufferIndex(int lineNumber) +{ + Q_ASSERT( lineNumber >= 0 ); + Q_ASSERT( lineNumber < _maxLineCount ); + Q_ASSERT( (_usedLines == _maxLineCount) || lineNumber <= _head ); + + if ( _usedLines == _maxLineCount ) + { + return (_head+lineNumber+1) % _maxLineCount; + } + else + { + return lineNumber; + } +} + + +// History Scroll None ////////////////////////////////////// + +HistoryScrollNone::HistoryScrollNone() + : HistoryScroll(new HistoryTypeNone()) +{ +} + +HistoryScrollNone::~HistoryScrollNone() +{ +} + +bool HistoryScrollNone::hasScroll() +{ + return false; +} + +int HistoryScrollNone::getLines() +{ + return 0; +} + +int HistoryScrollNone::getLineLen(int) +{ + return 0; +} + +bool HistoryScrollNone::isWrappedLine(int /*lineno*/) +{ + return false; +} + +void HistoryScrollNone::getCells(int, int, int, Character []) +{ +} + +void HistoryScrollNone::addCells(const Character [], int) +{ +} + +void HistoryScrollNone::addLine(bool) +{ +} + +// History Scroll BlockArray ////////////////////////////////////// + +HistoryScrollBlockArray::HistoryScrollBlockArray(size_t size) + : HistoryScroll(new HistoryTypeBlockArray(size)) +{ + m_blockArray.setHistorySize(size); // nb. of lines. +} + +HistoryScrollBlockArray::~HistoryScrollBlockArray() +{ +} + +int HistoryScrollBlockArray::getLines() +{ + return m_lineLengths.count(); +} + +int HistoryScrollBlockArray::getLineLen(int lineno) +{ + if ( m_lineLengths.contains(lineno) ) + return m_lineLengths[lineno]; + else + return 0; +} + +bool HistoryScrollBlockArray::isWrappedLine(int /*lineno*/) +{ + return false; +} + +void HistoryScrollBlockArray::getCells(int lineno, int colno, + int count, Character res[]) +{ + if (!count) return; + + const Block *b = m_blockArray.at(lineno); + + if (!b) { + memset(res, 0, count * sizeof(Character)); // still better than random data + return; + } + + assert(((colno + count) * sizeof(Character)) < ENTRIES); + memcpy(res, b->data + (colno * sizeof(Character)), count * sizeof(Character)); +} + +void HistoryScrollBlockArray::addCells(const Character a[], int count) +{ + Block *b = m_blockArray.lastBlock(); + + if (!b) return; + + // put cells in block's data + assert((count * sizeof(Character)) < ENTRIES); + + memset(b->data, 0, ENTRIES); + + memcpy(b->data, a, count * sizeof(Character)); + b->size = count * sizeof(Character); + + size_t res = m_blockArray.newBlock(); + assert (res > 0); + Q_UNUSED( res ); + + m_lineLengths.insert(m_blockArray.getCurrent(), count); +} + +void HistoryScrollBlockArray::addLine(bool) +{ +} + +//////////////////////////////////////////////////////////////// +// Compact History Scroll ////////////////////////////////////// +//////////////////////////////////////////////////////////////// +void* CompactHistoryBlock::allocate ( size_t length ) +{ + Q_ASSERT ( length > 0 ); + if ( tail-blockStart+length > blockLength ) + return NULL; + + void* block = tail; + tail += length; + //kDebug() << "allocated " << length << " bytes at address " << block; + allocCount++; + return block; +} + +void CompactHistoryBlock::deallocate ( ) +{ + allocCount--; + Q_ASSERT ( allocCount >= 0 ); +} + +void* CompactHistoryBlockList::allocate(size_t size) +{ + CompactHistoryBlock* block; + if ( list.isEmpty() || list.last()->remaining() < size) + { + block = new CompactHistoryBlock(); + list.append ( block ); + //kDebug() << "new block created, remaining " << block->remaining() << "number of blocks=" << list.size(); + } + else + { + block = list.last(); + //kDebug() << "old block used, remaining " << block->remaining(); + } + return block->allocate(size); +} + +void CompactHistoryBlockList::deallocate(void* ptr) +{ + Q_ASSERT( !list.isEmpty()); + + int i=0; + CompactHistoryBlock *block = list.at(i); + while ( i<list.size() && !block->contains(ptr) ) + { + i++; + block=list.at(i); + } + + Q_ASSERT( i<list.size() ); + + block->deallocate(); + + if (!block->isInUse()) + { + list.removeAt(i); + delete block; + //kDebug() << "block deleted, new size = " << list.size(); + } +} + +CompactHistoryBlockList::~CompactHistoryBlockList() +{ + qDeleteAll ( list.begin(), list.end() ); + list.clear(); +} + +void* CompactHistoryLine::operator new (size_t size, CompactHistoryBlockList& blockList) +{ + return blockList.allocate(size); +} + +CompactHistoryLine::CompactHistoryLine ( const TextLine& line, CompactHistoryBlockList& bList ) + : blockList(bList), + formatLength(0) +{ + length=line.size(); + + if (line.size() > 0) { + formatLength=1; + int k=1; + + // count number of different formats in this text line + Character c = line[0]; + while ( k<length ) + { + if ( !(line[k].equalsFormat(c))) + { + formatLength++; // format change detected + c=line[k]; + } + k++; + } + + //kDebug() << "number of different formats in string: " << formatLength; + formatArray = (CharacterFormat*) blockList.allocate(sizeof(CharacterFormat)*formatLength); + Q_ASSERT (formatArray!=NULL); + text = (quint16*) blockList.allocate(sizeof(quint16)*line.size()); + Q_ASSERT (text!=NULL); + + length=line.size(); + formatLength=formatLength; + wrapped=false; + + // record formats and their positions in the format array + c=line[0]; + formatArray[0].setFormat ( c ); + formatArray[0].startPos=0; // there's always at least 1 format (for the entire line, unless a change happens) + + k=1; // look for possible format changes + int j=1; + while ( k<length && j<formatLength ) + { + if (!(line[k].equalsFormat(c))) + { + c=line[k]; + formatArray[j].setFormat(c); + formatArray[j].startPos=k; + //kDebug() << "format entry " << j << " at pos " << formatArray[j].startPos << " " << &(formatArray[j].startPos) ; + j++; + } + k++; + } + + // copy character values + for ( int i=0; i<line.size(); i++ ) + { + text[i]=line[i].character; + //kDebug() << "char " << i << " at mem " << &(text[i]); + } + } + //kDebug() << "line created, length " << length << " at " << &(length); +} + +CompactHistoryLine::~CompactHistoryLine() +{ + //kDebug() << "~CHL"; + if (length>0) { + blockList.deallocate(text); + blockList.deallocate(formatArray); + } + blockList.deallocate(this); +} + +void CompactHistoryLine::getCharacter ( int index, Character &r ) +{ + Q_ASSERT ( index < length ); + int formatPos=0; + while ( ( formatPos+1 ) < formatLength && index >= formatArray[formatPos+1].startPos ) + formatPos++; + + r.character=text[index]; + r.rendition = formatArray[formatPos].rendition; + r.foregroundColor = formatArray[formatPos].fgColor; + r.backgroundColor = formatArray[formatPos].bgColor; +} + +void CompactHistoryLine::getCharacters ( Character* array, int length, int startColumn ) +{ + Q_ASSERT ( startColumn >= 0 && length >= 0 ); + Q_ASSERT ( startColumn+length <= ( int ) getLength() ); + + for ( int i=startColumn; i<length+startColumn; i++ ) + { + getCharacter ( i, array[i-startColumn] ); + } +} + +CompactHistoryScroll::CompactHistoryScroll ( unsigned int maxLineCount ) + : HistoryScroll ( new CompactHistoryType ( maxLineCount ) ) + ,lines() + ,blockList() +{ + //kDebug() << "scroll of length " << maxLineCount << " created"; + setMaxNbLines ( maxLineCount ); +} + +CompactHistoryScroll::~CompactHistoryScroll() +{ + qDeleteAll ( lines.begin(), lines.end() ); + lines.clear(); +} + +void CompactHistoryScroll::addCellsVector ( const TextLine& cells ) +{ + CompactHistoryLine *line; + line = new(blockList) CompactHistoryLine ( cells, blockList ); + + if ( lines.size() > ( int ) _maxLineCount ) + { + delete lines.takeAt ( 0 ); + } + lines.append ( line ); +} + +void CompactHistoryScroll::addCells ( const Character a[], int count ) +{ + TextLine newLine ( count ); + qCopy ( a,a+count,newLine.begin() ); + addCellsVector ( newLine ); +} + +void CompactHistoryScroll::addLine ( bool previousWrapped ) +{ + CompactHistoryLine *line = lines.last(); + //kDebug() << "last line at address " << line; + line->setWrapped(previousWrapped); +} + +int CompactHistoryScroll::getLines() +{ + return lines.size(); +} + +int CompactHistoryScroll::getLineLen ( int lineNumber ) +{ + Q_ASSERT ( lineNumber >= 0 && lineNumber < lines.size() ); + CompactHistoryLine* line = lines[lineNumber]; + //kDebug() << "request for line at address " << line; + return line->getLength(); +} + + +void CompactHistoryScroll::getCells ( int lineNumber, int startColumn, int count, Character buffer[] ) +{ + if ( count == 0 ) return; + Q_ASSERT ( lineNumber < lines.size() ); + CompactHistoryLine* line = lines[lineNumber]; + Q_ASSERT ( startColumn >= 0 ); + Q_ASSERT ( (unsigned int)startColumn <= line->getLength() - count ); + line->getCharacters ( buffer, count, startColumn ); +} + +void CompactHistoryScroll::setMaxNbLines ( unsigned int lineCount ) +{ + _maxLineCount = lineCount; + + while (lines.size() > (int) lineCount) { + delete lines.takeAt(0); + } + //kDebug() << "set max lines to: " << _maxLineCount; +} + +bool CompactHistoryScroll::isWrappedLine ( int lineNumber ) +{ + Q_ASSERT ( lineNumber < lines.size() ); + return lines[lineNumber]->isWrapped(); +} + + +////////////////////////////////////////////////////////////////////// +// History Types +////////////////////////////////////////////////////////////////////// + +HistoryType::HistoryType() +{ +} + +HistoryType::~HistoryType() +{ +} + +////////////////////////////// + +HistoryTypeNone::HistoryTypeNone() +{ +} + +bool HistoryTypeNone::isEnabled() const +{ + return false; +} + +HistoryScroll* HistoryTypeNone::scroll(HistoryScroll *old) const +{ + delete old; + return new HistoryScrollNone(); +} + +int HistoryTypeNone::maximumLineCount() const +{ + return 0; +} + +////////////////////////////// + +HistoryTypeBlockArray::HistoryTypeBlockArray(size_t size) + : m_size(size) +{ +} + +bool HistoryTypeBlockArray::isEnabled() const +{ + return true; +} + +int HistoryTypeBlockArray::maximumLineCount() const +{ + return m_size; +} + +HistoryScroll* HistoryTypeBlockArray::scroll(HistoryScroll *old) const +{ + delete old; + return new HistoryScrollBlockArray(m_size); +} + + +////////////////////////////// + +HistoryTypeBuffer::HistoryTypeBuffer(unsigned int nbLines) + : m_nbLines(nbLines) +{ +} + +bool HistoryTypeBuffer::isEnabled() const +{ + return true; +} + +int HistoryTypeBuffer::maximumLineCount() const +{ + return m_nbLines; +} + +HistoryScroll* HistoryTypeBuffer::scroll(HistoryScroll *old) const +{ + if (old) + { + HistoryScrollBuffer *oldBuffer = dynamic_cast<HistoryScrollBuffer*>(old); + if (oldBuffer) + { + oldBuffer->setMaxNbLines(m_nbLines); + return oldBuffer; + } + + HistoryScroll *newScroll = new HistoryScrollBuffer(m_nbLines); + int lines = old->getLines(); + int startLine = 0; + if (lines > (int) m_nbLines) + startLine = lines - m_nbLines; + + Character line[LINE_SIZE]; + for(int i = startLine; i < lines; i++) + { + int size = old->getLineLen(i); + if (size > LINE_SIZE) + { + Character *tmp_line = new Character[size]; + old->getCells(i, 0, size, tmp_line); + newScroll->addCells(tmp_line, size); + newScroll->addLine(old->isWrappedLine(i)); + delete [] tmp_line; + } + else + { + old->getCells(i, 0, size, line); + newScroll->addCells(line, size); + newScroll->addLine(old->isWrappedLine(i)); + } + } + delete old; + return newScroll; + } + return new HistoryScrollBuffer(m_nbLines); +} + +////////////////////////////// + +HistoryTypeFile::HistoryTypeFile(const QString& fileName) + : m_fileName(fileName) +{ +} + +bool HistoryTypeFile::isEnabled() const +{ + return true; +} + +const QString& HistoryTypeFile::getFileName() const +{ + return m_fileName; +} + +HistoryScroll* HistoryTypeFile::scroll(HistoryScroll *old) const +{ + if (dynamic_cast<HistoryFile *>(old)) + return old; // Unchanged. + + HistoryScroll *newScroll = new HistoryScrollFile(m_fileName); + + Character line[LINE_SIZE]; + int lines = (old != 0) ? old->getLines() : 0; + for(int i = 0; i < lines; i++) + { + int size = old->getLineLen(i); + if (size > LINE_SIZE) + { + Character *tmp_line = new Character[size]; + old->getCells(i, 0, size, tmp_line); + newScroll->addCells(tmp_line, size); + newScroll->addLine(old->isWrappedLine(i)); + delete [] tmp_line; + } + else + { + old->getCells(i, 0, size, line); + newScroll->addCells(line, size); + newScroll->addLine(old->isWrappedLine(i)); + } + } + + delete old; + return newScroll; +} + +int HistoryTypeFile::maximumLineCount() const +{ + return 0; +} + +////////////////////////////// + +CompactHistoryType::CompactHistoryType ( unsigned int nbLines ) + : m_nbLines ( nbLines ) +{ +} + +bool CompactHistoryType::isEnabled() const +{ + return true; +} + +int CompactHistoryType::maximumLineCount() const +{ + return m_nbLines; +} + +HistoryScroll* CompactHistoryType::scroll ( HistoryScroll *old ) const +{ + if ( old ) + { + CompactHistoryScroll *oldBuffer = dynamic_cast<CompactHistoryScroll*> ( old ); + if ( oldBuffer ) + { + oldBuffer->setMaxNbLines ( m_nbLines ); + return oldBuffer; + } + delete old; + } + return new CompactHistoryScroll ( m_nbLines ); +}
new file mode 100644 --- /dev/null +++ b/gui/konsole/History.h @@ -0,0 +1,494 @@ +/* + This file is part of Konsole, an X terminal. + Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de> + + 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 TEHISTORY_H +#define TEHISTORY_H + +// Qt +#include <QtCore/QBitRef> +#include <QtCore/QHash> +#include <QtCore/QVector> +#include <QtCore/QTemporaryFile> + +// KDE +//#include <ktemporaryfile.h> + +// Konsole +#include "konsole_export.h" +#include "BlockArray.h" +#include "Character.h" + +// map +#include <sys/mman.h> + +namespace Konsole +{ + +#if 1 +/* + An extendable tmpfile(1) based buffer. +*/ + +class HistoryFile +{ +public: + HistoryFile(); + virtual ~HistoryFile(); + + virtual void add(const unsigned char* bytes, int len); + virtual void get(unsigned char* bytes, int len, int loc); + virtual int len(); + + //mmaps the file in read-only mode + void map(); + //un-mmaps the file + void unmap(); + //returns true if the file is mmap'ed + bool isMapped(); + + +private: + int ion; + int length; + QTemporaryFile tmpFile; + + //pointer to start of mmap'ed file data, or 0 if the file is not mmap'ed + char* fileMap; + + //incremented whenver 'add' is called and decremented whenever + //'get' is called. + //this is used to detect when a large number of lines are being read and processed from the history + //and automatically mmap the file for better performance (saves the overhead of many lseek-read calls). + int readWriteBalance; + + //when readWriteBalance goes below this threshold, the file will be mmap'ed automatically + static const int MAP_THRESHOLD = -1000; +}; +#endif + +////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////// +// Abstract base class for file and buffer versions +////////////////////////////////////////////////////////////////////// +class HistoryType; + +class HistoryScroll +{ +public: + HistoryScroll(HistoryType*); + virtual ~HistoryScroll(); + + virtual bool hasScroll(); + + // access to history + virtual int getLines() = 0; + virtual int getLineLen(int lineno) = 0; + virtual void getCells(int lineno, int colno, int count, Character res[]) = 0; + virtual bool isWrappedLine(int lineno) = 0; + + // backward compatibility (obsolete) + Character getCell(int lineno, int colno) { Character res; getCells(lineno,colno,1,&res); return res; } + + // adding lines. + virtual void addCells(const Character a[], int count) = 0; + // convenience method - this is virtual so that subclasses can take advantage + // of QVector's implicit copying + virtual void addCellsVector(const QVector<Character>& cells) + { + addCells(cells.data(),cells.size()); + } + + virtual void addLine(bool previousWrapped=false) = 0; + + // + // FIXME: Passing around constant references to HistoryType instances + // is very unsafe, because those references will no longer + // be valid if the history scroll is deleted. + // + const HistoryType& getType() { return *m_histType; } + +protected: + HistoryType* m_histType; + +}; + +#if 1 + +////////////////////////////////////////////////////////////////////// +// File-based history (e.g. file log, no limitation in length) +////////////////////////////////////////////////////////////////////// + +class HistoryScrollFile : public HistoryScroll +{ +public: + HistoryScrollFile(const QString &logFileName); + virtual ~HistoryScrollFile(); + + virtual int getLines(); + virtual int getLineLen(int lineno); + virtual void getCells(int lineno, int colno, int count, Character res[]); + virtual bool isWrappedLine(int lineno); + + virtual void addCells(const Character a[], int count); + virtual void addLine(bool previousWrapped=false); + +private: + int startOfLine(int lineno); + + QString m_logFileName; + HistoryFile index; // lines Row(int) + HistoryFile cells; // text Row(Character) + HistoryFile lineflags; // flags Row(unsigned char) +}; + + +////////////////////////////////////////////////////////////////////// +// Buffer-based history (limited to a fixed nb of lines) +////////////////////////////////////////////////////////////////////// +class HistoryScrollBuffer : public HistoryScroll +{ +public: + typedef QVector<Character> HistoryLine; + + HistoryScrollBuffer(unsigned int maxNbLines = 1000); + virtual ~HistoryScrollBuffer(); + + virtual int getLines(); + virtual int getLineLen(int lineno); + virtual void getCells(int lineno, int colno, int count, Character res[]); + virtual bool isWrappedLine(int lineno); + + virtual void addCells(const Character a[], int count); + virtual void addCellsVector(const QVector<Character>& cells); + virtual void addLine(bool previousWrapped=false); + + void setMaxNbLines(unsigned int nbLines); + unsigned int maxNbLines() { return _maxLineCount; } + + +private: + int bufferIndex(int lineNumber); + + HistoryLine* _historyBuffer; + QBitArray _wrappedLine; + int _maxLineCount; + int _usedLines; + int _head; + + //QVector<histline*> m_histBuffer; + //QBitArray m_wrappedLine; + //unsigned int m_maxNbLines; + //unsigned int m_nbLines; + //unsigned int m_arrayIndex; + //bool m_buffFilled; +}; + +/*class HistoryScrollBufferV2 : public HistoryScroll +{ +public: + virtual int getLines(); + virtual int getLineLen(int lineno); + virtual void getCells(int lineno, int colno, int count, Character res[]); + virtual bool isWrappedLine(int lineno); + + virtual void addCells(const Character a[], int count); + virtual void addCells(const QVector<Character>& cells); + virtual void addLine(bool previousWrapped=false); + +};*/ + +#endif + +////////////////////////////////////////////////////////////////////// +// Nothing-based history (no history :-) +////////////////////////////////////////////////////////////////////// +class HistoryScrollNone : public HistoryScroll +{ +public: + HistoryScrollNone(); + virtual ~HistoryScrollNone(); + + virtual bool hasScroll(); + + virtual int getLines(); + virtual int getLineLen(int lineno); + virtual void getCells(int lineno, int colno, int count, Character res[]); + virtual bool isWrappedLine(int lineno); + + virtual void addCells(const Character a[], int count); + virtual void addLine(bool previousWrapped=false); +}; + +////////////////////////////////////////////////////////////////////// +// BlockArray-based history +////////////////////////////////////////////////////////////////////// +class HistoryScrollBlockArray : public HistoryScroll +{ +public: + HistoryScrollBlockArray(size_t size); + virtual ~HistoryScrollBlockArray(); + + virtual int getLines(); + virtual int getLineLen(int lineno); + virtual void getCells(int lineno, int colno, int count, Character res[]); + virtual bool isWrappedLine(int lineno); + + virtual void addCells(const Character a[], int count); + virtual void addLine(bool previousWrapped=false); + +protected: + BlockArray m_blockArray; + QHash<int,size_t> m_lineLengths; +}; + +////////////////////////////////////////////////////////////////////// +// History using compact storage +// This implementation uses a list of fixed-sized blocks +// where history lines are allocated in (avoids heap fragmentation) +////////////////////////////////////////////////////////////////////// +typedef QVector<Character> TextLine; + +class CharacterFormat +{ +public: + bool equalsFormat(const CharacterFormat &other) const { + return other.rendition==rendition && other.fgColor==fgColor && other.bgColor==bgColor; + } + + bool equalsFormat(const Character &c) const { + return c.rendition==rendition && c.foregroundColor==fgColor && c.backgroundColor==bgColor; + } + + void setFormat(const Character& c) { + rendition=c.rendition; + fgColor=c.foregroundColor; + bgColor=c.backgroundColor; + } + + CharacterColor fgColor, bgColor; + quint16 startPos; + quint8 rendition; +}; + +class CompactHistoryBlock +{ +public: + + CompactHistoryBlock(){ + blockLength = 4096*64; // 256kb + head = (quint8*) mmap(0, blockLength, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); + //head = (quint8*) malloc(blockLength); + Q_ASSERT(head != MAP_FAILED); + tail = blockStart = head; + allocCount=0; + } + + virtual ~CompactHistoryBlock(){ + //free(blockStart); + munmap(blockStart, blockLength); + } + + virtual unsigned int remaining(){ return blockStart+blockLength-tail;} + virtual unsigned length() { return blockLength; } + virtual void* allocate(size_t length); + virtual bool contains(void *addr) {return addr>=blockStart && addr<(blockStart+blockLength);} + virtual void deallocate(); + virtual bool isInUse(){ return allocCount!=0; } ; + +private: + size_t blockLength; + quint8* head; + quint8* tail; + quint8* blockStart; + int allocCount; +}; + +class CompactHistoryBlockList { +public: + CompactHistoryBlockList() {}; + ~CompactHistoryBlockList(); + + void *allocate( size_t size ); + void deallocate(void *); + int length() {return list.size();} +private: + QList<CompactHistoryBlock*> list; +}; + +class CompactHistoryLine +{ +public: + CompactHistoryLine(const TextLine&, CompactHistoryBlockList& blockList); + virtual ~CompactHistoryLine(); + + // custom new operator to allocate memory from custom pool instead of heap + static void *operator new( size_t size, CompactHistoryBlockList& blockList); + static void operator delete( void *) { /* do nothing, deallocation from pool is done in destructor*/ } ; + + virtual void getCharacters(Character* array, int length, int startColumn) ; + virtual void getCharacter(int index, Character &r) ; + virtual bool isWrapped() const {return wrapped;}; + virtual void setWrapped(bool isWrapped) { wrapped=isWrapped;}; + virtual unsigned int getLength() const {return length;}; + +protected: + CompactHistoryBlockList& blockList; + CharacterFormat* formatArray; + quint16 length; + quint16* text; + quint16 formatLength; + bool wrapped; +}; + +class CompactHistoryScroll : public HistoryScroll +{ + typedef QList<CompactHistoryLine*> HistoryArray; + +public: + CompactHistoryScroll(unsigned int maxNbLines = 1000); + virtual ~CompactHistoryScroll(); + + virtual int getLines(); + virtual int getLineLen(int lineno); + virtual void getCells(int lineno, int colno, int count, Character res[]); + virtual bool isWrappedLine(int lineno); + + virtual void addCells(const Character a[], int count); + virtual void addCellsVector(const TextLine& cells); + virtual void addLine(bool previousWrapped=false); + + void setMaxNbLines(unsigned int nbLines); + unsigned int maxNbLines() const { return _maxLineCount; } + +private: + bool hasDifferentColors(const TextLine& line) const; + HistoryArray lines; + CompactHistoryBlockList blockList; + + unsigned int _maxLineCount; +}; + +////////////////////////////////////////////////////////////////////// +// History type +////////////////////////////////////////////////////////////////////// + +class HistoryType +{ +public: + HistoryType(); + virtual ~HistoryType(); + + /** + * Returns true if the history is enabled ( can store lines of output ) + * or false otherwise. + */ + virtual bool isEnabled() const = 0; + /** + * Returns true if the history size is unlimited. + */ + bool isUnlimited() const { return maximumLineCount() == 0; } + /** + * Returns the maximum number of lines which this history type + * can store or 0 if the history can store an unlimited number of lines. + */ + virtual int maximumLineCount() const = 0; + + virtual HistoryScroll* scroll(HistoryScroll *) const = 0; +}; + +class HistoryTypeNone : public HistoryType +{ +public: + HistoryTypeNone(); + + virtual bool isEnabled() const; + virtual int maximumLineCount() const; + + virtual HistoryScroll* scroll(HistoryScroll *) const; +}; + +class HistoryTypeBlockArray : public HistoryType +{ +public: + HistoryTypeBlockArray(size_t size); + + virtual bool isEnabled() const; + virtual int maximumLineCount() const; + + virtual HistoryScroll* scroll(HistoryScroll *) const; + +protected: + size_t m_size; +}; + +#if 1 +class HistoryTypeFile : public HistoryType +{ +public: + HistoryTypeFile(const QString& fileName=QString()); + + virtual bool isEnabled() const; + virtual const QString& getFileName() const; + virtual int maximumLineCount() const; + + virtual HistoryScroll* scroll(HistoryScroll *) const; + +protected: + QString m_fileName; +}; + + +class HistoryTypeBuffer : public HistoryType +{ + friend class HistoryScrollBuffer; + +public: + HistoryTypeBuffer(unsigned int nbLines); + + virtual bool isEnabled() const; + virtual int maximumLineCount() const; + + virtual HistoryScroll* scroll(HistoryScroll *) const; + +protected: + unsigned int m_nbLines; +}; + +class CompactHistoryType : public HistoryType +{ +public: + CompactHistoryType(unsigned int size); + + virtual bool isEnabled() const; + virtual int maximumLineCount() const; + + virtual HistoryScroll* scroll(HistoryScroll *) const; + +protected: + unsigned int m_nbLines; +}; + + +#endif + +} + +#endif // TEHISTORY_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/HistorySizeDialog.cpp @@ -0,0 +1,168 @@ +/* + Copyright 2007-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 "HistorySizeDialog.h" + +// Qt +#include <QtGui/QButtonGroup> +#include <QtGui/QBoxLayout> +#include <QtGui/QCheckBox> +#include <QtGui/QLabel> +#include <QtGui/QRadioButton> +#include <QtGui/QWidget> + +// KDE +#include <KLocalizedString> +#include <KNumInput> + +// Konsole +#include "SessionManager.h" + +using namespace Konsole; + +HistorySizeDialog::HistorySizeDialog( QWidget* parent ) + : KDialog(parent) + , _noHistoryButton(0) + , _fixedHistoryButton(0) + , _unlimitedHistoryButton(0) + , _saveToCurrentProfileButton(0) + , _lineCountBox(0) + , _defaultMode(FixedSizeHistory) + , _defaultLineCount(1000) +{ + // basic dialog properties + setPlainCaption( i18n("Scrollback Options") ); + setButtons( Default | Ok | Cancel ); + setDefaultButton(Ok); + setModal( false ); + + // dialog widgets + QWidget* dialogWidget = new QWidget(this); + setMainWidget(dialogWidget); + + QVBoxLayout* dialogLayout = new QVBoxLayout(dialogWidget); + + QButtonGroup* modeGroup = new QButtonGroup(this); + + _noHistoryButton = new QRadioButton( i18n("No scrollback") ); + _fixedHistoryButton = new QRadioButton( i18n("Fixed size scrollback: ") ); + _unlimitedHistoryButton = new QRadioButton( i18n("Unlimited scrollback") ); + _saveToCurrentProfileButton = new QCheckBox( i18n("Save to current profile") ); + + modeGroup->addButton(_noHistoryButton); + modeGroup->addButton(_fixedHistoryButton); + modeGroup->addButton(_unlimitedHistoryButton); + + _lineCountBox = new KIntSpinBox(this); + + // minimum lines = 1 ( for 0 lines , "No History" mode should be used instead ) + // maximum lines is abritrarily chosen, I do not think it is sensible to allow this + // to be set to a very large figure because that will use large amounts of memory, + // if a very large log is required, "Unlimited History" mode should be used + _lineCountBox->setRange( 1 , 100000 ); + + // 1000 lines was the default in the KDE 3 series + // using that for now + _lineCountBox->setValue( _defaultLineCount ); + + _lineCountBox->setSingleStep( _defaultLineCount / 10 ); + + QLabel* lineCountLabel = new QLabel(i18n("lines"),this); + QHBoxLayout* lineCountLayout = new QHBoxLayout(); + + _fixedHistoryButton->setFocusProxy(_lineCountBox); + + connect( _fixedHistoryButton , SIGNAL(clicked()) , _lineCountBox , SLOT(selectAll()) ); + lineCountLayout->addWidget(_fixedHistoryButton); + lineCountLayout->addWidget(_lineCountBox); + lineCountLayout->addWidget(lineCountLabel); + + dialogLayout->addWidget(_noHistoryButton); + dialogLayout->addLayout(lineCountLayout); + dialogLayout->addWidget(_unlimitedHistoryButton); + dialogLayout->insertSpacing(3, 10); + dialogLayout->addWidget(_saveToCurrentProfileButton); + + // select the fixed size mode by default + _fixedHistoryButton->click(); + _fixedHistoryButton->setFocus( Qt::OtherFocusReason ); + + connect(this,SIGNAL(defaultClicked()),this,SLOT(useDefaults())); + + connect(this,SIGNAL(accepted()),this,SLOT(emitOptionsChanged())); +} + +void HistorySizeDialog::emitOptionsChanged() +{ + emit optionsChanged( mode() , lineCount(), saveToCurrentProfile() ); +} + +void HistorySizeDialog::setDefaultMode( HistoryMode mode ) { _defaultMode = mode; } +HistorySizeDialog::HistoryMode HistorySizeDialog::defaultMode() const { return _defaultMode; } +void HistorySizeDialog::setDefaultLineCount( int count ) { _defaultLineCount = count; } +int HistorySizeDialog::defaultLineCount() const { return _defaultLineCount; } +bool HistorySizeDialog::saveToCurrentProfile() const { return _saveToCurrentProfileButton->isChecked(); } + +void HistorySizeDialog::useDefaults() +{ + setMode( _defaultMode ); + setLineCount( _defaultLineCount ); + _saveToCurrentProfileButton->setChecked(false); +} + +void HistorySizeDialog::setMode( HistoryMode mode ) +{ + if ( mode == NoHistory ) + { + _noHistoryButton->setChecked(true); + } else if ( mode == FixedSizeHistory ) + { + _fixedHistoryButton->setChecked(true); + } else if ( mode == UnlimitedHistory ) + { + _unlimitedHistoryButton->setChecked(true); + } +} + +HistorySizeDialog::HistoryMode HistorySizeDialog::mode() const +{ + if ( _noHistoryButton->isChecked() ) + return NoHistory; + else if ( _fixedHistoryButton->isChecked() ) + return FixedSizeHistory; + else if ( _unlimitedHistoryButton->isChecked() ) + return UnlimitedHistory; + + Q_ASSERT(false); + return NoHistory; +} + +int HistorySizeDialog::lineCount() const +{ + return _lineCountBox->value(); +} + +void HistorySizeDialog::setLineCount(int lines) +{ + _lineCountBox->setValue(lines); +} + + +#include "HistorySizeDialog.moc"
new file mode 100644 --- /dev/null +++ b/gui/konsole/HistorySizeDialog.h @@ -0,0 +1,134 @@ +/* + Copyright 2007-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. +*/ + +#ifndef HISTORYSIZEDIALOG_H +#define HISTORYSIZEDIALOG_H + +// KDE +#include <KDialog> + +class QAbstractButton; +class KIntSpinBox; + +namespace Konsole +{ + +/** + * A dialog which allows the user to select the number of lines of output + * which are remembered for a session. + */ +class HistorySizeDialog : public KDialog +{ +Q_OBJECT + +public: + /** + * Construct a new history size dialog. + */ + HistorySizeDialog( QWidget* parent ); + + /** Specifies the type of history scroll */ + enum HistoryMode + { + /** + * No history. Lines of output are lost + * as soon as they are scrolled off-screen. + */ + NoHistory, + /** + * A history which stores up to a fixed number of lines + * in memory. + */ + FixedSizeHistory, + /** + * An 'unlimited' history which stores lines of output in + * a file on disk. + */ + UnlimitedHistory + }; + + + /** Specifies the history mode. */ + void setMode( HistoryMode mode ); + /** Returns the history mode chosen by the user. */ + HistoryMode mode() const; + /** + * Returns the number of lines of history to remember. + * This is only valid when mode() == FixedSizeHistory, + * and returns 0 otherwise. + */ + int lineCount() const; + /** Sets the number of lines for the fixed size history mode. */ + void setLineCount(int lines); + + /** + * Sets the default history mode. When the user clicks on the "Defaults" button, + * this mode will be used. + */ + void setDefaultMode( HistoryMode mode ); + + /** Returns the default mode, as set with setDefaultMode() */ + HistoryMode defaultMode() const; + + /** + * Sets the default line count. When the user clicks on the "Defaults" button, + * the line count will be set to this number. + */ + void setDefaultLineCount( int count ); + + /** Returns the default line count, as set with setDefaultLineCount() */ + int defaultLineCount() const; + + bool saveToCurrentProfile() const; + +signals: + /** + * Emitted when the user changes the scroll-back mode or line count and + * accepts the change by pressing the OK button + * + * @param mode The current history mode. This is a value from the HistoryMode enum. + * @param lineCount The current line count. This is only applicable if mode is + * FixedSizeHistory + * @param saveToCurrentProfile Determines if the changes are saved to current profile. + */ + void optionsChanged(int mode , int lineCount, bool saveToCurrentProfile); + +private slots: + // changes the mode and line count back to the defaults + // specified with setDefaultMode() and setDefaultLineCount() + void useDefaults(); + + // fires the optionsChanged() signal with the current mode + // and line count as arguments + void emitOptionsChanged(); + +private: + QAbstractButton* _noHistoryButton; + QAbstractButton* _fixedHistoryButton; + QAbstractButton* _unlimitedHistoryButton; + QAbstractButton* _saveToCurrentProfileButton; + KIntSpinBox* _lineCountBox; + + HistoryMode _defaultMode; + int _defaultLineCount; +}; + +} + +#endif // HISTORYSIZEDIALOG_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/IncrementalSearchBar.cpp @@ -0,0 +1,265 @@ +/* + 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 "IncrementalSearchBar.h" + +// Qt +#include <QtGui/QCheckBox> +#include <QtGui/QBoxLayout> +#include <QtGui/QLabel> +#include <QtGui/QProgressBar> +#include <QtGui/QKeyEvent> +#include <QtCore/QTimer> +#include <QtGui/QToolButton> + +// KDE +#include <KColorScheme> +#include <KLineEdit> +#include <KLocale> +#include <KIcon> + +using namespace Konsole; + +IncrementalSearchBar::IncrementalSearchBar(Features features , QWidget* parent) + : QWidget(parent) + , _foundMatch(false) + , _matchCaseBox(0) + , _matchRegExpBox(0) + , _highlightBox(0) + , _searchEdit(0) + , _continueLabel(0) +{ + QHBoxLayout* layout = new QHBoxLayout(this); + + QToolButton* close = new QToolButton(this); + close->setObjectName( QLatin1String("close-button" )); + close->setToolTip( i18n("Close the search bar") ); + close->setAutoRaise(true); + close->setIcon(KIcon("dialog-close")); + connect( close , SIGNAL(clicked()) , this , SIGNAL(closeClicked()) ); + + QLabel* findLabel = new QLabel(i18n("Find:"),this); + _searchEdit = new KLineEdit(this); + _searchEdit->setClearButtonShown(true); + _searchEdit->installEventFilter(this); + _searchEdit->setObjectName( QLatin1String("search-edit" )); + _searchEdit->setToolTip( i18n("Enter the text to search for here") ); + + // text box may be a minimum of 6 characters wide and a maximum of 10 characters wide + // (since the maxWidth metric is used here, more characters probably will fit in than 6 + // and 10) + QFontMetrics metrics(_searchEdit->font()); + int maxWidth = metrics.maxWidth(); + _searchEdit->setMinimumWidth(maxWidth*6); + _searchEdit->setMaximumWidth(maxWidth*10); + + _searchTimer = new QTimer(this); + _searchTimer->setInterval(250); + _searchTimer->setSingleShot(true); + connect( _searchTimer , SIGNAL(timeout()) , this , SLOT(notifySearchChanged()) ); + connect( _searchEdit , SIGNAL(clearButtonClicked()) , this , SLOT(clearLineEdit()) ); + connect( _searchEdit , SIGNAL(textChanged(const QString&)) , _searchTimer , SLOT(start())); + + QToolButton* findNext = new QToolButton(this); + findNext->setObjectName( QLatin1String("find-next-button" )); + findNext->setText(i18nc("@action:button Go to the next phrase", "Next")); + findNext->setAutoRaise(true); + findNext->setIcon( KIcon("go-down-search") ); + findNext->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + findNext->setToolTip( i18n("Find the next match for the current search phrase") ); + connect( findNext , SIGNAL(clicked()) , this , SIGNAL(findNextClicked()) ); + + QToolButton* findPrev = new QToolButton(this); + findPrev->setObjectName( QLatin1String("find-previous-button" )); + findPrev->setText(i18nc("@action:button Go to the previous phrase", "Previous")); + findPrev->setAutoRaise(true); + findPrev->setIcon( KIcon("go-up-search") ); + findPrev->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + findPrev->setToolTip( i18n("Find the previous match for the current search phrase") ); + connect( findPrev , SIGNAL(clicked()) , this , SIGNAL(findPreviousClicked()) ); + + if ( features & HighlightMatches ) + { + _highlightBox = new QCheckBox( i18n("Highlight all") , this ); + _highlightBox->setObjectName( QLatin1String("highlight-matches-box" )); + _highlightBox->setToolTip( i18n("Sets whether matching text should be highlighted") ); + _highlightBox->setChecked(true); + connect( _highlightBox , SIGNAL(toggled(bool)) , this , + SIGNAL(highlightMatchesToggled(bool)) ); + } + + if ( features & MatchCase ) + { + _matchCaseBox = new QCheckBox( i18n("Match case") , this ); + _matchCaseBox->setObjectName( QLatin1String("match-case-box" )); + _matchCaseBox->setToolTip( i18n("Sets whether the search is case sensitive") ); + connect( _matchCaseBox , SIGNAL(toggled(bool)) , this , SIGNAL(matchCaseToggled(bool)) ); + } + + if ( features & RegExp ) + { + _matchRegExpBox = new QCheckBox( i18n("Match regular expression") , this ); + _matchRegExpBox->setObjectName( QLatin1String("match-regexp-box" )); + _matchRegExpBox->setToolTip( i18n("Sets whether the search phrase is interpreted as normal text or" + " as a regular expression") ); + connect( _matchRegExpBox , SIGNAL(toggled(bool)) , this , SIGNAL(matchRegExpToggled(bool)) ); + } + + QProgressBar* _progress = new QProgressBar(this); + _progress->setMinimum(0); + _progress->setMaximum(0); + _progress->setVisible(false); + + QLabel* _continueLabel = new QLabel(this); + _continueLabel->setVisible(false); + + layout->addWidget(close); + layout->addWidget(findLabel); + layout->addWidget(_searchEdit); + layout->addWidget(findNext); + layout->addWidget(findPrev); + + // optional features + if ( features & HighlightMatches ) layout->addWidget(_highlightBox); + if ( features & MatchCase ) layout->addWidget(_matchCaseBox); + if ( features & RegExp ) layout->addWidget(_matchRegExpBox); + + layout->addWidget(_progress); + layout->addWidget(_continueLabel); + layout->addStretch(); + + layout->setContentsMargins(4, 4, 4, 4); + + setLayout(layout); +} +void IncrementalSearchBar::notifySearchChanged() +{ + emit searchChanged( searchText() ); +} +QString IncrementalSearchBar::searchText() +{ + return _searchEdit->text(); +} +bool IncrementalSearchBar::highlightMatches() +{ + if ( !_highlightBox ) + { + return true; + } + else + { + return _highlightBox->isChecked(); + } +} +bool IncrementalSearchBar::matchCase() +{ + if ( !_matchCaseBox ) + { + return false; + } + else + { + return _matchCaseBox->isChecked(); + } +} +bool IncrementalSearchBar::matchRegExp() +{ + if ( !_matchRegExpBox ) + { + return false; + } + else + { + return _matchRegExpBox->isChecked(); + } +} + +bool IncrementalSearchBar::eventFilter(QObject* watched , QEvent* event) +{ + if ( watched == _searchEdit ) + { + if ( event->type() == QEvent::KeyPress ) + { + QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); + + if ( keyEvent->key() == Qt::Key_Escape ) + { + emit closeClicked(); + return true; + } + } + } + + return QWidget::eventFilter(watched,event); +} + +void IncrementalSearchBar::setVisible(bool visible) +{ + QWidget::setVisible(visible); + + if ( visible ) + { + //TODO - Check if this is the correct reason value to use here + _searchEdit->setFocus( Qt::ActiveWindowFocusReason ); + _searchEdit->selectAll(); + } +} + +void IncrementalSearchBar::setFoundMatch( bool match ) +{ + if ( !match && !_searchEdit->text().isEmpty() ) + { + KStatefulBrush backgroundBrush(KColorScheme::View,KColorScheme::NegativeBackground); + + QString styleSheet = QString("QLineEdit{ background-color:%1 }") + .arg(backgroundBrush.brush(_searchEdit).color().name()); + + _searchEdit->setStyleSheet( styleSheet ); + } + else + { + _searchEdit->setStyleSheet( QString() ); + } +} + +void IncrementalSearchBar::setContinueFlag( Continue flag ) +{ + if ( flag == ContinueFromTop ) + { + _continueLabel->setText( i18n("Search reached bottom, continued from top.") ); + _continueLabel->show(); + } + else if ( flag == ContinueFromBottom ) + { + _continueLabel->setText( i18n("Search reached top, continued from bottom.") ); + _continueLabel->show(); + } + else if ( flag == ClearContinue ) + { + _continueLabel->hide(); + } +} + +void IncrementalSearchBar::clearLineEdit() +{ + _searchEdit->setStyleSheet( QString() ); +} + +#include "IncrementalSearchBar.moc"
new file mode 100644 --- /dev/null +++ b/gui/konsole/IncrementalSearchBar.h @@ -0,0 +1,195 @@ +/* + 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. +*/ + +#ifndef INCREMENTALSEARCHBAR_H +#define INCREMENTALSEARCHBAR_H + +// Qt +#include <QtGui/QWidget> + +class QCheckBox; +class QLabel; +class QProgressBar; +class QTimer; +class KLineEdit; + +namespace Konsole +{ + +/** + * A widget which allows users to search incrementally through a document for a + * a text string or regular expression. + * + * The widget consists of a text box into which the user can enter their search text and + * buttons to trigger a search for the next and previous matches for the search text. + * + * When the search text is changed, the searchChanged() signal is emitted. A search through + * the document for the new text should begin immediately and the active view of the document + * should jump to display any matches if found. setFoundMatch() should be called whenever the + * search text changes to indicate whether a match for the text was found in the document. + * + * findNextClicked() and findPreviousClicked() signals are emitted when the user presses buttons + * to find next and previous matches respectively. + * + * The search bar has a number of optional features which can be enabled or disabled by passing + * a set of Features flags to the constructor. + * + * An optional checkbox can be displayed to indicate whether all matches in the document + * for searchText() should be highlighted. + * The highlightMatchesToggled() signal is emitted when this checkbox is toggled. + * + * The two further optional checkboxes allow the user to control the matching process. + * The first indicates whether searches are case sensitive. + * The matchCaseToggled() signal is emitted when this is changed. + * The second indicates whether the search text should be treated as a plain string or + * as a regular expression. + * The matchRegExpToggled() signal is emitted when this is changed. + */ +class IncrementalSearchBar : public QWidget +{ +Q_OBJECT + +public: + enum Continue + { + /** + * Indicates that the search has reached the bottom of the document and has been continued from + * the top + */ + ContinueFromTop, + /** + * Indicates that the search has reached the top of the document and has been continued from + * the bottom + */ + ContinueFromBottom, + + /** Clears the Continue flag */ + ClearContinue + }; + + /** + * This enum defines the features which can be supported by an implementation of + * an incremental search bar + */ + enum Features + { + /** search facility supports highlighting of all matches */ + HighlightMatches = 1, + /** search facility supports case-sensitive and case-insensitive search */ + MatchCase = 2, + /** search facility supports regular expressions */ + RegExp = 4, + /** search facility supports all features */ + AllFeatures = HighlightMatches | MatchCase | RegExp + }; + + /** + * Constructs a new incremental search bar with the given parent widget + * @p features specifies the features which should be made available to the user. + */ + explicit IncrementalSearchBar(Features features , QWidget* parent = 0); + + /** + * Sets an indicator for the user as to whether or not a match for the + * current search text was found in the document. + * + * The indicator will not be shown if the search text is empty ( because + * the user has not yet entered a query ). + * + * @param match True if a match was found or false otherwise. If true, + * and the search text is non-empty, an indicator that no matches were + * found will be shown. + */ + void setFoundMatch( bool match ); + + /** + * Sets a flag to indicate that the current search for matches has reached the top or bottom of + * the document and has been continued again from the other end of the document. + * + * This flag will be cleared when the user presses the buttons to find a next or previous match. + */ + void setContinueFlag( Continue flag ); + + /** Returns the current search text */ + QString searchText(); + /** + * Returns whether matches for the current search text should be highlighted in the document. + * Always returns true if the highlight matches checkbox is not visible. + */ + bool highlightMatches(); + /** + * Returns whether matching for the current search text should be case sensitive. + * Always returns false if the match case checkbox is not visible. + */ + bool matchCase(); + /** + * Returns whether the current search text should be treated as plain text or a regular expression + * Always returns false if the match regular expression checkbox is not visible. + */ + bool matchRegExp(); + + // reimplemented + virtual void setVisible( bool visible ); +signals: + /** Emitted when the text entered in the search box is altered */ + void searchChanged( const QString& text ); + /** Emitted when the user clicks the button to find the next match */ + void findNextClicked(); + /** Emitted when the user clicks the button to find the previous match */ + void findPreviousClicked(); + /** + * Emitted when the user toggles the checkbox to indicate whether + * matches for the search text should be highlighted + */ + void highlightMatchesToggled(bool); + /** + * Emitted when the user toggles the checkbox to indicate whether + * matching for the search text should be case sensitive + */ + void matchCaseToggled(bool); + /** + * Emitted when the user toggles the checkbox to indicate whether + * the search text should be treated as a plain string or a regular expression + */ + void matchRegExpToggled(bool); + /** Emitted when the close button is clicked */ + void closeClicked(); + +protected: + virtual bool eventFilter( QObject* watched , QEvent* event ); + +private slots: + void notifySearchChanged(); + void clearLineEdit(); + +private: + bool _foundMatch; + QCheckBox* _matchCaseBox; + QCheckBox* _matchRegExpBox; + QCheckBox* _highlightBox; + + KLineEdit* _searchEdit; + QLabel* _continueLabel; + QProgressBar* _progress; + + QTimer* _searchTimer; +}; + +} +#endif // INCREMENTALSEARCHBAR_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/KeyBindingEditor.cpp @@ -0,0 +1,241 @@ +/* + Copyright 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 "KeyBindingEditor.h" + +// Qt +#include <QtGui/QHeaderView> +#include <QtGui/QKeyEvent> + +#include <KDebug> + +// Konsole +#include "ui_KeyBindingEditor.h" +#include "KeyboardTranslator.h" + +using namespace Konsole; + +KeyBindingEditor::KeyBindingEditor(QWidget* parent) + : QWidget(parent) + , _translator(new KeyboardTranslator( QString() )) +{ + _ui = new Ui::KeyBindingEditor(); + _ui->setupUi(this); + + // description edit + connect( _ui->descriptionEdit , SIGNAL(textChanged(const QString&)) , this , SLOT(setDescription(const QString&)) ); + + // key bindings table + _ui->keyBindingTable->setColumnCount(2); + + QStringList labels; + labels << i18n("Key Combination") << i18n("Output"); + + _ui->keyBindingTable->setHorizontalHeaderLabels(labels); + _ui->keyBindingTable->horizontalHeader()->setStretchLastSection(true); + _ui->keyBindingTable->verticalHeader()->hide(); + _ui->keyBindingTable->setSelectionBehavior(QAbstractItemView::SelectRows); + + // add and remove buttons + _ui->addEntryButton->setIcon( KIcon("list-add") ); + _ui->removeEntryButton->setIcon( KIcon("list-remove") ); + + connect( _ui->removeEntryButton , SIGNAL(clicked()) , this , SLOT(removeSelectedEntry()) ); + connect( _ui->addEntryButton , SIGNAL(clicked()) , this , SLOT(addNewEntry()) ); + + // test area + _ui->testAreaInputEdit->installEventFilter(this); +} + +KeyBindingEditor::~KeyBindingEditor() +{ + delete _ui; +} + +void KeyBindingEditor::removeSelectedEntry() +{ + QList<QTableWidgetItem*> selectedList = _ui->keyBindingTable->selectedItems(); + QList<QTableWidgetItem*> uniqueList; + + //Filter unique items + QListIterator<QTableWidgetItem*> iter( selectedList ); + while ( iter.hasNext() ) + { + QTableWidgetItem* item = iter.next(); + if (item->column() == 1) //Select item at the first column + item = _ui->keyBindingTable->item(item->row(),0); + + if ( !uniqueList.contains(item) ) + uniqueList.append(item); + } + + iter = QListIterator<QTableWidgetItem*>( uniqueList ); + while ( iter.hasNext() ) + { + // get the first item in the row which has the entry + QTableWidgetItem* item = iter.next(); + + KeyboardTranslator::Entry existing = item->data(Qt::UserRole). + value<KeyboardTranslator::Entry>(); + + _translator->removeEntry(existing); + + _ui->keyBindingTable->removeRow( item->row() ); + } +} + +void KeyBindingEditor::addNewEntry() +{ + _ui->keyBindingTable->insertRow( _ui->keyBindingTable->rowCount() ); + + int newRowCount = _ui->keyBindingTable->rowCount(); + + // block signals here to avoid triggering bindingTableItemChanged() slot call + _ui->keyBindingTable->blockSignals(true); + + _ui->keyBindingTable->setItem(newRowCount-1,0,new QTableWidgetItem() ); + _ui->keyBindingTable->setItem(newRowCount-1,1,new QTableWidgetItem() ); + + _ui->keyBindingTable->blockSignals(false); + + // make sure user can see new row + _ui->keyBindingTable->scrollToItem(_ui->keyBindingTable->item(newRowCount-1,0)); +} + +bool KeyBindingEditor::eventFilter( QObject* watched , QEvent* event ) +{ + if ( watched == _ui->testAreaInputEdit ) + { + if ( event->type() == QEvent::KeyPress ) + { + QKeyEvent* keyEvent = (QKeyEvent*)event; + + // The state here is currently set to the state that a newly started + // terminal in Konsole will be in ( which is also the same as the + // state just after a reset ), this has 'Ansi' turned on and all other + // states off. + // + // TODO: It may be useful to be able to specify the state in the 'test input' + // area, but preferably not in a way which clutters the UI with lots of + // checkboxes. + // + const KeyboardTranslator::States states = KeyboardTranslator::AnsiState; + + KeyboardTranslator::Entry entry = _translator->findEntry( keyEvent->key() , + keyEvent->modifiers(), + states ); + + if ( !entry.isNull() ) + { + _ui->testAreaInputEdit->setText(entry.conditionToString()); + _ui->testAreaOutputEdit->setText(entry.resultToString(true,keyEvent->modifiers())); + } + else + { + _ui->testAreaInputEdit->setText(keyEvent->text()); + _ui->testAreaOutputEdit->setText(keyEvent->text()); + } + + keyEvent->accept(); + return true; + } + } + return false; +} + +void KeyBindingEditor::setDescription(const QString& newDescription) +{ + _ui->descriptionEdit->setText(newDescription); + + if ( _translator ) + _translator->setDescription(newDescription); +} +QString KeyBindingEditor::description() const +{ + return _ui->descriptionEdit->text(); +} + +void KeyBindingEditor::setup(const KeyboardTranslator* translator) +{ + if ( _translator ) + delete _translator; + + _translator = new KeyboardTranslator(*translator); + + // setup description edit + _ui->descriptionEdit->setClearButtonShown(true); + _ui->descriptionEdit->setText(translator->description()); + + // setup key binding table + setupKeyBindingTable(translator); +} + +KeyboardTranslator* KeyBindingEditor::translator() const +{ + return _translator; +} + +void KeyBindingEditor::bindingTableItemChanged(QTableWidgetItem* item) +{ + QTableWidgetItem* key = _ui->keyBindingTable->item( item->row() , 0 ); + KeyboardTranslator::Entry existing = key->data(Qt::UserRole).value<KeyboardTranslator::Entry>(); + + QString condition = key->text(); + QString result = _ui->keyBindingTable->item( item->row() , 1 )->text(); + + KeyboardTranslator::Entry entry = KeyboardTranslatorReader::createEntry(condition,result); + _translator->replaceEntry(existing,entry); + + // block signals to prevent this slot from being called repeatedly + _ui->keyBindingTable->blockSignals(true); + + key->setData(Qt::UserRole,QVariant::fromValue(entry)); + + _ui->keyBindingTable->blockSignals(false); +} + +void KeyBindingEditor::setupKeyBindingTable(const KeyboardTranslator* translator) +{ + disconnect( _ui->keyBindingTable , SIGNAL(itemChanged(QTableWidgetItem*)) , this , + SLOT(bindingTableItemChanged(QTableWidgetItem*)) ); + + QList<KeyboardTranslator::Entry> entries = translator->entries(); + _ui->keyBindingTable->setRowCount(entries.count()); + + for ( int row = 0 ; row < entries.count() ; row++ ) + { + const KeyboardTranslator::Entry& entry = entries.at(row); + + QTableWidgetItem* keyItem = new QTableWidgetItem(entry.conditionToString()); + keyItem->setData( Qt::UserRole , QVariant::fromValue(entry) ); + + QTableWidgetItem* textItem = new QTableWidgetItem(QString(entry.resultToString())); + + _ui->keyBindingTable->setItem(row,0,keyItem); + _ui->keyBindingTable->setItem(row,1,textItem); + } + _ui->keyBindingTable->sortItems(0); + + connect( _ui->keyBindingTable , SIGNAL(itemChanged(QTableWidgetItem*)) , this , + SLOT(bindingTableItemChanged(QTableWidgetItem*)) ); +} + +#include "KeyBindingEditor.moc" +
new file mode 100644 --- /dev/null +++ b/gui/konsole/KeyBindingEditor.h @@ -0,0 +1,105 @@ +/* + Copyright 2007-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. +*/ + +#ifndef KEYBINDINGEDITOR_H +#define KEYBINDINGEDITOR_H + +// Qt +#include <QtGui/QWidget> + +class QTableWidgetItem; + +namespace Ui +{ + class KeyBindingEditor; +} + +namespace Konsole +{ + +class KeyboardTranslator; + +/** + * A dialog which allows the user to edit a key bindings list + * which maps between key combinations input by the user and + * the character sequence sent to the terminal when those + * combinations are pressed. + * + * The dialog can be initialised with the settings of an + * existing key bindings list using the setup() method. + * + * The dialog creates a copy of the supplied keyboard translator + * to which any changes are applied. The modified translator + * can be retrieved using the translator() method. + */ +class KeyBindingEditor : public QWidget +{ +Q_OBJECT + +public: + /** Constructs a new key bindings editor with the specified parent. */ + KeyBindingEditor(QWidget* parent = 0); + virtual ~KeyBindingEditor(); + + /** + * Intialises the dialog with the bindings and other settings + * from the specified @p translator. + */ + void setup(const KeyboardTranslator* translator); + + /** + * Returns the modified translator describing the changes to the bindings + * and other settings which the user made. + */ + KeyboardTranslator* translator() const; + + /** + * Returns the text of the editor's description field. + */ + QString description() const; + + // reimplemented to handle test area input + virtual bool eventFilter( QObject* watched , QEvent* event ); + +public slots: + /** + * Sets the text of the editor's description field. + */ + void setDescription(const QString& description); + +private slots: + void bindingTableItemChanged(QTableWidgetItem* item); + void removeSelectedEntry(); + void addNewEntry(); + +private: + void setupKeyBindingTable(const KeyboardTranslator* translator); + + Ui::KeyBindingEditor* _ui; + + // translator to which modifications are made as the user makes + // changes in the UI. + // this is initialized as a copy of the translator specified + // when setup() is called + KeyboardTranslator* _translator; +}; + +} + +#endif //KEYBINDINGEDITOR_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/KeyboardTranslator.cpp @@ -0,0 +1,979 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2007-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 "KeyboardTranslator.h" + +// System +#include <ctype.h> +#include <stdio.h> + +// Qt +#include <QtCore/QBuffer> +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QTextStream> +#include <QtGui/QKeySequence> +#include <QtCore/QDir> +#include <QtCore/QStringList> +#include <QtCore/QDebug> +#include <QtCore/QDataStream> + +// KDE +#include "konsole_export.h" +//#include <KDebug> +//#include <KLocale> +//#include <KStandardDirs> + +using namespace Konsole; + + +const QByteArray KeyboardTranslatorManager::defaultTranslatorText( +"keyboard \"Fallback Key Translator\"\n" +"key Tab : \"\\t\"" +); + +KeyboardTranslatorManager::KeyboardTranslatorManager() + : _haveLoadedAll(false) +{ +} +KeyboardTranslatorManager::~KeyboardTranslatorManager() +{ + qDeleteAll(_translators); +} +QString KeyboardTranslatorManager::findTranslatorPath(const QString& name) +{ + return QString("kb-layouts/" + name + ".keytab"); + // return KGlobal::dirs()->findResource("data","konsole/"+name+".keytab"); +} +void KeyboardTranslatorManager::findTranslators() +{ + //QStringList list = KGlobal::dirs()->findAllResources("data", + // "konsole/*.keytab", + // KStandardDirs::NoDuplicates); + + QDir dir("kb-layouts/"); + QStringList filters; + filters << "*.keytab"; + dir.setNameFilters(filters); + QStringList list = dir.entryList(filters); + + // add the name of each translator to the list and associated + // the name with a null pointer to indicate that the translator + // has not yet been loaded from disk + QStringListIterator listIter(list); + while (listIter.hasNext()) + { + QString translatorPath = listIter.next(); + + QString name = QFileInfo(translatorPath).baseName(); + + if ( !_translators.contains(name) ) + _translators.insert(name,0); + } + + _haveLoadedAll = true; +} + +const KeyboardTranslator* KeyboardTranslatorManager::findTranslator(const QString& name) +{ + if ( name.isEmpty() ) + return defaultTranslator(); + + if ( _translators.contains(name) && _translators[name] != 0 ) + return _translators[name]; + + KeyboardTranslator* translator = loadTranslator(name); + + if ( translator != 0 ) + _translators[name] = translator; + else if ( !name.isEmpty() ) + kWarning() << "Unable to load translator" << name.toStdString(); + + return translator; +} + +bool KeyboardTranslatorManager::saveTranslator(const KeyboardTranslator* translator) +{ + //const QString path = KGlobal::dirs()->saveLocation("data","konsole/")+translator->name() + // +".keytab"; + const QString path = ".keytab"; + + //kDebug() << "Saving translator to" << path; + + QFile destination(path); + if (!destination.open(QIODevice::WriteOnly | QIODevice::Text)) + { + kWarning() << "Unable to save keyboard translation:" + << destination.errorString().toStdString(); + return false; + } + + { + KeyboardTranslatorWriter writer(&destination); + writer.writeHeader(translator->description()); + + QListIterator<KeyboardTranslator::Entry> iter(translator->entries()); + while ( iter.hasNext() ) + writer.writeEntry(iter.next()); + } + + destination.close(); + + return true; +} + +KeyboardTranslator* KeyboardTranslatorManager::loadTranslator(const QString& name) +{ + const QString& path = findTranslatorPath(name); + + QFile source(path); + if (name.isEmpty() || !source.open(QIODevice::ReadOnly | QIODevice::Text)) + return 0; + + return loadTranslator(&source,name); +} + +const KeyboardTranslator* KeyboardTranslatorManager::defaultTranslator() +{ + // Try to find the default.keytab file if it exists, otherwise + // fall back to the hard-coded one + const KeyboardTranslator* translator = findTranslator("default"); + if (!translator) + { + QBuffer textBuffer; + textBuffer.setData(defaultTranslatorText); + textBuffer.open(QIODevice::ReadOnly); + translator = loadTranslator(&textBuffer,"fallback"); + } + return translator; +} + +KeyboardTranslator* KeyboardTranslatorManager::loadTranslator(QIODevice* source,const QString& name) +{ + KeyboardTranslator* translator = new KeyboardTranslator(name); + KeyboardTranslatorReader reader(source); + translator->setDescription( reader.description() ); + while ( reader.hasNextEntry() ) + translator->addEntry(reader.nextEntry()); + + source->close(); + + if ( !reader.parseError() ) + { + return translator; + } + else + { + delete translator; + return 0; + } +} + +KeyboardTranslatorWriter::KeyboardTranslatorWriter(QIODevice* destination) +: _destination(destination) +{ + Q_ASSERT( destination && destination->isWritable() ); + + _writer = new QTextStream(_destination); +} +KeyboardTranslatorWriter::~KeyboardTranslatorWriter() +{ + delete _writer; +} +void KeyboardTranslatorWriter::writeHeader( const QString& description ) +{ + *_writer << "keyboard \"" << description << '\"' << '\n'; +} +void KeyboardTranslatorWriter::writeEntry( const KeyboardTranslator::Entry& entry ) +{ + QString result; + if ( entry.command() != KeyboardTranslator::NoCommand ) + result = entry.resultToString(); + else + result = '\"' + entry.resultToString() + '\"'; + + *_writer << "key " << entry.conditionToString() << " : " << result << '\n'; +} + + +// each line of the keyboard translation file is one of: +// +// - keyboard "name" +// - key KeySequence : "characters" +// - key KeySequence : CommandName +// +// KeySequence begins with the name of the key ( taken from the Qt::Key enum ) +// and is followed by the keyboard modifiers and state flags ( with + or - in front +// of each modifier or flag to indicate whether it is required ). All keyboard modifiers +// and flags are optional, if a particular modifier or state is not specified it is +// assumed not to be a part of the sequence. The key sequence may contain whitespace +// +// eg: "key Up+Shift : scrollLineUp" +// "key Next-Shift : "\E[6~" +// +// (lines containing only whitespace are ignored, parseLine assumes that comments have +// already been removed) +// + +KeyboardTranslatorReader::KeyboardTranslatorReader( QIODevice* source ) + : _source(source) + , _hasNext(false) +{ + // read input until we find the description + while ( _description.isEmpty() && !source->atEnd() ) + { + QList<Token> tokens = tokenize( QString(source->readLine()) ); + if ( !tokens.isEmpty() && tokens.first().type == Token::TitleKeyword ) + _description = i18n(tokens[1].text.toLatin1().data()); + } + // read first entry (if any) + readNext(); +} +void KeyboardTranslatorReader::readNext() +{ + // find next entry + while ( !_source->atEnd() ) + { + const QList<Token>& tokens = tokenize( QString(_source->readLine()) ); + if ( !tokens.isEmpty() && tokens.first().type == Token::KeyKeyword ) + { + KeyboardTranslator::States flags = KeyboardTranslator::NoState; + KeyboardTranslator::States flagMask = KeyboardTranslator::NoState; + Qt::KeyboardModifiers modifiers = Qt::NoModifier; + Qt::KeyboardModifiers modifierMask = Qt::NoModifier; + + int keyCode = Qt::Key_unknown; + + decodeSequence(tokens[1].text.toLower(), + keyCode, + modifiers, + modifierMask, + flags, + flagMask); + + KeyboardTranslator::Command command = KeyboardTranslator::NoCommand; + QByteArray text; + + // get text or command + if ( tokens[2].type == Token::OutputText ) + { + text = tokens[2].text.toLocal8Bit(); + } + else if ( tokens[2].type == Token::Command ) + { + // identify command + if (!parseAsCommand(tokens[2].text,command)) + kWarning() << "Command" << tokens[2].text.toStdString() << "not understood."; + } + + KeyboardTranslator::Entry newEntry; + newEntry.setKeyCode( keyCode ); + newEntry.setState( flags ); + newEntry.setStateMask( flagMask ); + newEntry.setModifiers( modifiers ); + newEntry.setModifierMask( modifierMask ); + newEntry.setText( text ); + newEntry.setCommand( command ); + + _nextEntry = newEntry; + + _hasNext = true; + + return; + } + } + + _hasNext = false; +} + +bool KeyboardTranslatorReader::parseAsCommand(const QString& text,KeyboardTranslator::Command& command) +{ + if ( text.compare("erase",Qt::CaseInsensitive) == 0 ) + command = KeyboardTranslator::EraseCommand; + else if ( text.compare("scrollpageup",Qt::CaseInsensitive) == 0 ) + command = KeyboardTranslator::ScrollPageUpCommand; + else if ( text.compare("scrollpagedown",Qt::CaseInsensitive) == 0 ) + command = KeyboardTranslator::ScrollPageDownCommand; + else if ( text.compare("scrolllineup",Qt::CaseInsensitive) == 0 ) + command = KeyboardTranslator::ScrollLineUpCommand; + else if ( text.compare("scrolllinedown",Qt::CaseInsensitive) == 0 ) + command = KeyboardTranslator::ScrollLineDownCommand; + else if ( text.compare("scrolllock",Qt::CaseInsensitive) == 0 ) + command = KeyboardTranslator::ScrollLockCommand; + else + return false; + + return true; +} + +bool KeyboardTranslatorReader::decodeSequence(const QString& text, + int& keyCode, + Qt::KeyboardModifiers& modifiers, + Qt::KeyboardModifiers& modifierMask, + KeyboardTranslator::States& flags, + KeyboardTranslator::States& flagMask) +{ + bool isWanted = true; + bool endOfItem = false; + QString buffer; + + Qt::KeyboardModifiers tempModifiers = modifiers; + Qt::KeyboardModifiers tempModifierMask = modifierMask; + KeyboardTranslator::States tempFlags = flags; + KeyboardTranslator::States tempFlagMask = flagMask; + + for ( int i = 0 ; i < text.count() ; i++ ) + { + const QChar& ch = text[i]; + bool isFirstLetter = i == 0; + bool isLastLetter = ( i == text.count()-1 ); + endOfItem = true; + if ( ch.isLetterOrNumber() ) + { + endOfItem = false; + buffer.append(ch); + } else if ( isFirstLetter ) + { + buffer.append(ch); + } + + if ( (endOfItem || isLastLetter) && !buffer.isEmpty() ) + { + Qt::KeyboardModifier itemModifier = Qt::NoModifier; + int itemKeyCode = 0; + KeyboardTranslator::State itemFlag = KeyboardTranslator::NoState; + + if ( parseAsModifier(buffer,itemModifier) ) + { + tempModifierMask |= itemModifier; + + if ( isWanted ) + tempModifiers |= itemModifier; + } + else if ( parseAsStateFlag(buffer,itemFlag) ) + { + tempFlagMask |= itemFlag; + + if ( isWanted ) + tempFlags |= itemFlag; + } + else if ( parseAsKeyCode(buffer,itemKeyCode) ) + keyCode = itemKeyCode; + else + kWarning() << "Unable to parse key binding item:" << buffer.toStdString(); + + buffer.clear(); + } + + // check if this is a wanted / not-wanted flag and update the + // state ready for the next item + if ( ch == '+' ) + isWanted = true; + else if ( ch == '-' ) + isWanted = false; + } + + modifiers = tempModifiers; + modifierMask = tempModifierMask; + flags = tempFlags; + flagMask = tempFlagMask; + + return true; +} + +bool KeyboardTranslatorReader::parseAsModifier(const QString& item , Qt::KeyboardModifier& modifier) +{ + if ( item == "shift" ) + modifier = Qt::ShiftModifier; + else if ( item == "ctrl" || item == "control" ) + modifier = Qt::ControlModifier; + else if ( item == "alt" ) + modifier = Qt::AltModifier; + else if ( item == "meta" ) + modifier = Qt::MetaModifier; + else if ( item == "keypad" ) + modifier = Qt::KeypadModifier; + else + return false; + + return true; +} +bool KeyboardTranslatorReader::parseAsStateFlag(const QString& item , KeyboardTranslator::State& flag) +{ + if ( item == "appcukeys" || item == "appcursorkeys" ) + flag = KeyboardTranslator::CursorKeysState; + else if ( item == "ansi" ) + flag = KeyboardTranslator::AnsiState; + else if ( item == "newline" ) + flag = KeyboardTranslator::NewLineState; + else if ( item == "appscreen" ) + flag = KeyboardTranslator::AlternateScreenState; + else if ( item == "anymod" || item == "anymodifier" ) + flag = KeyboardTranslator::AnyModifierState; + else if ( item == "appkeypad" ) + flag = KeyboardTranslator::ApplicationKeypadState; + else + return false; + + return true; +} +bool KeyboardTranslatorReader::parseAsKeyCode(const QString& item , int& keyCode) +{ + QKeySequence sequence = QKeySequence::fromString(item); + if ( !sequence.isEmpty() ) + { + keyCode = sequence[0]; + + if ( sequence.count() > 1 ) + { + kWarning() << "Unhandled key codes in sequence: " << item.toStdString(); + } + } + // additional cases implemented for backwards compatibility with KDE 3 + else if ( item == "prior" ) + keyCode = Qt::Key_PageUp; + else if ( item == "next" ) + keyCode = Qt::Key_PageDown; + else + return false; + + return true; +} + +QString KeyboardTranslatorReader::description() const +{ + return _description; +} +bool KeyboardTranslatorReader::hasNextEntry() +{ + return _hasNext; +} +KeyboardTranslator::Entry KeyboardTranslatorReader::createEntry( const QString& condition , + const QString& result ) +{ + QString entryString("keyboard \"temporary\"\nkey "); + entryString.append(condition); + entryString.append(" : "); + + // if 'result' is the name of a command then the entry result will be that command, + // otherwise the result will be treated as a string to echo when the key sequence + // specified by 'condition' is pressed + KeyboardTranslator::Command command; + if (parseAsCommand(result,command)) + entryString.append(result); + else + entryString.append('\"' + result + '\"'); + + QByteArray array = entryString.toUtf8(); + QBuffer buffer(&array); + buffer.open(QIODevice::ReadOnly); + KeyboardTranslatorReader reader(&buffer); + + KeyboardTranslator::Entry entry; + if ( reader.hasNextEntry() ) + entry = reader.nextEntry(); + + return entry; +} + +KeyboardTranslator::Entry KeyboardTranslatorReader::nextEntry() +{ + Q_ASSERT( _hasNext ); + KeyboardTranslator::Entry entry = _nextEntry; + readNext(); + return entry; +} +bool KeyboardTranslatorReader::parseError() +{ + return false; +} +QList<KeyboardTranslatorReader::Token> KeyboardTranslatorReader::tokenize(const QString& line) +{ + QString text = line; + + // remove comments + bool inQuotes = false; + int commentPos = -1; + for (int i=text.length()-1;i>=0;i--) + { + QChar ch = text[i]; + if (ch == '\"') + inQuotes = !inQuotes; + else if (ch == '#' && !inQuotes) + commentPos = i; + } + if (commentPos != -1) + text.remove(commentPos,text.length()); + + text = text.simplified(); + + // title line: keyboard "title" + static QRegExp title("keyboard\\s+\"(.*)\""); + // key line: key KeySequence : "output" + // key line: key KeySequence : command + static QRegExp key("key\\s+([\\w\\+\\s\\-\\*\\.]+)\\s*:\\s*(\"(.*)\"|\\w+)"); + + QList<Token> list; + if ( text.isEmpty() ) + { + return list; + } + + if ( title.exactMatch(text) ) + { + Token titleToken = { Token::TitleKeyword , QString() }; + Token textToken = { Token::TitleText , title.capturedTexts()[1] }; + + list << titleToken << textToken; + } + else if ( key.exactMatch(text) ) + { + Token keyToken = { Token::KeyKeyword , QString() }; + Token sequenceToken = { Token::KeySequence , key.capturedTexts()[1].remove(' ') }; + + list << keyToken << sequenceToken; + + if ( key.capturedTexts()[3].isEmpty() ) + { + // capturedTexts()[2] is a command + Token commandToken = { Token::Command , key.capturedTexts()[2] }; + list << commandToken; + } + else + { + // capturedTexts()[3] is the output string + Token outputToken = { Token::OutputText , key.capturedTexts()[3] }; + list << outputToken; + } + } + else + { + kWarning() << "Line in keyboard translator file could not be understood:" << text.toStdString(); + } + + return list; +} + +QList<QString> KeyboardTranslatorManager::allTranslators() +{ + if ( !_haveLoadedAll ) + { + findTranslators(); + } + + return _translators.keys(); +} + +KeyboardTranslator::Entry::Entry() +: _keyCode(0) +, _modifiers(Qt::NoModifier) +, _modifierMask(Qt::NoModifier) +, _state(NoState) +, _stateMask(NoState) +, _command(NoCommand) +{ +} + +bool KeyboardTranslator::Entry::operator==(const Entry& rhs) const +{ + return _keyCode == rhs._keyCode && + _modifiers == rhs._modifiers && + _modifierMask == rhs._modifierMask && + _state == rhs._state && + _stateMask == rhs._stateMask && + _command == rhs._command && + _text == rhs._text; +} + +bool KeyboardTranslator::Entry::matches(int keyCode , + Qt::KeyboardModifiers modifiers, + States testState) const +{ + if ( _keyCode != keyCode ) + return false; + + if ( (modifiers & _modifierMask) != (_modifiers & _modifierMask) ) + return false; + + // if modifiers is non-zero, the 'any modifier' state is implicit + if ( modifiers != 0 ) + testState |= AnyModifierState; + + if ( (testState & _stateMask) != (_state & _stateMask) ) + return false; + + // special handling for the 'Any Modifier' state, which checks for the presence of + // any or no modifiers. In this context, the 'keypad' modifier does not count. + bool anyModifiersSet = modifiers != 0 && modifiers != Qt::KeypadModifier; + bool wantAnyModifier = _state & KeyboardTranslator::AnyModifierState; + if ( _stateMask & KeyboardTranslator::AnyModifierState ) + { + if ( wantAnyModifier != anyModifiersSet ) + return false; + } + + return true; +} +QByteArray KeyboardTranslator::Entry::escapedText(bool expandWildCards,Qt::KeyboardModifiers modifiers) const +{ + QByteArray result(text(expandWildCards,modifiers)); + + for ( int i = 0 ; i < result.count() ; i++ ) + { + char ch = result[i]; + char replacement = 0; + + switch ( ch ) + { + case 27 : replacement = 'E'; break; + case 8 : replacement = 'b'; break; + case 12 : replacement = 'f'; break; + case 9 : replacement = 't'; break; + case 13 : replacement = 'r'; break; + case 10 : replacement = 'n'; break; + default: + // any character which is not printable is replaced by an equivalent + // \xhh escape sequence (where 'hh' are the corresponding hex digits) + if ( !QChar(ch).isPrint() ) + replacement = 'x'; + } + + if ( replacement == 'x' ) + { + result.replace(i,1,"\\x"+QByteArray(1,ch).toHex()); + } else if ( replacement != 0 ) + { + result.remove(i,1); + result.insert(i,'\\'); + result.insert(i+1,replacement); + } + } + + return result; +} +QByteArray KeyboardTranslator::Entry::unescape(const QByteArray& input) const +{ + QByteArray result(input); + + for ( int i = 0 ; i < result.count()-1 ; i++ ) + { + + QByteRef ch = result[i]; + if ( ch == '\\' ) + { + char replacement[2] = {0,0}; + int charsToRemove = 2; + bool escapedChar = true; + + switch ( result[i+1] ) + { + case 'E' : replacement[0] = 27; break; + case 'b' : replacement[0] = 8 ; break; + case 'f' : replacement[0] = 12; break; + case 't' : replacement[0] = 9 ; break; + case 'r' : replacement[0] = 13; break; + case 'n' : replacement[0] = 10; break; + case 'x' : + { + // format is \xh or \xhh where 'h' is a hexadecimal + // digit from 0-9 or A-F which should be replaced + // with the corresponding character value + char hexDigits[3] = {0}; + + if ( (i < result.count()-2) && isxdigit(result[i+2]) ) + hexDigits[0] = result[i+2]; + if ( (i < result.count()-3) && isxdigit(result[i+3]) ) + hexDigits[1] = result[i+3]; + + unsigned charValue = 0; + sscanf(hexDigits,"%x",&charValue); + + replacement[0] = (char)charValue; + charsToRemove = 2 + strlen(hexDigits); + } + break; + default: + escapedChar = false; + } + + if ( escapedChar ) + result.replace(i,charsToRemove,replacement); + } + } + + return result; +} + +void KeyboardTranslator::Entry::insertModifier( QString& item , int modifier ) const +{ + if ( !(modifier & _modifierMask) ) + return; + + if ( modifier & _modifiers ) + item += '+'; + else + item += '-'; + + if ( modifier == Qt::ShiftModifier ) + item += "Shift"; + else if ( modifier == Qt::ControlModifier ) + item += "Ctrl"; + else if ( modifier == Qt::AltModifier ) + item += "Alt"; + else if ( modifier == Qt::MetaModifier ) + item += "Meta"; + else if ( modifier == Qt::KeypadModifier ) + item += "KeyPad"; +} +void KeyboardTranslator::Entry::insertState( QString& item , int state ) const +{ + if ( !(state & _stateMask) ) + return; + + if ( state & _state ) + item += '+' ; + else + item += '-' ; + + if ( state == KeyboardTranslator::AlternateScreenState ) + item += "AppScreen"; + else if ( state == KeyboardTranslator::NewLineState ) + item += "NewLine"; + else if ( state == KeyboardTranslator::AnsiState ) + item += "Ansi"; + else if ( state == KeyboardTranslator::CursorKeysState ) + item += "AppCursorKeys"; + else if ( state == KeyboardTranslator::AnyModifierState ) + item += "AnyModifier"; + else if ( state == KeyboardTranslator::ApplicationKeypadState ) + item += "AppKeypad"; +} +QString KeyboardTranslator::Entry::resultToString(bool expandWildCards,Qt::KeyboardModifiers modifiers) const +{ + if ( !_text.isEmpty() ) + return escapedText(expandWildCards,modifiers); + else if ( _command == EraseCommand ) + return "Erase"; + else if ( _command == ScrollPageUpCommand ) + return "ScrollPageUp"; + else if ( _command == ScrollPageDownCommand ) + return "ScrollPageDown"; + else if ( _command == ScrollLineUpCommand ) + return "ScrollLineUp"; + else if ( _command == ScrollLineDownCommand ) + return "ScrollLineDown"; + else if ( _command == ScrollLockCommand ) + return "ScrollLock"; + + return QString(); +} +QString KeyboardTranslator::Entry::conditionToString() const +{ + QString result = QKeySequence(_keyCode).toString(); + + insertModifier( result , Qt::ShiftModifier ); + insertModifier( result , Qt::ControlModifier ); + insertModifier( result , Qt::AltModifier ); + insertModifier( result , Qt::MetaModifier ); + insertModifier( result , Qt::KeypadModifier ); + + insertState( result , KeyboardTranslator::AlternateScreenState ); + insertState( result , KeyboardTranslator::NewLineState ); + insertState( result , KeyboardTranslator::AnsiState ); + insertState( result , KeyboardTranslator::CursorKeysState ); + insertState( result , KeyboardTranslator::AnyModifierState ); + insertState( result , KeyboardTranslator::ApplicationKeypadState ); + + return result; +} + +KeyboardTranslator::KeyboardTranslator(const QString& name) +: _name(name) +{ +} + +void KeyboardTranslator::setDescription(const QString& description) +{ + _description = description; +} +QString KeyboardTranslator::description() const +{ + return _description; +} +void KeyboardTranslator::setName(const QString& name) +{ + _name = name; +} +QString KeyboardTranslator::name() const +{ + return _name; +} + +QList<KeyboardTranslator::Entry> KeyboardTranslator::entries() const +{ + return _entries.values(); +} + +void KeyboardTranslator::addEntry(const Entry& entry) +{ + const int keyCode = entry.keyCode(); + _entries.insert(keyCode,entry); +} +void KeyboardTranslator::replaceEntry(const Entry& existing , const Entry& replacement) +{ + if ( !existing.isNull() ) + _entries.remove(existing.keyCode(),existing); + _entries.insert(replacement.keyCode(),replacement); +} +void KeyboardTranslator::removeEntry(const Entry& entry) +{ + _entries.remove(entry.keyCode(),entry); +} +KeyboardTranslator::Entry KeyboardTranslator::findEntry(int keyCode, Qt::KeyboardModifiers modifiers, States state) const +{ + foreach(const Entry& entry, _entries.values(keyCode)) + { + if ( entry.matches(keyCode,modifiers,state) ) + return entry; + } + return Entry(); // entry not found +} +void KeyboardTranslatorManager::addTranslator(KeyboardTranslator* translator) +{ + _translators.insert(translator->name(),translator); + + if ( !saveTranslator(translator) ) + kWarning() << "Unable to save translator" << translator->name().toStdString() + << "to disk."; +} +bool KeyboardTranslatorManager::deleteTranslator(const QString& name) +{ + Q_ASSERT( _translators.contains(name) ); + + // locate and delete + QString path = findTranslatorPath(name); + if ( QFile::remove(path) ) + { + _translators.remove(name); + return true; + } + else + { + kWarning() << "Failed to remove translator - " << path.toStdString(); + return false; + } +} + +/** + * @internal + */ +typedef void (*KdeCleanUpFunction)(); + +/** + * @internal + * + * Helper class for K_GLOBAL_STATIC to clean up the object on library unload or application + * shutdown. + */ +class KCleanUpGlobalStatic +{ + public: + KdeCleanUpFunction func; + + inline ~KCleanUpGlobalStatic() { func(); } +}; + + + +#ifdef Q_CC_MSVC +/** + * @internal + * + * MSVC seems to give anonymous structs the same name which fails at link time. So instead we name + * the struct and hope that by adding the line number to the name it's unique enough to never clash. + */ +# define K_GLOBAL_STATIC_STRUCT_NAME(NAME) _k_##NAME##__LINE__ +#else +/** + * @internal + * + * Make the struct of the K_GLOBAL_STATIC anonymous. + */ +# define K_GLOBAL_STATIC_STRUCT_NAME(NAME) +#endif + + + +#define K_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \ +static QBasicAtomicPointer<TYPE > _k_static_##NAME = Q_BASIC_ATOMIC_INITIALIZER(0); \ +static bool _k_static_##NAME##_destroyed; \ +static struct K_GLOBAL_STATIC_STRUCT_NAME(NAME) \ +{ \ + inline bool isDestroyed() const \ + { \ + return _k_static_##NAME##_destroyed; \ + } \ + inline bool exists() const \ + { \ + return _k_static_##NAME != 0; \ + } \ + inline operator TYPE*() \ + { \ + return operator->(); \ + } \ + inline TYPE *operator->() \ + { \ + if (!_k_static_##NAME) { \ + if (isDestroyed()) { \ + qFatal("Fatal Error: Accessed global static '%s *%s()' after destruction. " \ + "Defined at %s:%d", #TYPE, #NAME, __FILE__, __LINE__); \ + } \ + TYPE *x = new TYPE ARGS; \ + if (!_k_static_##NAME.testAndSetOrdered(0, x) \ + && _k_static_##NAME != x ) { \ + delete x; \ + } else { \ + static KCleanUpGlobalStatic cleanUpObject = { destroy }; \ + } \ + } \ + return _k_static_##NAME; \ + } \ + inline TYPE &operator*() \ + { \ + return *operator->(); \ + } \ + static void destroy() \ + { \ + _k_static_##NAME##_destroyed = true; \ + TYPE *x = _k_static_##NAME; \ + _k_static_##NAME = 0; \ + delete x; \ + } \ +} NAME; + +#define K_GLOBAL_STATIC(TYPE, NAME) K_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ()) + +K_GLOBAL_STATIC( KeyboardTranslatorManager , theKeyboardTranslatorManager ) +KeyboardTranslatorManager* KeyboardTranslatorManager::instance() +{ + return theKeyboardTranslatorManager; +}
new file mode 100644 --- /dev/null +++ b/gui/konsole/KeyboardTranslator.h @@ -0,0 +1,584 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2007-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. +*/ + +#ifndef KEYBOARDTRANSLATOR_H +#define KEYBOARDTRANSLATOR_H + +// Qt +#include <QtCore/QHash> +#include <QtCore/QList> +#include <QtGui/QKeySequence> +#include <QtCore/QMetaType> +#include <QtCore/QVarLengthArray> + +// Konsole +#include "konsole_export.h" + +class QIODevice; +class QTextStream; + +namespace Konsole +{ + +/** + * A convertor which maps between key sequences pressed by the user and the + * character strings which should be sent to the terminal and commands + * which should be invoked when those character sequences are pressed. + * + * Konsole supports multiple keyboard translators, allowing the user to + * specify the character sequences which are sent to the terminal + * when particular key sequences are pressed. + * + * A key sequence is defined as a key code, associated keyboard modifiers + * (Shift,Ctrl,Alt,Meta etc.) and state flags which indicate the state + * which the terminal must be in for the key sequence to apply. + */ +class KeyboardTranslator +{ +public: + /** + * The meaning of a particular key sequence may depend upon the state which + * the terminal emulation is in. Therefore findEntry() may return a different + * Entry depending upon the state flags supplied. + * + * This enum describes the states which may be associated with with a particular + * entry in the keyboard translation entry. + */ + enum State + { + /** Indicates that no special state is active */ + NoState = 0, + /** + * TODO More documentation + */ + NewLineState = 1, + /** + * Indicates that the terminal is in 'Ansi' mode. + * TODO: More documentation + */ + AnsiState = 2, + /** + * TODO More documentation + */ + CursorKeysState = 4, + /** + * Indicates that the alternate screen ( typically used by interactive programs + * such as screen or vim ) is active + */ + AlternateScreenState = 8, + /** Indicates that any of the modifier keys is active. */ + AnyModifierState = 16, + /** Indicates that the numpad is in application mode. */ + ApplicationKeypadState = 32 + }; + Q_DECLARE_FLAGS(States,State) + + /** + * This enum describes commands which are associated with particular key sequences. + */ + enum Command + { + /** Indicates that no command is associated with this command sequence */ + NoCommand = 0, + /** TODO Document me */ + SendCommand = 1, + /** Scroll the terminal display up one page */ + ScrollPageUpCommand = 2, + /** Scroll the terminal display down one page */ + ScrollPageDownCommand = 4, + /** Scroll the terminal display up one line */ + ScrollLineUpCommand = 8, + /** Scroll the terminal display down one line */ + ScrollLineDownCommand = 16, + /** Toggles scroll lock mode */ + ScrollLockCommand = 32, + /** Echos the operating system specific erase character. */ + EraseCommand = 64 + }; + Q_DECLARE_FLAGS(Commands,Command) + + /** + * Represents an association between a key sequence pressed by the user + * and the character sequence and commands associated with it for a particular + * KeyboardTranslator. + */ + class Entry + { + public: + /** + * Constructs a new entry for a keyboard translator. + */ + Entry(); + + /** + * Returns true if this entry is null. + * This is true for newly constructed entries which have no properties set. + */ + bool isNull() const; + + /** Returns the commands associated with this entry */ + Command command() const; + /** Sets the command associated with this entry. */ + void setCommand(Command command); + + /** + * Returns the character sequence associated with this entry, optionally replacing + * wildcard '*' characters with numbers to indicate the keyboard modifiers being pressed. + * + * TODO: The numbers used to replace '*' characters are taken from the Konsole/KDE 3 code. + * Document them. + * + * @param expandWildCards Specifies whether wild cards (occurrences of the '*' character) in + * the entry should be replaced with a number to indicate the modifier keys being pressed. + * + * @param modifiers The keyboard modifiers being pressed. + */ + QByteArray text(bool expandWildCards = false, + Qt::KeyboardModifiers modifiers = Qt::NoModifier) const; + + /** Sets the character sequence associated with this entry */ + void setText(const QByteArray& text); + + /** + * Returns the character sequence associated with this entry, + * with any non-printable characters replaced with escape sequences. + * + * eg. \\E for Escape, \\t for tab, \\n for new line. + * + * @param expandWildCards See text() + * @param modifiers See text() + */ + QByteArray escapedText(bool expandWildCards = false, + Qt::KeyboardModifiers modifiers = Qt::NoModifier) const; + + /** Returns the character code ( from the Qt::Key enum ) associated with this entry */ + int keyCode() const; + /** Sets the character code associated with this entry */ + void setKeyCode(int keyCode); + + /** + * Returns a bitwise-OR of the enabled keyboard modifiers associated with this entry. + * If a modifier is set in modifierMask() but not in modifiers(), this means that the entry + * only matches when that modifier is NOT pressed. + * + * If a modifier is not set in modifierMask() then the entry matches whether the modifier + * is pressed or not. + */ + Qt::KeyboardModifiers modifiers() const; + + /** Returns the keyboard modifiers which are valid in this entry. See modifiers() */ + Qt::KeyboardModifiers modifierMask() const; + + /** See modifiers() */ + void setModifiers( Qt::KeyboardModifiers modifiers ); + /** See modifierMask() and modifiers() */ + void setModifierMask( Qt::KeyboardModifiers modifiers ); + + /** + * Returns a bitwise-OR of the enabled state flags associated with this entry. + * If flag is set in stateMask() but not in state(), this means that the entry only + * matches when the terminal is NOT in that state. + * + * If a state is not set in stateMask() then the entry matches whether the terminal + * is in that state or not. + */ + States state() const; + + /** Returns the state flags which are valid in this entry. See state() */ + States stateMask() const; + + /** See state() */ + void setState( States state ); + /** See stateMask() */ + void setStateMask( States mask ); + + /** + * Returns the key code and modifiers associated with this entry + * as a QKeySequence + */ + //QKeySequence keySequence() const; + + /** + * Returns this entry's conditions ( ie. its key code, modifier and state criteria ) + * as a string. + */ + QString conditionToString() const; + + /** + * Returns this entry's result ( ie. its command or character sequence ) + * as a string. + * + * @param expandWildCards See text() + * @param modifiers See text() + */ + QString resultToString(bool expandWildCards = false, + Qt::KeyboardModifiers modifiers = Qt::NoModifier) const; + + /** + * Returns true if this entry matches the given key sequence, specified + * as a combination of @p keyCode , @p modifiers and @p state. + */ + bool matches( int keyCode , + Qt::KeyboardModifiers modifiers , + States flags ) const; + + bool operator==(const Entry& rhs) const; + + private: + void insertModifier( QString& item , int modifier ) const; + void insertState( QString& item , int state ) const; + QByteArray unescape(const QByteArray& text) const; + + int _keyCode; + Qt::KeyboardModifiers _modifiers; + Qt::KeyboardModifiers _modifierMask; + States _state; + States _stateMask; + + Command _command; + QByteArray _text; + }; + + /** Constructs a new keyboard translator with the given @p name */ + KeyboardTranslator(const QString& name); + + //KeyboardTranslator(const KeyboardTranslator& other); + + /** Returns the name of this keyboard translator */ + QString name() const; + + /** Sets the name of this keyboard translator */ + void setName(const QString& name); + + /** Returns the descriptive name of this keyboard translator */ + QString description() const; + + /** Sets the descriptive name of this keyboard translator */ + void setDescription(const QString& description); + + /** + * Looks for an entry in this keyboard translator which matches the given + * key code, keyboard modifiers and state flags. + * + * Returns the matching entry if found or a null Entry otherwise ( ie. + * entry.isNull() will return true ) + * + * @param keyCode A key code from the Qt::Key enum + * @param modifiers A combination of modifiers + * @param state Optional flags which specify the current state of the terminal + */ + Entry findEntry(int keyCode , + Qt::KeyboardModifiers modifiers , + States state = NoState) const; + + /** + * Adds an entry to this keyboard translator's table. Entries can be looked up according + * to their key sequence using findEntry() + */ + void addEntry(const Entry& entry); + + /** + * Replaces an entry in the translator. If the @p existing entry is null, + * then this is equivalent to calling addEntry(@p replacement) + */ + void replaceEntry(const Entry& existing , const Entry& replacement); + + /** + * Removes an entry from the table. + */ + void removeEntry(const Entry& entry); + + /** Returns a list of all entries in the translator. */ + QList<Entry> entries() const; + +private: + + QMultiHash<int,Entry> _entries; // entries in this keyboard translation, + // entries are indexed according to + // their keycode + QString _name; + QString _description; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(KeyboardTranslator::States) +Q_DECLARE_OPERATORS_FOR_FLAGS(KeyboardTranslator::Commands) + +/** + * Parses the contents of a Keyboard Translator (.keytab) file and + * returns the entries found in it. + * + * Usage example: + * + * @code + * QFile source( "/path/to/keytab" ); + * source.open( QIODevice::ReadOnly ); + * + * KeyboardTranslator* translator = new KeyboardTranslator( "name-of-translator" ); + * + * KeyboardTranslatorReader reader(source); + * while ( reader.hasNextEntry() ) + * translator->addEntry(reader.nextEntry()); + * + * source.close(); + * + * if ( !reader.parseError() ) + * { + * // parsing succeeded, do something with the translator + * } + * else + * { + * // parsing failed + * } + * @endcode + */ +class KeyboardTranslatorReader +{ +public: + /** Constructs a new reader which parses the given @p source */ + KeyboardTranslatorReader( QIODevice* source ); + + /** + * Returns the description text. + * TODO: More documentation + */ + QString description() const; + + /** Returns true if there is another entry in the source stream */ + bool hasNextEntry(); + /** Returns the next entry found in the source stream */ + KeyboardTranslator::Entry nextEntry(); + + /** + * Returns true if an error occurred whilst parsing the input or + * false if no error occurred. + */ + bool parseError(); + + /** + * Parses a condition and result string for a translator entry + * and produces a keyboard translator entry. + * + * The condition and result strings are in the same format as in + */ + static KeyboardTranslator::Entry createEntry( const QString& condition , + const QString& result ); +private: + struct Token + { + enum Type + { + TitleKeyword, + TitleText, + KeyKeyword, + KeySequence, + Command, + OutputText + }; + Type type; + QString text; + }; + QList<Token> tokenize(const QString&); + void readNext(); + bool decodeSequence(const QString& , + int& keyCode, + Qt::KeyboardModifiers& modifiers, + Qt::KeyboardModifiers& modifierMask, + KeyboardTranslator::States& state, + KeyboardTranslator::States& stateFlags); + + static bool parseAsModifier(const QString& item , Qt::KeyboardModifier& modifier); + static bool parseAsStateFlag(const QString& item , KeyboardTranslator::State& state); + static bool parseAsKeyCode(const QString& item , int& keyCode); + static bool parseAsCommand(const QString& text , KeyboardTranslator::Command& command); + + QIODevice* _source; + QString _description; + KeyboardTranslator::Entry _nextEntry; + bool _hasNext; +}; + +/** Writes a keyboard translation to disk. */ +class KeyboardTranslatorWriter +{ +public: + /** + * Constructs a new writer which saves data into @p destination. + * The caller is responsible for closing the device when writing is complete. + */ + KeyboardTranslatorWriter(QIODevice* destination); + ~KeyboardTranslatorWriter(); + + /** + * Writes the header for the keyboard translator. + * @param description Description of the keyboard translator. + */ + void writeHeader( const QString& description ); + /** Writes a translator entry. */ + void writeEntry( const KeyboardTranslator::Entry& entry ); + +private: + QIODevice* _destination; + QTextStream* _writer; +}; + +/** + * Manages the keyboard translations available for use by terminal sessions, + * see KeyboardTranslator. + */ +class KONSOLEPRIVATE_EXPORT KeyboardTranslatorManager +{ +public: + /** + * Constructs a new KeyboardTranslatorManager and loads the list of + * available keyboard translations. + * + * The keyboard translations themselves are not loaded until they are + * first requested via a call to findTranslator() + */ + KeyboardTranslatorManager(); + ~KeyboardTranslatorManager(); + + /** + * Adds a new translator. If a translator with the same name + * already exists, it will be replaced by the new translator. + * + * TODO: More documentation. + */ + void addTranslator(KeyboardTranslator* translator); + + /** + * Deletes a translator. Returns true on successful deletion or false otherwise. + * + * TODO: More documentation + */ + bool deleteTranslator(const QString& name); + + /** Returns the default translator for Konsole. */ + const KeyboardTranslator* defaultTranslator(); + + /** + * Returns the keyboard translator with the given name or 0 if no translator + * with that name exists. + * + * The first time that a translator with a particular name is requested, + * the on-disk .keyboard file is loaded and parsed. + */ + const KeyboardTranslator* findTranslator(const QString& name); + /** + * Returns a list of the names of available keyboard translators. + * + * The first time this is called, a search for available + * translators is started. + */ + QList<QString> allTranslators(); + + /** Returns the global KeyboardTranslatorManager instance. */ + static KeyboardTranslatorManager* instance(); + +private: + static const QByteArray defaultTranslatorText; + + void findTranslators(); // locate the available translators + KeyboardTranslator* loadTranslator(const QString& name); // loads the translator + // with the given name + KeyboardTranslator* loadTranslator(QIODevice* device,const QString& name); + + bool saveTranslator(const KeyboardTranslator* translator); + QString findTranslatorPath(const QString& name); + + QHash<QString,KeyboardTranslator*> _translators; // maps translator-name -> KeyboardTranslator + // instance + bool _haveLoadedAll; +}; + +inline int KeyboardTranslator::Entry::keyCode() const { return _keyCode; } +inline void KeyboardTranslator::Entry::setKeyCode(int keyCode) { _keyCode = keyCode; } + +inline void KeyboardTranslator::Entry::setModifiers( Qt::KeyboardModifiers modifier ) +{ + _modifiers = modifier; +} +inline Qt::KeyboardModifiers KeyboardTranslator::Entry::modifiers() const { return _modifiers; } + +inline void KeyboardTranslator::Entry::setModifierMask( Qt::KeyboardModifiers mask ) +{ + _modifierMask = mask; +} +inline Qt::KeyboardModifiers KeyboardTranslator::Entry::modifierMask() const { return _modifierMask; } + +inline bool KeyboardTranslator::Entry::isNull() const +{ + return ( *this == Entry() ); +} + +inline void KeyboardTranslator::Entry::setCommand( Command command ) +{ + _command = command; +} +inline KeyboardTranslator::Command KeyboardTranslator::Entry::command() const { return _command; } + +inline void KeyboardTranslator::Entry::setText( const QByteArray& text ) +{ + _text = unescape(text); +} +inline int oneOrZero(int value) +{ + return value ? 1 : 0; +} +inline QByteArray KeyboardTranslator::Entry::text(bool expandWildCards,Qt::KeyboardModifiers modifiers) const +{ + QByteArray expandedText = _text; + + if (expandWildCards) + { + int modifierValue = 1; + modifierValue += oneOrZero(modifiers & Qt::ShiftModifier); + modifierValue += oneOrZero(modifiers & Qt::AltModifier) << 1; + modifierValue += oneOrZero(modifiers & Qt::ControlModifier) << 2; + + for (int i=0;i<_text.length();i++) + { + if (expandedText[i] == '*') + expandedText[i] = '0' + modifierValue; + } + } + + return expandedText; +} + +inline void KeyboardTranslator::Entry::setState( States state ) +{ + _state = state; +} +inline KeyboardTranslator::States KeyboardTranslator::Entry::state() const { return _state; } + +inline void KeyboardTranslator::Entry::setStateMask( States stateMask ) +{ + _stateMask = stateMask; +} +inline KeyboardTranslator::States KeyboardTranslator::Entry::stateMask() const { return _stateMask; } + +} + +Q_DECLARE_METATYPE(Konsole::KeyboardTranslator::Entry) +Q_DECLARE_METATYPE(const Konsole::KeyboardTranslator*) + +#endif // KEYBOARDTRANSLATOR_H +
new file mode 100644 --- /dev/null +++ b/gui/konsole/LineFont.h @@ -0,0 +1,21 @@ +// WARNING: Autogenerated by "fontembedder ./linefont.src". +// You probably do not want to hand-edit this! + +static const quint32 LineChars[] = { + 0x00007c00, 0x000fffe0, 0x00421084, 0x00e739ce, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00427000, 0x004e7380, 0x00e77800, 0x00ef7bc0, + 0x00421c00, 0x00439ce0, 0x00e73c00, 0x00e7bde0, 0x00007084, 0x000e7384, 0x000079ce, 0x000f7bce, + 0x00001c84, 0x00039ce4, 0x00003dce, 0x0007bdee, 0x00427084, 0x004e7384, 0x004279ce, 0x00e77884, + 0x00e779ce, 0x004f7bce, 0x00ef7bc4, 0x00ef7bce, 0x00421c84, 0x00439ce4, 0x00423dce, 0x00e73c84, + 0x00e73dce, 0x0047bdee, 0x00e7bde4, 0x00e7bdee, 0x00427c00, 0x0043fce0, 0x004e7f80, 0x004fffe0, + 0x004fffe0, 0x00e7fde0, 0x006f7fc0, 0x00efffe0, 0x00007c84, 0x0003fce4, 0x000e7f84, 0x000fffe4, + 0x00007dce, 0x0007fdee, 0x000f7fce, 0x000fffee, 0x00427c84, 0x0043fce4, 0x004e7f84, 0x004fffe4, + 0x00427dce, 0x00e77c84, 0x00e77dce, 0x0047fdee, 0x004e7fce, 0x00e7fde4, 0x00ef7f84, 0x004fffee, + 0x00efffe4, 0x00e7fdee, 0x00ef7fce, 0x00efffee, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x000f83e0, 0x00a5294a, 0x004e1380, 0x00a57800, 0x00ad0bc0, 0x004390e0, 0x00a53c00, 0x00a5a1e0, + 0x000e1384, 0x0000794a, 0x000f0b4a, 0x000390e4, 0x00003d4a, 0x0007a16a, 0x004e1384, 0x00a5694a, + 0x00ad2b4a, 0x004390e4, 0x00a52d4a, 0x00a5a16a, 0x004f83e0, 0x00a57c00, 0x00ad83e0, 0x000f83e4, + 0x00007d4a, 0x000f836a, 0x004f93e4, 0x00a57d4a, 0x00ad836a, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00001c00, 0x00001084, 0x00007000, 0x00421000, + 0x00039ce0, 0x000039ce, 0x000e7380, 0x00e73800, 0x000e7f80, 0x00e73884, 0x0003fce0, 0x004239ce +};
new file mode 100644 --- /dev/null +++ b/gui/konsole/MainWindow.cpp @@ -0,0 +1,527 @@ +/* + 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 "MainWindow.h" +#include "SessionManager.h" + +// Qt +#include <QtGui/QBoxLayout> + +// KDE +#include <KAcceleratorManager> +#include <KAction> +#include <KActionCollection> +#include <KActionMenu> +#include <KApplication> +#include <KCmdLineArgs> +#include <KShortcutsDialog> +#include <KLocale> +#include <KMenu> +#include <KMenuBar> +#include <KMessageBox> +#include <KService> +#include <KToggleAction> +#include <KToggleFullScreenAction> +#include <KToolInvocation> +#include <KStandardAction> +#include <KStandardGuiItem> +#include <KWindowSystem> +#include <KXMLGUIFactory> +#include <KNotifyConfigWidget> + +// Konsole +#include "BookmarkHandler.h" +#include "IncrementalSearchBar.h" +#include "RemoteConnectionDialog.h" +#include "SessionController.h" +#include "ProfileList.h" +#include "ManageProfilesDialog.h" +#include "Session.h" +#include "ViewManager.h" +#include "ViewSplitter.h" + +using namespace Konsole; + +static bool useTransparency() +{ + KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); + bool compositingAvailable = KWindowSystem::compositingActive() || + args->isSet("force-transparency"); + return compositingAvailable && args->isSet("transparency"); +} + +MainWindow::MainWindow() + : KXmlGuiWindow() , + _bookmarkHandler(0), + _pluggedController(0), + _menuBarVisibilitySet(false) +{ + if (useTransparency()) { + setAttribute(Qt::WA_TranslucentBackground); + setAttribute(Qt::WA_NoSystemBackground, false); + } + + // create actions for menus + setupActions(); + + // create view manager + _viewManager = new ViewManager(this,actionCollection()); + connect( _viewManager , SIGNAL(empty()) , this , SLOT(close()) ); + connect( _viewManager , SIGNAL(activeViewChanged(SessionController*)) , this , + SLOT(activeViewChanged(SessionController*)) ); + connect( _viewManager , SIGNAL(unplugController(SessionController*)) , this , + SLOT(disconnectController(SessionController*)) ); + connect( _viewManager , SIGNAL(viewPropertiesChanged(const QList<ViewProperties*>&)) , + bookmarkHandler() , SLOT(setViews(const QList<ViewProperties*>&)) ); + + connect( _viewManager , SIGNAL(setMenuBarVisibleRequest(bool)) , this , + SLOT(setMenuBarVisibleOnce(bool)) ); + connect( _viewManager , SIGNAL(setSaveGeometryOnExitRequest(bool)) , this , + SLOT(setSaveGeometryOnExit(bool)) ); + connect( _viewManager , SIGNAL(newViewRequest(Profile::Ptr)) , + this , SLOT(newFromProfile(Profile::Ptr)) ); + connect( _viewManager , SIGNAL(newViewRequest()) , + this , SLOT(newTab())); + + // create main window widgets + setupWidgets(); + + // disable automatically generated accelerators in top-level + // menu items - to avoid conflicting with Alt+[Letter] shortcuts + // in terminal applications + KAcceleratorManager::setNoAccel(menuBar()); + // create menus + createGUI(); + // remove accelerators for standard menu items (eg. &File, &View, &Edit) + // etc. which are defined in kdelibs/kdeui/xmlgui/ui_standards.rc, again, + // to avoid conflicting with Alt+[Letter] terminal shortcuts + // + // TODO - Modify XMLGUI so that it allows the text for standard actions + // defined in ui_standards.rc to be re-defined in the local application + // XMLGUI file (konsoleui.rc in this case) - the text for standard items + // can then be redefined there to exclude the standard accelerators + removeMenuAccelerators(); + // replace standard shortcuts which cannot be used in a terminal + // (as they are reserved for use by terminal programs) + correctShortcuts(); + + // enable save and restore of window size + setAutoSaveSettings("MainWindow",true); +} +void MainWindow::removeMenuAccelerators() +{ + foreach(QAction* menuItem, menuBar()->actions()) + { + QString itemText = menuItem->text(); + itemText = KGlobal::locale()->removeAcceleratorMarker(itemText); + menuItem->setText(itemText); + } +} +void MainWindow::setMenuBarVisibleOnce(bool visible) +{ + if (_menuBarVisibilitySet || menuBar()->isTopLevelMenu() ) + return; + + menuBar()->setVisible(visible); + _toggleMenuBarAction->setChecked(visible); + + _menuBarVisibilitySet = true; +} + +void MainWindow::setSaveGeometryOnExit(bool save) +{ + setAutoSaveSettings("MainWindow",save); +} + +void MainWindow::correctShortcuts() +{ + // replace F1 shortcut for help contents + QAction* helpAction = actionCollection()->action("help_contents"); + + Q_ASSERT( helpAction ); + + helpAction->setShortcut(QKeySequence()); + + // replace Ctrl+B shortcut for bookmarks + // TODO - Make this configurable + QAction* bookmarkAction = actionCollection()->action("add_bookmark"); + Q_ASSERT(bookmarkAction); + bookmarkAction->setShortcut(QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_B)); +} + +void MainWindow::setDefaultProfile(Profile::Ptr profile) +{ + _defaultProfile = profile; +} +Profile::Ptr MainWindow::defaultProfile() const +{ + return _defaultProfile; +} + +ViewManager* MainWindow::viewManager() const +{ + return _viewManager; +} + +void MainWindow::disconnectController(SessionController* controller) +{ + disconnect( controller , SIGNAL(titleChanged(ViewProperties*)) + , this , SLOT(activeViewTitleChanged(ViewProperties*)) ); + + // KXmlGuiFactory::removeClient() will try to access actions associated + // with the controller internally, which may not be valid after the controller + // itself is no longer valid (after the associated session and or view have + // been destroyed) + if (controller->isValid()) + guiFactory()->removeClient(controller); + + controller->setSearchBar(0); +} + +void MainWindow::activeViewChanged(SessionController* controller) +{ + // associate bookmark menu with current session + bookmarkHandler()->setActiveView(controller); + disconnect( bookmarkHandler() , SIGNAL(openUrl(const KUrl&)) , 0 , 0 ); + connect( bookmarkHandler() , SIGNAL(openUrl(const KUrl&)) , controller , + SLOT(openUrl(const KUrl&)) ); + + if ( _pluggedController ) + disconnectController(_pluggedController); + + // listen for title changes from the current session + Q_ASSERT( controller ); + + connect( controller , SIGNAL(titleChanged(ViewProperties*)) , + this , SLOT(activeViewTitleChanged(ViewProperties*)) ); + + controller->setShowMenuAction( _toggleMenuBarAction ); + guiFactory()->addClient(controller); + + // set the current session's search bar + controller->setSearchBar( searchBar() ); + + // update session title to match newly activated session + activeViewTitleChanged(controller); + + _pluggedController = controller; +} + +void MainWindow::activeViewTitleChanged(ViewProperties* properties) +{ + setPlainCaption(properties->title()); +} + +IncrementalSearchBar* MainWindow::searchBar() const +{ + return _viewManager->searchBar(); +} + +void MainWindow::setupActions() +{ + KActionCollection* collection = actionCollection(); + + // File Menu + _newTabMenuAction = new KActionMenu(KIcon("tab-new"), i18n("&New Tab"), collection); + _newTabMenuAction->setShortcut( QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_T) ); + _newTabMenuAction->setShortcutConfigurable(true); + _newTabMenuAction->setAutoRepeat( false ); + connect(_newTabMenuAction, SIGNAL(triggered()), this, SLOT(newTab())); + collection->addAction("new-tab", _newTabMenuAction); + + KAction* action = collection->addAction("new-window"); + action->setIcon( KIcon("window-new") ); + action->setText( i18n("New &Window") ); + action->setShortcut( QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_N) ); + action->setAutoRepeat( false ); + connect( action , SIGNAL(triggered()) , this , SLOT(newWindow()) ); + + action = collection->addAction("remote-connection"); + action->setText( i18n("Remote Connection...") ); + action->setIcon( KIcon("network-connect") ); + action->setShortcut( QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_R) ); + connect( action , SIGNAL(triggered()) , this , SLOT(showRemoteConnectionDialog()) ); + + action = KStandardAction::quit( this , SLOT(close()) , collection ); + // the default shortcut for quit is typically Ctrl+[Some Letter, usually Q] but that is reserved for + // use by terminal applications + action->setShortcut( QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Q) ); + + // Bookmark Menu + KActionMenu* bookmarkMenu = new KActionMenu(i18n("&Bookmarks") , collection ); + _bookmarkHandler = new BookmarkHandler( collection , bookmarkMenu->menu() , true , this ); + collection->addAction("bookmark" , bookmarkMenu); + + connect( _bookmarkHandler , SIGNAL(openUrls(QList<KUrl>)) , this , SLOT(openUrls(QList<KUrl>)) ); + + //TODO: The 'Add Bookmark' menu action currently has a Ctrl+B shortcut by + // default which cannot be overridden + //NOTE: This is currently handled by correctShortcuts() + + // View Menu + _toggleMenuBarAction = KStandardAction::showMenubar(menuBar(), SLOT(setVisible(bool)), collection); + _toggleMenuBarAction->setShortcut( QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_M) ); + + // Hide the Show/Hide menubar item if the menu bar is a MacOS-style menu bar + if ( menuBar()->isTopLevelMenu() ) + _toggleMenuBarAction->setVisible(false); + + // Full Screen + action = KStandardAction::fullScreen(this, SLOT(viewFullScreen(bool)), this, collection); + action->setShortcut( QKeySequence() ); + + // Settings Menu + KStandardAction::configureNotifications( this , SLOT(configureNotifications()) , collection ); + KStandardAction::keyBindings( this , SLOT(showShortcutsDialog()) , collection ); + + action = collection->addAction("configure-profiles"); + action->setText( i18n("Configure Profiles...") ); + action->setIcon( KIcon("configure") ); + connect( action, SIGNAL(triggered()) , this , SLOT(showManageProfilesDialog()) ); + +} + +void MainWindow::viewFullScreen(bool fullScreen) +{ + if ( fullScreen ) + setWindowState( windowState() | Qt::WindowFullScreen ); + else + setWindowState( windowState() & ~Qt::WindowFullScreen ); +} + +BookmarkHandler* MainWindow::bookmarkHandler() const +{ + return _bookmarkHandler; +} + +void MainWindow::setSessionList(ProfileList* list) +{ + sessionListChanged(list->actions()); + + connect( list , SIGNAL(profileSelected(Profile::Ptr)) , this , + SLOT(newFromProfile(Profile::Ptr)) ); + + connect( list , SIGNAL(actionsChanged(const QList<QAction*>&)) , this , + SLOT(sessionListChanged(const QList<QAction*>&)) ); +} + +void MainWindow::sessionListChanged(const QList<QAction*>& actions) +{ + // Update the 'New Tab' KActionMenu + KMenu *newTabMenu = _newTabMenuAction->menu(); + newTabMenu->clear(); + foreach (QAction *action, actions) { + newTabMenu->addAction(action); + + // NOTE: _defaultProfile seems to not work here, sigh. + Profile::Ptr profile = SessionManager::instance()->defaultProfile(); + if (profile && profile->name() == action->text()) { + action->setIcon(KIcon(profile->icon(), NULL, QStringList("emblem-favorite"))); + newTabMenu->setDefaultAction(action); + QFont font = action->font(); + font.setBold(true); + action->setFont(font); + } + } + +} + +QString MainWindow::activeSessionDir() const +{ + if ( _pluggedController ) + return _pluggedController->currentDir(); + else + return QString(); +} + +void MainWindow::openUrls(const QList<KUrl>& urls) +{ + foreach( const KUrl& url , urls ) + { + if ( url.isLocalFile() ) + emit newSessionRequest( _defaultProfile , url.path() , _viewManager ); + + else if ( url.protocol() == "ssh" ) + emit newSSHSessionRequest( _defaultProfile , url , _viewManager ); + } +} + +void MainWindow::newTab() +{ + emit newSessionRequest( _defaultProfile , activeSessionDir() , _viewManager); +} + +void MainWindow::newWindow() +{ + emit newWindowRequest( _defaultProfile , activeSessionDir() ); +} + +bool MainWindow::queryClose() +{ + if (kapp->sessionSaving() || + _viewManager->viewProperties().count() < 2) + return true; + + int result = KMessageBox::warningYesNoCancel(this, + i18n("You have multiple tabs in this window, " + "are you sure you want to quit?"), + i18n("Confirm Close"), + KStandardGuiItem::quit(), + KGuiItem(i18n("Close Current Tab"), "tab-close"), + KStandardGuiItem::cancel(), + "CloseAllTabs"); + + switch (result) + { + case KMessageBox::Yes: + return true; + case KMessageBox::No: + if (_pluggedController && _pluggedController->session()) + { + disconnectController(_pluggedController); + _pluggedController->session()->close(); + } + return false; + case KMessageBox::Cancel: + return false; + } + + return true; +} + +void MainWindow::saveProperties(KConfigGroup& group) +{ + if (_defaultProfile) + group.writePathEntry("Default Profile", _defaultProfile->path()); + _viewManager->saveSessions(group); +} + +void MainWindow::readProperties(const KConfigGroup& group) +{ + SessionManager *manager = SessionManager::instance(); + QString profilePath = group.readPathEntry("Default Profile", QString()); + Profile::Ptr profile = manager->defaultProfile(); + if (!profilePath.isEmpty()) + profile = manager->loadProfile(profilePath); + setDefaultProfile(profile); + _viewManager->restoreSessions(group); +} + +void MainWindow::saveGlobalProperties(KConfig* config) +{ + SessionManager::instance()->saveSessions(config); +} + +void MainWindow::readGlobalProperties(KConfig* config) +{ + SessionManager::instance()->restoreSessions(config); +} + +void MainWindow::syncActiveShortcuts(KActionCollection* dest, const KActionCollection* source) +{ + foreach(QAction* qAction, source->actions()) + { + if (KAction* kAction = qobject_cast<KAction*>(qAction)) + { + if (KAction* destKAction = qobject_cast<KAction*>(dest->action(kAction->objectName()))) + destKAction->setShortcut(kAction->shortcut(KAction::ActiveShortcut),KAction::ActiveShortcut); + } + } +} +void MainWindow::showShortcutsDialog() +{ + KShortcutsDialog dialog(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsDisallowed, this); + + // add actions from this window and the current session controller + foreach(KXMLGUIClient* client, guiFactory()->clients()) + dialog.addCollection(client->actionCollection()); + + if (dialog.configure()) + { + // sync shortcuts for non-session actions (defined in "konsoleui.rc") in other main windows + foreach(QWidget* widget, QApplication::topLevelWidgets()) + { + MainWindow* window = qobject_cast<MainWindow*>(widget); + if (window && window != this) + syncActiveShortcuts(window->actionCollection(),actionCollection()); + } + // sync shortcuts for session actions (defined in "sessionui.rc") in other session controllers. + // Controllers which are currently plugged in (ie. their actions are part of the current menu) + // must be updated immediately via syncActiveShortcuts(). Other controllers will be updated + // when they are plugged into a main window. + foreach(SessionController* controller, SessionController::allControllers()) + { + controller->reloadXML(); + if (controller->factory() && controller != _pluggedController) + syncActiveShortcuts(controller->actionCollection(),_pluggedController->actionCollection()); + } + } +} + +void MainWindow::newFromProfile(Profile::Ptr profile) +{ + emit newSessionRequest(profile, activeSessionDir(), _viewManager); +} +void MainWindow::showManageProfilesDialog() +{ + ManageProfilesDialog* dialog = new ManageProfilesDialog(this); + dialog->show(); +} + +void MainWindow::showRemoteConnectionDialog() +{ +// RemoteConnectionDialog dialog(this); +// if ( dialog.exec() == QDialog::Accepted ) +// emit newSessionRequest(dialog.sessionKey(),QString(),_viewManager); +} + +void MainWindow::setupWidgets() +{ + QWidget* widget = new QWidget(this); + QVBoxLayout* layout = new QVBoxLayout(); + + layout->addWidget( _viewManager->widget() ); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + + widget->setLayout(layout); + + setCentralWidget(widget); +} + +void MainWindow::configureNotifications() +{ + KNotifyConfigWidget::configure( this ); +} + +void MainWindow::showEvent(QShowEvent *event) +{ + // This code from Konqueror. + // We need to check if our toolbars are shown/hidden here, and set + // our menu items accordingly. We can't do it in the constructor because + // view profiles store toolbar info, and that info is read after + // construct time. + _toggleMenuBarAction->setChecked( !menuBar()->isHidden() ); + // Call parent method + KXmlGuiWindow::showEvent(event); +} + +#include "MainWindow.moc" +
new file mode 100644 --- /dev/null +++ b/gui/konsole/MainWindow.h @@ -0,0 +1,213 @@ +/* + 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. +*/ + +#ifndef KONSOLEMAINWINDOW_H +#define KONSOLEMAINWINDOW_H + +// Qt +#include <QtCore/QPointer> + +// KDE +#include <KXmlGuiWindow> +#include <KUrl> + +// Local +#include "Profile.h" + +class KAction; +class KActionMenu; +class KToggleAction; + +namespace Konsole +{ + +class IncrementalSearchBar; +class ViewManager; +class ViewProperties; +class SessionController; +class ProfileList; +class BookmarkHandler; + +/** + * The main window. This contains the menus and an area which contains the terminal displays. + * + * The main window does not create the views or the container widgets which hold the views. + * This is done by the ViewManager class. When a MainWindow is instantiated, it creates + * a new ViewManager. The ViewManager can then be used to create new terminal displays + * inside the window. + * + * Do not construct new main windows directly, use Application's newMainWindow() method. + */ +class MainWindow : public KXmlGuiWindow +{ + Q_OBJECT + + public: + /** + * Constructs a new main window. Do not create new main windows directly, use Application's + * newMainWindow() method instead. + */ + MainWindow(); + + /** + * Returns the view manager associated with this window. The view manager can be used to + * create new views on particular session objects inside this window. + */ + ViewManager* viewManager() const; + + /** + * Returns the search bar. + * TODO - More documentation + */ + IncrementalSearchBar* searchBar() const; + + /** Sets the list of sessions to be displayed in the File menu */ + void setSessionList(ProfileList* list); + + /** + * Returns the bookmark handler associated with this window. + */ + BookmarkHandler* bookmarkHandler() const; + + /** + * Sets the default profile for this window. + * This is the default value for the profile argument + * when the newSessionRequest() and newWindow() signals + * are emitted. + */ + void setDefaultProfile(Profile::Ptr profile); + + /** + * Returns the default profile for this window. + * See setDefaultProfile() + */ + Profile::Ptr defaultProfile() const; + + + signals: + /** + * Emitted by the main window to request the creation of a new session. + * + * @param profile The profile to use to create the new session. + * @param directory Initial working directory for the new session or empty + * if the default working directory associated with the profile should be used. + * @param view The view manager owned by this main window + */ + void newSessionRequest(Profile::Ptr profile, + const QString& directory, + ViewManager* view); + + /** + * Emitted by the main window to request the creation of a new SSH session. + * + * @param profile The profile to use to create the new session. + * @param url URL for the new session + * @param view The view manager owned by this main window + */ + void newSSHSessionRequest(Profile::Ptr profile, + const KUrl& url, + ViewManager* view); + + /** + * Emitted by the main window to request the creation of a + * new session in a new window. + * + * @param profile The profile to use to create the + * first session in the new window. + * @param directory Initial working directory for the new window or empty + * if the default working directory associated with the profile should + * be used. + */ + void newWindowRequest(Profile::Ptr profile, + const QString& directory); + + /** + * Emitted by the main window to request the current session to close. + */ + void closeActiveSessionRequest(); + + protected: + // Reimplemented for internal reasons. + virtual void showEvent(QShowEvent *event); + + // reimplemented from KMainWindow + virtual bool queryClose(); + virtual void saveProperties(KConfigGroup& group); + virtual void readProperties(const KConfigGroup& group); + virtual void saveGlobalProperties(KConfig* config); + virtual void readGlobalProperties(KConfig* config); + + private slots: + void newTab(); + void newWindow(); + void showManageProfilesDialog(); + void showRemoteConnectionDialog(); + void showShortcutsDialog(); + void newFromProfile(Profile::Ptr profile); + void activeViewChanged(SessionController* controller); + void disconnectController(SessionController* controller); + void activeViewTitleChanged(ViewProperties*); + + void sessionListChanged(const QList<QAction*>& actions); + void viewFullScreen(bool fullScreen); + void configureNotifications(); + + // single shot call to set the visibility of the menu bar. Has no + // effect if the menu bar is a MacOS-style top-level menu + void setMenuBarVisibleOnce(bool visible); + + void setSaveGeometryOnExit(bool visible); + + void openUrls(const QList<KUrl>& urls); + + private: + void correctShortcuts(); + void removeMenuAccelerators(); + void setupActions(); + void setupWidgets(); + QString activeSessionDir() const; + + // sets the active shortcuts of actions in 'dest' to the shortcuts of actions + // with the same name in 'source' (see KAction::ActiveShortcut) + static void syncActiveShortcuts(KActionCollection* dest, const KActionCollection* source); + + private: + ViewManager* _viewManager; + BookmarkHandler* _bookmarkHandler; + KToggleAction* _toggleMenuBarAction; + KActionMenu *_newTabMenuAction; + + QPointer<SessionController> _pluggedController; + + Profile::Ptr _defaultProfile; + bool _menuBarVisibilitySet; +}; + +} + +#endif // KONSOLEMAINWINDOW_H + +/* + Local Variables: + mode: c++ + c-file-style: "stroustrup" + indent-tabs-mode: nil + tab-width: 4 + End: +*/
new file mode 100644 --- /dev/null +++ b/gui/konsole/Makefile @@ -0,0 +1,676 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 2.8 + +# Default target executed when no arguments are given to make. +default_target: all +.PHONY : default_target + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canoncical targets will work. +.SUFFIXES: + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + +# A target that is always out of date. +cmake_force: +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# The program to use to edit the cache. +CMAKE_EDIT_COMMAND = /usr/bin/ccmake + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /home/jacob/octave/gui/konsole + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /home/jacob/octave/gui/konsole + +#============================================================================= +# Targets provided globally by CMake. + +# Special rule for the target edit_cache +edit_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake cache editor..." + /usr/bin/ccmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : edit_cache + +# Special rule for the target edit_cache +edit_cache/fast: edit_cache +.PHONY : edit_cache/fast + +# Special rule for the target rebuild_cache +rebuild_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." + /usr/bin/cmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : rebuild_cache + +# Special rule for the target rebuild_cache +rebuild_cache/fast: rebuild_cache +.PHONY : rebuild_cache/fast + +# The main all target +all: cmake_check_build_system + $(CMAKE_COMMAND) -E cmake_progress_start /home/jacob/octave/gui/konsole/CMakeFiles /home/jacob/octave/gui/konsole/CMakeFiles/progress.marks + $(MAKE) -f CMakeFiles/Makefile2 all + $(CMAKE_COMMAND) -E cmake_progress_start /home/jacob/octave/gui/konsole/CMakeFiles 0 +.PHONY : all + +# The main clean target +clean: + $(MAKE) -f CMakeFiles/Makefile2 clean +.PHONY : clean + +# The main clean target +clean/fast: clean +.PHONY : clean/fast + +# Prepare targets for installation. +preinstall: all + $(MAKE) -f CMakeFiles/Makefile2 preinstall +.PHONY : preinstall + +# Prepare targets for installation. +preinstall/fast: + $(MAKE) -f CMakeFiles/Makefile2 preinstall +.PHONY : preinstall/fast + +# clear depends +depend: + $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 +.PHONY : depend + +#============================================================================= +# Target rules for targets named qtermwidget + +# Build rule for target. +qtermwidget: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 qtermwidget +.PHONY : qtermwidget + +# fast build rule for target. +qtermwidget/fast: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/build +.PHONY : qtermwidget/fast + +BlockArray.o: BlockArray.cpp.o +.PHONY : BlockArray.o + +# target to build an object file +BlockArray.cpp.o: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/BlockArray.cpp.o +.PHONY : BlockArray.cpp.o + +BlockArray.i: BlockArray.cpp.i +.PHONY : BlockArray.i + +# target to preprocess a source file +BlockArray.cpp.i: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/BlockArray.cpp.i +.PHONY : BlockArray.cpp.i + +BlockArray.s: BlockArray.cpp.s +.PHONY : BlockArray.s + +# target to generate assembly for a file +BlockArray.cpp.s: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/BlockArray.cpp.s +.PHONY : BlockArray.cpp.s + +Emulation.o: Emulation.cpp.o +.PHONY : Emulation.o + +# target to build an object file +Emulation.cpp.o: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/Emulation.cpp.o +.PHONY : Emulation.cpp.o + +Emulation.i: Emulation.cpp.i +.PHONY : Emulation.i + +# target to preprocess a source file +Emulation.cpp.i: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/Emulation.cpp.i +.PHONY : Emulation.cpp.i + +Emulation.s: Emulation.cpp.s +.PHONY : Emulation.s + +# target to generate assembly for a file +Emulation.cpp.s: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/Emulation.cpp.s +.PHONY : Emulation.cpp.s + +Filter.o: Filter.cpp.o +.PHONY : Filter.o + +# target to build an object file +Filter.cpp.o: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/Filter.cpp.o +.PHONY : Filter.cpp.o + +Filter.i: Filter.cpp.i +.PHONY : Filter.i + +# target to preprocess a source file +Filter.cpp.i: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/Filter.cpp.i +.PHONY : Filter.cpp.i + +Filter.s: Filter.cpp.s +.PHONY : Filter.s + +# target to generate assembly for a file +Filter.cpp.s: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/Filter.cpp.s +.PHONY : Filter.cpp.s + +History.o: History.cpp.o +.PHONY : History.o + +# target to build an object file +History.cpp.o: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/History.cpp.o +.PHONY : History.cpp.o + +History.i: History.cpp.i +.PHONY : History.i + +# target to preprocess a source file +History.cpp.i: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/History.cpp.i +.PHONY : History.cpp.i + +History.s: History.cpp.s +.PHONY : History.s + +# target to generate assembly for a file +History.cpp.s: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/History.cpp.s +.PHONY : History.cpp.s + +KeyboardTranslator.o: KeyboardTranslator.cpp.o +.PHONY : KeyboardTranslator.o + +# target to build an object file +KeyboardTranslator.cpp.o: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/KeyboardTranslator.cpp.o +.PHONY : KeyboardTranslator.cpp.o + +KeyboardTranslator.i: KeyboardTranslator.cpp.i +.PHONY : KeyboardTranslator.i + +# target to preprocess a source file +KeyboardTranslator.cpp.i: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/KeyboardTranslator.cpp.i +.PHONY : KeyboardTranslator.cpp.i + +KeyboardTranslator.s: KeyboardTranslator.cpp.s +.PHONY : KeyboardTranslator.s + +# target to generate assembly for a file +KeyboardTranslator.cpp.s: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/KeyboardTranslator.cpp.s +.PHONY : KeyboardTranslator.cpp.s + +ProcessInfo.o: ProcessInfo.cpp.o +.PHONY : ProcessInfo.o + +# target to build an object file +ProcessInfo.cpp.o: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/ProcessInfo.cpp.o +.PHONY : ProcessInfo.cpp.o + +ProcessInfo.i: ProcessInfo.cpp.i +.PHONY : ProcessInfo.i + +# target to preprocess a source file +ProcessInfo.cpp.i: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/ProcessInfo.cpp.i +.PHONY : ProcessInfo.cpp.i + +ProcessInfo.s: ProcessInfo.cpp.s +.PHONY : ProcessInfo.s + +# target to generate assembly for a file +ProcessInfo.cpp.s: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/ProcessInfo.cpp.s +.PHONY : ProcessInfo.cpp.s + +Pty.o: Pty.cpp.o +.PHONY : Pty.o + +# target to build an object file +Pty.cpp.o: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/Pty.cpp.o +.PHONY : Pty.cpp.o + +Pty.i: Pty.cpp.i +.PHONY : Pty.i + +# target to preprocess a source file +Pty.cpp.i: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/Pty.cpp.i +.PHONY : Pty.cpp.i + +Pty.s: Pty.cpp.s +.PHONY : Pty.s + +# target to generate assembly for a file +Pty.cpp.s: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/Pty.cpp.s +.PHONY : Pty.cpp.s + +Screen.o: Screen.cpp.o +.PHONY : Screen.o + +# target to build an object file +Screen.cpp.o: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/Screen.cpp.o +.PHONY : Screen.cpp.o + +Screen.i: Screen.cpp.i +.PHONY : Screen.i + +# target to preprocess a source file +Screen.cpp.i: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/Screen.cpp.i +.PHONY : Screen.cpp.i + +Screen.s: Screen.cpp.s +.PHONY : Screen.s + +# target to generate assembly for a file +Screen.cpp.s: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/Screen.cpp.s +.PHONY : Screen.cpp.s + +ScreenWindow.o: ScreenWindow.cpp.o +.PHONY : ScreenWindow.o + +# target to build an object file +ScreenWindow.cpp.o: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/ScreenWindow.cpp.o +.PHONY : ScreenWindow.cpp.o + +ScreenWindow.i: ScreenWindow.cpp.i +.PHONY : ScreenWindow.i + +# target to preprocess a source file +ScreenWindow.cpp.i: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/ScreenWindow.cpp.i +.PHONY : ScreenWindow.cpp.i + +ScreenWindow.s: ScreenWindow.cpp.s +.PHONY : ScreenWindow.s + +# target to generate assembly for a file +ScreenWindow.cpp.s: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/ScreenWindow.cpp.s +.PHONY : ScreenWindow.cpp.s + +Session.o: Session.cpp.o +.PHONY : Session.o + +# target to build an object file +Session.cpp.o: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/Session.cpp.o +.PHONY : Session.cpp.o + +Session.i: Session.cpp.i +.PHONY : Session.i + +# target to preprocess a source file +Session.cpp.i: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/Session.cpp.i +.PHONY : Session.cpp.i + +Session.s: Session.cpp.s +.PHONY : Session.s + +# target to generate assembly for a file +Session.cpp.s: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/Session.cpp.s +.PHONY : Session.cpp.s + +ShellCommand.o: ShellCommand.cpp.o +.PHONY : ShellCommand.o + +# target to build an object file +ShellCommand.cpp.o: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/ShellCommand.cpp.o +.PHONY : ShellCommand.cpp.o + +ShellCommand.i: ShellCommand.cpp.i +.PHONY : ShellCommand.i + +# target to preprocess a source file +ShellCommand.cpp.i: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/ShellCommand.cpp.i +.PHONY : ShellCommand.cpp.i + +ShellCommand.s: ShellCommand.cpp.s +.PHONY : ShellCommand.s + +# target to generate assembly for a file +ShellCommand.cpp.s: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/ShellCommand.cpp.s +.PHONY : ShellCommand.cpp.s + +TerminalCharacterDecoder.o: TerminalCharacterDecoder.cpp.o +.PHONY : TerminalCharacterDecoder.o + +# target to build an object file +TerminalCharacterDecoder.cpp.o: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/TerminalCharacterDecoder.cpp.o +.PHONY : TerminalCharacterDecoder.cpp.o + +TerminalCharacterDecoder.i: TerminalCharacterDecoder.cpp.i +.PHONY : TerminalCharacterDecoder.i + +# target to preprocess a source file +TerminalCharacterDecoder.cpp.i: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/TerminalCharacterDecoder.cpp.i +.PHONY : TerminalCharacterDecoder.cpp.i + +TerminalCharacterDecoder.s: TerminalCharacterDecoder.cpp.s +.PHONY : TerminalCharacterDecoder.s + +# target to generate assembly for a file +TerminalCharacterDecoder.cpp.s: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/TerminalCharacterDecoder.cpp.s +.PHONY : TerminalCharacterDecoder.cpp.s + +TerminalDisplay.o: TerminalDisplay.cpp.o +.PHONY : TerminalDisplay.o + +# target to build an object file +TerminalDisplay.cpp.o: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/TerminalDisplay.cpp.o +.PHONY : TerminalDisplay.cpp.o + +TerminalDisplay.i: TerminalDisplay.cpp.i +.PHONY : TerminalDisplay.i + +# target to preprocess a source file +TerminalDisplay.cpp.i: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/TerminalDisplay.cpp.i +.PHONY : TerminalDisplay.cpp.i + +TerminalDisplay.s: TerminalDisplay.cpp.s +.PHONY : TerminalDisplay.s + +# target to generate assembly for a file +TerminalDisplay.cpp.s: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/TerminalDisplay.cpp.s +.PHONY : TerminalDisplay.cpp.s + +Vt102Emulation.o: Vt102Emulation.cpp.o +.PHONY : Vt102Emulation.o + +# target to build an object file +Vt102Emulation.cpp.o: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/Vt102Emulation.cpp.o +.PHONY : Vt102Emulation.cpp.o + +Vt102Emulation.i: Vt102Emulation.cpp.i +.PHONY : Vt102Emulation.i + +# target to preprocess a source file +Vt102Emulation.cpp.i: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/Vt102Emulation.cpp.i +.PHONY : Vt102Emulation.cpp.i + +Vt102Emulation.s: Vt102Emulation.cpp.s +.PHONY : Vt102Emulation.s + +# target to generate assembly for a file +Vt102Emulation.cpp.s: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/Vt102Emulation.cpp.s +.PHONY : Vt102Emulation.cpp.s + +konsole_wcwidth.o: konsole_wcwidth.cpp.o +.PHONY : konsole_wcwidth.o + +# target to build an object file +konsole_wcwidth.cpp.o: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/konsole_wcwidth.cpp.o +.PHONY : konsole_wcwidth.cpp.o + +konsole_wcwidth.i: konsole_wcwidth.cpp.i +.PHONY : konsole_wcwidth.i + +# target to preprocess a source file +konsole_wcwidth.cpp.i: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/konsole_wcwidth.cpp.i +.PHONY : konsole_wcwidth.cpp.i + +konsole_wcwidth.s: konsole_wcwidth.cpp.s +.PHONY : konsole_wcwidth.s + +# target to generate assembly for a file +konsole_wcwidth.cpp.s: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/konsole_wcwidth.cpp.s +.PHONY : konsole_wcwidth.cpp.s + +kprocess.o: kprocess.cpp.o +.PHONY : kprocess.o + +# target to build an object file +kprocess.cpp.o: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/kprocess.cpp.o +.PHONY : kprocess.cpp.o + +kprocess.i: kprocess.cpp.i +.PHONY : kprocess.i + +# target to preprocess a source file +kprocess.cpp.i: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/kprocess.cpp.i +.PHONY : kprocess.cpp.i + +kprocess.s: kprocess.cpp.s +.PHONY : kprocess.s + +# target to generate assembly for a file +kprocess.cpp.s: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/kprocess.cpp.s +.PHONY : kprocess.cpp.s + +kpty.o: kpty.cpp.o +.PHONY : kpty.o + +# target to build an object file +kpty.cpp.o: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/kpty.cpp.o +.PHONY : kpty.cpp.o + +kpty.i: kpty.cpp.i +.PHONY : kpty.i + +# target to preprocess a source file +kpty.cpp.i: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/kpty.cpp.i +.PHONY : kpty.cpp.i + +kpty.s: kpty.cpp.s +.PHONY : kpty.s + +# target to generate assembly for a file +kpty.cpp.s: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/kpty.cpp.s +.PHONY : kpty.cpp.s + +kptydevice.o: kptydevice.cpp.o +.PHONY : kptydevice.o + +# target to build an object file +kptydevice.cpp.o: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/kptydevice.cpp.o +.PHONY : kptydevice.cpp.o + +kptydevice.i: kptydevice.cpp.i +.PHONY : kptydevice.i + +# target to preprocess a source file +kptydevice.cpp.i: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/kptydevice.cpp.i +.PHONY : kptydevice.cpp.i + +kptydevice.s: kptydevice.cpp.s +.PHONY : kptydevice.s + +# target to generate assembly for a file +kptydevice.cpp.s: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/kptydevice.cpp.s +.PHONY : kptydevice.cpp.s + +kptyprocess.o: kptyprocess.cpp.o +.PHONY : kptyprocess.o + +# target to build an object file +kptyprocess.cpp.o: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/kptyprocess.cpp.o +.PHONY : kptyprocess.cpp.o + +kptyprocess.i: kptyprocess.cpp.i +.PHONY : kptyprocess.i + +# target to preprocess a source file +kptyprocess.cpp.i: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/kptyprocess.cpp.i +.PHONY : kptyprocess.cpp.i + +kptyprocess.s: kptyprocess.cpp.s +.PHONY : kptyprocess.s + +# target to generate assembly for a file +kptyprocess.cpp.s: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/kptyprocess.cpp.s +.PHONY : kptyprocess.cpp.s + +qtermwidget.o: qtermwidget.cpp.o +.PHONY : qtermwidget.o + +# target to build an object file +qtermwidget.cpp.o: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/qtermwidget.cpp.o +.PHONY : qtermwidget.cpp.o + +qtermwidget.i: qtermwidget.cpp.i +.PHONY : qtermwidget.i + +# target to preprocess a source file +qtermwidget.cpp.i: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/qtermwidget.cpp.i +.PHONY : qtermwidget.cpp.i + +qtermwidget.s: qtermwidget.cpp.s +.PHONY : qtermwidget.s + +# target to generate assembly for a file +qtermwidget.cpp.s: + $(MAKE) -f CMakeFiles/qtermwidget.dir/build.make CMakeFiles/qtermwidget.dir/qtermwidget.cpp.s +.PHONY : qtermwidget.cpp.s + +# Help Target +help: + @echo "The following are some of the valid targets for this Makefile:" + @echo "... all (the default if no target is provided)" + @echo "... clean" + @echo "... depend" + @echo "... edit_cache" + @echo "... qtermwidget" + @echo "... rebuild_cache" + @echo "... BlockArray.o" + @echo "... BlockArray.i" + @echo "... BlockArray.s" + @echo "... Emulation.o" + @echo "... Emulation.i" + @echo "... Emulation.s" + @echo "... Filter.o" + @echo "... Filter.i" + @echo "... Filter.s" + @echo "... History.o" + @echo "... History.i" + @echo "... History.s" + @echo "... KeyboardTranslator.o" + @echo "... KeyboardTranslator.i" + @echo "... KeyboardTranslator.s" + @echo "... ProcessInfo.o" + @echo "... ProcessInfo.i" + @echo "... ProcessInfo.s" + @echo "... Pty.o" + @echo "... Pty.i" + @echo "... Pty.s" + @echo "... Screen.o" + @echo "... Screen.i" + @echo "... Screen.s" + @echo "... ScreenWindow.o" + @echo "... ScreenWindow.i" + @echo "... ScreenWindow.s" + @echo "... Session.o" + @echo "... Session.i" + @echo "... Session.s" + @echo "... ShellCommand.o" + @echo "... ShellCommand.i" + @echo "... ShellCommand.s" + @echo "... TerminalCharacterDecoder.o" + @echo "... TerminalCharacterDecoder.i" + @echo "... TerminalCharacterDecoder.s" + @echo "... TerminalDisplay.o" + @echo "... TerminalDisplay.i" + @echo "... TerminalDisplay.s" + @echo "... Vt102Emulation.o" + @echo "... Vt102Emulation.i" + @echo "... Vt102Emulation.s" + @echo "... konsole_wcwidth.o" + @echo "... konsole_wcwidth.i" + @echo "... konsole_wcwidth.s" + @echo "... kprocess.o" + @echo "... kprocess.i" + @echo "... kprocess.s" + @echo "... kpty.o" + @echo "... kpty.i" + @echo "... kpty.s" + @echo "... kptydevice.o" + @echo "... kptydevice.i" + @echo "... kptydevice.s" + @echo "... kptyprocess.o" + @echo "... kptyprocess.i" + @echo "... kptyprocess.s" + @echo "... qtermwidget.o" + @echo "... qtermwidget.i" + @echo "... qtermwidget.s" +.PHONY : help + + + +#============================================================================= +# Special targets to cleanup operation of make. + +# Special rule to run CMake to check the build system integrity. +# No rule that depends on this can have commands that come from listfiles +# because they might be regenerated. +cmake_check_build_system: + $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 +.PHONY : cmake_check_build_system +
new file mode 100644 --- /dev/null +++ b/gui/konsole/ManageProfilesDialog.cpp @@ -0,0 +1,514 @@ +/* + Copyright 2007-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 "ManageProfilesDialog.h" + +// Qt +#include <QtGui/QCheckBox> +#include <QtGui/QHeaderView> +#include <QtGui/QItemDelegate> +#include <QtGui/QItemEditorCreator> +#include <QtCore/QMetaEnum> +#include <QtGui/QScrollBar> +#include <QtGui/QShowEvent> +#include <QtGui/QStandardItem> + +// KDE +#include <KKeySequenceWidget> +#include <KDebug> + +// Konsole +#include "EditProfileDialog.h" +#include "SessionManager.h" +#include "ui_ManageProfilesDialog.h" + +using namespace Konsole; + +ManageProfilesDialog::ManageProfilesDialog(QWidget* parent) + : KDialog(parent) + , _sessionModel(new QStandardItemModel(this)) +{ + setCaption(i18nc("@title:window", "Manage Profiles")); + setButtons( KDialog::Ok | KDialog::Apply | KDialog::Cancel ); + + connect( this, SIGNAL(applyClicked()) , this , SLOT(setMenuOrder()) ); + + _ui = new Ui::ManageProfilesDialog(); + _ui->setupUi(mainWidget()); + + // hide vertical header + _ui->sessionTable->verticalHeader()->hide(); + _ui->sessionTable->setItemDelegateForColumn(FavoriteStatusColumn,new FavoriteItemDelegate(this)); + _ui->sessionTable->setItemDelegateForColumn(ShortcutColumn,new ShortcutItemDelegate(this)); + _ui->sessionTable->setEditTriggers(_ui->sessionTable->editTriggers() | QAbstractItemView::SelectedClicked); + _ui->sessionTable->setShowGrid(false); + + // update table and listen for changes to the session types + connect( SessionManager::instance() , SIGNAL(profileAdded(Profile::Ptr)) , this, + SLOT(addItems(Profile::Ptr)) ); + connect( SessionManager::instance() , SIGNAL(profileRemoved(Profile::Ptr)) , this, + SLOT(removeItems(Profile::Ptr)) ); + connect( SessionManager::instance() , SIGNAL(profileChanged(Profile::Ptr)) , this, + SLOT(updateItems(Profile::Ptr)) ); + connect( SessionManager::instance() , + SIGNAL(favoriteStatusChanged(Profile::Ptr,bool)) , this , + SLOT(updateFavoriteStatus(Profile::Ptr,bool)) ); + populateTable(); + + // resize the session table to the full width of the table + _ui->sessionTable->horizontalHeader()->setHighlightSections(false); + _ui->sessionTable->resizeColumnsToContents(); + + // allow a larger width for the shortcut column to account for the + // increased with needed by the shortcut editor compared with just + // displaying the text of the shortcut + _ui->sessionTable->setColumnWidth(ShortcutColumn, + _ui->sessionTable->columnWidth(ShortcutColumn)+100); + + // setup buttons + connect( _ui->newSessionButton , SIGNAL(clicked()) , this , SLOT(newType()) ); + connect( _ui->editSessionButton , SIGNAL(clicked()) , this , SLOT(editSelected()) ); + connect( _ui->deleteSessionButton , SIGNAL(clicked()) , this , SLOT(deleteSelected()) ); + connect( _ui->setAsDefaultButton , SIGNAL(clicked()) , this , SLOT(setSelectedAsDefault()) ); +} + +void ManageProfilesDialog::showEvent(QShowEvent*) +{ + Q_ASSERT( _ui->sessionTable->model() ); + + // try to ensure that all the text in all the columns is visible initially. + // FIXME: this is not a good solution, look for a more correct way to do this + + int totalWidth = 0; + int columnCount = _ui->sessionTable->model()->columnCount(); + + for ( int i = 0 ; i < columnCount ; i++ ) + totalWidth += _ui->sessionTable->columnWidth(i); + + // the margin is added to account for the space taken by the resize grips + // between the columns, this ensures that a horizontal scroll bar is not added + // automatically + int margin = style()->pixelMetric( QStyle::PM_HeaderGripMargin ) * columnCount; + _ui->sessionTable->setMinimumWidth( totalWidth + margin ); + _ui->sessionTable->horizontalHeader()->setStretchLastSection(true); +} + +ManageProfilesDialog::~ManageProfilesDialog() +{ + delete _ui; +} +void ManageProfilesDialog::itemDataChanged(QStandardItem* item) +{ + if ( item->column() == ShortcutColumn ) + { + QKeySequence sequence = QKeySequence::fromString(item->text()); + SessionManager::instance()->setShortcut(item->data(ShortcutRole).value<Profile::Ptr>(), + sequence); + } +} + +void ManageProfilesDialog::setMenuOrder(void) +{ + return; +// TODO fix +/* + for (int i=0;i<_sessionModel->rowCount();i++) + { + } + + SessionManager::instance()->setMenuOrder(); +*/ +} + +int ManageProfilesDialog::rowForProfile(const Profile::Ptr info) const +{ + for (int i=0;i<_sessionModel->rowCount();i++) + { + if (_sessionModel->item(i,ProfileNameColumn)->data(ProfileKeyRole) + .value<Profile::Ptr>() == info) + { + return i; + } + } + return -1; +} +void ManageProfilesDialog::removeItems(const Profile::Ptr info) +{ + int row = rowForProfile(info); + if (row < 0) + return; + _sessionModel->removeRow(row); +} +void ManageProfilesDialog::updateItems(const Profile::Ptr info) +{ + int row = rowForProfile(info); + if (row < 0) + return; + + QList<QStandardItem*> items; + items << _sessionModel->item(row,ProfileNameColumn); + items << _sessionModel->item(row,FavoriteStatusColumn); + items << _sessionModel->item(row,ShortcutColumn); + updateItemsForProfile(info,items); +} +void ManageProfilesDialog::updateItemsForProfile(const Profile::Ptr info, QList<QStandardItem*>& items) const +{ + // Profile Name + items[ProfileNameColumn]->setText(info->name()); + if ( !info->icon().isEmpty() ) + items[ProfileNameColumn]->setIcon( KIcon(info->icon()) ); + + items[ProfileNameColumn]->setData(QVariant::fromValue(info),ProfileKeyRole); + + // Favorite Status + const bool isFavorite = SessionManager::instance()->findFavorites().contains(info); + if ( isFavorite ) + items[FavoriteStatusColumn]->setData(KIcon("dialog-ok-apply"),Qt::DecorationRole); + else + items[FavoriteStatusColumn]->setData(KIcon(),Qt::DecorationRole); + items[FavoriteStatusColumn]->setData(QVariant::fromValue(info),ProfileKeyRole); + + // Shortcut + QString shortcut = SessionManager::instance()->shortcut(info). + toString(); + items[ShortcutColumn]->setText(shortcut); + items[ShortcutColumn]->setData(QVariant::fromValue(info),ShortcutRole); +} +void ManageProfilesDialog::addItems(const Profile::Ptr profile) +{ + if (profile->isHidden()) + return; + + QList<QStandardItem*> items; + for (int i=0;i<3;i++) + items << new QStandardItem; + updateItemsForProfile(profile,items); + _sessionModel->appendRow(items); +} +void ManageProfilesDialog::populateTable() +{ + Q_ASSERT(!_ui->sessionTable->model()); + + _ui->sessionTable->setModel(_sessionModel); + // ensure profiles list is complete + // this may be expensive, but will only be done the first time + // that the dialog is shown. + SessionManager::instance()->loadAllProfiles(); + + _sessionModel->clear(); + // setup session table + _sessionModel->setHorizontalHeaderLabels( QStringList() << i18nc("@title:column Profile label", "Name") + << i18nc("@title:column Display profile in file menu", "Show in Menu") + << i18nc("@title:column Profile shortcut text", "Shortcut") ); + + QList<Profile::Ptr> profiles = SessionManager::instance()->loadedProfiles(); + SessionManager::instance()->sortProfiles(profiles); + + foreach(const Profile::Ptr &info, profiles) + { + addItems(info); + } + updateDefaultItem(); + + connect( _sessionModel , SIGNAL(itemChanged(QStandardItem*)) , this , + SLOT(itemDataChanged(QStandardItem*)) ); + + // listen for changes in the table selection and update the state of the form's buttons + // accordingly. + // + // it appears that the selection model is changed when the model itself is replaced, + // so the signals need to be reconnected each time the model is updated. + connect( _ui->sessionTable->selectionModel() , + SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)) , this , + SLOT(tableSelectionChanged(const QItemSelection&)) ); + + _ui->sessionTable->selectRow(0); + tableSelectionChanged( _ui->sessionTable->selectionModel()->selection() ); +} +void ManageProfilesDialog::updateDefaultItem() +{ + Profile::Ptr defaultProfile = SessionManager::instance()->defaultProfile(); + + for ( int i = 0 ; i < _sessionModel->rowCount() ; i++ ) + { + QStandardItem* item = _sessionModel->item(i); + QFont font = item->font(); + + bool isDefault = ( defaultProfile == item->data().value<Profile::Ptr>() ); + + if ( isDefault && !font.bold() ) + { + item->setIcon(KIcon(defaultProfile->icon(), NULL, QStringList("emblem-favorite"))); + font.setBold(true); + item->setFont(font); + } + else if ( !isDefault && font.bold() ) + { + item->setIcon(KIcon(defaultProfile->icon())); + font.setBold(false); + item->setFont(font); + } + } +} +void ManageProfilesDialog::tableSelectionChanged(const QItemSelection&) +{ + const int selectedRows = _ui->sessionTable->selectionModel()->selectedRows().count(); + const SessionManager* manager = SessionManager::instance(); + const bool isNotDefault = (selectedRows > 0) && currentProfile() != manager->defaultProfile(); + const int rowIndex = _ui->sessionTable->currentIndex().row(); + + _ui->newSessionButton->setEnabled(selectedRows < 2); + _ui->editSessionButton->setEnabled(selectedRows > 0); + // do not allow the default session type to be removed + _ui->deleteSessionButton->setEnabled(isNotDefault); + _ui->setAsDefaultButton->setEnabled(isNotDefault && (selectedRows < 2)); + + // TODO handle multiple moves + // TODO re-enable when saving profile order works - khindenburg +// _ui->moveUpButton->setEnabled((selectedRows == 1) && (rowIndex > 0)); +// _ui->moveDownButton->setEnabled((selectedRows == 1) && (rowIndex < (_sessionModel->rowCount()-1))); + + _ui->sessionTable->selectRow(rowIndex); +} +void ManageProfilesDialog::deleteSelected() +{ + foreach(const Profile::Ptr &profile, selectedProfiles()) + { + if (profile != SessionManager::instance()->defaultProfile()) + SessionManager::instance()->deleteProfile(profile); + } +} +void ManageProfilesDialog::setSelectedAsDefault() +{ + SessionManager::instance()->setDefaultProfile(currentProfile()); + // do not allow the new default session type to be removed + _ui->deleteSessionButton->setEnabled(false); + _ui->setAsDefaultButton->setEnabled(false); + + // update font of new default item + updateDefaultItem(); +} + +void ManageProfilesDialog::moveUpSelected() +{ + Q_ASSERT(_sessionModel); + + const int rowIndex = _ui->sessionTable->currentIndex().row(); + const QList<QStandardItem*>items = _sessionModel->takeRow(rowIndex); + _sessionModel->insertRow(rowIndex-1, items); + _ui->sessionTable->selectRow(rowIndex-1); +} + +void ManageProfilesDialog::moveDownSelected() +{ + Q_ASSERT(_sessionModel); + + const int rowIndex = _ui->sessionTable->currentIndex().row(); + const QList<QStandardItem*>items = _sessionModel->takeRow(rowIndex); + _sessionModel->insertRow(rowIndex+1, items); + _ui->sessionTable->selectRow(rowIndex+1); +} + +void ManageProfilesDialog::newType() +{ + EditProfileDialog dialog(this); + + // setup a temporary profile which is a clone of the selected profile + // or the default if no profile is selected + Profile::Ptr sourceProfile; + + Profile::Ptr selectedProfile = currentProfile(); + if ( !selectedProfile ) + sourceProfile = SessionManager::instance()->defaultProfile(); + else + sourceProfile = selectedProfile; + + Q_ASSERT( sourceProfile ); + + Profile::Ptr newProfile = Profile::Ptr(new Profile(SessionManager::instance()->fallbackProfile())); + newProfile->clone(sourceProfile,true); + newProfile->setProperty(Profile::Name, i18nc("@item This will be used as part of the file name", "New Profile")); + newProfile->setProperty(Profile::MenuIndex, QString("0")); + + dialog.setProfile(newProfile); + dialog.selectProfileName(); + + if ( dialog.exec() == QDialog::Accepted ) + { + SessionManager::instance()->addProfile(newProfile); + SessionManager::instance()->setFavorite(newProfile,true); + SessionManager::instance()->changeProfile(newProfile, newProfile->setProperties()); + } +} +void ManageProfilesDialog::editSelected() +{ + EditProfileDialog dialog(this); + // the dialog will delete the profile group when it is destroyed + ProfileGroup* group = new ProfileGroup; + foreach(const Profile::Ptr &profile,selectedProfiles()) + group->addProfile(profile); + group->updateValues(); + + dialog.setProfile(Profile::Ptr(group)); + dialog.exec(); +} +QList<Profile::Ptr> ManageProfilesDialog::selectedProfiles() const +{ + QList<Profile::Ptr> list; + QItemSelectionModel* selection = _ui->sessionTable->selectionModel(); + if (!selection) + return list; + + foreach(const QModelIndex& index, selection->selectedIndexes()) + { + if (index.column() == ProfileNameColumn) + list << index.data(ProfileKeyRole).value<Profile::Ptr>(); + } + + return list; +} +Profile::Ptr ManageProfilesDialog::currentProfile() const +{ + QItemSelectionModel* selection = _ui->sessionTable->selectionModel(); + + if ( !selection || selection->selectedRows().count() != 1 ) + return Profile::Ptr(); + + return selection-> + selectedIndexes().first().data(ProfileKeyRole).value<Profile::Ptr>(); +} +void ManageProfilesDialog::updateFavoriteStatus(Profile::Ptr profile, bool favorite) +{ + Q_ASSERT( _sessionModel ); + + int rowCount = _sessionModel->rowCount(); + for (int i=0;i < rowCount;i++) + { + QModelIndex index = _sessionModel->index(i,FavoriteStatusColumn); + if (index.data(ProfileKeyRole).value<Profile::Ptr>() == + profile ) + { + const KIcon icon = favorite ? KIcon("dialog-ok-apply") : KIcon(); + _sessionModel->setData(index,icon,Qt::DecorationRole); + } + } +} +void ManageProfilesDialog::setShortcutEditorVisible(bool visible) +{ + _ui->sessionTable->setColumnHidden(ShortcutColumn,!visible); +} +void StyledBackgroundPainter::drawBackground(QPainter* painter, const QStyleOptionViewItem& option, + const QModelIndex&) +{ + const QStyleOptionViewItemV3* v3option = qstyleoption_cast<const QStyleOptionViewItemV3*>(&option); + const QWidget* widget = v3option ? v3option->widget : 0; + + QStyle* style = widget ? widget->style() : QApplication::style(); + + style->drawPrimitive(QStyle::PE_PanelItemViewItem,&option,painter,widget); +} + +FavoriteItemDelegate::FavoriteItemDelegate(QObject* parent) + : QStyledItemDelegate(parent) +{ +} +void FavoriteItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + // See implementation of QStyledItemDelegate::paint() + QStyleOptionViewItemV4 opt = option; + initStyleOption(&opt,index); + + StyledBackgroundPainter::drawBackground(painter,opt,index); + + int margin = (opt.rect.height()-opt.decorationSize.height())/2; + margin++; + + opt.rect.setTop(opt.rect.top()+margin); + opt.rect.setBottom(opt.rect.bottom()-margin); + + QIcon icon = index.data(Qt::DecorationRole).value<QIcon>(); + icon.paint(painter,opt.rect,Qt::AlignCenter); +} + +bool FavoriteItemDelegate::editorEvent(QEvent* event,QAbstractItemModel*, + const QStyleOptionViewItem&,const QModelIndex& index) +{ + if ( event->type() == QEvent::MouseButtonPress || event->type() == QEvent::KeyPress + || event->type() == QEvent::MouseButtonDblClick ) + { + Profile::Ptr profile = index.data(ManageProfilesDialog::ProfileKeyRole).value<Profile::Ptr>(); + const bool isFavorite = !SessionManager::instance()->findFavorites().contains(profile); + + SessionManager::instance()->setFavorite(profile, + isFavorite); + } + + return true; +} +ShortcutItemDelegate::ShortcutItemDelegate(QObject* parent) + : QStyledItemDelegate(parent) +{ +} +void ShortcutItemDelegate::editorModified(const QKeySequence& keys) +{ + Q_UNUSED(keys); + //kDebug() << keys.toString(); + + KKeySequenceWidget* editor = qobject_cast<KKeySequenceWidget*>(sender()); + Q_ASSERT(editor); + _modifiedEditors.insert(editor); +} +void ShortcutItemDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, + const QModelIndex& index) const +{ + _itemsBeingEdited.remove(index); + + if (!_modifiedEditors.contains(editor)) + return; + + QString shortcut = qobject_cast<KKeySequenceWidget*>(editor)->keySequence().toString(); + model->setData(index,shortcut,Qt::DisplayRole); + + _modifiedEditors.remove(editor); +} + +QWidget* ShortcutItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem&, const QModelIndex& index) const +{ + _itemsBeingEdited.insert(index); + + KKeySequenceWidget* editor = new KKeySequenceWidget(parent); + editor->setFocusPolicy(Qt::StrongFocus); + editor->setModifierlessAllowed(false); + QString shortcutString = index.data(Qt::DisplayRole).toString(); + editor->setKeySequence(QKeySequence::fromString(shortcutString)); + connect(editor,SIGNAL(keySequenceChanged(QKeySequence)),this,SLOT(editorModified(QKeySequence))); + editor->captureKeySequence(); + return editor; +} +void ShortcutItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + if (_itemsBeingEdited.contains(index)) + StyledBackgroundPainter::drawBackground(painter,option,index); + else + QStyledItemDelegate::paint(painter,option,index); +} + +#include "ManageProfilesDialog.moc"
new file mode 100644 --- /dev/null +++ b/gui/konsole/ManageProfilesDialog.h @@ -0,0 +1,166 @@ +/* + Copyright 2007-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. +*/ + +#ifndef MANAGEPROFILESDIALOG_H +#define MANAGEPROFILESDIALOG_H + +// Qt +#include <QtGui/QStyledItemDelegate> +#include <QtCore/QSet> + +// KDE +#include <KDialog> + +// Konsole +#include "Profile.h" + +class QItemSelection; +class QShowEvent; +class QStandardItem; +class QStandardItemModel; +class QTableView; + +namespace Ui +{ + class ManageProfilesDialog; +} + +namespace Konsole +{ + +/** + * A dialog which lists the available types of profiles and allows + * the user to add new profiles, and remove or edit existing + * profile types. + */ +class KONSOLEPRIVATE_EXPORT ManageProfilesDialog : public KDialog +{ +Q_OBJECT + + +friend class FavoriteItemDelegate; +friend class ShortcutItemDelegate; +friend class ::QTableView; + +public: + /** Constructs a new profile type with the specified parent. */ + ManageProfilesDialog(QWidget* parent = 0); + virtual ~ManageProfilesDialog(); + + /** + * Specifies whether the shorcut editor should be show. + * The shortcut editor allows shortcuts to be associated with + * profiles. When a shortcut is changed, the dialog will call + * SessionManager::instance()->setShortcut() to update the shortcut + * associated with the profile. + * + * By default the editor is visible. + */ + void setShortcutEditorVisible(bool visible); + +protected: + virtual void showEvent(QShowEvent* event); + +private slots: + void deleteSelected(); + void setSelectedAsDefault(); + void newType(); + void editSelected(); + void moveUpSelected(); + void moveDownSelected(); + + void itemDataChanged(QStandardItem* item); + + // enables or disables Edit/Delete/Set as Default buttons when the + // selection changes + void tableSelectionChanged(const QItemSelection&); + + void updateFavoriteStatus(Profile::Ptr profile, bool favorite); + + void addItems(const Profile::Ptr); + void updateItems(const Profile::Ptr); + void removeItems(const Profile::Ptr); + + void setMenuOrder(void); + +private: + Profile::Ptr currentProfile() const; + QList<Profile::Ptr> selectedProfiles() const; + + // updates the font of the items to match + // their default / non-default profile status + void updateDefaultItem(); + void updateItemsForProfile(const Profile::Ptr profile, QList<QStandardItem*>& items) const; + // updates the profile table to be in sync with the + // session manager + void populateTable(); + int rowForProfile(const Profile::Ptr info) const; + + Ui::ManageProfilesDialog* _ui; + QStandardItemModel* _sessionModel; + + static const int ProfileNameColumn = 0; + static const int FavoriteStatusColumn = 1; + static const int ShortcutColumn = 2; + static const int ProfileKeyRole = Qt::UserRole + 1; + static const int ShortcutRole = Qt::UserRole + 1; +}; + +class StyledBackgroundPainter +{ +public: + static void drawBackground(QPainter* painter, const QStyleOptionViewItem& option, + const QModelIndex& index); +}; + +class FavoriteItemDelegate : public QStyledItemDelegate +{ +public: + FavoriteItemDelegate(QObject* parent = 0); + + virtual bool editorEvent(QEvent* event,QAbstractItemModel* model, + const QStyleOptionViewItem& option,const QModelIndex& index); + virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, + const QModelIndex& index) const; +}; + +class ShortcutItemDelegate : public QStyledItemDelegate +{ +Q_OBJECT + +public: + ShortcutItemDelegate(QObject* parent = 0); + + virtual void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const; + virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, + const QModelIndex& index) const; + virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, + const QModelIndex& index) const; + +private slots: + void editorModified(const QKeySequence& keys); + +private: + mutable QSet<QWidget*> _modifiedEditors; + mutable QSet<QModelIndex> _itemsBeingEdited; +}; + +} +#endif // MANAGEPROFILESDIALOG_H +
new file mode 100644 --- /dev/null +++ b/gui/konsole/Part.cpp @@ -0,0 +1,307 @@ +/* + Copyright 2007-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 "Part.h" + +// Qt +#include <QtCore/QStringList> +#include <QDir> + +// KDE +#include <KAction> +#include <KActionCollection> +#include <KDebug> +#include <KLocale> +#include <KWindowSystem> +#include <KPluginFactory> +#include <kdeversion.h> +#include <kde_file.h> + +// Konsole +#include "ColorScheme.h" +#include "EditProfileDialog.h" +#include "Emulation.h" +#include "KeyboardTranslator.h" +#include "ManageProfilesDialog.h" +#include "Session.h" +#include "SessionController.h" +#include "SessionManager.h" +#include "TerminalDisplay.h" +#include "ViewManager.h" +#include "MainWindow.h" + +#include <config-konsole.h> + +using namespace Konsole; + +K_PLUGIN_FACTORY(KonsolePartFactory, registerPlugin<Konsole::Part>();) +K_EXPORT_PLUGIN(KonsolePartFactory("konsole")) + + +Part::Part(QWidget* parentWidget , QObject* parent, const QVariantList&) + : KParts::ReadOnlyPart(parent) + ,_viewManager(0) + ,_pluggedController(0) + ,_manageProfilesAction(0) +{ + // make sure the konsole catalog is loaded + KGlobal::locale()->insertCatalog("konsole"); + + + + // setup global actions + createGlobalActions(); + + // create view widget + _viewManager = new ViewManager(this,actionCollection()); + _viewManager->setNavigationMethod( ViewManager::NoNavigation ); + + connect( _viewManager , SIGNAL(activeViewChanged(SessionController*)) , this , + SLOT(activeViewChanged(SessionController*)) ); + connect( _viewManager , SIGNAL(empty()) , this , SLOT(terminalExited()) ); + connect( _viewManager , SIGNAL(newViewRequest()) , this , SLOT(newTab()) ); + + _viewManager->widget()->setParent(parentWidget); + + setWidget(_viewManager->widget()); + actionCollection()->addAssociatedWidget(_viewManager->widget()); + foreach (QAction* action, actionCollection()->actions()) + action->setShortcutContext(Qt::WidgetWithChildrenShortcut); + + // Enable translucency support. + _viewManager->widget()->setAttribute(Qt::WA_TranslucentBackground, true); + TerminalDisplay::HAVE_TRANSPARENCY = KWindowSystem::compositingActive(); + + // create basic session + createSession(); +} +Part::~Part() +{ + SessionManager::instance()->saveState(); +} +void Part::createGlobalActions() +{ + _manageProfilesAction = new QAction(i18n("Configure Profiles..."),this); + connect(_manageProfilesAction,SIGNAL(triggered()),this,SLOT(showManageProfilesDialog())); +} +void Part::setupActionsForSession(SessionController* session) +{ + KActionCollection* collection = session->actionCollection(); + collection->addAction("configure-profiles",_manageProfilesAction); +} +bool Part::openFile() +{ + return false; +} +void Part::terminalExited() +{ + deleteLater(); +} +void Part::newTab() +{ + createSession(); + showShellInDir( QString() ); +} +Session* Part::activeSession() const +{ + if ( _viewManager->activeViewController() ) + { + Q_ASSERT( _viewManager->activeViewController()->session()); + + return _viewManager->activeViewController()->session(); + } + else + { + return 0; + } +} +void Part::startProgram( const QString& program, + const QStringList& arguments ) +{ + Q_ASSERT( activeSession() ); + + if ( !activeSession()->isRunning() ) + { + if ( !program.isEmpty() && !arguments.isEmpty() ) + { + activeSession()->setProgram(program); + activeSession()->setArguments(arguments); + } + + activeSession()->run(); + } +} +void Part::openTeletype(int fd) +{ + Q_ASSERT( activeSession() ); + + activeSession()->openTeletype(fd); +} +void Part::showShellInDir( const QString& dir ) +{ + Q_ASSERT( activeSession() ); + + if ( !activeSession()->isRunning() ) + { + if ( !dir.isEmpty() ) + activeSession()->setInitialWorkingDirectory(dir); + activeSession()->run(); + } +} +void Part::sendInput( const QString& text ) +{ + Q_ASSERT( activeSession() ); + activeSession()->emulation()->sendText(text); +} + +int Part::terminalProcessId() +{ + Q_ASSERT( activeSession() ); + + return activeSession()->processId(); + +} + +int Part::foregroundProcessId() +{ + Q_ASSERT( activeSession() ); + + if (activeSession()->isForegroundProcessActive()) { + return activeSession()->foregroundProcessId(); + } else { + return -1; + } +} + +QString Part::foregroundProcessName() +{ + Q_ASSERT( activeSession() ); + + if (activeSession()->isForegroundProcessActive()) { + return activeSession()->foregroundProcessName(); + } else { + return ""; + } +} + +Session* Part::createSession(const Profile::Ptr profile) +{ + Session* session = SessionManager::instance()->createSession(profile); + _viewManager->createView(session); + + return session; +} +void Part::activeViewChanged(SessionController* controller) +{ + Q_ASSERT( controller ); + Q_ASSERT( controller->view() ); + + // remove existing controller + if (_pluggedController) + { + removeChildClient (_pluggedController); + disconnect(_pluggedController,SIGNAL(titleChanged(ViewProperties*)),this, + SLOT(activeViewTitleChanged(ViewProperties*))); + } + + // insert new controller + setupActionsForSession(controller); + insertChildClient(controller); + connect(controller,SIGNAL(titleChanged(ViewProperties*)),this, + SLOT(activeViewTitleChanged(ViewProperties*))); + activeViewTitleChanged(controller); + + const char* displaySignal = SIGNAL(overrideShortcutCheck(QKeyEvent*,bool&)); + const char* partSlot = SLOT(overrideTerminalShortcut(QKeyEvent*,bool&)); + + disconnect(controller->view(),displaySignal,this,partSlot); + connect(controller->view(),displaySignal,this,partSlot); + + _pluggedController = controller; +} +void Part::overrideTerminalShortcut(QKeyEvent* event, bool& override) +{ + // override all shortcuts in the embedded terminal by default + override = true; + emit overrideShortcut(event,override); +} +void Part::activeViewTitleChanged(ViewProperties* properties) +{ + emit setWindowCaption(properties->title()); +} +void Part::showManageProfilesDialog() +{ + showManageProfilesDialog(_viewManager->widget()); +} +void Part::showManageProfilesDialog(QWidget* parent) +{ + ManageProfilesDialog* dialog = new ManageProfilesDialog(parent); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->setShortcutEditorVisible(false); + dialog->show(); +} +void Part::showEditCurrentProfileDialog(QWidget* parent) +{ + Q_ASSERT( activeSession() ); + + EditProfileDialog* dialog = new EditProfileDialog(parent); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->setProfile( SessionManager::instance()->sessionProfile(activeSession()) ); + dialog->show(); +} +void Part::changeSessionSettings(const QString& text) +{ + // send a profile change command, the escape code format + // is the same as the normal X-Term commands used to change the window title or icon, + // but with a magic value of '50' for the parameter which specifies what to change + Q_ASSERT( activeSession() ); + QByteArray buffer; + buffer.append("\033]50;").append(text.toUtf8()).append('\a'); + + activeSession()->emulation()->receiveData(buffer.constData(),buffer.length()); +} + +// Konqueror integration +bool Part::openUrl( const KUrl & _url ) +{ + if ( url() == _url ) { + emit completed(); + return true; + } + + setUrl( _url ); + emit setWindowCaption( _url.pathOrUrl() ); + //kdDebug(1211) << "Set Window Caption to " << url.pathOrUrl(); + emit started( 0 ); + + if ( _url.isLocalFile() /*&& b_openUrls*/ ) { + KDE_struct_stat buff; + KDE_stat( QFile::encodeName( _url.path() ), &buff ); + QString text = ( S_ISDIR( buff.st_mode ) ? _url.path() : _url.directory() ); + showShellInDir( text ); + } else { + showShellInDir( QDir::homePath() ); + } + + emit completed(); + return true; +} + +#include "Part.moc"
new file mode 100644 --- /dev/null +++ b/gui/konsole/Part.h @@ -0,0 +1,162 @@ +/* + Copyright 2007-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. +*/ + +#ifndef PART_H +#define PART_H + +// KDE +#include <KParts/Part> +#include <kde_terminal_interface_v2.h> +#include <QVariantList> + +// Konsole +#include "Profile.h" + +class QAction; +class QStringList; +class QKeyEvent; + +namespace Konsole +{ +class Session; +class SessionController; +class ViewManager; +class ViewProperties; + +/** + * A re-usable terminal emulator component using the KParts framework which can + * be used to embed terminal emulators into other applications. + */ +class Part : public KParts::ReadOnlyPart , public TerminalInterfaceV2 +{ +Q_OBJECT + Q_INTERFACES(TerminalInterface TerminalInterfaceV2) +public: + /** Constructs a new Konsole part with the specified parent. */ + explicit Part(QWidget* parentWidget , QObject* parent, const QVariantList&); + virtual ~Part(); + + /** Reimplemented from TerminalInterface. */ + virtual void startProgram( const QString& program, + const QStringList& arguments ); + /** Reimplemented from TerminalInterface. */ + virtual void showShellInDir( const QString& dir ); + /** Reimplemented from TerminalInterface. */ + virtual void sendInput( const QString& text ); + + /** Reimplemented from TerminalInterfaceV2. */ + virtual int terminalProcessId(); + + /** Reimplemented from TerminalInterfaceV2. */ + virtual int foregroundProcessId(); + + /** Reimplemented from TerminalInterfaceV2. */ + virtual QString foregroundProcessName(); + +public slots: + /** + * Shows the dialog used to manage profiles in Konsole. The dialog + * will be non-modal and will delete itself when it is closed. + * + * This is experimental API and not guaranteed to be present in later + * KDE 4 releases. + * + * @param parent The parent widget of the new dialog. + */ + void showManageProfilesDialog(QWidget* parent); + /** + * Shows the dialog used to edit the profile used by the active session. The + * dialog will be non-modal and will delete itself when it is closed. + * + * This is experimental API and not guaranteed to be present in later KDE 4 + * releases. + * + * @param parent The parent widget of the new dialog. + */ + void showEditCurrentProfileDialog(QWidget* parent); + /** + * Sends a profile change command to the active session. This is equivalent to using + * the konsoleprofile tool within the session to change its settings. The @p text string + * is a semi-colon separated list of property=value pairs, eg. "colors=Linux Colors" + * + * See the documentation for konsoleprofile for information on the format of @p text + * + * This is experimental API and not guaranteed to be present in later KDE 4 releases. + */ + void changeSessionSettings(const QString& text); + + /** + * Connects to an existing pseudo-teletype. See Session::openTeletype(). + * This must be called before the session is started by startProgram(), + * or showShellInDir() + * + * @param ptyMasterFd The file descriptor of the pseudo-teletype (pty) master + */ + void openTeletype(int ptyMasterFd); + +signals: + /** + * Emitted when the key sequence for a shortcut, which is also a valid terminal key sequence, + * is pressed while the terminal has focus. By responding to this signal, the + * controlling application can choose whether to execute the action associated with + * the shortcut or ignore the shortcut and send the key + * sequence to the terminal application. + * + * In the embedded terminal, shortcuts are overridden and sent to the terminal by default. + * Set @p override to false to prevent this happening and allow the shortcut to be triggered + * normally. + * + * overrideShortcut() is not called for shortcuts which are not valid terminal key sequences, + * eg. shortcuts with two or more modifiers. + * + * @param event Describes the keys that were pressed. + * @param override Set this to false to prevent the terminal display from overriding the shortcut + */ + void overrideShortcut(QKeyEvent* event, bool& override); + +protected: + /** Reimplemented from KParts::PartBase. */ + virtual bool openFile(); + virtual bool openUrl(const KUrl & url); + +private slots: + // creates a new session using the specified profile. + // call the run() method on the returned Session instance to begin the session + Session* createSession(const Profile::Ptr profile = Profile::Ptr()); + void activeViewChanged(SessionController* controller); + void activeViewTitleChanged(ViewProperties* properties); + void showManageProfilesDialog(); + void terminalExited(); + void newTab(); + void overrideTerminalShortcut(QKeyEvent*,bool& override); + +private: + Session* activeSession() const; + void setupActionsForSession(SessionController* session); + void createGlobalActions(); + +private: + ViewManager* _viewManager; + SessionController* _pluggedController; + QAction* _manageProfilesAction; +}; + +} + +#endif // PART_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/ProcessInfo.cpp @@ -0,0 +1,1122 @@ +/* + Copyright 2007-2008 by Robert Knight <robertknight@gmail.countm> + + 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 "ProcessInfo.h" +//#include <config-konsole.h> + +// Unix +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <pwd.h> + +// Qt +//#include <KDebug> +#include <QtCore/QDir> +#include <QtCore/QFileInfo> +#include <QtCore/QRegExp> +#include <QtCore/QTextStream> +#include <QtCore/QStringList> +#include <QtCore/QSet> + +// KDE +#include "konsole_export.h" +//#include <KConfigGroup> +//#include <KGlobal> +//#include <KSharedConfig> +//#include <KUser> + +#if defined(Q_OS_MAC) +#include <sys/sysctl.h> +#include <libproc.h> +#ifdef HAVE_SYS_PROC_INFO_H +#include <sys/proc_info.h> +#endif +#ifdef HAVE_SYS_PROC_H +#include <sys/proc.h> +#endif +//#include <kde_file.h> +#define KDE_struct_stat struct stat +#define KDE_stat ::stat +#endif + +#if defined(Q_OS_FREEBSD) +#include <sys/sysctl.h> //krazy:exclude=includes +#include <sys/types.h> +#include <sys/user.h> +#include <sys/syslimits.h> +#include <libutil.h> +#endif + +using namespace Konsole; + +ProcessInfo::ProcessInfo(int pid , bool enableEnvironmentRead) + : _fields( ARGUMENTS | ENVIRONMENT ) // arguments and environments + // are currently always valid, + // they just return an empty + // vector / map respectively + // if no arguments + // or environment bindings + // have been explicitly set + , _enableEnvironmentRead(enableEnvironmentRead) + , _pid(pid) + , _parentPid(0) + , _foregroundPid(0) + , _userId(0) + , _lastError(NoError) + , _userName(QString()) + , _userHomeDir(QString()) +{ +} + +ProcessInfo::Error ProcessInfo::error() const { return _lastError; } +void ProcessInfo::setError(Error error) { _lastError = error; } + +void ProcessInfo::update() +{ + readProcessInfo(_pid,_enableEnvironmentRead); +} + +QString ProcessInfo::validCurrentDir() const +{ + bool ok = false; + + // read current dir, if an error occurs try the parent as the next + // best option + int currentPid = parentPid(&ok); + QString dir = currentDir(&ok); + while ( !ok && currentPid != 0 ) + { + ProcessInfo* current = ProcessInfo::newInstance(currentPid); + current->update(); + currentPid = current->parentPid(&ok); + dir = current->currentDir(&ok); + delete current; + } + + return dir; +} + +QString ProcessInfo::format(const QString& input) const +{ + bool ok = false; + + QString output(input); + + // search for and replace known marker + output.replace("%u",userName()); + output.replace("%n",name(&ok)); + output.replace("%c",formatCommand(name(&ok),arguments(&ok),ShortCommandFormat)); + output.replace("%C",formatCommand(name(&ok),arguments(&ok),LongCommandFormat)); + + QString dir = validCurrentDir(); + if (output.contains("%D")) + { + QString homeDir = userHomeDir(); + QString tempDir = dir; + // Change User's Home Dir w/ ~ only at the beginning + if (tempDir.startsWith(homeDir)) + { + tempDir.remove(0, homeDir.length()); + tempDir.prepend('~'); + } + output.replace("%D", tempDir); + } + output.replace("%d",formatShortDir(dir)); + + // remove any remaining %[LETTER] sequences + // output.replace(QRegExp("%\\w"), QString()); + + return output; +} + +QString ProcessInfo::formatCommand(const QString& name, + const QVector<QString>& arguments, + CommandFormat format) const +{ + Q_UNUSED(name); + Q_UNUSED(format); + + // TODO Implement me + return QStringList(QList<QString>::fromVector(arguments)).join(" "); +} + +QSet<QString> ProcessInfo::_commonDirNames; + +QSet<QString> ProcessInfo::commonDirNames() +{ + // JPS: Don't know of a good replacement for this in QT + /* + if ( _commonDirNames.isEmpty() ) + { + KSharedConfigPtr config = KGlobal::config(); + KConfigGroup configGroup = config->group("ProcessInfo"); + + QStringList defaults = QStringList() + << "src" << "build" << "debug" << "release" + << "bin" << "lib" << "libs" << "tmp" + << "doc" << "docs" << "data" << "share" + << "examples" << "icons" << "pics" << "plugins" + << "tests" << "media" << "l10n" << "include" + << "includes" << "locale" << "ui"; + + _commonDirNames = QSet<QString>::fromList(configGroup.readEntry("CommonDirNames",defaults)); + + } + */ + return _commonDirNames; +} + +QString ProcessInfo::formatShortDir(const QString& input) const +{ + QString result; + + QStringList parts = input.split( QDir::separator() ); + + // temporarily hard-coded + QSet<QString> dirNamesToShorten = commonDirNames(); + + QListIterator<QString> iter(parts); + iter.toBack(); + + // go backwards through the list of the path's parts + // adding abbreviations of common directory names + // and stopping when we reach a dir name which is not + // in the commonDirNames set + while ( iter.hasPrevious() ) + { + QString part = iter.previous(); + + if ( dirNamesToShorten.contains(part) ) + { + result.prepend(QDir::separator() + part[0]); + } + else + { + result.prepend(part); + break; + } + } + + return result; +} + +QVector<QString> ProcessInfo::arguments(bool* ok) const +{ + *ok = _fields & ARGUMENTS; + + return _arguments; +} + +QMap<QString,QString> ProcessInfo::environment(bool* ok) const +{ + *ok = _fields & ENVIRONMENT; + + return _environment; +} + +bool ProcessInfo::isValid() const +{ + return _fields & PROCESS_ID; +} + +int ProcessInfo::pid(bool* ok) const +{ + *ok = _fields & PROCESS_ID; + + return _pid; +} + +int ProcessInfo::parentPid(bool* ok) const +{ + *ok = _fields & PARENT_PID; + + return _parentPid; +} + +int ProcessInfo::foregroundPid(bool* ok) const +{ + *ok = _fields & FOREGROUND_PID; + + return _foregroundPid; +} + +QString ProcessInfo::name(bool* ok) const +{ + *ok = _fields & NAME; + + return _name; +} + +int ProcessInfo::userId(bool* ok) const +{ + *ok = _fields & UID; + + return _userId; +} + +QString ProcessInfo::userName() const +{ + return _userName; +} + +QString ProcessInfo::userHomeDir() const +{ + return _userHomeDir; +} + +void ProcessInfo::setPid(int pid) +{ + _pid = pid; + _fields |= PROCESS_ID; +} + +void ProcessInfo::setUserId(int uid) +{ + _userId = uid; + _fields |= UID; +} + +void ProcessInfo::setUserName(const QString& name) +{ + _userName = name; + setUserHomeDir(); +} + +void ProcessInfo::setUserHomeDir() +{ + QString usersName = userName(); + // JPS: I don't know a good QT replacement + //if (!usersName.isEmpty()) + // _userHomeDir = KUser(usersName).homeDir(); + //else + _userHomeDir = QDir::homePath(); +} + +void ProcessInfo::setParentPid(int pid) +{ + _parentPid = pid; + _fields |= PARENT_PID; +} +void ProcessInfo::setForegroundPid(int pid) +{ + _foregroundPid = pid; + _fields |= FOREGROUND_PID; +} + +QString ProcessInfo::currentDir(bool* ok) const +{ + if (ok) + *ok = _fields & CURRENT_DIR; + + return _currentDir; +} +void ProcessInfo::setCurrentDir(const QString& dir) +{ + _fields |= CURRENT_DIR; + _currentDir = dir; +} + +void ProcessInfo::setName(const QString& name) +{ + _name = name; + _fields |= NAME; +} +void ProcessInfo::addArgument(const QString& argument) +{ + _arguments << argument; +} + +void ProcessInfo::addEnvironmentBinding(const QString& name , const QString& value) +{ + _environment.insert(name,value); +} + +void ProcessInfo::setFileError( QFile::FileError error ) +{ + switch ( error ) + { + case PermissionsError: + setError( PermissionsError ); + break; + case NoError: + setError( NoError ); + break; + default: + setError( UnknownError ); + } +} + +// +// ProcessInfo::newInstance() is way at the bottom so it can see all of the +// implementations of the UnixProcessInfo abstract class. +// + +NullProcessInfo::NullProcessInfo(int pid,bool enableEnvironmentRead) + : ProcessInfo(pid,enableEnvironmentRead) +{ +} + +bool NullProcessInfo::readProcessInfo(int /*pid*/ , bool /*enableEnvironmentRead*/) +{ + return false; +} + +void NullProcessInfo::readUserName() +{ +} + +UnixProcessInfo::UnixProcessInfo(int pid,bool enableEnvironmentRead) + : ProcessInfo(pid,enableEnvironmentRead) +{ +} + +bool UnixProcessInfo::readProcessInfo(int pid , bool enableEnvironmentRead) +{ + bool ok = readProcInfo(pid); + if (ok) + { + ok |= readArguments(pid); + ok |= readCurrentDir(pid); + if ( enableEnvironmentRead ) + { + ok |= readEnvironment(pid); + } + } + return ok; +} + +void UnixProcessInfo::readUserName() +{ + bool ok = false; + int uid = userId(&ok); + if (!ok) return; + + struct passwd passwdStruct; + struct passwd *getpwResult; + char *getpwBuffer; + long getpwBufferSize; + int getpwStatus; + + getpwBufferSize = sysconf(_SC_GETPW_R_SIZE_MAX); + if (getpwBufferSize == -1) + getpwBufferSize = 16384; + + getpwBuffer = new char[getpwBufferSize]; + if (getpwBuffer == NULL) + return; + getpwStatus = getpwuid_r(uid, &passwdStruct, getpwBuffer, getpwBufferSize, &getpwResult); + if (getpwResult != NULL) + setUserName(QString(passwdStruct.pw_name)); + else + setUserName(QString()); + delete [] getpwBuffer; +} + + +class LinuxProcessInfo : public UnixProcessInfo +{ +public: + LinuxProcessInfo(int pid, bool env) : + UnixProcessInfo(pid,env) + { + } + +private: + virtual bool readProcInfo(int pid) + { + // indicies of various fields within the process status file which + // contain various information about the process + const int PARENT_PID_FIELD = 3; + const int PROCESS_NAME_FIELD = 1; + const int GROUP_PROCESS_FIELD = 7; + + QString parentPidString; + QString processNameString; + QString foregroundPidString; + QString uidLine; + QString uidString; + QStringList uidStrings; + + // For user id read process status file ( /proc/<pid>/status ) + // Can not use getuid() due to it does not work for 'su' + QFile statusInfo( QString("/proc/%1/status").arg(pid) ); + if ( statusInfo.open(QIODevice::ReadOnly) ) + { + QTextStream stream(&statusInfo); + QString statusLine; + do { + statusLine = stream.readLine(0); + if (statusLine.startsWith(QLatin1String("Uid:"))) + uidLine = statusLine; + } while (!statusLine.isNull() && uidLine.isNull()); + + uidStrings << uidLine.split('\t', QString::SkipEmptyParts); + // Must be 5 entries: 'Uid: %d %d %d %d' and + // uid string must be less than 5 chars (uint) + if (uidStrings.size() == 5) + uidString = uidStrings[1]; + if (uidString.size() > 5) + uidString.clear(); + + bool ok = false; + int uid = uidString.toInt(&ok); + if (ok) + setUserId(uid); + readUserName(); + } + else + { + setFileError( statusInfo.error() ); + return false; + } + + + // read process status file ( /proc/<pid/stat ) + // + // the expected file format is a list of fields separated by spaces, using + // parenthesies to escape fields such as the process name which may itself contain + // spaces: + // + // FIELD FIELD (FIELD WITH SPACES) FIELD FIELD + // + QFile processInfo( QString("/proc/%1/stat").arg(pid) ); + if ( processInfo.open(QIODevice::ReadOnly) ) + { + QTextStream stream(&processInfo); + QString data = stream.readAll(); + + int stack = 0; + int field = 0; + int pos = 0; + + while (pos < data.count()) + { + QChar c = data[pos]; + + if ( c == '(' ) + stack++; + else if ( c == ')' ) + stack--; + else if ( stack == 0 && c == ' ' ) + field++; + else + { + switch ( field ) + { + case PARENT_PID_FIELD: + parentPidString.append(c); + break; + case PROCESS_NAME_FIELD: + processNameString.append(c); + break; + case GROUP_PROCESS_FIELD: + foregroundPidString.append(c); + break; + } + } + + pos++; + } + } + else + { + setFileError( processInfo.error() ); + return false; + } + + // check that data was read successfully + bool ok = false; + int foregroundPid = foregroundPidString.toInt(&ok); + if (ok) + setForegroundPid(foregroundPid); + + int parentPid = parentPidString.toInt(&ok); + if (ok) + setParentPid(parentPid); + + if (!processNameString.isEmpty()) + setName(processNameString); + + // update object state + setPid(pid); + + return ok; + } + + virtual bool readArguments(int pid) + { + // read command-line arguments file found at /proc/<pid>/cmdline + // the expected format is a list of strings delimited by null characters, + // and ending in a double null character pair. + + QFile argumentsFile( QString("/proc/%1/cmdline").arg(pid) ); + if ( argumentsFile.open(QIODevice::ReadOnly) ) + { + QTextStream stream(&argumentsFile); + QString data = stream.readAll(); + + QStringList argList = data.split( QChar('\0') ); + + foreach ( const QString &entry , argList ) + { + if (!entry.isEmpty()) + addArgument(entry); + } + } + else + { + setFileError( argumentsFile.error() ); + } + + return true; + } + + virtual bool readCurrentDir(int pid) + { + QFileInfo info( QString("/proc/%1/cwd").arg(pid) ); + + const bool readable = info.isReadable(); + + if ( readable && info.isSymLink() ) + { + setCurrentDir( info.symLinkTarget() ); + return true; + } + else + { + if ( !readable ) + setError( PermissionsError ); + else + setError( UnknownError ); + + return false; + } + } + + virtual bool readEnvironment(int pid) + { + // read environment bindings file found at /proc/<pid>/environ + // the expected format is a list of KEY=VALUE strings delimited by null + // characters and ending in a double null character pair. + + QFile environmentFile( QString("/proc/%1/environ").arg(pid) ); + if ( environmentFile.open(QIODevice::ReadOnly) ) + { + QTextStream stream(&environmentFile); + QString data = stream.readAll(); + + QStringList bindingList = data.split( QChar('\0') ); + + foreach( const QString &entry , bindingList ) + { + QString name; + QString value; + + int splitPos = entry.indexOf('='); + + if ( splitPos != -1 ) + { + name = entry.mid(0,splitPos); + value = entry.mid(splitPos+1,-1); + + addEnvironmentBinding(name,value); + } + } + } + else + { + setFileError( environmentFile.error() ); + } + + return true; + } +} ; + +#if defined(Q_OS_FREEBSD) +class FreeBSDProcessInfo : public UnixProcessInfo +{ +public: + FreeBSDProcessInfo(int pid, bool readEnvironment) : + UnixProcessInfo(pid, readEnvironment) + { + } + +private: + virtual bool readProcInfo(int pid) + { + int managementInfoBase[4]; + size_t mibLength; + struct kinfo_proc* kInfoProc; + + managementInfoBase[0] = CTL_KERN; + managementInfoBase[1] = KERN_PROC; + managementInfoBase[2] = KERN_PROC_PID; + managementInfoBase[3] = pid; + + if (sysctl(managementInfoBase, 4, NULL, &mibLength, NULL, 0) == -1) + return false; + + kInfoProc = new struct kinfo_proc [mibLength]; + + if (sysctl(managementInfoBase, 4, kInfoProc, &mibLength, NULL, 0) == -1) + { + delete [] kInfoProc; + return false; + } + +#if defined(__DragonFly__) + setName(kInfoProc->kp_comm); + setPid(kInfoProc->kp_pid); + setParentPid(kInfoProc->kp_ppid); + setForegroundPid(kInfoProc->kp_pgid); + setUserId(kInfoProc->kp_uid); +#else + setName(kInfoProc->ki_comm); + setPid(kInfoProc->ki_pid); + setParentPid(kInfoProc->ki_ppid); + setForegroundPid(kInfoProc->ki_pgid); + setUserId(kInfoProc->ki_uid); +#endif + + readUserName(); + + delete [] kInfoProc; + return true; + } + + virtual bool readArguments(int pid) + { + char args[ARG_MAX]; + int managementInfoBase[4]; + size_t len; + + managementInfoBase[0] = CTL_KERN; + managementInfoBase[1] = KERN_PROC; + managementInfoBase[2] = KERN_PROC_PID; + managementInfoBase[3] = pid; + + len = sizeof(args); + if (sysctl(managementInfoBase, 4, args, &len, NULL, 0) == -1) + return false; + + const QStringList argumentList = QString(args).split(QChar('\0')); + + for (QStringList::const_iterator it = argumentList.begin(); it != argumentList.end(); ++it) + { + addArgument(*it); + } + + return true; + } + + virtual bool readEnvironment(int pid) + { + // Not supported in FreeBSD? + return false; + } + + virtual bool readCurrentDir(int pid) + { +#if defined(__DragonFly__) + char buf[PATH_MAX]; + int managementInfoBase[4]; + size_t len; + + managementInfoBase[0] = CTL_KERN; + managementInfoBase[1] = KERN_PROC; + managementInfoBase[2] = KERN_PROC_CWD; + managementInfoBase[3] = pid; + + len = sizeof(buf); + if (sysctl(managementInfoBase, 4, buf, &len, NULL, 0) == -1) + return false; + + setCurrentDir(buf); + + return true; +#else + int numrecords; + struct kinfo_file* info = 0; + + info = kinfo_getfile(pid, &numrecords); + + if (!info) + return false; + + for (int i = 0; i < numrecords; ++i) + { + if (info[i].kf_fd == KF_FD_TYPE_CWD) + { + setCurrentDir(info[i].kf_path); + + free(info); + return true; + } + } + + free(info); + return false; +#endif + } +} ; +#endif + +#if defined(Q_OS_MAC) +class MacProcessInfo : public UnixProcessInfo +{ +public: + MacProcessInfo(int pid, bool env) : + UnixProcessInfo(pid, env) + { + } + +private: + virtual bool readProcInfo(int pid) + { + int managementInfoBase[4]; + size_t mibLength; + struct kinfo_proc* kInfoProc; + KDE_struct_stat statInfo; + + // Find the tty device of 'pid' (Example: /dev/ttys001) + managementInfoBase[0] = CTL_KERN; + managementInfoBase[1] = KERN_PROC; + managementInfoBase[2] = KERN_PROC_PID; + managementInfoBase[3] = pid; + + if (sysctl(managementInfoBase, 4, NULL, &mibLength, NULL, 0) == -1) + { + return false; + } + else + { + kInfoProc = new struct kinfo_proc [mibLength]; + if (sysctl(managementInfoBase, 4, kInfoProc, &mibLength, NULL, 0) == -1) + { + delete [] kInfoProc; + return false; + } + else + { + QString deviceNumber = QString(devname(((&kInfoProc->kp_eproc)->e_tdev), S_IFCHR)); + QString fullDeviceName = QString("/dev/") + deviceNumber.rightJustified(3, '0'); + delete [] kInfoProc; + + QByteArray deviceName = fullDeviceName.toLatin1(); + const char* ttyName = deviceName.data(); + + if (KDE_stat(ttyName, &statInfo) != 0) + return false; + + // Find all processes attached to ttyName + managementInfoBase[0] = CTL_KERN; + managementInfoBase[1] = KERN_PROC; + managementInfoBase[2] = KERN_PROC_TTY; + managementInfoBase[3] = statInfo.st_rdev; + + mibLength = 0; + if (sysctl(managementInfoBase, sizeof(managementInfoBase)/sizeof(int), NULL, &mibLength, NULL, 0) == -1) + return false; + + kInfoProc = new struct kinfo_proc [mibLength]; + if (sysctl(managementInfoBase, sizeof(managementInfoBase)/sizeof(int), kInfoProc, &mibLength, NULL, 0) == -1) + return false; + + // The foreground program is the first one + setName(QString(kInfoProc->kp_proc.p_comm)); + + delete [] kInfoProc; + } + } + return true; + } + + virtual bool readArguments(int pid) + { + Q_UNUSED(pid); + return false; + } + virtual bool readCurrentDir(int pid) + { + struct proc_vnodepathinfo vpi; + int nb = proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, 0, &vpi, sizeof(vpi)); + if (nb == sizeof(vpi)) + { + setCurrentDir(QString(vpi.pvi_cdir.vip_path)); + return true; + } + return false; + } + virtual bool readEnvironment(int pid) + { + Q_UNUSED(pid); + return false; + } +} ; +#endif + +#ifdef Q_OS_SOLARIS + // The procfs structure definition requires off_t to be + // 32 bits, which only applies if FILE_OFFSET_BITS=32. + // Futz around here to get it to compile regardless, + // although some of the structure sizes might be wrong. + // Fortunately, the structures we actually use don't use + // off_t, and we're safe. + #if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS==64) + #undef _FILE_OFFSET_BITS + #endif + #include <procfs.h> +#else + // On non-Solaris platforms, define a fake psinfo structure + // so that the SolarisProcessInfo class can be compiled + // + // That avoids the trap where you change the API and + // don't notice it in #ifdeffed platform-specific parts + // of the code. + struct psinfo { + int pr_ppid; + int pr_pgid; + char* pr_fname; + char* pr_psargs; + } ; + static const int PRARGSZ=1; +#endif + +class SolarisProcessInfo : public UnixProcessInfo +{ +public: + SolarisProcessInfo(int pid, bool readEnvironment) + : UnixProcessInfo(pid,readEnvironment) + { + } +private: + virtual bool readProcInfo(int pid) + { + QFile psinfo( QString("/proc/%1/psinfo").arg(pid) ); + if ( psinfo.open( QIODevice::ReadOnly ) ) + { + struct psinfo info; + if (psinfo.read((char *)&info,sizeof(info)) != sizeof(info)) + { + return false; + } + + setParentPid(info.pr_ppid); + setForegroundPid(info.pr_pgid); + setName(info.pr_fname); + setPid(pid); + + // Bogus, because we're treating the arguments as one single string + info.pr_psargs[PRARGSZ-1]=0; + addArgument(info.pr_psargs); + } + return true; + } + + virtual bool readArguments(int /*pid*/) + { + // Handled in readProcInfo() + return false; + } + + virtual bool readEnvironment(int /*pid*/) + { + // Not supported in Solaris + return false; + } + + virtual bool readCurrentDir(int pid) + { + QFileInfo info( QString("/proc/%1/path/cwd").arg(pid) ); + const bool readable = info.isReadable(); + + if ( readable && info.isSymLink() ) + { + setCurrentDir( info.symLinkTarget() ); + return true; + } + else + { + if ( !readable ) + setError( PermissionsError ); + else + setError( UnknownError ); + + return false; + } + } +} ; + +SSHProcessInfo::SSHProcessInfo(const ProcessInfo& process) + : _process(process) +{ + bool ok = false; + + // check that this is a SSH process + const QString& name = _process.name(&ok); + + if ( !ok || name != "ssh" ) + { + if ( !ok ) + kWarning() << "Could not read process info"; + else + kWarning() << "Process is not a SSH process"; + + return; + } + + // read arguments + const QVector<QString>& args = _process.arguments(&ok); + + // SSH options + // these are taken from the SSH manual ( accessed via 'man ssh' ) + + // options which take no arguments + static const QString noOptionsArguments("1246AaCfgkMNnqsTtVvXxY"); + // options which take one argument + static const QString singleOptionArguments("bcDeFiLlmOopRSw"); + + if ( ok ) + { + // find the username, host and command arguments + // + // the username/host is assumed to be the first argument + // which is not an option + // ( ie. does not start with a dash '-' character ) + // or an argument to a previous option. + // + // the command, if specified, is assumed to be the argument following + // the username and host + // + // note that we skip the argument at index 0 because that is the + // program name ( expected to be 'ssh' in this case ) + for ( int i = 1 ; i < args.count() ; i++ ) + { + // if this argument is an option then skip it, plus any + // following arguments which refer to this option + if ( args[i].startsWith('-') ) + { + QChar argChar = ( args[i].length() > 1 ) ? args[i][1] : '\0'; + + if ( noOptionsArguments.contains(argChar) ) + continue; + else if ( singleOptionArguments.contains(argChar) ) + { + i++; + continue; + } + } + + // check whether the host has been found yet + // if not, this must be the username/host argument + if ( _host.isEmpty() ) + { + // check to see if only a hostname is specified, or whether + // both a username and host are specified ( in which case they + // are separated by an '@' character: username@host ) + + int separatorPosition = args[i].indexOf('@'); + if ( separatorPosition != -1 ) + { + // username and host specified + _user = args[i].left(separatorPosition); + _host = args[i].mid(separatorPosition+1); + } + else + { + // just the host specified + _host = args[i]; + } + } + else + { + // host has already been found, this must be the command argument + _command = args[i]; + } + + } + } + else + { + kWarning() << "Could not read arguments"; + + return; + } +} + +QString SSHProcessInfo::userName() const +{ + return _user; +} +QString SSHProcessInfo::host() const +{ + return _host; +} +QString SSHProcessInfo::command() const +{ + return _command; +} +QString SSHProcessInfo::format(const QString& input) const +{ + QString output(input); + + // test whether host is an ip address + // in which case 'short host' and 'full host' + // markers in the input string are replaced with + // the full address + bool isIpAddress = false; + + struct in_addr address; + if ( inet_aton(_host.toLocal8Bit().constData(),&address) != 0 ) + isIpAddress = true; + else + isIpAddress = false; + + // search for and replace known markers + output.replace("%u",_user); + + if ( isIpAddress ) + output.replace("%h",_host); + else + output.replace("%h",_host.left(_host.indexOf('.'))); + + output.replace("%H",_host); + output.replace("%c",_command); + + return output; +} + +ProcessInfo* ProcessInfo::newInstance(int pid,bool enableEnvironmentRead) +{ +#ifdef Q_OS_LINUX + return new LinuxProcessInfo(pid,enableEnvironmentRead); +#elif defined(Q_OS_SOLARIS) + return new SolarisProcessInfo(pid,enableEnvironmentRead); +#elif defined(Q_OS_MAC) + return new MacProcessInfo(pid,enableEnvironmentRead); +#elif defined(Q_OS_FREEBSD) + return new FreeBSDProcessInfo(pid,enableEnvironmentRead); +#else + return new NullProcessInfo(pid,enableEnvironmentRead); +#endif +} +
new file mode 100644 --- /dev/null +++ b/gui/konsole/ProcessInfo.h @@ -0,0 +1,466 @@ +/* + Copyright 2007-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. +*/ + +#ifndef PROCESSINFO_H +#define PROCESSINFO_H + +// Qt +#include <QtCore/QFile> +#include <QtCore/QMap> +#include <QtCore/QString> +#include <QtCore/QVector> + +namespace Konsole +{ + +/** + * Takes a snapshot of the state of a process and provides access to + * information such as the process name, parent process, + * the foreground process in the controlling terminal, + * the arguments with which the process was started and the + * environment. + * + * To create a new snapshot, construct a new ProcessInfo instance, + * using ProcessInfo::newInstance(), + * passing the process identifier of the process you are interested in. + * + * After creating a new instance, call the update() method to take a + * snapshot of the current state of the process. + * + * Before calling any additional methods, check that the process state + * was read successfully using the isValid() method. + * + * Each accessor method which provides information about the process state ( such as pid(), + * currentDir(), name() ) takes a pointer to a boolean as an argument. If the information + * requested was read successfully then the boolean is set to true, otherwise it is set + * to false, in which case the return value from the function should be ignored. + * If this boolean is set to false, it may indicate an error reading the process information, + * or it may indicate that the information is not available on the current platform. + * + * eg. + * + * @code + * ProcessInfo* info = ProcessInfo::newInstance(pid); + * info->update(); + * + * if ( info->isValid() ) + * { + * bool ok; + * QString value = info->name(&ok); + * + * if ( ok ) kDebug() << "process name - " << name; + * int parentPid = info->parentPid(&ok); + * if ( ok ) kDebug() << "parent process - " << parentPid; + * int foregroundPid = info->foregroundColororegroundPid(&ok); + * if ( ok ) kDebug() << "foreground process - " << foregroundPid; + * } + * @endcode + */ +class ProcessInfo +{ +public: + /** + * Constructs a new instance of a suitable ProcessInfo sub-class for + * the current platform which provides information about a given process. + * + * @param pid The pid of the process to examine + * @param readEnvironment Specifies whether environment bindings should + * be read. If this is false, then environment() calls will + * always fail. This is an optimization to avoid the overhead + * of reading the (potentially large) environment data when it + * is not required. + */ + static ProcessInfo* newInstance(int pid,bool readEnvironment = false); + + virtual ~ProcessInfo() {} + + /** + * Updates the information about the process. This must + * be called before attempting to use any of the accessor methods. + */ + void update(); + + /** Returns true if the process state was read successfully. */ + bool isValid() const; + /** + * Returns the process id. + * + * @param ok Set to true if the process id was read successfully or false otherwise + */ + int pid(bool* ok) const; + /** + * Returns the id of the parent process id was read successfully or false otherwise + * + * @param ok Set to true if the parent process id + */ + int parentPid(bool* ok) const; + + /** + * Returns the id of the current foreground process + * + * NOTE: Using the foregroundProcessGroup() method of the Pty + * instance associated with the terminal of interest is preferred + * over using this method. + * + * @param ok Set to true if the foreground process id was read successfully or false otherwise + */ + int foregroundPid(bool* ok) const; + + /* Returns the user id of the process */ + int userId(bool* ok) const; + + /** Returns the user's name of the process */ + QString userName() const; + + /** Returns the user's home directory of the process */ + QString userHomeDir() const; + + /** Returns the name of the current process */ + QString name(bool* ok) const; + + /** + * Returns the command-line arguments which the process + * was started with. + * + * The first argument is the name used to launch the process. + * + * @param ok Set to true if the arguments were read successfully or false otherwise. + */ + QVector<QString> arguments(bool* ok) const; + /** + * Returns the environment bindings which the process + * was started with. + * In the returned map, the key is the name of the environment variable, + * and the value is the corresponding value. + * + * @param ok Set to true if the environment bindings were read successfully or false otherwise + */ + QMap<QString,QString> environment(bool* ok) const; + + /** + * Returns the current working directory of the process + * + * @param ok Set to true if the current working directory was read successfully or false otherwise + */ + QString currentDir(bool* ok) const; + + /** + * Returns the current working directory of the process (or its parent) + */ + QString validCurrentDir() const; + + /** Forces the user home directory to be calculated */ + void setUserHomeDir(); + + /** + * Parses an input string, looking for markers beginning with a '%' + * character and returns a string with the markers replaced + * with information from this process description. + * <br> + * The markers recognised are: + * <ul> + * <li> %u - Name of the user which owns the process. </li> + * <li> %n - Replaced with the name of the process. </li> + * <li> %d - Replaced with the last part of the path name of the + * process' current working directory. + * + * (eg. if the current directory is '/home/bob' then + * 'bob' would be returned) + * </li> + * <li> %D - Replaced with the current working directory of the process. </li> + * </ul> + */ + QString format(const QString& text) const; + + /** + * This enum describes the errors which can occur when trying to read + * a process's information. + */ + enum Error + { + /** No error occurred. */ + NoError, + /** The nature of the error is unknown. */ + UnknownError, + /** Konsole does not have permission to obtain the process information. */ + PermissionsError + }; + + /** + * Returns the last error which occurred. + */ + Error error() const; + +protected: + /** + * Constructs a new process instance. You should not call the constructor + * of ProcessInfo or its subclasses directly. Instead use the + * static ProcessInfo::newInstance() method which will return + * a suitable ProcessInfo instance for the current platform. + */ + explicit ProcessInfo(int pid , bool readEnvironment = false); + + /** + * This is called on construction to read the process state + * Subclasses should reimplement this function to provide + * platform-specific process state reading functionality. + * + * When called, readProcessInfo() should attempt to read all + * of the necessary state information. If the attempt is successful, + * it should set the process id using setPid(), and update + * the other relevant information using setParentPid(), setName(), + * setArguments() etc. + * + * Calls to isValid() will return true only if the process id + * has been set using setPid() + * + * @param pid The process id of the process to read + * @param readEnvironment Specifies whether the environment bindings + * for the process should be read + */ + virtual bool readProcessInfo(int pid , bool readEnvironment) = 0; + + /* Read the user name */ + virtual void readUserName(void) = 0; + + /** Sets the process id associated with this ProcessInfo instance */ + void setPid(int pid); + /** Sets the parent process id as returned by parentPid() */ + void setParentPid(int pid); + /** Sets the foreground process id as returend by foregroundPid() */ + void setForegroundPid(int pid); + /** Sets the user id associated with this ProcessInfo instance */ + void setUserId(int uid); + /** Sets the user name of the process as set by readUserName() */ + void setUserName(const QString& name); + /** Sets the name of the process as returned by name() */ + void setName(const QString& name); + /** Sets the current working directory for the process */ + void setCurrentDir(const QString& dir); + + /** Sets the error */ + void setError( Error error ); + + /** Convenience method. Sets the error based on a QFile error code. */ + void setFileError( QFile::FileError error ); + + /** + * Adds a commandline argument for the process, as returned + * by arguments() + */ + void addArgument(const QString& argument); + /** + * Adds an environment binding for the process, as returned by + * environment() + * + * @param name The name of the environment variable, eg. "PATH" + * @param value The value of the environment variable, eg. "/bin" + */ + void addEnvironmentBinding(const QString& name , const QString& value); + +private: + // takes a full directory path and returns a + // shortened version suitable for display in + // space-constrained UI elements (eg. tabs) + QString formatShortDir(const QString& dirPath) const; + + enum CommandFormat + { + ShortCommandFormat, + LongCommandFormat + }; + // takes a process name and its arguments and produces formatted output + QString formatCommand(const QString& name , const QVector<QString>& arguments , + CommandFormat format) const; + + // valid bits for _fields variable, ensure that + // _fields is changed to an int if more than 8 fields are added + enum FIELD_BITS + { + PROCESS_ID = 1, + PARENT_PID = 2, + FOREGROUND_PID = 4, + ARGUMENTS = 8, + ENVIRONMENT = 16, + NAME = 32, + CURRENT_DIR = 64, + UID =128 + }; + + char _fields; // a bitmap indicating which fields are valid + // used to set the "ok" parameters for the public + // accessor functions + + bool _enableEnvironmentRead; // specifies whether to read the environment + // bindings when update() is called + int _pid; + int _parentPid; + int _foregroundPid; + int _userId; + + Error _lastError; + + QString _name; + QString _userName; + QString _userHomeDir; + QString _currentDir; + + QVector<QString> _arguments; + QMap<QString,QString> _environment; + + static QSet<QString> commonDirNames(); + static QSet<QString> _commonDirNames; +}; + +/** + * Implementation of ProcessInfo which does nothing. + * Used on platforms where a suitable ProcessInfo subclass is not + * available. + * + * isValid() will always return false for instances of NullProcessInfo + */ +class NullProcessInfo : public ProcessInfo +{ +public: + /** + * Constructs a new NullProcessInfo instance. + * See ProcessInfo::newInstance() + */ + explicit NullProcessInfo(int pid,bool readEnvironment = false); +protected: + virtual bool readProcessInfo(int pid,bool readEnvironment); + virtual void readUserName(void); +}; + +/** + * Implementation of ProcessInfo for Unix platforms which uses + * the /proc filesystem + */ +class UnixProcessInfo : public ProcessInfo +{ +public: + /** + * Constructs a new instance of UnixProcessInfo. + * See ProcessInfo::newInstance() + */ + explicit UnixProcessInfo(int pid,bool readEnvironment = false); + +protected: + /** + * Implementation of ProcessInfo::readProcessInfo(); calls the + * four private methods below in turn. + */ + virtual bool readProcessInfo(int pid , bool readEnvironment); + + virtual void readUserName(void); + +private: + /** + * Read the standard process information -- PID, parent PID, foreground PID. + * @param pid process ID to use + * @return true on success + */ + virtual bool readProcInfo(int pid)=0; + + /** + * Read the environment of the process. Sets _environment. + * @param pid process ID to use + * @return true on success + */ + virtual bool readEnvironment(int pid)=0; + + /** + * Determine what arguments were passed to the process. Sets _arguments. + * @param pid process ID to use + * @return true on success + */ + virtual bool readArguments(int pid)=0; + + /** + * Determine the current directory of the process. + * @param pid process ID to use + * @return true on success + */ + virtual bool readCurrentDir(int pid)=0; +}; + +/** + * Lightweight class which provides additional information about SSH processes. + */ +class SSHProcessInfo +{ +public: + /** + * Constructs a new SSHProcessInfo instance which provides additional + * information about the specified SSH process. + * + * @param process A ProcessInfo instance for a SSH process. + */ + SSHProcessInfo(const ProcessInfo& process); + + /** + * Returns the user name which the user initially logged into on + * the remote computer. + */ + QString userName() const; + + /** + * Returns the host which the user has connected to. + */ + QString host() const; + + /** + * Returns the command which the user specified to execute on the + * remote computer when starting the SSH process. + */ + QString command() const; + + /** + * Operates in the same way as ProcessInfo::format(), except + * that the set of markers understood is different: + * + * %u - Replaced with user name which the user initially logged + * into on the remote computer. + * %h - Replaced with the first part of the host name which + * is connected to. + * %H - Replaced with the full host name of the computer which + * is connected to. + * %c - Replaced with the command which the user specified + * to execute when starting the SSH process. + */ + QString format(const QString& input) const; + +private: + const ProcessInfo& _process; + QString _user; + QString _host; + QString _command; +}; + +} +#endif //PROCESSINFO_H + +/* + Local Variables: + mode: c++ + c-file-style: "stroustrup" + indent-tabs-mode: nil + tab-width: 4 + End: +*/
new file mode 100644 --- /dev/null +++ b/gui/konsole/Profile.cpp @@ -0,0 +1,553 @@ +/* + This source file is part of Konsole, a terminal emulator. + + 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 "Profile.h" + +// Qt +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QTextCodec> + +// KDE +#include <KConfigGroup> +#include <KDesktopFile> +#include <KGlobal> +#include <KGlobalSettings> +#include <KLocale> +#include <KDebug> +#include <KStandardDirs> + +// System +#include <unistd.h> + +// Konsole +#include "ShellCommand.h" + +using namespace Konsole; + +// mappings between property enum values and names +// +// multiple names are defined for some property values, +// in these cases, the "proper" string name comes first, +// as that is used when reading/writing profiles from/to disk +// +// the other names are usually shorter versions for convenience +// when parsing konsoleprofile commands +static const char GENERAL_GROUP[] = "General"; +static const char KEYBOARD_GROUP[] = "Keyboard"; +static const char APPEARANCE_GROUP[] = "Appearance"; +static const char SCROLLING_GROUP[] = "Scrolling"; +static const char TERMINAL_GROUP[] = "Terminal Features"; +static const char CURSOR_GROUP[] = "Cursor Options"; +static const char INTERACTION_GROUP[] = "Interaction Options"; +static const char ENCODING_GROUP[] = "Encoding Options"; + +const Profile::PropertyInfo Profile::DefaultPropertyNames[] = +{ + // General + { Path , "Path" , 0 , QVariant::String } + , { Name , "Name" , GENERAL_GROUP , QVariant::String } + , { Title , "Title" , 0 , QVariant::String } + , { Icon , "Icon" , GENERAL_GROUP , QVariant::String } + , { Command , "Command" , 0 , QVariant::String } + , { Arguments , "Arguments" , 0 , QVariant::StringList } + , { Environment , "Environment" , GENERAL_GROUP , QVariant::StringList } + , { Directory , "Directory" , GENERAL_GROUP , QVariant::String } + , { LocalTabTitleFormat , "LocalTabTitleFormat" , GENERAL_GROUP , QVariant::String } + , { LocalTabTitleFormat , "tabtitle" , 0 , QVariant::String } + , { RemoteTabTitleFormat , "RemoteTabTitleFormat" , GENERAL_GROUP , QVariant::String } + , { ShowMenuBar , "ShowMenuBar" , GENERAL_GROUP , QVariant::Bool } + , { SaveGeometryOnExit , "SaveGeometryOnExit" , GENERAL_GROUP , QVariant::Bool } + , { TabBarMode , "TabBarMode" , GENERAL_GROUP , QVariant::Int } + , { TabBarPosition , "TabBarPosition" , GENERAL_GROUP , QVariant::Int } + , { StartInCurrentSessionDir , "StartInCurrentSessionDir" , GENERAL_GROUP , QVariant::Bool } + , { ShowNewAndCloseTabButtons, "ShowNewAndCloseTabButtons" , GENERAL_GROUP , QVariant::Bool } + , { MenuIndex, "MenuIndex" , GENERAL_GROUP , QVariant::String } + + // Appearance + , { Font , "Font" , APPEARANCE_GROUP , QVariant::Font } + , { ColorScheme , "ColorScheme" , APPEARANCE_GROUP , QVariant::String } + , { ColorScheme , "colors" , 0 , QVariant::String } + , { AntiAliasFonts, "AntiAliasFonts" , APPEARANCE_GROUP , QVariant::Bool } + , { BoldIntense, "BoldIntense", APPEARANCE_GROUP, QVariant::Bool } + + // Keyboard + , { KeyBindings , "KeyBindings" , KEYBOARD_GROUP , QVariant::String } + + // Scrolling + , { HistoryMode , "HistoryMode" , SCROLLING_GROUP , QVariant::Int } + , { HistorySize , "HistorySize" , SCROLLING_GROUP , QVariant::Int } + , { ScrollBarPosition , "ScrollBarPosition" , SCROLLING_GROUP , QVariant::Int } + + // Terminal Features + , { BlinkingTextEnabled , "BlinkingTextEnabled" , TERMINAL_GROUP , QVariant::Bool } + , { FlowControlEnabled , "FlowControlEnabled" , TERMINAL_GROUP , QVariant::Bool } + , { AllowProgramsToResizeWindow , "AllowProgramsToResizeWindow" , TERMINAL_GROUP , QVariant::Bool } + , { BidiRenderingEnabled , "BidiRenderingEnabled" , TERMINAL_GROUP , QVariant::Bool } + , { BlinkingCursorEnabled , "BlinkingCursorEnabled" , TERMINAL_GROUP , QVariant::Bool } + + // Cursor + , { UseCustomCursorColor , "UseCustomCursorColor" , CURSOR_GROUP , QVariant::Bool} + , { CursorShape , "CursorShape" , CURSOR_GROUP , QVariant::Int} + , { CustomCursorColor , "CustomCursorColor" , CURSOR_GROUP , QVariant::Color } + + // Interaction + , { WordCharacters , "WordCharacters" , INTERACTION_GROUP , QVariant::String } + , { TripleClickMode , "TripleClickSelectsFromCursor" , INTERACTION_GROUP , QVariant::Bool } + + // Encoding + , { DefaultEncoding , "DefaultEncoding" , ENCODING_GROUP , QVariant::String } + + , { (Property)0 , 0 , 0, QVariant::Invalid } +}; + +QHash<QString,Profile::PropertyInfo> Profile::_propertyInfoByName; +QHash<Profile::Property,Profile::PropertyInfo> Profile::_infoByProperty; + +void Profile::fillTableWithDefaultNames() +{ + static bool filledDefaults = false; + + if ( filledDefaults ) + return; + + const PropertyInfo* iter = DefaultPropertyNames; + while ( iter->name != 0 ) + { + registerProperty(*iter); + iter++; + } + + filledDefaults = true; +} + +FallbackProfile::FallbackProfile() + : Profile() +{ + // Fallback settings + setProperty(Name,i18n("Shell")); + // magic path for the fallback profile which is not a valid + // non-directory file name + setProperty(Path,"FALLBACK/"); + setProperty(Command,qgetenv("SHELL")); + setProperty(Icon,"utilities-terminal"); + setProperty(Arguments,QStringList() << qgetenv("SHELL")); + setProperty(Environment,QStringList() << "TERM=xterm"); + setProperty(LocalTabTitleFormat,"%d : %n"); + setProperty(RemoteTabTitleFormat,"%H (%u)"); + setProperty(TabBarMode,AlwaysShowTabBar); + setProperty(TabBarPosition,TabBarBottom); + setProperty(ShowMenuBar,true); + setProperty(SaveGeometryOnExit,true); + setProperty(StartInCurrentSessionDir,true); + setProperty(ShowNewAndCloseTabButtons,false); + setProperty(MenuIndex,"0"); + + setProperty(KeyBindings,"default"); + setProperty(ColorScheme,"Linux"); //use DarkPastels when is start support blue ncurses UI properly + setProperty(Font,KGlobalSettings::fixedFont()); + + setProperty(HistoryMode,FixedSizeHistory); + setProperty(HistorySize,1000); + setProperty(ScrollBarPosition,ScrollBarRight); + + setProperty(FlowControlEnabled,true); + setProperty(AllowProgramsToResizeWindow,true); + setProperty(BlinkingTextEnabled,true); + + setProperty(BlinkingCursorEnabled,false); + setProperty(BidiRenderingEnabled,false); + setProperty(CursorShape,BlockCursor); + setProperty(UseCustomCursorColor,false); + setProperty(CustomCursorColor,Qt::black); + + setProperty(DefaultEncoding,QString(QTextCodec::codecForLocale()->name())); + setProperty(AntiAliasFonts,true); + setProperty(BoldIntense,true); + + // default taken from KDE 3 + setProperty(WordCharacters,":@-./_~?&=%+#"); + + // Fallback should not be shown in menus + setHidden(true); +} +Profile::Profile(Profile::Ptr parent) + : _parent(parent) + ,_hidden(false) +{ +} +void Profile::clone(Profile::Ptr profile, bool differentOnly) +{ + const PropertyInfo* properties = DefaultPropertyNames; + while (properties->name != 0) + { + Property current = properties->property; + QVariant otherValue = profile->property<QVariant>(current); + switch (current) + { + case Name: + case Path: + break; + default: + if (!differentOnly || + property<QVariant>(current) != + otherValue) + { + setProperty(current,otherValue); + } + } + properties++; + } +} +Profile::~Profile() +{ +} +bool Profile::isHidden() const { return _hidden; } +void Profile::setHidden(bool hidden) { _hidden = hidden; } + +void Profile::setParent(Profile::Ptr parent) { _parent = parent; } +const Profile::Ptr Profile::parent() const { return _parent; } + +bool Profile::isEmpty() const +{ + return _propertyValues.isEmpty(); +} +QHash<Profile::Property,QVariant> Profile::setProperties() const +{ + return _propertyValues; +} +void Profile::setProperty(Property property , const QVariant& value) +{ + _propertyValues.insert(property,value); +} +bool Profile::isPropertySet(Property property) const +{ + return _propertyValues.contains(property); +} + +bool Profile::isNameRegistered(const QString& name) +{ + // insert default names into table the first time this is called + fillTableWithDefaultNames(); + + return _propertyInfoByName.contains(name); +} + +Profile::Property Profile::lookupByName(const QString& name) +{ + // insert default names into table the first time this is called + fillTableWithDefaultNames(); + + return _propertyInfoByName[name.toLower()].property; +} +QString Profile::primaryNameForProperty(Property property) +{ + // insert default names into table the first time this is called + fillTableWithDefaultNames(); + + return _infoByProperty[property].name; +} +QList<QString> Profile::namesForProperty(Property property) +{ + // insert default names into table the first time this is called + fillTableWithDefaultNames(); + + return QList<QString>() << primaryNameForProperty(property); +} +void Profile::registerProperty(const PropertyInfo& info) +{ + _propertyInfoByName.insert(QString(info.name).toLower(),info); + + // only allow one property -> name map + // (multiple name -> property mappings are allowed though) + if ( !_infoByProperty.contains(info.property) ) + _infoByProperty.insert(info.property,info); +} + +int Profile::menuIndexAsInt() const +{ + bool ok; + int index = menuIndex().toInt(&ok, 10); + if (ok) return index; + return 0; +} + +QString KDE4ProfileWriter::getPath(const Profile::Ptr info) +{ + QString newPath; + + if ( info->isPropertySet(Profile::Path) && + info->path().startsWith(KGlobal::dirs()->saveLocation("data", "konsole/")) ) + { + newPath = info->path(); + } + else + { + // use the profile name + ".profile" and save it in $KDEHOME + newPath = KGlobal::dirs()->saveLocation("data","konsole/") + info->name() + ".profile"; + } + + //kDebug(1211) << "Saving profile under name: " << newPath; + + return newPath; +} +void KDE4ProfileWriter::writeProperties(KConfig& config, + const Profile::Ptr profile, + const Profile::PropertyInfo* properties) +{ + const char* groupName = 0; + KConfigGroup group; + + while (properties->name != 0) + { + if (properties->group != 0) + { + if (groupName == 0 || strcmp(groupName,properties->group) != 0) + { + group = config.group(properties->group); + groupName = properties->group; + } + + if ( profile->isPropertySet(properties->property) ) + group.writeEntry(QString(properties->name), + profile->property<QVariant>(properties->property)); + } + + properties++; + } +} +bool KDE4ProfileWriter::writeProfile(const QString& path , const Profile::Ptr profile) +{ + KConfig config(path,KConfig::NoGlobals); + + KConfigGroup general = config.group(GENERAL_GROUP); + + // Parent profile if set, when loading the profile in future, the parent + // must be loaded as well if it exists. + if ( profile->parent() ) + general.writeEntry("Parent",profile->parent()->path()); + + if ( profile->isPropertySet(Profile::Command) + || profile->isPropertySet(Profile::Arguments) ) + general.writeEntry("Command", + ShellCommand(profile->command(),profile->arguments()).fullCommand()); + + // Write remaining properties + writeProperties(config,profile,Profile::DefaultPropertyNames); + + return true; +} + +QStringList KDE4ProfileReader::findProfiles() +{ + return KGlobal::dirs()->findAllResources("data","konsole/*.profile", + KStandardDirs::NoDuplicates); +} +void KDE4ProfileReader::readProperties(const KConfig& config, Profile::Ptr profile, + const Profile::PropertyInfo* properties) +{ + const char* groupName = 0; + KConfigGroup group; + + while (properties->name != 0) + { + if (properties->group != 0) + { + if (groupName == 0 || strcmp(groupName,properties->group) != 0) + { + group = config.group(properties->group); + groupName = properties->group; + } + + QString name(properties->name); + + if (group.hasKey(name)) + profile->setProperty(properties->property, + group.readEntry(name,QVariant(properties->type))); + + } + + properties++; + } +} + +bool KDE4ProfileReader::readProfile(const QString& path , Profile::Ptr profile , QString& parentProfile) +{ + if (!QFile::exists(path)) + return false; + + KConfig config(path,KConfig::NoGlobals); + + KConfigGroup general = config.group(GENERAL_GROUP); + if (general.hasKey("Parent")) + parentProfile = general.readEntry("Parent"); + + if ( general.hasKey("Command") ) + { + ShellCommand shellCommand(general.readEntry("Command")); + + profile->setProperty(Profile::Command,shellCommand.command()); + profile->setProperty(Profile::Arguments,shellCommand.arguments()); + } + + // Read remaining properties + readProperties(config,profile,Profile::DefaultPropertyNames); + + return true; +} +QStringList KDE3ProfileReader::findProfiles() +{ + return KGlobal::dirs()->findAllResources("data", "konsole/*.desktop", + KStandardDirs::NoDuplicates); +} +bool KDE3ProfileReader::readProfile(const QString& path , Profile::Ptr profile , QString& parentProfile) +{ + if (!QFile::exists(path)) + return false; + + // KDE 3 profiles do not have parents + parentProfile.clear(); + + KDesktopFile* desktopFile = new KDesktopFile(path); + KConfigGroup* config = new KConfigGroup( desktopFile->desktopGroup() ); + + if ( config->hasKey("Name") ) + profile->setProperty(Profile::Name,config->readEntry("Name")); + + //kDebug() << "reading KDE 3 profile " << profile->name(); + + if ( config->hasKey("Icon") ) + profile->setProperty(Profile::Icon,config->readEntry("Icon")); + if ( config->hasKey("Exec") ) + { + const QString& fullCommand = config->readEntry("Exec"); + ShellCommand shellCommand(fullCommand); + + profile->setProperty(Profile::Command,shellCommand.command()); + profile->setProperty(Profile::Arguments,shellCommand.arguments()); + } + if ( config->hasKey("Schema") ) + { + profile->setProperty(Profile::ColorScheme,config->readEntry("Schema").replace + (".schema",QString())); + } + if ( config->hasKey("defaultfont") ) + { + profile->setProperty(Profile::Font,config->readEntry("defaultfont")); + } + if ( config->hasKey("KeyTab") ) + { + profile->setProperty(Profile::KeyBindings,config->readEntry("KeyTab")); + } + if ( config->hasKey("Term") ) + { + profile->setProperty(Profile::Environment, + QStringList() << "TERM="+config->readEntry("Term")); + } + if ( config->hasKey("Cwd") ) + { + profile->setProperty(Profile::Directory,config->readEntry("Cwd")); + } + + delete desktopFile; + delete config; + + return true; +} + +QHash<Profile::Property,QVariant> ProfileCommandParser::parse(const QString& input) +{ + QHash<Profile::Property,QVariant> changes; + + // regular expression to parse profile change requests. + // + // format: property=value;property=value ... + // + // where 'property' is a word consisting only of characters from A-Z + // where 'value' is any sequence of characters other than a semi-colon + // + static QRegExp regExp("([a-zA-Z]+)=([^;]+)"); + + int offset = 0; + while ( regExp.indexIn(input,offset) != -1 ) + { + if ( regExp.capturedTexts().count() == 3 ) + { + Profile::Property property = Profile::lookupByName( + regExp.capturedTexts()[1]); + const QString value = regExp.capturedTexts()[2]; + changes.insert(property,value); + } + + offset = input.indexOf(';',offset) + 1; + if ( offset == 0 ) + break; + } + + return changes; +} + +void ProfileGroup::updateValues() +{ + const PropertyInfo* properties = Profile::DefaultPropertyNames; + while (properties->name != 0) + { + // the profile group does not store a value for some properties + // (eg. name, path) if even they are equal between profiles - + // + // the exception is when the group has only one profile in which + // case it behaves like a standard Profile + if (_profiles.count() > 1 && + !canInheritProperty(properties->property)) + { + properties++; + continue; + } + + QVariant value; + for (int i=0;i<_profiles.count();i++) + { + QVariant profileValue = _profiles[i]->property<QVariant>(properties->property); + if (value.isNull()) + value = profileValue; + else if (value != profileValue) + { + value = QVariant(); + break; + } + } + Profile::setProperty(properties->property,value); + properties++; + } +} +void ProfileGroup::setProperty(Property property, const QVariant& value) +{ + if (_profiles.count() > 1 && !canInheritProperty(property)) + return; + + Profile::setProperty(property,value); + foreach(Profile::Ptr profile,_profiles) + profile->setProperty(property,value); +} + + +
new file mode 100644 --- /dev/null +++ b/gui/konsole/Profile.h @@ -0,0 +1,634 @@ +/* + This source file is part of Konsole, a terminal emulator. + + Copyright 2007-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. +*/ + +#ifndef PROFILE_H +#define PROFILE_H + +// Qt +#include <QtCore/QHash> +#include <QtCore/QObject> +#include <QtCore/QPointer> +#include <QtCore/QStringList> +#include <QtCore/QVariant> + +#include <QtGui/QFont> + +// KDE +#include <KSharedPtr> +#include <KDebug> + +// Konsole +#include "konsole_export.h" + +class KConfig; + +namespace Konsole +{ +class ProfileGroup; + +/** + * Represents a terminal set-up which can be used to + * set the initial state of new terminal sessions or applied + * to existing sessions. Profiles consist of a number of named + * properties, which can be retrieved using property() and + * set using setProperty(). isPropertySet() can be used to check + * whether a particular property has been set in a profile. + * + * Profiles support a simple form of inheritance. When a new Profile + * is constructed, a pointer to a parent profile can be passed to + * the constructor. When querying a particular property of a profile + * using property(), the profile will return its own value for that + * property if one has been set or otherwise it will return the + * parent's value for that property. + * + * Profiles can be loaded from disk using ProfileReader instances + * and saved to disk using ProfileWriter instances. + */ +class KONSOLEPRIVATE_EXPORT Profile : public QSharedData +{ + +friend class KDE4ProfileReader; +friend class KDE4ProfileWriter; +friend class ProfileGroup; + +public: + typedef KSharedPtr<Profile> Ptr; + typedef KSharedPtr<ProfileGroup> GroupPtr; + + /** + * This enum describes the available properties + * which a Profile may consist of. + * + * Properties can be set using setProperty() and read + * using property() + */ + enum Property + { + /** (QString) Path to the profile's configuration file on-disk. */ + Path, + /** (QString) The descriptive name of this profile. */ + Name, + /** (QString) Title of this profile that will be displayed. */ + Title, + /** (QString) The name of the icon associated with this profile. This + * is used in menus and tabs to represent the profile. + */ + Icon, + /** (QString) The command to execute ( excluding arguments ) when creating a new terminal + * session using this profile. + */ + Command, + /** (QStringList) The arguments which are passed to the program specified by + * the Command property when creating a new terminal session using this profile. + */ + Arguments, + /** (QStringList) Additional environment variables ( in the form of NAME=VALUE pairs ) + * which are passed to the program specified by the Command property + * when creating a new terminal session using this profile. + */ + Environment, + /** (QString) The initial working directory for sessions created using this profile. */ + Directory, + /** (QString) The format used for tab titles when running normal commands. */ + LocalTabTitleFormat, + /** (QString) The format used for tab titles when the session is running + * a remote command (eg. SSH) */ + RemoteTabTitleFormat, + /** (bool) Specifies whether the menu bar should be shown in the main application window. */ + ShowMenuBar, + SaveGeometryOnExit, + /** (TabBarModeEnum) Specifies when the tab bar should be shown in + * the main application window. */ + TabBarMode, + /** (QFont) The font to use in terminal displays using this profile. */ + Font, + /** (QString) + * The name of the color scheme to use in terminal displays using this profile. + * Color schemes are managed by the ColorSchemeManager class. + */ + ColorScheme, + /** (QString) The name of the key bindings. + * Key bindings are managed by the KeyboardTranslatorManager class. + */ + KeyBindings, + /** (HistoryModeEnum) Specifies the storage type used for keeping the output produced + * by terminal sessions using this profile. + */ + HistoryMode, + /** (int) Specifies the number of lines of output to remember in terminal sessions + * using this profile. Once the limit is reached, the oldest lines are lost. + * Only applicable if the HistoryMode property is FixedSizeHistory + */ + HistorySize, + /** (ScrollBarPositionEnum) Specifies the position of the scroll bar in + * terminal displays using this profile. + */ + ScrollBarPosition, + /** (bool) Specifies whether the terminal will enable Bidirectional text display */ + BidiRenderingEnabled, + /** (bool) Specifies whether text in terminal displays is allowed to blink. */ + BlinkingTextEnabled, + /** (bool) Specifies whether the flow control keys ( typically Ctrl+S , Ctrl+Q ) + * have any effect. Also known as Xon/Xoff + */ + FlowControlEnabled, + /** (bool) Specifies whether programs running in the terminal are allowed to + * resize the terminal display. + */ + AllowProgramsToResizeWindow, + /** (bool) Specifies whether the cursor blinks ( in a manner similar + * to text editing applications ) + */ + BlinkingCursorEnabled, + /** (bool) If true, terminal displays use a fixed color to draw the cursor, + * specified by the CustomCursorColor property. Otherwise the cursor changes + * color to match the character underneath it. + */ + UseCustomCursorColor, + /** (CursorShapeEnum) The shape used by terminal displays to represent the cursor. */ + CursorShape, + /** (QColor) The color used by terminal displays to draw the cursor. Only applicable + * if the UseCustomCursorColor property is true. */ + CustomCursorColor, + /** (QString) A string consisting of the characters used to delimit words when + * selecting text in the terminal display. + */ + WordCharacters, + /** (TabBarPositionEnum) Position of the tab-bar relative to the terminal displays. */ + TabBarPosition, + /** (bool) If true, the triple click selects from current word onwards. Otherwise + * selects whole line. + */ + TripleClickMode, + /** (String) Default text codec */ + DefaultEncoding, + /** (bool) Whether fonts should be aliased or not */ + AntiAliasFonts, + /** (bool) Whether new sessions should be started in the same directory as the + * currently active session. */ + BoldIntense, + /** (bool) Whether character with intense colors should be rendered in bold font + * or just in bright color. */ + StartInCurrentSessionDir, + /** (bool) Whether a 'New Tab' and 'Close Tab' buttons should be shown on the tab bar */ + ShowNewAndCloseTabButtons, + /** Index of profile in the File Menu + * In future, format will be #.#.# to account for levels + */ + MenuIndex + }; + + /** + * This enum describes the available modes for showing or hiding the tab bar. + */ + enum TabBarModeEnum + { + /** The tab bar is never shown. */ + AlwaysHideTabBar = 0, + /** The tab bar is shown if there are multiple tabs open or hidden otherwise. */ + ShowTabBarAsNeeded = 1, + /** The tab bar is always shown. */ + AlwaysShowTabBar = 2 + }; + + /** + * This enum describes the available tab bar positions. + */ + enum TabBarPositionEnum + { + /** Show tab bar below displays. */ + TabBarBottom = 0, + /** Show tab bar above displays. */ + TabBarTop = 1 + }; + + /** + * This enum describes the modes available to remember lines of output produced + * by the terminal. + */ + enum HistoryModeEnum + { + /** No output is remembered. As soon as lines of text are scrolled off-screen they are lost. */ + DisableHistory = 0, + /** A fixed number of lines of output are remembered. Once the limit is reached, the oldest + * lines are lost. */ + FixedSizeHistory = 1, + /** All output is remembered for the duration of the session. + * Typically this means that lines are recorded to + * a file as they are scrolled off-screen. + */ + UnlimitedHistory = 2 + }; + + /** + * This enum describes the positions where the terminal display's scroll bar may be placed. + */ + enum ScrollBarPositionEnum + { + /** Show the scroll-bar on the left of the terminal display. */ + ScrollBarLeft = 0, + /** Show the scroll-bar on the right of the terminal display. */ + ScrollBarRight = 1, + /** Do not show the scroll-bar. */ + ScrollBarHidden = 2 + }; + + /** This enum describes the shapes used to draw the cursor in terminal displays. */ + enum CursorShapeEnum + { + /** Use a solid rectangular block to draw the cursor. */ + BlockCursor = 0, + /** Use an 'I' shape, similar to that used in text editing applications, to draw the cursor. */ + IBeamCursor = 1, + /** Draw a line underneath the cursor's position. */ + UnderlineCursor = 2 + }; + + /** + * Constructs a new profile + * + * @param parent The parent profile. When querying the value of a property + * using property(), if the property has not been set in this profile then + * the parent's value for the property will be returned instead. + */ + explicit Profile(Ptr parent = Ptr()); + virtual ~Profile(); + + /** + * Copies all properties except Name and Path from the specified @p profile + * into this profile + * + * @param profile The profile to copy properties from + * @param differentOnly If true, only properties in @p profile which have a + * different value from this profile's current value (either set via + * setProperty() or inherited from the parent profile) will be set. + */ + void clone(Ptr profile, bool differentOnly = true); + + /** + * Changes the parent profile. When calling the property() method, + * if the specified property has not been set for this profile, + * the parent's value for the property will be returned instead. + */ + void setParent(Ptr parent); + + /** Returns the parent profile. */ + const Ptr parent() const; + + /** Returns this profile as a group or null if this profile is not a group. */ + const GroupPtr asGroup() const; + GroupPtr asGroup(); + + /** + * Returns the current value of the specified @p property, cast to type T. + * Internally properties are stored using the QVariant type and cast to T + * using QVariant::value<T>(); + * + * If the specified @p property has not been set in this profile, + * and a non-null parent was specified in the Profile's constructor, + * the parent's value for @p property will be returned. + */ + template <class T> + T property(Property property) const; + + /** Sets the value of the specified @p property to @p value. */ + virtual void setProperty(Property property,const QVariant& value); + /** Returns true if the specified property has been set in this Profile instance. */ + virtual bool isPropertySet(Property property) const; + + /** Returns a map of the properties set in this Profile instance. */ + virtual QHash<Property,QVariant> setProperties() const; + + /** Returns true if no properties have been set in this Profile instance. */ + bool isEmpty() const; + + /** + * Returns true if this is a 'hidden' profile which should not be displayed + * in menus or saved to disk. + * + * This is used for the fallback profile, in case there are no profiles on + * disk which can be loaded, or for overlay profiles created to handle + * command-line arguments which change profile properties. + */ + bool isHidden() const; + + /** Specifies whether this is a hidden profile. See isHidden() */ + void setHidden(bool hidden); + + // + // Convenience methods for property() and setProperty() go here + // + + /** Convenience method for property<QString>(Profile::Path) */ + QString path() const { return property<QString>(Profile::Path); } + + /** Convenience method for property<QString>(Profile::Name) */ + QString name() const { return property<QString>(Profile::Name); } + + /** Convenience method for property<QString>(Profile::Directory) */ + QString defaultWorkingDirectory() const + { return property<QString>(Profile::Directory); } + + /** Convenience method for property<QString>(Profile::Icon) */ + QString icon() const { return property<QString>(Profile::Icon); } + + /** Convenience method for property<QString>(Profile::Command) */ + QString command() const { return property<QString>(Profile::Command); } + + /** Convenience method for property<QStringList>(Profile::Arguments) */ + QStringList arguments() const { return property<QStringList>(Profile::Arguments); } + + /** Convenience method for property<QFont>(Profile::Font) */ + QFont font() const { return property<QFont>(Profile::Font); } + + /** Convenience method for property<QString>(Profile::ColorScheme) */ + QString colorScheme() const { return property<QString>(Profile::ColorScheme); } + + /** Convenience method for property<QStringList>(Profile::Environment) */ + QStringList environment() const { return property<QStringList>(Profile::Environment); } + + /** Convenience method for property<QString>(Profile::MenuIndex) */ + QString menuIndex() const { return property<QString>(Profile::MenuIndex); } + + int menuIndexAsInt() const; + + /** + * Returns true if @p name has been associated with an element + * from the Property enum or false otherwise. + */ + static bool isNameRegistered(const QString& name); + + /** + * Returns the element from the Property enum associated with the + * specified @p name. + * + * @param name The name of the property to look for, this is case insensitive. + */ + static Property lookupByName(const QString& name); + /** + * Returns the string names associated with the specified @p property from + * the Property enum, in the order the associations were created using + * registerName() + */ + static QList<QString> namesForProperty(Property property); + + /** + * Returns the primary name for the specified @p property. + * TODO More documentation + */ + static QString primaryNameForProperty(Property property); + +private: + struct PropertyInfo; + // Defines a new property, this property is then available + // to all Profile instances. + static void registerProperty(const PropertyInfo& info); + + // fills the table with default names for profile properties + // the first time it is called. + // subsequent calls return immediately + static void fillTableWithDefaultNames(); + + // returns true if the property can be inherited + static bool canInheritProperty(Property property); + + QHash<Property,QVariant> _propertyValues; + Ptr _parent; + + bool _hidden; + + static QHash<QString,PropertyInfo> _propertyInfoByName; + static QHash<Property,PropertyInfo> _infoByProperty; + + // Describes a property. Each property has a name and group + // which is used when saving/loading the profile. + struct PropertyInfo + { + Property property; + const char* name; + const char* group; + QVariant::Type type; + }; + static const PropertyInfo DefaultPropertyNames[]; +}; + +template <class T> +inline T Profile::property(Property theProperty) const +{ + return property<QVariant>(theProperty).value<T>(); +} +template <> +inline QVariant Profile::property(Property property) const +{ + if ( _propertyValues.contains(property) ) { + return _propertyValues[property]; + } + else if ( _parent && canInheritProperty(property) ) { + return _parent->property<QVariant>(property); + } + else { + return QVariant(); + } +} +inline bool Profile::canInheritProperty(Property property) +{ return property != Name && property != Path; } + + +/** + * A profile which contains a number of default settings for various properties. + * This can be used as a parent for other profiles or a fallback in case + * a profile cannot be loaded from disk. + */ +class KONSOLEPRIVATE_EXPORT FallbackProfile : public Profile +{ +public: + FallbackProfile(); +}; + +/** + * A composite profile which allows a group of profiles to be treated as one. + * When setting a property, the new value is applied to all profiles in the group. + * When reading a property, if all profiles in the group have the same value + * then that value is returned, otherwise the result is null. + * + * Profiles can be added to the group using addProfile(). When all profiles + * have been added updateValues() must be called + * to sync the group's property values with those of the group's profiles. + * + * The Profile::Name and Profile::Path properties are unique to individual profiles, + * setting these properties on a ProfileGroup has no effect. + */ +class KONSOLEPRIVATE_EXPORT ProfileGroup : public Profile +{ +public: + typedef KSharedPtr<ProfileGroup> Ptr; + + /** Construct a new profile group, which is hidden by default. */ + ProfileGroup(Profile::Ptr parent = Profile::Ptr()); + + /** Add a profile to the group. Calling setProperty() will update this profile. + * When creating a group, add the profiles to the group then call updateValues() to + * make the group's property values reflect the profiles currently in the group. */ + void addProfile(Profile::Ptr profile) + { _profiles.append(profile); } + + /** Remove a profile from the group. Calling setProperty() will no longer + * affect this profile. */ + void removeProfile(Profile::Ptr profile) + { _profiles.removeAll(profile); } + + /** Returns the profiles in this group .*/ + QList<Profile::Ptr> profiles() const + { return _profiles; } + + /** + * Updates the property values in this ProfileGroup to match those from + * the group's profiles() + * + * For each available property, if each profile in the group has the same value then + * the ProfileGroup will use that value for the property. Otherwise the value for the property + * will be set to a null QVariant + * + * Some properties such as the name and the path of the profile + * will always be set to null if the group has more than one profile. + */ + void updateValues(); + + /** Sets the value of @p property in each of the group's profiles to @p value. */ + void setProperty(Property property, const QVariant& value); + +private: + QList<Profile::Ptr> _profiles; +}; +inline ProfileGroup::ProfileGroup(Profile::Ptr parent) +: Profile(parent) +{ + setHidden(true); +} +inline const Profile::GroupPtr Profile::asGroup() const +{ + const Profile::GroupPtr ptr(dynamic_cast<ProfileGroup*>( + const_cast<Profile*>(this))); + return ptr; +} +inline Profile::GroupPtr Profile::asGroup() +{ + return Profile::GroupPtr(dynamic_cast<ProfileGroup*>(this)); +} + +/** Interface for all classes which can load profile settings from a file. */ +class ProfileReader +{ +public: + virtual ~ProfileReader() {} + /** Returns a list of paths to profiles which this reader can read. */ + virtual QStringList findProfiles() { return QStringList(); } + /** + * Attempts to read a profile from @p path and + * save the property values described into @p profile. + * + * Returns true if the profile was successfully read or false otherwise. + * + * @param path Path to the profile to read + * @param profile Pointer to the Profile the settings will be read into + * @param parentProfile Receives the name of the parent profile specified in + */ + virtual bool readProfile(const QString& path , Profile::Ptr profile , QString& parentProfile) = 0; +}; +/** Reads a KDE 3 profile .desktop file. */ +class KDE3ProfileReader : public ProfileReader +{ +public: + virtual QStringList findProfiles(); + virtual bool readProfile(const QString& path , Profile::Ptr profile, QString& parentProfile); +}; +/** Reads a KDE 4 .profile file. */ +class KDE4ProfileReader : public ProfileReader +{ +public: + virtual QStringList findProfiles(); + virtual bool readProfile(const QString& path , Profile::Ptr profile, QString& parentProfile); +private: + void readProperties(const KConfig& config, Profile::Ptr profile, + const Profile::PropertyInfo* properties); +}; +/** Interface for all classes which can write profile settings to a file. */ +class ProfileWriter +{ +public: + virtual ~ProfileWriter() {} + /** + * Returns a suitable path-name for writing + * @p profile to. The path-name should be accepted by + * the corresponding ProfileReader class. + */ + virtual QString getPath(const Profile::Ptr profile) = 0; + /** + * Writes the properties and values from @p profile to the file specified by + * @p path. This profile should be readable by the corresponding ProfileReader class. + */ + virtual bool writeProfile(const QString& path , const Profile::Ptr profile) = 0; +}; +/** Writes a KDE 4 .profile file. */ +class KDE4ProfileWriter : public ProfileWriter +{ +public: + virtual QString getPath(const Profile::Ptr profile); + virtual bool writeProfile(const QString& path , const Profile::Ptr profile); + +private: + void writeProperties(KConfig& config, const Profile::Ptr profile, + const Profile::PropertyInfo* properties); +}; + +/** + * Parses an input string consisting of property names + * and assigned values and returns a table of properties + * and values. + * + * The input string will typically look like this: + * + * @code + * PropertyName=Value;PropertyName=Value ... + * @endcode + * + * For example: + * + * @code + * Icon=konsole;Directory=/home/bob + * @endcode + */ +class KONSOLEPRIVATE_EXPORT ProfileCommandParser +{ +public: + /** + * Parses an input string consisting of property names + * and assigned values and returns a table of + * properties and values. + */ + QHash<Profile::Property,QVariant> parse(const QString& input); + +}; + +} +Q_DECLARE_METATYPE(Konsole::Profile::Ptr) + +#endif // PROFILE_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/ProfileList.cpp @@ -0,0 +1,185 @@ +/* + 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 "ProfileList.h" + +// Qt +#include <QtGui/QAction> +#include <QtGui/QActionGroup> +#include <KDebug> + +// KDE +#include <KIcon> +#include <KLocalizedString> + +// Konsole +#include "SessionManager.h" + +using namespace Konsole; + +ProfileList::ProfileList(bool addShortcuts , QObject* parent) + : QObject(parent) + , _addShortcuts(addShortcuts) + , _emptyListAction(0) +{ + SessionManager* manager = SessionManager::instance(); + + // construct the list of favorite session types + _group = new QActionGroup(this); + + // Even when there are no profiles in the menu list, allow user to + // create new tabs from the menu + _emptyListAction = new QAction(i18n("Default profile"),_group); + + // TODO - Handle re-sorts when user changes profile names + + QList<Profile::Ptr> list = manager->sortedFavorites(); + + QListIterator<Profile::Ptr> iter(list); + + while (iter.hasNext()) + { + favoriteChanged(iter.next(),true); + } + + connect( _group , SIGNAL(triggered(QAction*)) , this , SLOT(triggered(QAction*)) ); + + + // listen for future changes to the session list + connect( manager , SIGNAL(favoriteStatusChanged(Profile::Ptr,bool)) , this , + SLOT(favoriteChanged(Profile::Ptr,bool)) ); + connect( manager , SIGNAL(shortcutChanged(Profile::Ptr,QKeySequence)) , this , + SLOT(shortcutChanged(Profile::Ptr,QKeySequence)) ); + connect( manager , SIGNAL(profileChanged(Profile::Ptr)) , this , + SLOT(profileChanged(Profile::Ptr)) ); +} +void ProfileList::updateEmptyAction() +{ + Q_ASSERT( _group ); + Q_ASSERT( _emptyListAction ); + + // show empty list action when it is the only action + // in the group + const bool showEmptyAction = _group->actions().count() == 1; + + if ( showEmptyAction != _emptyListAction->isVisible() ) + _emptyListAction->setVisible(showEmptyAction); +} +QAction* ProfileList::actionForKey(Profile::Ptr key) const +{ + QListIterator<QAction*> iter(_group->actions()); + while ( iter.hasNext() ) + { + QAction* next = iter.next(); + if ( next->data().value<Profile::Ptr>() == key ) + return next; + } + return 0; // not found +} + +void ProfileList::profileChanged(Profile::Ptr key) +{ + QAction* action = actionForKey(key); + if ( action ) + updateAction(action,key); +} + +void ProfileList::updateAction(QAction* action , Profile::Ptr info) +{ + Q_ASSERT(action); + Q_ASSERT(info); + + action->setText(info->name()); + action->setIcon(KIcon(info->icon())); +} +void ProfileList::shortcutChanged(Profile::Ptr info,const QKeySequence& sequence) +{ + if ( !_addShortcuts ) + return; + + QAction* action = actionForKey(info); + + if ( action ) + { + action->setShortcut(sequence); + } +} +void ProfileList::syncWidgetActions(QWidget* widget, bool sync) +{ + if (!sync) + { + _registeredWidgets.remove(widget); + return; + } + + _registeredWidgets.insert(widget); + + const QList<QAction*> currentActions = widget->actions(); + foreach(QAction* currentAction, currentActions) + widget->removeAction(currentAction); + + widget->addActions(_group->actions()); +} +void ProfileList::favoriteChanged(Profile::Ptr info,bool isFavorite) +{ + SessionManager* manager = SessionManager::instance(); + + if ( isFavorite ) + { + QAction* action = new QAction(_group); + action->setData( QVariant::fromValue(info) ); + + if ( _addShortcuts ) + { + action->setShortcut(manager->shortcut(info)); + } + + updateAction(action,info); + + foreach(QWidget* widget,_registeredWidgets) + widget->addAction(action); + emit actionsChanged(_group->actions()); + } + else + { + QAction* action = actionForKey(info); + + if ( action ) + { + _group->removeAction(action); + foreach(QWidget* widget,_registeredWidgets) + widget->removeAction(action); + emit actionsChanged(_group->actions()); + } + } + + updateEmptyAction(); +} +void ProfileList::triggered(QAction* action) +{ + emit profileSelected( action->data().value<Profile::Ptr>() ); +} + +QList<QAction*> ProfileList::actions() +{ + return _group->actions(); +} + +#include "ProfileList.moc"
new file mode 100644 --- /dev/null +++ b/gui/konsole/ProfileList.h @@ -0,0 +1,108 @@ +/* + 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. +*/ + +#ifndef PROFILELIST_H +#define PROFILELIST_H + +#include <QtCore/QList> +#include <QtCore/QSet> +#include <QtCore/QObject> + +#include "Profile.h" +#include "konsole_export.h" + +class QAction; +class QActionGroup; +class QKeySequence; + +namespace Konsole +{ + +class Profile; + +/** + * ProfileList provides a list of actions which represent session profiles + * that a SessionManager can create a session from. + * + * These actions can be plugged into a GUI. + * + * Currently only profiles marked as favorites in the SessionManager are included. + * + * The user-data associated with each session can be passed to the createProfile() method of the + * SessionManager to create a new terminal session. + */ +class KONSOLEPRIVATE_EXPORT ProfileList : public QObject +{ +Q_OBJECT + +public: + /** + * Constructs a new session list which displays sessions + * that can be created by @p manager + * + * @param addShortcuts True if the shortcuts associated with profiles + * in the session manager should be added to the actions + * @param parent The parent GUI object + */ + ProfileList(bool addShortcuts , QObject* parent); + + /** + * Returns a list of actions representing the types of sessions which can be created by + * manager(). + * The user-data associated with each action is the string key that can be passed to + * the manager to request creation of a new session. + */ + QList<QAction*> actions(); + + /** TODO: Document me */ + void syncWidgetActions(QWidget* widget,bool sync); +signals: + /** + * Emitted when the user selects an action from the list. + * + * @param profile The profile to select + */ + void profileSelected(Profile::Ptr profile); + /** + * Emitted when the list of actions changes. + */ + void actionsChanged(const QList<QAction*>& actions); + +private slots: + void triggered(QAction* action); + void favoriteChanged(Profile::Ptr profile, bool isFavorite); + void profileChanged(Profile::Ptr profile); + void shortcutChanged(Profile::Ptr profile, const QKeySequence& sequence); + +private: + QAction* actionForKey(Profile::Ptr profile) const; + void updateAction(QAction* action , Profile::Ptr profile); + void updateEmptyAction(); + + QActionGroup* _group; + bool _addShortcuts; + + // action to show when the list is empty + QAction* _emptyListAction; + QSet<QWidget*> _registeredWidgets; +}; + +} + +#endif // PROFILELIST_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/ProfileListWidget.cpp @@ -0,0 +1,96 @@ +/* + 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 "ProfileListWidget.h" + +// Qt +#include <KDebug> +#include <QtGui/QDrag> +#include <QtGui/QKeyEvent> +#include <QtCore/QMimeData> + +static const char konsoleSessionMimeFormat[] = "konsole/session"; + +ProfileListWidget::ProfileListWidget(QWidget* parent) + : QListWidget(parent) +{ + // use large icons so that there is a big area for the user to click + // on to switch between sessions + setIconSize( QSize(32,32) ); + + // turn the frame off + setFrameStyle( QFrame::NoFrame ); + + QPalette p = palette(); + p.setBrush( QPalette::Base , QColor(220,220,220) ); + setPalette(p); +} + +void ProfileListWidget::startDrag(Qt::DropActions /*supportedActions*/) +{ + //kDebug() << "drag and drop started in session list widget"; + + QMimeData* mimeData = new QMimeData(); + + QByteArray data; + data.setNum(42); + mimeData->setData(konsoleSessionMimeFormat,data); + + QDrag* drag = new QDrag(this); + drag->setMimeData(mimeData); + + Qt::DropAction action = drag->start( Qt::MoveAction ); + + if ( action & Qt::MoveAction ) + { + emit takeSessionEvent(currentRow()); + } +} + +void ProfileListWidget::dragEnterEvent(QDragEnterEvent* event) +{ + if ( event->mimeData()->hasFormat(konsoleSessionMimeFormat) ) + { + event->accept(); + } +} + +void ProfileListWidget::dragMoveEvent(QDragMoveEvent* event) +{ + if ( event->mimeData()->hasFormat(konsoleSessionMimeFormat) ) + { + event->setDropAction(Qt::MoveAction); + event->accept(); + } +} + +void ProfileListWidget::dropEvent(QDropEvent* event) +{ + if ( event->mimeData()->hasFormat(konsoleSessionMimeFormat) ) + { + event->setDropAction(Qt::MoveAction); + event->accept(); + + emit dropSessionEvent( event->mimeData()->data(konsoleSessionMimeFormat).toInt() ); + } +} + + +#include "ProfileListWidget.moc"
new file mode 100644 --- /dev/null +++ b/gui/konsole/ProfileListWidget.h @@ -0,0 +1,57 @@ +/* + 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. +*/ + +#ifndef PROFILELISTWIDGET_H +#define PROFILELISTWIDGET_H + +// Qt +#include <QtGui/QListWidget> + +class ProfileListWidget : public QListWidget +{ +Q_OBJECT + +public: + ProfileListWidget(QWidget* parent); + +signals: + void takeSessionEvent(int itemIndex); + void dropSessionEvent(int id); + +protected: + virtual void startDrag(Qt::DropActions supportedActions); + virtual void dropEvent(QDropEvent* event); + virtual void dragEnterEvent(QDragEnterEvent* event); + virtual void dragMoveEvent(QDragMoveEvent* event); + +}; + +/*class SessionListDelegate : public QAbstractItemDelegate +{ +Q_OBJECT + +public: + virtual void paint(QPainter* painter , const QStyleOptionViewItem& option, + const QModelIndex& index) const; + + virtual QSize sizeHint( const QStyleOptionViewItem& option, + const QModelIndex& index) const; +};*/ + +#endif // PROFILELISTWIDGET_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/Pty.cpp @@ -0,0 +1,320 @@ +/* + This file is part of Konsole, an X terminal. + Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de> + + 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 "Pty.h" + +// System +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#include <termios.h> +#include <signal.h> + +// Qt +#include <QtCore/QStringList> + +// KDE +//#include <KStandardDirs> +//#include <KLocale> +//#include <KDebug> +//#include <KPty> +//#include <KPtyDevice> +//#include <kde_file.h> + +#include "kpty.h" +#include "kptydevice.h" + +using namespace Konsole; + +void Pty::setWindowSize(int lines, int cols) +{ + _windowColumns = cols; + _windowLines = lines; + + if (pty()->masterFd() >= 0) + pty()->setWinSize(lines, cols); +} +QSize Pty::windowSize() const +{ + return QSize(_windowColumns,_windowLines); +} + +void Pty::setFlowControlEnabled(bool enable) +{ + _xonXoff = enable; + + if (pty()->masterFd() >= 0) + { + struct ::termios ttmode; + pty()->tcGetAttr(&ttmode); + if (!enable) + ttmode.c_iflag &= ~(IXOFF | IXON); + else + ttmode.c_iflag |= (IXOFF | IXON); + if (!pty()->tcSetAttr(&ttmode)) + kWarning() << "Unable to set terminal attributes."; + } +} +bool Pty::flowControlEnabled() const +{ + if (pty()->masterFd() >= 0) + { + struct ::termios ttmode; + pty()->tcGetAttr(&ttmode); + return ttmode.c_iflag & IXOFF && + ttmode.c_iflag & IXON; + } + kWarning() << "Unable to get flow control status, terminal not connected."; + return false; +} + +void Pty::setUtf8Mode(bool enable) +{ +#ifdef IUTF8 // XXX not a reasonable place to check it. + _utf8 = enable; + + if (pty()->masterFd() >= 0) + { + struct ::termios ttmode; + pty()->tcGetAttr(&ttmode); + if (!enable) + ttmode.c_iflag &= ~IUTF8; + else + ttmode.c_iflag |= IUTF8; + if (!pty()->tcSetAttr(&ttmode)) + kWarning() << "Unable to set terminal attributes."; + } +#endif +} + +void Pty::setErase(char erase) +{ + _eraseChar = erase; + + if (pty()->masterFd() >= 0) + { + struct ::termios ttmode; + pty()->tcGetAttr(&ttmode); + ttmode.c_cc[VERASE] = erase; + if (!pty()->tcSetAttr(&ttmode)) + kWarning() << "Unable to set terminal attributes."; + } +} + +char Pty::erase() const +{ + if (pty()->masterFd() >= 0) + { + struct ::termios ttyAttributes; + pty()->tcGetAttr(&ttyAttributes); + return ttyAttributes.c_cc[VERASE]; + } + + return _eraseChar; +} + +void Pty::addEnvironmentVariables(const QStringList& environment) +{ + QListIterator<QString> iter(environment); + while (iter.hasNext()) + { + QString pair = iter.next(); + + // split on the first '=' character + int pos = pair.indexOf('='); + + if ( pos >= 0 ) + { + QString variable = pair.left(pos); + QString value = pair.mid(pos+1); + + setEnv(variable,value); + } + } +} + +int Pty::start(const QString& program, + const QStringList& programArguments, + const QStringList& environment, + ulong winid, + bool addToUtmp, + const QString& dbusService, + const QString& dbusSession) +{ + clearProgram(); + + // For historical reasons, the first argument in programArguments is the + // name of the program to execute, so create a list consisting of all + // but the first argument to pass to setProgram() + Q_ASSERT(programArguments.count() >= 1); + setProgram(program.toLatin1(),programArguments.mid(1)); + + addEnvironmentVariables(environment); + + if ( !dbusService.isEmpty() ) + setEnv("KONSOLE_DBUS_SERVICE",dbusService); + if ( !dbusSession.isEmpty() ) + setEnv("KONSOLE_DBUS_SESSION", dbusSession); + + setEnv("WINDOWID", QString::number(winid)); + + // unless the LANGUAGE environment variable has been set explicitly + // set it to a null string + // this fixes the problem where KCatalog sets the LANGUAGE environment + // variable during the application's startup to something which + // differs from LANG,LC_* etc. and causes programs run from + // the terminal to display messages in the wrong language + // + // this can happen if LANG contains a language which KDE + // does not have a translation for + // + // BR:149300 + setEnv("LANGUAGE",QString(),false /* do not overwrite existing value if any */); + + setUseUtmp(addToUtmp); + + struct ::termios ttmode; + pty()->tcGetAttr(&ttmode); + if (!_xonXoff) + ttmode.c_iflag &= ~(IXOFF | IXON); + else + ttmode.c_iflag |= (IXOFF | IXON); +#ifdef IUTF8 // XXX not a reasonable place to check it. + if (!_utf8) + ttmode.c_iflag &= ~IUTF8; + else + ttmode.c_iflag |= IUTF8; +#endif + + if (_eraseChar != 0) + ttmode.c_cc[VERASE] = _eraseChar; + + if (!pty()->tcSetAttr(&ttmode)) + kWarning() << "Unable to set terminal attributes."; + + pty()->setWinSize(_windowLines, _windowColumns); + + //KProcess::start(); + + if (!waitForStarted()) + return -1; + + return 0; +} + +void Pty::setWriteable(bool writeable) +{ + //KDE_struct_stat sbuf; + struct stat sbuf; + //KDE_stat(pty()->ttyName(), &sbuf); + ::stat(pty()->ttyName(), &sbuf); + if (writeable) + chmod(pty()->ttyName(), sbuf.st_mode | S_IWGRP); + else + chmod(pty()->ttyName(), sbuf.st_mode & ~(S_IWGRP|S_IWOTH)); +} + +Pty::Pty(int masterFd, QObject* parent) + : KPtyProcess(masterFd,parent) +{ + init(); +} +Pty::Pty(QObject* parent) + : KPtyProcess(parent) +{ + init(); +} +void Pty::init() +{ + _windowColumns = 0; + _windowLines = 0; + _eraseChar = 0; + _xonXoff = true; + _utf8 =true; + + connect(pty(), SIGNAL(readyRead()) , this , SLOT(dataReceived())); + setPtyChannels(KPtyProcess::AllChannels); +} + +Pty::~Pty() +{ +} + +void Pty::sendData(const char* data, int length) +{ + if (!length) + return; + + if (!pty()->write(data,length)) + { + kWarning() << "Pty::doSendJobs - Could not send input data to terminal process."; + return; + } +} + +void Pty::dataReceived() +{ + QByteArray data = pty()->readAll(); + emit receivedData(data.constData(),data.count()); +} + +void Pty::lockPty(bool lock) +{ + Q_UNUSED(lock); + +// TODO: Support for locking the Pty + //if (lock) + //suspend(); + //else + //resume(); +} + +int Pty::foregroundProcessGroup() const +{ + int pid = tcgetpgrp(pty()->masterFd()); + + if ( pid != -1 ) + { + return pid; + } + + return 0; +} + +void Pty::setupChildProcess() +{ + KPtyProcess::setupChildProcess(); + + // reset all signal handlers + // this ensures that terminal applications respond to + // signals generated via key sequences such as Ctrl+C + // (which sends SIGINT) + struct sigaction action; + sigemptyset(&action.sa_mask); + action.sa_handler = SIG_DFL; + action.sa_flags = 0; + for (int signal=1;signal < NSIG; signal++) + sigaction(signal,&action,0L); +} + + +#include "Pty.moc"
new file mode 100644 --- /dev/null +++ b/gui/konsole/Pty.h @@ -0,0 +1,209 @@ +/* + This file is part of Konsole, KDE's terminal emulator. + + Copyright 2007-2008 by Robert Knight <robertknight@gmail.com> + Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de> + + 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 PTY_H +#define PTY_H + +// Qt +#include <QtCore/QStringList> +#include <QtCore/QVector> +#include <QtCore/QList> +#include <QtCore/QSize> + +// KDE +//#include <KPtyProcess> +#include "kptyprocess.h" + +// Konsole +#include "konsole_export.h" + +namespace Konsole +{ + +/** + * The Pty class is used to start the terminal process, + * send data to it, receive data from it and manipulate + * various properties of the pseudo-teletype interface + * used to communicate with the process. + * + * To use this class, construct an instance and connect + * to the sendData slot and receivedData signal to + * send data to or receive data from the process. + * + * To start the terminal process, call the start() method + * with the program name and appropriate arguments. + */ +//class KONSOLEPRIVATE_EXPORT Pty: public KPtyProcess +class KONSOLEPRIVATE_EXPORT Pty: public KPtyProcess +{ +Q_OBJECT + + public: + + /** + * Constructs a new Pty. + * + * Connect to the sendData() slot and receivedData() signal to prepare + * for sending and receiving data from the terminal process. + * + * To start the terminal process, call the run() method with the + * name of the program to start and appropriate arguments. + */ + explicit Pty(QObject* parent = 0); + + /** + * Construct a process using an open pty master. + * See KPtyProcess::KPtyProcess() + */ + explicit Pty(int ptyMasterFd, QObject* parent = 0); + + ~Pty(); + + /** + * Starts the terminal process. + * + * Returns 0 if the process was started successfully or non-zero + * otherwise. + * + * @param program Path to the program to start + * @param arguments Arguments to pass to the program being started + * @param environment A list of key=value pairs which will be added + * to the environment for the new process. At the very least this + * should include an assignment for the TERM environment variable. + * @param winid Specifies the value of the WINDOWID environment variable + * in the process's environment. + * @param addToUtmp Specifies whether a utmp entry should be created for + * the pty used. See K3Process::setUsePty() + * @param dbusService Specifies the value of the KONSOLE_DBUS_SERVICE + * environment variable in the process's environment. + * @param dbusSession Specifies the value of the KONSOLE_DBUS_SESSION + * environment variable in the process's environment. + */ + int start( const QString& program, + const QStringList& arguments, + const QStringList& environment, + ulong winid, + bool addToUtmp, + const QString& dbusService, + const QString& dbusSession + ); + + /** TODO: Document me */ + void setWriteable(bool writeable); + + /** + * Enables or disables Xon/Xoff flow control. The flow control setting + * may be changed later by a terminal application, so flowControlEnabled() + * may not equal the value of @p on in the previous call to setFlowControlEnabled() + */ + void setFlowControlEnabled(bool on); + + /** Queries the terminal state and returns true if Xon/Xoff flow control is enabled. */ + bool flowControlEnabled() const; + + /** + * Sets the size of the window (in lines and columns of characters) + * used by this teletype. + */ + void setWindowSize(int lines, int cols); + + /** Returns the size of the window used by this teletype. See setWindowSize() */ + QSize windowSize() const; + + /** TODO Document me */ + void setErase(char erase); + + /** */ + char erase() const; + + /** + * Returns the process id of the teletype's current foreground + * process. This is the process which is currently reading + * input sent to the terminal via. sendData() + * + * If there is a problem reading the foreground process group, + * 0 will be returned. + */ + int foregroundProcessGroup() const; + + public slots: + + /** + * Put the pty into UTF-8 mode on systems which support it. + */ + void setUtf8Mode(bool on); + + /** + * Suspend or resume processing of data from the standard + * output of the terminal process. + * + * See K3Process::suspend() and K3Process::resume() + * + * @param lock If true, processing of output is suspended, + * otherwise processing is resumed. + */ + void lockPty(bool lock); + + /** + * Sends data to the process currently controlling the + * teletype ( whose id is returned by foregroundProcessGroup() ) + * + * @param buffer Pointer to the data to send. + * @param length Length of @p buffer. + */ + void sendData(const char* buffer, int length); + + signals: + + /** + * Emitted when a new block of data is received from + * the teletype. + * + * @param buffer Pointer to the data received. + * @param length Length of @p buffer + */ + void receivedData(const char* buffer, int length); + + protected: + void setupChildProcess(); + + private slots: + // called when data is received from the terminal process + void dataReceived(); + + private: + void init(); + + // takes a list of key=value pairs and adds them + // to the environment for the process + void addEnvironmentVariables(const QStringList& environment); + + int _windowColumns; + int _windowLines; + char _eraseChar; + bool _xonXoff; + bool _utf8; +}; + +} + +#endif // PTY_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/RemoteConnectionDialog.cpp @@ -0,0 +1,87 @@ +/* + Copyright 2007 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 "RemoteConnectionDialog.h" + +// Qt +#include <KDebug> + +// KDE +#include <KLocale> + +// Konsole +#include "SessionManager.h" + +#include "ui_RemoteConnectionDialog.h" + +using namespace Konsole; + +RemoteConnectionDialog::RemoteConnectionDialog(QWidget* parent) + : KDialog(parent) +{ + setCaption(i18n("New Remote Connection")); + setButtons( KDialog::Ok | KDialog::Cancel ); + setButtonText( KDialog::Ok , i18n("Connect") ); + + _ui = new Ui::RemoteConnectionDialog(); + _ui->setupUi(mainWidget()); + + // set initial UI state + _ui->userEdit->setFocus(Qt::OtherFocusReason); + + _ui->userEdit->setClearButtonShown(true); + _ui->hostEdit->setClearButtonShown(true); +} +RemoteConnectionDialog::~RemoteConnectionDialog() +{ + delete _ui; +} +QString RemoteConnectionDialog::user() const +{ + return _ui->userEdit->text(); +} +QString RemoteConnectionDialog::host() const +{ + return _ui->hostEdit->text(); +} +QString RemoteConnectionDialog::service() const +{ + return "ssh"; +} + +QString RemoteConnectionDialog::sessionKey() const +{ + // SessionManager* manager = SessionManager::instance(); + + /*MutableSessionInfo* customSession = + new MutableSessionInfo(manager->defaultSessionType()->path()); + customSession->setCommand( service() ); + customSession->setName( i18n("%1 at %2",user(),host()) ); + customSession->setArguments( QStringList() << customSession->command(true,true) << + user() + '@' + host() ); +*/ + //QString key = manager->addSessionType( customSession ); + + //kDebug() << "session key = " << key; + + return QString(); + //return key; +} +
new file mode 100644 --- /dev/null +++ b/gui/konsole/RemoteConnectionDialog.h @@ -0,0 +1,52 @@ +/* + Copyright 2007-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. +*/ + +#ifndef REMOTECONNECTIONDIALOG_H +#define REMOTECONNECTIONDIALOG_H + +// KDE +#include <KDialog> + +namespace Ui +{ + class RemoteConnectionDialog; +} + +namespace Konsole +{ + +class RemoteConnectionDialog : public KDialog +{ +public: + RemoteConnectionDialog(QWidget* parent = 0 ); + ~RemoteConnectionDialog(); + + QString user() const; + QString host() const; + QString service() const; + + QString sessionKey() const; + +private: + Ui::RemoteConnectionDialog* _ui; +}; + +} + +#endif // REMOTECONNECTIONDIALOG_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/RenameTabsDialog.cpp @@ -0,0 +1,98 @@ +/* + Copyright 2010 by Kurt Hindenburg <kurt.hindenburg@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 "RenameTabsDialog.h" + +// Konsole +#include "ui_RenameTabsDialog.h" + +using namespace Konsole; + +RenameTabsDialog::RenameTabsDialog(QWidget* parent) +: KDialog(parent) +{ + setCaption(i18n("Rename Tab")); + setButtons( KDialog::Ok | KDialog::Cancel ); + + _ui = new Ui::RenameTabsDialog(); + _ui->setupUi(mainWidget()); + _ui->tabTitleEdit->setClearButtonShown(true); + _ui->remoteTabTitleEdit->setClearButtonShown(true); + + // menus for local and remote tab title dynamic elements + TabTitleFormatAction* localTabTitleAction = new TabTitleFormatAction(this); + localTabTitleAction->setContext(Session::LocalTabTitle); + _ui->tabTitleEditButton->setMenu(localTabTitleAction->menu()); + connect(localTabTitleAction, SIGNAL(dynamicElementSelected(const QString&)), + this, SLOT(insertTabTitleText(const QString&))); + + TabTitleFormatAction* remoteTabTitleAction = new TabTitleFormatAction(this); + remoteTabTitleAction->setContext(Session::RemoteTabTitle); + _ui->remoteTabTitleEditButton->setMenu(remoteTabTitleAction->menu()); + connect(remoteTabTitleAction, SIGNAL(dynamicElementSelected(const QString&)), + this, SLOT(insertRemoteTabTitleText(const QString&))); + +} + +RenameTabsDialog::~RenameTabsDialog() +{ + delete _ui; +} + +void RenameTabsDialog::focusTabTitleText() +{ + _ui->tabTitleEdit->setFocus(); +} + +void RenameTabsDialog::focusRemoteTabTitleText() +{ + _ui->remoteTabTitleEdit->setFocus(); +} + +void RenameTabsDialog::setTabTitleText(const QString& text) +{ + _ui->tabTitleEdit->setText(text); +} + +void RenameTabsDialog::setRemoteTabTitleText(const QString& text) +{ + _ui->remoteTabTitleEdit->setText(text); +} + +QString RenameTabsDialog::tabTitleText() const +{ + return(_ui->tabTitleEdit->text()); +} + +QString RenameTabsDialog::remoteTabTitleText() const +{ + return(_ui->remoteTabTitleEdit->text()); +} + +void RenameTabsDialog::insertTabTitleText(const QString& text) +{ + _ui->tabTitleEdit->insert(text); +} + +void RenameTabsDialog::insertRemoteTabTitleText(const QString& text) +{ + _ui->remoteTabTitleEdit->insert(text); +} +
new file mode 100644 --- /dev/null +++ b/gui/konsole/RenameTabsDialog.h @@ -0,0 +1,65 @@ +/* + Copyright 2010 by Kurt Hindenburg <kurt.hindenburg@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. +*/ + +#ifndef RENAMETABSDIALOG +#define RENAMETABSDIALOG + +// Qt +#include <QPointer> + +// KDE +#include <KDialog> + +#include "Profile.h" +#include "TabTitleFormatAction.h" + +namespace Ui +{ + class RenameTabsDialog; +} + +namespace Konsole +{ + +class RenameTabsDialog : public KDialog +{ +Q_OBJECT + +public: + RenameTabsDialog(QWidget* parent = 0); + ~RenameTabsDialog(); + QString tabTitleText() const; + QString remoteTabTitleText() const; + void setTabTitleText(const QString&); + void setRemoteTabTitleText(const QString&); + + void focusTabTitleText(); + void focusRemoteTabTitleText(); + +public slots: + void insertTabTitleText(const QString& text); + void insertRemoteTabTitleText(const QString& text); + +private: + Ui::RenameTabsDialog* _ui; +}; + +} + +#endif
new file mode 100644 --- /dev/null +++ b/gui/konsole/Screen.cpp @@ -0,0 +1,1360 @@ +/* + This file is part of Konsole, an X terminal. + + Copyright 2007-2008 by Robert Knight <robert.knight@gmail.com> + Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de> + + 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 "Screen.h" + +// Standard +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <assert.h> +#include <string.h> +#include <ctype.h> + +// Qt +#include <QtCore/QTextStream> +#include <QtCore/QDate> + +// KDE +//#include <kdebug.h> + +// Konsole +#include "konsole_wcwidth.h" +#include "TerminalCharacterDecoder.h" + +using namespace Konsole; + +//FIXME: this is emulation specific. Use false for xterm, true for ANSI. +//FIXME: see if we can get this from terminfo. +#define BS_CLEARS false + +//Macro to convert x,y position on screen to position within an image. +// +//Originally the image was stored as one large contiguous block of +//memory, so a position within the image could be represented as an +//offset from the beginning of the block. For efficiency reasons this +//is no longer the case. +//Many internal parts of this class still use this representation for parameters and so on, +//notably moveImage() and clearImage(). +//This macro converts from an X,Y position into an image offset. +#ifndef loc +#define loc(X,Y) ((Y)*columns+(X)) +#endif + + +Character Screen::defaultChar = Character(' ', + CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR), + CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR), + DEFAULT_RENDITION); + +//#define REVERSE_WRAPPED_LINES // for wrapped line debug + + Screen::Screen(int l, int c) +: lines(l), + columns(c), + screenLines(new ImageLine[lines+1] ), + _scrolledLines(0), + _droppedLines(0), + history(new HistoryScrollNone()), + cuX(0), cuY(0), + currentRendition(0), + _topMargin(0), _bottomMargin(0), + selBegin(0), selTopLeft(0), selBottomRight(0), + blockSelectionMode(false), + effectiveForeground(CharacterColor()), effectiveBackground(CharacterColor()), effectiveRendition(0), + lastPos(-1) +{ + lineProperties.resize(lines+1); + for (int i=0;i<lines+1;i++) + lineProperties[i]=LINE_DEFAULT; + + initTabStops(); + clearSelection(); + reset(); +} + +/*! Destructor +*/ + +Screen::~Screen() +{ + delete[] screenLines; + delete history; +} + +void Screen::cursorUp(int n) + //=CUU +{ + if (n == 0) n = 1; // Default + int stop = cuY < _topMargin ? 0 : _topMargin; + cuX = qMin(columns-1,cuX); // nowrap! + cuY = qMax(stop,cuY-n); +} + +void Screen::cursorDown(int n) + //=CUD +{ + if (n == 0) n = 1; // Default + int stop = cuY > _bottomMargin ? lines-1 : _bottomMargin; + cuX = qMin(columns-1,cuX); // nowrap! + cuY = qMin(stop,cuY+n); +} + +void Screen::cursorLeft(int n) + //=CUB +{ + if (n == 0) n = 1; // Default + cuX = qMin(columns-1,cuX); // nowrap! + cuX = qMax(0,cuX-n); +} + +void Screen::cursorRight(int n) + //=CUF +{ + if (n == 0) n = 1; // Default + cuX = qMin(columns-1,cuX+n); +} + +void Screen::setMargins(int top, int bot) + //=STBM +{ + if (top == 0) top = 1; // Default + if (bot == 0) bot = lines; // Default + top = top - 1; // Adjust to internal lineno + bot = bot - 1; // Adjust to internal lineno + if ( !( 0 <= top && top < bot && bot < lines ) ) + { //Debug()<<" setRegion("<<top<<","<<bot<<") : bad range."; + return; // Default error action: ignore + } + _topMargin = top; + _bottomMargin = bot; + cuX = 0; + cuY = getMode(MODE_Origin) ? top : 0; + +} + +int Screen::topMargin() const +{ + return _topMargin; +} +int Screen::bottomMargin() const +{ + return _bottomMargin; +} + +void Screen::index() + //=IND +{ + if (cuY == _bottomMargin) + scrollUp(1); + else if (cuY < lines-1) + cuY += 1; +} + +void Screen::reverseIndex() + //=RI +{ + if (cuY == _topMargin) + scrollDown(_topMargin,1); + else if (cuY > 0) + cuY -= 1; +} + +void Screen::nextLine() + //=NEL +{ + toStartOfLine(); index(); +} + +void Screen::eraseChars(int n) +{ + if (n == 0) n = 1; // Default + int p = qMax(0,qMin(cuX+n-1,columns-1)); + clearImage(loc(cuX,cuY),loc(p,cuY),' '); +} + +void Screen::deleteChars(int n) +{ + Q_ASSERT( n >= 0 ); + + // always delete at least one char + if (n == 0) + n = 1; + + // if cursor is beyond the end of the line there is nothing to do + if ( cuX >= screenLines[cuY].count() ) + return; + + if ( cuX+n > screenLines[cuY].count() ) + n = screenLines[cuY].count() - cuX; + + Q_ASSERT( n >= 0 ); + Q_ASSERT( cuX+n <= screenLines[cuY].count() ); + + screenLines[cuY].remove(cuX,n); +} + +void Screen::insertChars(int n) +{ + if (n == 0) n = 1; // Default + + if ( screenLines[cuY].size() < cuX ) + screenLines[cuY].resize(cuX); + + screenLines[cuY].insert(cuX,n,' '); + + if ( screenLines[cuY].count() > columns ) + screenLines[cuY].resize(columns); +} + +void Screen::deleteLines(int n) +{ + if (n == 0) n = 1; // Default + scrollUp(cuY,n); +} + +void Screen::insertLines(int n) +{ + if (n == 0) n = 1; // Default + scrollDown(cuY,n); +} + +void Screen::setMode(int m) +{ + currentModes[m] = true; + switch(m) + { + case MODE_Origin : cuX = 0; cuY = _topMargin; break; //FIXME: home + } +} + +void Screen::resetMode(int m) +{ + currentModes[m] = false; + switch(m) + { + case MODE_Origin : cuX = 0; cuY = 0; break; //FIXME: home + } +} + +void Screen::saveMode(int m) +{ + savedModes[m] = currentModes[m]; +} + +void Screen::restoreMode(int m) +{ + currentModes[m] = savedModes[m]; +} + +bool Screen::getMode(int m) const +{ + return currentModes[m]; +} + +void Screen::saveCursor() +{ + savedState.cursorColumn = cuX; + savedState.cursorLine = cuY; + savedState.rendition = currentRendition; + savedState.foreground = currentForeground; + savedState.background = currentBackground; +} + +void Screen::restoreCursor() +{ + cuX = qMin(savedState.cursorColumn,columns-1); + cuY = qMin(savedState.cursorLine,lines-1); + currentRendition = savedState.rendition; + currentForeground = savedState.foreground; + currentBackground = savedState.background; + updateEffectiveRendition(); +} + +void Screen::resizeImage(int new_lines, int new_columns) +{ + if ((new_lines==lines) && (new_columns==columns)) return; + + if (cuY > new_lines-1) + { // attempt to preserve focus and lines + _bottomMargin = lines-1; //FIXME: margin lost + for (int i = 0; i < cuY-(new_lines-1); i++) + { + addHistLine(); scrollUp(0,1); + } + } + + // create new screen lines and copy from old to new + + ImageLine* newScreenLines = new ImageLine[new_lines+1]; + for (int i=0; i < qMin(lines-1,new_lines+1) ;i++) + newScreenLines[i]=screenLines[i]; + for (int i=lines;(i > 0) && (i<new_lines+1);i++) + newScreenLines[i].resize( new_columns ); + + lineProperties.resize(new_lines+1); + for (int i=lines;(i > 0) && (i<new_lines+1);i++) + lineProperties[i] = LINE_DEFAULT; + + clearSelection(); + + delete[] screenLines; + screenLines = newScreenLines; + + lines = new_lines; + columns = new_columns; + cuX = qMin(cuX,columns-1); + cuY = qMin(cuY,lines-1); + + // FIXME: try to keep values, evtl. + _topMargin=0; + _bottomMargin=lines-1; + initTabStops(); + clearSelection(); +} + +void Screen::setDefaultMargins() +{ + _topMargin = 0; + _bottomMargin = lines-1; +} + + +/* + Clarifying rendition here and in the display. + + currently, the display's color table is + 0 1 2 .. 9 10 .. 17 + dft_fg, dft_bg, dim 0..7, intensive 0..7 + + currentForeground, currentBackground contain values 0..8; + - 0 = default color + - 1..8 = ansi specified color + + re_fg, re_bg contain values 0..17 + due to the TerminalDisplay's color table + + rendition attributes are + + attr widget screen + -------------- ------ ------ + RE_UNDERLINE XX XX affects foreground only + RE_BLINK XX XX affects foreground only + RE_BOLD XX XX affects foreground only + RE_REVERSE -- XX + RE_TRANSPARENT XX -- affects background only + RE_INTENSIVE XX -- affects foreground only + + Note that RE_BOLD is used in both widget + and screen rendition. Since xterm/vt102 + is to poor to distinguish between bold + (which is a font attribute) and intensive + (which is a color attribute), we translate + this and RE_BOLD in falls eventually appart + into RE_BOLD and RE_INTENSIVE. + */ + +void Screen::reverseRendition(Character& p) const +{ + CharacterColor f = p.foregroundColor; + CharacterColor b = p.backgroundColor; + + p.foregroundColor = b; + p.backgroundColor = f; //p->r &= ~RE_TRANSPARENT; +} + +void Screen::updateEffectiveRendition() +{ + effectiveRendition = currentRendition; + if (currentRendition & RE_REVERSE) + { + effectiveForeground = currentBackground; + effectiveBackground = currentForeground; + } + else + { + effectiveForeground = currentForeground; + effectiveBackground = currentBackground; + } + + if (currentRendition & RE_BOLD) + effectiveForeground.toggleIntensive(); +} + +void Screen::copyFromHistory(Character* dest, int startLine, int count) const +{ + Q_ASSERT( startLine >= 0 && count > 0 && startLine + count <= history->getLines() ); + + for (int line = startLine; line < startLine + count; line++) + { + const int length = qMin(columns,history->getLineLen(line)); + const int destLineOffset = (line-startLine)*columns; + + history->getCells(line,0,length,dest + destLineOffset); + + for (int column = length; column < columns; column++) + dest[destLineOffset+column] = defaultChar; + + // invert selected text + if (selBegin !=-1) + { + for (int column = 0; column < columns; column++) + { + if (isSelected(column,line)) + { + reverseRendition(dest[destLineOffset + column]); + } + } + } + } +} + +void Screen::copyFromScreen(Character* dest , int startLine , int count) const +{ + Q_ASSERT( startLine >= 0 && count > 0 && startLine + count <= lines ); + + for (int line = startLine; line < (startLine+count) ; line++) + { + int srcLineStartIndex = line*columns; + int destLineStartIndex = (line-startLine)*columns; + + for (int column = 0; column < columns; column++) + { + int srcIndex = srcLineStartIndex + column; + int destIndex = destLineStartIndex + column; + + dest[destIndex] = screenLines[srcIndex/columns].value(srcIndex%columns,defaultChar); + + // invert selected text + if (selBegin != -1 && isSelected(column,line + history->getLines())) + reverseRendition(dest[destIndex]); + } + + } +} + +void Screen::getImage( Character* dest, int size, int startLine, int endLine ) const +{ + Q_ASSERT( startLine >= 0 ); + Q_ASSERT( endLine >= startLine && endLine < history->getLines() + lines ); + + const int mergedLines = endLine - startLine + 1; + + Q_ASSERT( size >= mergedLines * columns ); + Q_UNUSED( size ); + + const int linesInHistoryBuffer = qBound(0,history->getLines()-startLine,mergedLines); + const int linesInScreenBuffer = mergedLines - linesInHistoryBuffer; + + // copy lines from history buffer + if (linesInHistoryBuffer > 0) + copyFromHistory(dest,startLine,linesInHistoryBuffer); + + // copy lines from screen buffer + if (linesInScreenBuffer > 0) + copyFromScreen(dest + linesInHistoryBuffer*columns, + startLine + linesInHistoryBuffer - history->getLines(), + linesInScreenBuffer); + + // invert display when in screen mode + if (getMode(MODE_Screen)) + { + for (int i = 0; i < mergedLines*columns; i++) + reverseRendition(dest[i]); // for reverse display + } + + // mark the character at the current cursor position + int cursorIndex = loc(cuX, cuY + linesInHistoryBuffer); + if(getMode(MODE_Cursor) && cursorIndex < columns*mergedLines) + dest[cursorIndex].rendition |= RE_CURSOR; +} + +QVector<LineProperty> Screen::getLineProperties( int startLine , int endLine ) const +{ + Q_ASSERT( startLine >= 0 ); + Q_ASSERT( endLine >= startLine && endLine < history->getLines() + lines ); + + const int mergedLines = endLine-startLine+1; + const int linesInHistory = qBound(0,history->getLines()-startLine,mergedLines); + const int linesInScreen = mergedLines - linesInHistory; + + QVector<LineProperty> result(mergedLines); + int index = 0; + + // copy properties for lines in history + for (int line = startLine; line < startLine + linesInHistory; line++) + { + //TODO Support for line properties other than wrapped lines + if (history->isWrappedLine(line)) + { + result[index] = (LineProperty)(result[index] | LINE_WRAPPED); + } + index++; + } + + // copy properties for lines in screen buffer + const int firstScreenLine = startLine + linesInHistory - history->getLines(); + for (int line = firstScreenLine; line < firstScreenLine+linesInScreen; line++) + { + result[index]=lineProperties[line]; + index++; + } + + return result; +} + +void Screen::reset(bool clearScreen) +{ + setMode(MODE_Wrap ); saveMode(MODE_Wrap ); // wrap at end of margin + resetMode(MODE_Origin); saveMode(MODE_Origin); // position refere to [1,1] + resetMode(MODE_Insert); saveMode(MODE_Insert); // overstroke + setMode(MODE_Cursor); // cursor visible + resetMode(MODE_Screen); // screen not inverse + resetMode(MODE_NewLine); + + _topMargin=0; + _bottomMargin=lines-1; + + setDefaultRendition(); + saveCursor(); + + if ( clearScreen ) + clear(); +} + +void Screen::clear() +{ + clearEntireScreen(); + home(); +} + +void Screen::backspace() +{ + cuX = qMin(columns-1,cuX); // nowrap! + cuX = qMax(0,cuX-1); + + if (screenLines[cuY].size() < cuX+1) + screenLines[cuY].resize(cuX+1); + + if (BS_CLEARS) + screenLines[cuY][cuX].character = ' '; +} + +void Screen::tab(int n) +{ + // note that TAB is a format effector (does not write ' '); + if (n == 0) n = 1; + while((n > 0) && (cuX < columns-1)) + { + cursorRight(1); + while((cuX < columns-1) && !tabStops[cuX]) + cursorRight(1); + n--; + } +} + +void Screen::backtab(int n) +{ + // note that TAB is a format effector (does not write ' '); + if (n == 0) n = 1; + while((n > 0) && (cuX > 0)) + { + cursorLeft(1); while((cuX > 0) && !tabStops[cuX]) cursorLeft(1); + n--; + } +} + +void Screen::clearTabStops() +{ + for (int i = 0; i < columns; i++) tabStops[i] = false; +} + +void Screen::changeTabStop(bool set) +{ + if (cuX >= columns) return; + tabStops[cuX] = set; +} + +void Screen::initTabStops() +{ + tabStops.resize(columns); + + // Arrg! The 1st tabstop has to be one longer than the other. + // i.e. the kids start counting from 0 instead of 1. + // Other programs might behave correctly. Be aware. + for (int i = 0; i < columns; i++) + tabStops[i] = (i%8 == 0 && i != 0); +} + +void Screen::newLine() +{ + if (getMode(MODE_NewLine)) + toStartOfLine(); + index(); +} + +void Screen::checkSelection(int from, int to) +{ + if (selBegin == -1) + return; + int scr_TL = loc(0, history->getLines()); + //Clear entire selection if it overlaps region [from, to] + if ( (selBottomRight >= (from+scr_TL)) && (selTopLeft <= (to+scr_TL)) ) + clearSelection(); +} + +void Screen::displayCharacter(unsigned short c) +{ + // Note that VT100 does wrapping BEFORE putting the character. + // This has impact on the assumption of valid cursor positions. + // We indicate the fact that a newline has to be triggered by + // putting the cursor one right to the last column of the screen. + + int w = konsole_wcwidth(c); + if (w <= 0) + return; + + if (cuX+w > columns) { + if (getMode(MODE_Wrap)) { + lineProperties[cuY] = (LineProperty)(lineProperties[cuY] | LINE_WRAPPED); + nextLine(); + } + else + cuX = columns-w; + } + + // ensure current line vector has enough elements + int size = screenLines[cuY].size(); + if (size < cuX+w) + { + screenLines[cuY].resize(cuX+w); + } + + if (getMode(MODE_Insert)) insertChars(w); + + lastPos = loc(cuX,cuY); + + // check if selection is still valid. + checkSelection(lastPos, lastPos); + + Character& currentChar = screenLines[cuY][cuX]; + + currentChar.character = c; + currentChar.foregroundColor = effectiveForeground; + currentChar.backgroundColor = effectiveBackground; + currentChar.rendition = effectiveRendition; + + int i = 0; + int newCursorX = cuX + w--; + while(w) + { + i++; + + if ( screenLines[cuY].size() < cuX + i + 1 ) + screenLines[cuY].resize(cuX+i+1); + + Character& ch = screenLines[cuY][cuX + i]; + ch.character = 0; + ch.foregroundColor = effectiveForeground; + ch.backgroundColor = effectiveBackground; + ch.rendition = effectiveRendition; + + w--; + } + cuX = newCursorX; +} + +void Screen::compose(const QString& /*compose*/) +{ + Q_ASSERT( 0 /*Not implemented yet*/ ); + + /* if (lastPos == -1) + return; + + QChar c(image[lastPos].character); + compose.prepend(c); + //compose.compose(); ### FIXME! + image[lastPos].character = compose[0].unicode();*/ +} + +int Screen::scrolledLines() const +{ + return _scrolledLines; +} +int Screen::droppedLines() const +{ + return _droppedLines; +} +void Screen::resetDroppedLines() +{ + _droppedLines = 0; +} +void Screen::resetScrolledLines() +{ + _scrolledLines = 0; +} + +void Screen::scrollUp(int n) +{ + if (n == 0) n = 1; // Default + if (_topMargin == 0) addHistLine(); // history.history + scrollUp(_topMargin, n); +} + +QRect Screen::lastScrolledRegion() const +{ + return _lastScrolledRegion; +} + +void Screen::scrollUp(int from, int n) +{ + if (n <= 0 || from + n > _bottomMargin) return; + + _scrolledLines -= n; + _lastScrolledRegion = QRect(0,_topMargin,columns-1,(_bottomMargin-_topMargin)); + + //FIXME: make sure `topMargin', `bottomMargin', `from', `n' is in bounds. + moveImage(loc(0,from),loc(0,from+n),loc(columns-1,_bottomMargin)); + clearImage(loc(0,_bottomMargin-n+1),loc(columns-1,_bottomMargin),' '); +} + +void Screen::scrollDown(int n) +{ + if (n == 0) n = 1; // Default + scrollDown(_topMargin, n); +} + +void Screen::scrollDown(int from, int n) +{ + _scrolledLines += n; + + //FIXME: make sure `topMargin', `bottomMargin', `from', `n' is in bounds. + if (n <= 0) + return; + if (from > _bottomMargin) + return; + if (from + n > _bottomMargin) + n = _bottomMargin - from; + moveImage(loc(0,from+n),loc(0,from),loc(columns-1,_bottomMargin-n)); + clearImage(loc(0,from),loc(columns-1,from+n-1),' '); +} + +void Screen::setCursorYX(int y, int x) +{ + setCursorY(y); setCursorX(x); +} + +void Screen::setCursorX(int x) +{ + if (x == 0) x = 1; // Default + x -= 1; // Adjust + cuX = qMax(0,qMin(columns-1, x)); +} + +void Screen::setCursorY(int y) +{ + if (y == 0) y = 1; // Default + y -= 1; // Adjust + cuY = qMax(0,qMin(lines -1, y + (getMode(MODE_Origin) ? _topMargin : 0) )); +} + +void Screen::home() +{ + cuX = 0; + cuY = 0; +} + +void Screen::toStartOfLine() +{ + cuX = 0; +} + +int Screen::getCursorX() const +{ + return cuX; +} + +int Screen::getCursorY() const +{ + return cuY; +} + +void Screen::clearImage(int loca, int loce, char c) +{ + int scr_TL=loc(0,history->getLines()); + //FIXME: check positions + + //Clear entire selection if it overlaps region to be moved... + if ( (selBottomRight > (loca+scr_TL) )&&(selTopLeft < (loce+scr_TL)) ) + { + clearSelection(); + } + + int topLine = loca/columns; + int bottomLine = loce/columns; + + Character clearCh(c,currentForeground,currentBackground,DEFAULT_RENDITION); + + //if the character being used to clear the area is the same as the + //default character, the affected lines can simply be shrunk. + bool isDefaultCh = (clearCh == Character()); + + for (int y=topLine;y<=bottomLine;y++) + { + lineProperties[y] = 0; + + int endCol = ( y == bottomLine) ? loce%columns : columns-1; + int startCol = ( y == topLine ) ? loca%columns : 0; + + QVector<Character>& line = screenLines[y]; + + if ( isDefaultCh && endCol == columns-1 ) + { + line.resize(startCol); + } + else + { + if (line.size() < endCol + 1) + line.resize(endCol+1); + + Character* data = line.data(); + for (int i=startCol;i<=endCol;i++) + data[i]=clearCh; + } + } +} + +void Screen::moveImage(int dest, int sourceBegin, int sourceEnd) +{ + Q_ASSERT( sourceBegin <= sourceEnd ); + + int lines=(sourceEnd-sourceBegin)/columns; + + //move screen image and line properties: + //the source and destination areas of the image may overlap, + //so it matters that we do the copy in the right order - + //forwards if dest < sourceBegin or backwards otherwise. + //(search the web for 'memmove implementation' for details) + if (dest < sourceBegin) + { + for (int i=0;i<=lines;i++) + { + screenLines[ (dest/columns)+i ] = screenLines[ (sourceBegin/columns)+i ]; + lineProperties[(dest/columns)+i]=lineProperties[(sourceBegin/columns)+i]; + } + } + else + { + for (int i=lines;i>=0;i--) + { + screenLines[ (dest/columns)+i ] = screenLines[ (sourceBegin/columns)+i ]; + lineProperties[(dest/columns)+i]=lineProperties[(sourceBegin/columns)+i]; + } + } + + if (lastPos != -1) + { + int diff = dest - sourceBegin; // Scroll by this amount + lastPos += diff; + if ((lastPos < 0) || (lastPos >= (lines*columns))) + lastPos = -1; + } + + // Adjust selection to follow scroll. + if (selBegin != -1) + { + bool beginIsTL = (selBegin == selTopLeft); + int diff = dest - sourceBegin; // Scroll by this amount + int scr_TL=loc(0,history->getLines()); + int srca = sourceBegin+scr_TL; // Translate index from screen to global + int srce = sourceEnd+scr_TL; // Translate index from screen to global + int desta = srca+diff; + int deste = srce+diff; + + if ((selTopLeft >= srca) && (selTopLeft <= srce)) + selTopLeft += diff; + else if ((selTopLeft >= desta) && (selTopLeft <= deste)) + selBottomRight = -1; // Clear selection (see below) + + if ((selBottomRight >= srca) && (selBottomRight <= srce)) + selBottomRight += diff; + else if ((selBottomRight >= desta) && (selBottomRight <= deste)) + selBottomRight = -1; // Clear selection (see below) + + if (selBottomRight < 0) + { + clearSelection(); + } + else + { + if (selTopLeft < 0) + selTopLeft = 0; + } + + if (beginIsTL) + selBegin = selTopLeft; + else + selBegin = selBottomRight; + } +} + +void Screen::clearToEndOfScreen() +{ + clearImage(loc(cuX,cuY),loc(columns-1,lines-1),' '); +} + +void Screen::clearToBeginOfScreen() +{ + clearImage(loc(0,0),loc(cuX,cuY),' '); +} + +void Screen::clearEntireScreen() +{ + // Add entire screen to history + for (int i = 0; i < (lines-1); i++) + { + addHistLine(); scrollUp(0,1); + } + + clearImage(loc(0,0),loc(columns-1,lines-1),' '); +} + +/*! fill screen with 'E' + This is to aid screen alignment + */ + +void Screen::helpAlign() +{ + clearImage(loc(0,0),loc(columns-1,lines-1),'E'); +} + +void Screen::clearToEndOfLine() +{ + clearImage(loc(cuX,cuY),loc(columns-1,cuY),' '); +} + +void Screen::clearToBeginOfLine() +{ + clearImage(loc(0,cuY),loc(cuX,cuY),' '); +} + +void Screen::clearEntireLine() +{ + clearImage(loc(0,cuY),loc(columns-1,cuY),' '); +} + +void Screen::setRendition(int re) +{ + currentRendition |= re; + updateEffectiveRendition(); +} + +void Screen::resetRendition(int re) +{ + currentRendition &= ~re; + updateEffectiveRendition(); +} + +void Screen::setDefaultRendition() +{ + setForeColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR); + setBackColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR); + currentRendition = DEFAULT_RENDITION; + updateEffectiveRendition(); +} + +void Screen::setForeColor(int space, int color) +{ + currentForeground = CharacterColor(space, color); + + if ( currentForeground.isValid() ) + updateEffectiveRendition(); + else + setForeColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR); +} + +void Screen::setBackColor(int space, int color) +{ + currentBackground = CharacterColor(space, color); + + if ( currentBackground.isValid() ) + updateEffectiveRendition(); + else + setBackColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR); +} + +void Screen::clearSelection() +{ + selBottomRight = -1; + selTopLeft = -1; + selBegin = -1; +} + +void Screen::getSelectionStart(int& column , int& line) const +{ + if ( selTopLeft != -1 ) + { + column = selTopLeft % columns; + line = selTopLeft / columns; + } + else + { + column = cuX + getHistLines(); + line = cuY + getHistLines(); + } +} +void Screen::getSelectionEnd(int& column , int& line) const +{ + if ( selBottomRight != -1 ) + { + column = selBottomRight % columns; + line = selBottomRight / columns; + } + else + { + column = cuX + getHistLines(); + line = cuY + getHistLines(); + } +} +void Screen::setSelectionStart(const int x, const int y, const bool mode) +{ + selBegin = loc(x,y); + /* FIXME, HACK to correct for x too far to the right... */ + if (x == columns) selBegin--; + + selBottomRight = selBegin; + selTopLeft = selBegin; + blockSelectionMode = mode; +} + +void Screen::setSelectionEnd( const int x, const int y) +{ + if (selBegin == -1) + return; + + int endPos = loc(x,y); + + if (endPos < selBegin) + { + selTopLeft = endPos; + selBottomRight = selBegin; + } + else + { + /* FIXME, HACK to correct for x too far to the right... */ + if (x == columns) + endPos--; + + selTopLeft = selBegin; + selBottomRight = endPos; + } + + // Normalize the selection in column mode + if (blockSelectionMode) + { + int topRow = selTopLeft / columns; + int topColumn = selTopLeft % columns; + int bottomRow = selBottomRight / columns; + int bottomColumn = selBottomRight % columns; + + selTopLeft = loc(qMin(topColumn,bottomColumn),topRow); + selBottomRight = loc(qMax(topColumn,bottomColumn),bottomRow); + } +} + +bool Screen::isSelected( const int x,const int y) const +{ + bool columnInSelection = true; + if (blockSelectionMode) + { + columnInSelection = x >= (selTopLeft % columns) && + x <= (selBottomRight % columns); + } + + int pos = loc(x,y); + return pos >= selTopLeft && pos <= selBottomRight && columnInSelection; +} + +QString Screen::selectedText(bool preserveLineBreaks) const +{ + QString result; + QTextStream stream(&result, QIODevice::ReadWrite); + + PlainTextDecoder decoder; + decoder.begin(&stream); + writeSelectionToStream(&decoder , preserveLineBreaks); + decoder.end(); + + return result; +} + +bool Screen::isSelectionValid() const +{ + return selTopLeft >= 0 && selBottomRight >= 0; +} + +void Screen::writeSelectionToStream(TerminalCharacterDecoder* decoder , + bool preserveLineBreaks) const +{ + if (!isSelectionValid()) + return; + writeToStream(decoder,selTopLeft,selBottomRight,preserveLineBreaks); +} + +void Screen::writeToStream(TerminalCharacterDecoder* decoder, + int startIndex, int endIndex, + bool preserveLineBreaks) const +{ + int top = startIndex / columns; + int left = startIndex % columns; + + int bottom = endIndex / columns; + int right = endIndex % columns; + + Q_ASSERT( top >= 0 && left >= 0 && bottom >= 0 && right >= 0 ); + + for (int y=top;y<=bottom;y++) + { + int start = 0; + if ( y == top || blockSelectionMode ) start = left; + + int count = -1; + if ( y == bottom || blockSelectionMode ) count = right - start + 1; + + const bool appendNewLine = ( y != bottom ); + int copied = copyLineToStream( y, + start, + count, + decoder, + appendNewLine, + preserveLineBreaks ); + + // if the selection goes beyond the end of the last line then + // append a new line character. + // + // this makes it possible to 'select' a trailing new line character after + // the text on a line. + if ( y == bottom && + copied < count ) + { + Character newLineChar('\n'); + decoder->decodeLine(&newLineChar,1,0); + } + } +} + +int Screen::copyLineToStream(int line , + int start, + int count, + TerminalCharacterDecoder* decoder, + bool appendNewLine, + bool preserveLineBreaks) const +{ + //buffer to hold characters for decoding + //the buffer is static to avoid initialising every + //element on each call to copyLineToStream + //(which is unnecessary since all elements will be overwritten anyway) + static const int MAX_CHARS = 1024; + static Character characterBuffer[MAX_CHARS]; + + assert( count < MAX_CHARS ); + + LineProperty currentLineProperties = 0; + + //determine if the line is in the history buffer or the screen image + if (line < history->getLines()) + { + const int lineLength = history->getLineLen(line); + + // ensure that start position is before end of line + start = qMin(start,qMax(0,lineLength-1)); + + // retrieve line from history buffer. It is assumed + // that the history buffer does not store trailing white space + // at the end of the line, so it does not need to be trimmed here + if (count == -1) + { + count = lineLength-start; + } + else + { + count = qMin(start+count,lineLength)-start; + } + + // safety checks + assert( start >= 0 ); + assert( count >= 0 ); + assert( (start+count) <= history->getLineLen(line) ); + + history->getCells(line,start,count,characterBuffer); + + if ( history->isWrappedLine(line) ) + currentLineProperties |= LINE_WRAPPED; + } + else + { + if ( count == -1 ) + count = columns - start; + + assert( count >= 0 ); + + const int screenLine = line-history->getLines(); + + Character* data = screenLines[screenLine].data(); + int length = screenLines[screenLine].count(); + + //retrieve line from screen image + for (int i=start;i < qMin(start+count,length);i++) + { + characterBuffer[i-start] = data[i]; + } + + // count cannot be any greater than length + count = qBound(0,count,length-start); + + Q_ASSERT( screenLine < lineProperties.count() ); + currentLineProperties |= lineProperties[screenLine]; + } + + // add new line character at end + const bool omitLineBreak = (currentLineProperties & LINE_WRAPPED) || + !preserveLineBreaks; + + if ( !omitLineBreak && appendNewLine && (count+1 < MAX_CHARS) ) + { + characterBuffer[count] = '\n'; + count++; + } + + //decode line and write to text stream + decoder->decodeLine( (Character*) characterBuffer , + count, currentLineProperties ); + + return count; +} + +void Screen::writeLinesToStream(TerminalCharacterDecoder* decoder, int fromLine, int toLine) const +{ + writeToStream(decoder,loc(0,fromLine),loc(columns-1,toLine)); +} + +void Screen::addHistLine() +{ + // add line to history buffer + // we have to take care about scrolling, too... + + if (hasScroll()) + { + int oldHistLines = history->getLines(); + + history->addCellsVector(screenLines[0]); + history->addLine( lineProperties[0] & LINE_WRAPPED ); + + int newHistLines = history->getLines(); + + bool beginIsTL = (selBegin == selTopLeft); + + // If the history is full, increment the count + // of dropped lines + if ( newHistLines == oldHistLines ) + _droppedLines++; + + // Adjust selection for the new point of reference + if (newHistLines > oldHistLines) + { + if (selBegin != -1) + { + selTopLeft += columns; + selBottomRight += columns; + } + } + + if (selBegin != -1) + { + // Scroll selection in history up + int top_BR = loc(0, 1+newHistLines); + + if (selTopLeft < top_BR) + selTopLeft -= columns; + + if (selBottomRight < top_BR) + selBottomRight -= columns; + + if (selBottomRight < 0) + clearSelection(); + else + { + if (selTopLeft < 0) + selTopLeft = 0; + } + + if (beginIsTL) + selBegin = selTopLeft; + else + selBegin = selBottomRight; + } + } + +} + +int Screen::getHistLines() const +{ + return history->getLines(); +} + +void Screen::setScroll(const HistoryType& t , bool copyPreviousScroll) +{ + clearSelection(); + + if ( copyPreviousScroll ) + history = t.scroll(history); + else + { + HistoryScroll* oldScroll = history; + history = t.scroll(0); + delete oldScroll; + } +} + +bool Screen::hasScroll() const +{ + return history->hasScroll(); +} + +const HistoryType& Screen::getScroll() const +{ + return history->getType(); +} + +void Screen::setLineProperty(LineProperty property , bool enable) +{ + if ( enable ) + lineProperties[cuY] = (LineProperty)(lineProperties[cuY] | property); + else + lineProperties[cuY] = (LineProperty)(lineProperties[cuY] & ~property); +} +void Screen::fillWithDefaultChar(Character* dest, int count) +{ + for (int i=0;i<count;i++) + dest[i] = defaultChar; +}
new file mode 100644 --- /dev/null +++ b/gui/konsole/Screen.h @@ -0,0 +1,675 @@ +/* + This file is part of Konsole, KDE's terminal. + + Copyright 2007-2008 by Robert Knight <robertknight@gmail.com> + Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de> + + 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 SCREEN_H +#define SCREEN_H + +// Qt +#include <QtCore/QRect> +#include <QtCore/QTextStream> +#include <QtCore/QVarLengthArray> + +// Konsole +#include "Character.h" +#include "History.h" + +#define MODE_Origin 0 +#define MODE_Wrap 1 +#define MODE_Insert 2 +#define MODE_Screen 3 +#define MODE_Cursor 4 +#define MODE_NewLine 5 +#define MODES_SCREEN 6 + +namespace Konsole +{ + +class TerminalCharacterDecoder; + +/** + \brief An image of characters with associated attributes. + + The terminal emulation ( Emulation ) receives a serial stream of + characters from the program currently running in the terminal. + From this stream it creates an image of characters which is ultimately + rendered by the display widget ( TerminalDisplay ). Some types of emulation + may have more than one screen image. + + getImage() is used to retrieve the currently visible image + which is then used by the display widget to draw the output from the + terminal. + + The number of lines of output history which are kept in addition to the current + screen image depends on the history scroll being used to store the output. + The scroll is specified using setScroll() + The output history can be retrieved using writeToStream() + + The screen image has a selection associated with it, specified using + setSelectionStart() and setSelectionEnd(). The selected text can be retrieved + using selectedText(). When getImage() is used to retrieve the visible image, + characters which are part of the selection have their colours inverted. +*/ +class Screen +{ +public: + /** Construct a new screen image of size @p lines by @p columns. */ + Screen(int lines, int columns); + ~Screen(); + + // VT100/2 Operations + // Cursor Movement + + /** + * Move the cursor up by @p n lines. The cursor will stop at the + * top margin. + */ + void cursorUp(int n); + /** + * Move the cursor down by @p n lines. The cursor will stop at the + * bottom margin. + */ + void cursorDown(int n); + /** + * Move the cursor to the left by @p n columns. + * The cursor will stop at the first column. + */ + void cursorLeft(int n); + /** + * Move the cursor to the right by @p n columns. + * The cursor will stop at the right-most column. + */ + void cursorRight(int n); + /** Position the cursor on line @p y. */ + void setCursorY(int y); + /** Position the cursor at column @p x. */ + void setCursorX(int x); + /** Position the cursor at line @p y, column @p x. */ + void setCursorYX(int y, int x); + /** + * Sets the margins for scrolling the screen. + * + * @param topLine The top line of the new scrolling margin. + * @param bottomLine The bottom line of the new scrolling margin. + */ + void setMargins(int topLine , int bottomLine); + /** Returns the top line of the scrolling region. */ + int topMargin() const; + /** Returns the bottom line of the scrolling region. */ + int bottomMargin() const; + + /** + * Resets the scrolling margins back to the top and bottom lines + * of the screen. + */ + void setDefaultMargins(); + + /** + * Moves the cursor down one line, if the MODE_NewLine mode + * flag is enabled then the cursor is returned to the leftmost + * column first. + * + * Equivalent to NextLine() if the MODE_NewLine flag is set + * or index() otherwise. + */ + void newLine(); + /** + * Moves the cursor down one line and positions it at the beginning + * of the line. Equivalent to calling Return() followed by index() + */ + void nextLine(); + + /** + * Move the cursor down one line. If the cursor is on the bottom + * line of the scrolling region (as returned by bottomMargin()) the + * scrolling region is scrolled up by one line instead. + */ + void index(); + /** + * Move the cursor up one line. If the cursor is on the top line + * of the scrolling region (as returned by topMargin()) the scrolling + * region is scrolled down by one line instead. + */ + void reverseIndex(); + + /** + * Scroll the scrolling region of the screen up by @p n lines. + * The scrolling region is initially the whole screen, but can be changed + * using setMargins() + */ + void scrollUp(int n); + /** + * Scroll the scrolling region of the screen down by @p n lines. + * The scrolling region is initially the whole screen, but can be changed + * using setMargins() + */ + void scrollDown(int n); + /** + * Moves the cursor to the beginning of the current line. + * Equivalent to setCursorX(0) + */ + void toStartOfLine(); + /** + * Moves the cursor one column to the left and erases the character + * at the new cursor position. + */ + void backspace(); + /** Moves the cursor @p n tab-stops to the right. */ + void tab(int n = 1); + /** Moves the cursor @p n tab-stops to the left. */ + void backtab(int n); + + // Editing + + /** + * Erase @p n characters beginning from the current cursor position. + * This is equivalent to over-writing @p n characters starting with the current + * cursor position with spaces. + * If @p n is 0 then one character is erased. + */ + void eraseChars(int n); + /** + * Delete @p n characters beginning from the current cursor position. + * If @p n is 0 then one character is deleted. + */ + void deleteChars(int n); + /** + * Insert @p n blank characters beginning from the current cursor position. + * The position of the cursor is not altered. + * If @p n is 0 then one character is inserted. + */ + void insertChars(int n); + /** + * Removes @p n lines beginning from the current cursor position. + * The position of the cursor is not altered. + * If @p n is 0 then one line is removed. + */ + void deleteLines(int n); + /** + * Inserts @p lines beginning from the current cursor position. + * The position of the cursor is not altered. + * If @p n is 0 then one line is inserted. + */ + void insertLines(int n); + /** Clears all the tab stops. */ + void clearTabStops(); + /** Sets or removes a tab stop at the cursor's current column. */ + void changeTabStop(bool set); + + /** Resets (clears) the specified screen @p mode. */ + void resetMode(int mode); + /** Sets (enables) the specified screen @p mode. */ + void setMode(int mode); + /** + * Saves the state of the specified screen @p mode. It can be restored + * using restoreMode() + */ + void saveMode(int mode); + /** Restores the state of a screen @p mode saved by calling saveMode() */ + void restoreMode(int mode); + /** Returns whether the specified screen @p mode is enabled or not .*/ + bool getMode(int mode) const; + + /** + * Saves the current position and appearance (text color and style) of the cursor. + * It can be restored by calling restoreCursor() + */ + void saveCursor(); + /** Restores the position and appearance of the cursor. See saveCursor() */ + void restoreCursor(); + + /** Clear the whole screen, moving the current screen contents into the history first. */ + void clearEntireScreen(); + /** + * Clear the area of the screen from the current cursor position to the end of + * the screen. + */ + void clearToEndOfScreen(); + /** + * Clear the area of the screen from the current cursor position to the start + * of the screen. + */ + void clearToBeginOfScreen(); + /** Clears the whole of the line on which the cursor is currently positioned. */ + void clearEntireLine(); + /** Clears from the current cursor position to the end of the line. */ + void clearToEndOfLine(); + /** Clears from the current cursor position to the beginning of the line. */ + void clearToBeginOfLine(); + + /** Fills the entire screen with the letter 'E' */ + void helpAlign(); + + /** + * Enables the given @p rendition flag. Rendition flags control the appearance + * of characters on the screen. + * + * @see Character::rendition + */ + void setRendition(int rendition); + /** + * Disables the given @p rendition flag. Rendition flags control the appearance + * of characters on the screen. + * + * @see Character::rendition + */ + void resetRendition(int rendition); + + /** + * Sets the cursor's foreground color. + * @param space The color space used by the @p color argument + * @param color The new foreground color. The meaning of this depends on + * the color @p space used. + * + * @see CharacterColor + */ + void setForeColor(int space, int color); + /** + * Sets the cursor's background color. + * @param space The color space used by the @p color argumnet. + * @param color The new background color. The meaning of this depends on + * the color @p space used. + * + * @see CharacterColor + */ + void setBackColor(int space, int color); + /** + * Resets the cursor's color back to the default and sets the + * character's rendition flags back to the default settings. + */ + void setDefaultRendition(); + + /** Returns the column which the cursor is positioned at. */ + int getCursorX() const; + /** Returns the line which the cursor is positioned on. */ + int getCursorY() const; + + /** Clear the entire screen and move the cursor to the home position. + * Equivalent to calling clearEntireScreen() followed by home(). + */ + void clear(); + /** + * Sets the position of the cursor to the 'home' position at the top-left + * corner of the screen (0,0) + */ + void home(); + /** + * Resets the state of the screen. This resets the various screen modes + * back to their default states. The cursor style and colors are reset + * (as if setDefaultRendition() had been called) + * + * <ul> + * <li>Line wrapping is enabled.</li> + * <li>Origin mode is disabled.</li> + * <li>Insert mode is disabled.</li> + * <li>Cursor mode is enabled. TODO Document me</li> + * <li>Screen mode is disabled. TODO Document me</li> + * <li>New line mode is disabled. TODO Document me</li> + * </ul> + * + * If @p clearScreen is true then the screen contents are erased entirely, + * otherwise they are unaltered. + */ + void reset(bool clearScreen = true); + + /** + * Displays a new character at the current cursor position. + * + * If the cursor is currently positioned at the right-edge of the screen and + * line wrapping is enabled then the character is added at the start of a new + * line below the current one. + * + * If the MODE_Insert screen mode is currently enabled then the character + * is inserted at the current cursor position, otherwise it will replace the + * character already at the current cursor position. + */ + void displayCharacter(unsigned short c); + + // Do composition with last shown character FIXME: Not implemented yet for KDE 4 + void compose(const QString& compose); + + /** + * Resizes the image to a new fixed size of @p new_lines by @p new_columns. + * In the case that @p new_columns is smaller than the current number of columns, + * existing lines are not truncated. This prevents characters from being lost + * if the terminal display is resized smaller and then larger again. + * + * The top and bottom margins are reset to the top and bottom of the new + * screen size. Tab stops are also reset and the current selection is + * cleared. + */ + void resizeImage(int new_lines, int new_columns); + + /** + * Returns the current screen image. + * The result is an array of Characters of size [getLines()][getColumns()] which + * must be freed by the caller after use. + * + * @param dest Buffer to copy the characters into + * @param size Size of @p dest in Characters + * @param startLine Index of first line to copy + * @param endLine Index of last line to copy + */ + void getImage( Character* dest , int size , int startLine , int endLine ) const; + + /** + * Returns the additional attributes associated with lines in the image. + * The most important attribute is LINE_WRAPPED which specifies that the + * line is wrapped, + * other attributes control the size of characters in the line. + */ + QVector<LineProperty> getLineProperties( int startLine , int endLine ) const; + + + /** Return the number of lines. */ + int getLines() const + { return lines; } + /** Return the number of columns. */ + int getColumns() const + { return columns; } + /** Return the number of lines in the history buffer. */ + int getHistLines() const; + /** + * Sets the type of storage used to keep lines in the history. + * If @p copyPreviousScroll is true then the contents of the previous + * history buffer are copied into the new scroll. + */ + void setScroll(const HistoryType& , bool copyPreviousScroll = true); + /** Returns the type of storage used to keep lines in the history. */ + const HistoryType& getScroll() const; + /** + * Returns true if this screen keeps lines that are scrolled off the screen + * in a history buffer. + */ + bool hasScroll() const; + + /** + * Sets the start of the selection. + * + * @param column The column index of the first character in the selection. + * @param line The line index of the first character in the selection. + * @param blockSelectionMode True if the selection is in column mode. + */ + void setSelectionStart(const int column, const int line, const bool blockSelectionMode); + + /** + * Sets the end of the current selection. + * + * @param column The column index of the last character in the selection. + * @param line The line index of the last character in the selection. + */ + void setSelectionEnd(const int column, const int line); + + /** + * Retrieves the start of the selection or the cursor position if there + * is no selection. + */ + void getSelectionStart(int& column , int& line) const; + + /** + * Retrieves the end of the selection or the cursor position if there + * is no selection. + */ + void getSelectionEnd(int& column , int& line) const; + + /** Clears the current selection */ + void clearSelection(); + + /** + * Returns true if the character at (@p column, @p line) is part of the + * current selection. + */ + bool isSelected(const int column,const int line) const; + + /** + * Convenience method. Returns the currently selected text. + * @param preserveLineBreaks Specifies whether new line characters should + * be inserted into the returned text at the end of each terminal line. + */ + QString selectedText(bool preserveLineBreaks) const; + + /** + * Copies part of the output to a stream. + * + * @param decoder A decoder which converts terminal characters into text + * @param fromLine The first line in the history to retrieve + * @param toLine The last line in the history to retrieve + */ + void writeLinesToStream(TerminalCharacterDecoder* decoder, int fromLine, int toLine) const; + + /** + * Copies the selected characters, set using @see setSelBeginXY and @see setSelExtentXY + * into a stream. + * + * @param decoder A decoder which converts terminal characters into text. + * PlainTextDecoder is the most commonly used decoder which converts characters + * into plain text with no formatting. + * @param preserveLineBreaks Specifies whether new line characters should + * be inserted into the returned text at the end of each terminal line. + */ + void writeSelectionToStream(TerminalCharacterDecoder* decoder , bool + preserveLineBreaks = true) const; + + /** + * Checks if the text between from and to is inside the current + * selection. If this is the case, the selection is cleared. The + * from and to are coordinates in the current viewable window. + * The loc(x,y) macro can be used to generate these values from a + * column,line pair. + * + * @param from The start of the area to check. + * @param to The end of the area to check + */ + void checkSelection(int from, int to); + + /** + * Sets or clears an attribute of the current line. + * + * @param property The attribute to set or clear + * Possible properties are: + * LINE_WRAPPED: Specifies that the line is wrapped. + * LINE_DOUBLEWIDTH: Specifies that the characters in the current line + * should be double the normal width. + * LINE_DOUBLEHEIGHT:Specifies that the characters in the current line + * should be double the normal height. + * Double-height lines are formed of two lines containing the same characters, + * with both having the LINE_DOUBLEHEIGHT attribute. + * This allows other parts of the code to work on the + * assumption that all lines are the same height. + * + * @param enable true to apply the attribute to the current line or false to remove it + */ + void setLineProperty(LineProperty property , bool enable); + + /** + * Returns the number of lines that the image has been scrolled up or down by, + * since the last call to resetScrolledLines(). + * + * a positive return value indicates that the image has been scrolled up, + * a negative return value indicates that the image has been scrolled down. + */ + int scrolledLines() const; + + /** + * Returns the region of the image which was last scrolled. + * + * This is the area of the image from the top margin to the + * bottom margin when the last scroll occurred. + */ + QRect lastScrolledRegion() const; + + /** + * Resets the count of the number of lines that the image has been scrolled up or down by, + * see scrolledLines() + */ + void resetScrolledLines(); + + /** + * Returns the number of lines of output which have been + * dropped from the history since the last call + * to resetDroppedLines() + * + * If the history is not unlimited then it will drop + * the oldest lines of output if new lines are added when + * it is full. + */ + int droppedLines() const; + + /** + * Resets the count of the number of lines dropped from + * the history. + */ + void resetDroppedLines(); + + /** + * Fills the buffer @p dest with @p count instances of the default (ie. blank) + * Character style. + */ + static void fillWithDefaultChar(Character* dest, int count); + +private: + + //copies a line of text from the screen or history into a stream using a + //specified character decoder. Returns the number of lines actually copied, + //which may be less than 'count' if (start+count) is more than the number of characters on + //the line + // + //line - the line number to copy, from 0 (the earliest line in the history) up to + // history->getLines() + lines - 1 + //start - the first column on the line to copy + //count - the number of characters on the line to copy + //decoder - a decoder which converts terminal characters (an Character array) into text + //appendNewLine - if true a new line character (\n) is appended to the end of the line + int copyLineToStream(int line, + int start, + int count, + TerminalCharacterDecoder* decoder, + bool appendNewLine, + bool preserveLineBreaks) const; + + //fills a section of the screen image with the character 'c' + //the parameters are specified as offsets from the start of the screen image. + //the loc(x,y) macro can be used to generate these values from a column,line pair. + void clearImage(int loca, int loce, char c); + + //move screen image between 'sourceBegin' and 'sourceEnd' to 'dest'. + //the parameters are specified as offsets from the start of the screen image. + //the loc(x,y) macro can be used to generate these values from a column,line pair. + // + //NOTE: moveImage() can only move whole lines + void moveImage(int dest, int sourceBegin, int sourceEnd); + // scroll up 'i' lines in current region, clearing the bottom 'i' lines + void scrollUp(int from, int i); + // scroll down 'i' lines in current region, clearing the top 'i' lines + void scrollDown(int from, int i); + + void addHistLine(); + + void initTabStops(); + + void updateEffectiveRendition(); + void reverseRendition(Character& p) const; + + bool isSelectionValid() const; + // copies text from 'startIndex' to 'endIndex' to a stream + // startIndex and endIndex are positions generated using the loc(x,y) macro + void writeToStream(TerminalCharacterDecoder* decoder, int startIndex, + int endIndex, bool preserveLineBreaks = true) const; + // copies 'count' lines from the screen buffer into 'dest', + // starting from 'startLine', where 0 is the first line in the screen buffer + void copyFromScreen(Character* dest, int startLine, int count) const; + // copies 'count' lines from the history buffer into 'dest', + // starting from 'startLine', where 0 is the first line in the history + void copyFromHistory(Character* dest, int startLine, int count) const; + + + // screen image ---------------- + int lines; + int columns; + + typedef QVector<Character> ImageLine; // [0..columns] + ImageLine* screenLines; // [lines] + + int _scrolledLines; + QRect _lastScrolledRegion; + + int _droppedLines; + + QVarLengthArray<LineProperty,64> lineProperties; + + // history buffer --------------- + HistoryScroll* history; + + // cursor location + int cuX; + int cuY; + + // cursor color and rendition info + CharacterColor currentForeground; + CharacterColor currentBackground; + quint8 currentRendition; + + // margins ---------------- + int _topMargin; + int _bottomMargin; + + // states ---------------- + int currentModes[MODES_SCREEN]; + int savedModes[MODES_SCREEN]; + + // ---------------------------- + + QBitArray tabStops; + + // selection ------------------- + int selBegin; // The first location selected. + int selTopLeft; // TopLeft Location. + int selBottomRight; // Bottom Right Location. + bool blockSelectionMode; // Column selection mode + + // effective colors and rendition ------------ + CharacterColor effectiveForeground; // These are derived from + CharacterColor effectiveBackground; // the cu_* variables above + quint8 effectiveRendition; // to speed up operation + + class SavedState + { + public: + SavedState() + : cursorColumn(0),cursorLine(0),rendition(0) {} + + int cursorColumn; + int cursorLine; + quint8 rendition; + CharacterColor foreground; + CharacterColor background; + }; + SavedState savedState; + + // last position where we added a character + int lastPos; + + static Character defaultChar; +}; + +} + +#endif // SCREEN_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/ScreenWindow.cpp @@ -0,0 +1,294 @@ +/* + Copyright (C) 2007 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 "ScreenWindow.h" + +// Qt +//#include <KDebug> + +// Konsole +#include "Screen.h" + +using namespace Konsole; + +ScreenWindow::ScreenWindow(QObject* parent) + : QObject(parent) + , _windowBuffer(0) + , _windowBufferSize(0) + , _bufferNeedsUpdate(true) + , _windowLines(1) + , _currentLine(0) + , _trackOutput(true) + , _scrollCount(0) +{ +} +ScreenWindow::~ScreenWindow() +{ + delete[] _windowBuffer; +} +void ScreenWindow::setScreen(Screen* screen) +{ + Q_ASSERT( screen ); + + _screen = screen; +} + +Screen* ScreenWindow::screen() const +{ + return _screen; +} + +Character* ScreenWindow::getImage() +{ + // reallocate internal buffer if the window size has changed + int size = windowLines() * windowColumns(); + if (_windowBuffer == 0 || _windowBufferSize != size) + { + delete[] _windowBuffer; + _windowBufferSize = size; + _windowBuffer = new Character[size]; + _bufferNeedsUpdate = true; + } + + if (!_bufferNeedsUpdate) + return _windowBuffer; + + _screen->getImage(_windowBuffer,size, + currentLine(),endWindowLine()); + + // this window may look beyond the end of the screen, in which + // case there will be an unused area which needs to be filled + // with blank characters + fillUnusedArea(); + + _bufferNeedsUpdate = false; + return _windowBuffer; +} + +void ScreenWindow::fillUnusedArea() +{ + int screenEndLine = _screen->getHistLines() + _screen->getLines() - 1; + int windowEndLine = currentLine() + windowLines() - 1; + + int unusedLines = windowEndLine - screenEndLine; + int charsToFill = unusedLines * windowColumns(); + + Screen::fillWithDefaultChar(_windowBuffer + _windowBufferSize - charsToFill,charsToFill); +} + +// return the index of the line at the end of this window, or if this window +// goes beyond the end of the screen, the index of the line at the end +// of the screen. +// +// when passing a line number to a Screen method, the line number should +// never be more than endWindowLine() +// +int ScreenWindow::endWindowLine() const +{ + return qMin(currentLine() + windowLines() - 1, + lineCount() - 1); +} +QVector<LineProperty> ScreenWindow::getLineProperties() +{ + QVector<LineProperty> result = _screen->getLineProperties(currentLine(),endWindowLine()); + + if (result.count() != windowLines()) + result.resize(windowLines()); + + return result; +} + +QString ScreenWindow::selectedText( bool preserveLineBreaks ) const +{ + return _screen->selectedText( preserveLineBreaks ); +} + +void ScreenWindow::getSelectionStart( int& column , int& line ) +{ + _screen->getSelectionStart(column,line); + line -= currentLine(); +} +void ScreenWindow::getSelectionEnd( int& column , int& line ) +{ + _screen->getSelectionEnd(column,line); + line -= currentLine(); +} +void ScreenWindow::setSelectionStart( int column , int line , bool columnMode ) +{ + _screen->setSelectionStart( column , qMin(line + currentLine(),endWindowLine()) , columnMode); + + _bufferNeedsUpdate = true; + emit selectionChanged(); +} + +void ScreenWindow::setSelectionEnd( int column , int line ) +{ + _screen->setSelectionEnd( column , qMin(line + currentLine(),endWindowLine()) ); + + _bufferNeedsUpdate = true; + emit selectionChanged(); +} + +bool ScreenWindow::isSelected( int column , int line ) +{ + return _screen->isSelected( column , qMin(line + currentLine(),endWindowLine()) ); +} + +void ScreenWindow::clearSelection() +{ + _screen->clearSelection(); + + emit selectionChanged(); +} + +void ScreenWindow::setWindowLines(int lines) +{ + Q_ASSERT(lines > 0); + _windowLines = lines; +} +int ScreenWindow::windowLines() const +{ + return _windowLines; +} + +int ScreenWindow::windowColumns() const +{ + return _screen->getColumns(); +} + +int ScreenWindow::lineCount() const +{ + return _screen->getHistLines() + _screen->getLines(); +} + +int ScreenWindow::columnCount() const +{ + return _screen->getColumns(); +} + +QPoint ScreenWindow::cursorPosition() const +{ + QPoint position; + + position.setX( _screen->getCursorX() ); + position.setY( _screen->getCursorY() ); + + return position; +} + +int ScreenWindow::currentLine() const +{ + return qBound(0,_currentLine,lineCount()-windowLines()); +} + +void ScreenWindow::scrollBy( RelativeScrollMode mode , int amount ) +{ + if ( mode == ScrollLines ) + { + scrollTo( currentLine() + amount ); + } + else if ( mode == ScrollPages ) + { + scrollTo( currentLine() + amount * ( windowLines() / 2 ) ); + } +} + +bool ScreenWindow::atEndOfOutput() const +{ + return currentLine() == (lineCount()-windowLines()); +} + +void ScreenWindow::scrollTo( int line ) +{ + int maxCurrentLineNumber = lineCount() - windowLines(); + line = qBound(0,line,maxCurrentLineNumber); + + const int delta = line - _currentLine; + _currentLine = line; + + // keep track of number of lines scrolled by, + // this can be reset by calling resetScrollCount() + _scrollCount += delta; + + _bufferNeedsUpdate = true; + + emit scrolled(_currentLine); +} + +void ScreenWindow::setTrackOutput(bool trackOutput) +{ + _trackOutput = trackOutput; +} + +bool ScreenWindow::trackOutput() const +{ + return _trackOutput; +} + +int ScreenWindow::scrollCount() const +{ + return _scrollCount; +} + +void ScreenWindow::resetScrollCount() +{ + _scrollCount = 0; +} + +QRect ScreenWindow::scrollRegion() const +{ + bool equalToScreenSize = windowLines() == _screen->getLines(); + + if ( atEndOfOutput() && equalToScreenSize ) + return _screen->lastScrolledRegion(); + else + return QRect(0,0,windowColumns(),windowLines()); +} + +void ScreenWindow::notifyOutputChanged() +{ + // move window to the bottom of the screen and update scroll count + // if this window is currently tracking the bottom of the screen + if ( _trackOutput ) + { + _scrollCount -= _screen->scrolledLines(); + _currentLine = qMax(0,_screen->getHistLines() - (windowLines()-_screen->getLines())); + } + else + { + // if the history is not unlimited then it may + // have run out of space and dropped the oldest + // lines of output - in this case the screen + // window's current line number will need to + // be adjusted - otherwise the output will scroll + _currentLine = qMax(0,_currentLine - + _screen->droppedLines()); + + // ensure that the screen window's current position does + // not go beyond the bottom of the screen + _currentLine = qMin( _currentLine , _screen->getHistLines() ); + } + + _bufferNeedsUpdate = true; + + emit outputChanged(); +} + +#include "ScreenWindow.moc"
new file mode 100644 --- /dev/null +++ b/gui/konsole/ScreenWindow.h @@ -0,0 +1,259 @@ +/* + Copyright 2007-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. +*/ + +#ifndef SCREENWINDOW_H +#define SCREENWINDOW_H + +// Qt +#include <QtCore/QObject> +#include <QtCore/QPoint> +#include <QtCore/QRect> + +// Konsole +#include "Character.h" + +namespace Konsole +{ + +class Screen; + +/** + * Provides a window onto a section of a terminal screen. A terminal widget can then render + * the contents of the window and use the window to change the terminal screen's selection + * in response to mouse or keyboard input. + * + * A new ScreenWindow for a terminal session can be created by calling Emulation::createWindow() + * + * Use the scrollTo() method to scroll the window up and down on the screen. + * Use the getImage() method to retrieve the character image which is currently visible in the window. + * + * setTrackOutput() controls whether the window moves to the bottom of the associated screen when new + * lines are added to it. + * + * Whenever the output from the underlying screen is changed, the notifyOutputChanged() slot should + * be called. This in turn will update the window's position and emit the outputChanged() signal + * if necessary. + */ +class ScreenWindow : public QObject +{ +Q_OBJECT + +public: + /** + * Constructs a new screen window with the given parent. + * A screen must be specified by calling setScreen() before calling getImage() or getLineProperties(). + * + * You should not call this constructor directly, instead use the Emulation::createWindow() method + * to create a window on the emulation which you wish to view. This allows the emulation + * to notify the window when the associated screen has changed and synchronize selection updates + * between all views on a session. + */ + ScreenWindow(QObject* parent = 0); + virtual ~ScreenWindow(); + + /** Sets the screen which this window looks onto */ + void setScreen(Screen* screen); + /** Returns the screen which this window looks onto */ + Screen* screen() const; + + /** + * Returns the image of characters which are currently visible through this window + * onto the screen. + * + * The returned buffer is managed by the ScreenWindow instance and does not need to be + * deleted by the caller. + */ + Character* getImage(); + + /** + * Returns the line attributes associated with the lines of characters which + * are currently visible through this window + */ + QVector<LineProperty> getLineProperties(); + + /** + * Returns the number of lines which the region of the window + * specified by scrollRegion() has been scrolled by since the last call + * to resetScrollCount(). scrollRegion() is in most cases the + * whole window, but will be a smaller area in, for example, applications + * which provide split-screen facilities. + * + * This is not guaranteed to be accurate, but allows views to optimize + * rendering by reducing the amount of costly text rendering that + * needs to be done when the output is scrolled. + */ + int scrollCount() const; + + /** + * Resets the count of scrolled lines returned by scrollCount() + */ + void resetScrollCount(); + + /** + * Returns the area of the window which was last scrolled, this is + * usually the whole window area. + * + * Like scrollCount(), this is not guaranteed to be accurate, + * but allows views to optimize rendering. + */ + QRect scrollRegion() const; + + /** + * Sets the start of the selection to the given @p line and @p column within + * the window. + */ + void setSelectionStart( int column , int line , bool columnMode ); + /** + * Sets the end of the selection to the given @p line and @p column within + * the window. + */ + void setSelectionEnd( int column , int line ); + /** + * Retrieves the start of the selection within the window. + */ + void getSelectionStart( int& column , int& line ); + /** + * Retrieves the end of the selection within the window. + */ + void getSelectionEnd( int& column , int& line ); + /** + * Returns true if the character at @p line , @p column is part of the selection. + */ + bool isSelected( int column , int line ); + /** + * Clears the current selection + */ + void clearSelection(); + + /** Sets the number of lines in the window */ + void setWindowLines(int lines); + /** Returns the number of lines in the window */ + int windowLines() const; + /** Returns the number of columns in the window */ + int windowColumns() const; + + /** Returns the total number of lines in the screen */ + int lineCount() const; + /** Returns the total number of columns in the screen */ + int columnCount() const; + + /** Returns the index of the line which is currently at the top of this window */ + int currentLine() const; + + /** + * Returns the position of the cursor + * within the window. + */ + QPoint cursorPosition() const; + + /** + * Convenience method. Returns true if the window is currently at the bottom + * of the screen. + */ + bool atEndOfOutput() const; + + /** Scrolls the window so that @p line is at the top of the window */ + void scrollTo( int line ); + + /** Describes the units which scrollBy() moves the window by. */ + enum RelativeScrollMode + { + /** Scroll the window down by a given number of lines. */ + ScrollLines, + /** + * Scroll the window down by a given number of pages, where + * one page is windowLines() lines + */ + ScrollPages + }; + + /** + * Scrolls the window relative to its current position on the screen. + * + * @param mode Specifies whether @p amount refers to the number of lines or the number + * of pages to scroll. + * @param amount The number of lines or pages ( depending on @p mode ) to scroll by. If + * this number is positive, the view is scrolled down. If this number is negative, the view + * is scrolled up. + */ + void scrollBy( RelativeScrollMode mode , int amount ); + + /** + * Specifies whether the window should automatically move to the bottom + * of the screen when new output is added. + * + * If this is set to true, the window will be moved to the bottom of the associated screen ( see + * screen() ) when the notifyOutputChanged() method is called. + */ + void setTrackOutput(bool trackOutput); + /** + * Returns whether the window automatically moves to the bottom of the screen as + * new output is added. See setTrackOutput() + */ + bool trackOutput() const; + + /** + * Returns the text which is currently selected. + * + * @param preserveLineBreaks See Screen::selectedText() + */ + QString selectedText( bool preserveLineBreaks ) const; + +public slots: + /** + * Notifies the window that the contents of the associated terminal screen have changed. + * This moves the window to the bottom of the screen if trackOutput() is true and causes + * the outputChanged() signal to be emitted. + */ + void notifyOutputChanged(); + +signals: + /** + * Emitted when the contents of the associated terminal screen (see screen()) changes. + */ + void outputChanged(); + + /** + * Emitted when the screen window is scrolled to a different position. + * + * @param line The line which is now at the top of the window. + */ + void scrolled(int line); + + /** Emitted when the selection is changed. */ + void selectionChanged(); + +private: + int endWindowLine() const; + void fillUnusedArea(); + + Screen* _screen; // see setScreen() , screen() + Character* _windowBuffer; + int _windowBufferSize; + bool _bufferNeedsUpdate; + + int _windowLines; + int _currentLine; // see scrollTo() , currentLine() + bool _trackOutput; // see setTrackOutput() , trackOutput() + int _scrollCount; // count of lines which the window has been scrolled by since + // the last call to resetScrollCount() +}; + +} +#endif // SCREENWINDOW_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/Session.cpp @@ -0,0 +1,1411 @@ +/* + This file is part of Konsole + + Copyright 2006-2008 by Robert Knight <robertknight@gmail.com> + Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de> + Copyright 2009 by Thomas Dreibholz <dreibh@iem.uni-due.de> + + 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 "Session.h" + +// Standard +#include <assert.h> +#include <stdlib.h> +#include <signal.h> + +// Qt +#include <QtGui/QApplication> +#include <QtCore/QByteRef> +#include <QtCore/QDir> +#include <QtCore/QFile> +#include <QtCore/QRegExp> +#include <QtCore/QStringList> +//#include <QtDBus/QtDBus> +#include <QtCore/QDate> + + +// KDE +//#include <KDebug> +//#include <KLocale> +//#include <KMessageBox> +//#include <KNotification> +//#include <KProcess> +//#include <KRun> +//#include <kshell.h> +//#include <KStandardDirs> +//#include <KPtyDevice> +//#include <KUrl> +#include "kprocess.h" +#include "kptydevice.h" + +// Konsole +//#include <config-konsole.h> +//#include <sessionadaptor.h> + +#include "ProcessInfo.h" +#include "Pty.h" +#include "TerminalDisplay.h" +#include "ShellCommand.h" +#include "Vt102Emulation.h" +//#include "ZModemDialog.h" + +using namespace Konsole; + +int Session::lastSessionId = 0; + +// HACK This is copied out of QUuid::createUuid with reseeding forced. +// Required because color schemes repeatedly seed the RNG... +// ...with a constant. +QUuid createUuid() +{ + static const int intbits = sizeof(int)*8; + static int randbits = 0; + if (!randbits) + { + int max = RAND_MAX; + do { ++randbits; } while ((max=max>>1)); + } + + qsrand(uint(QDateTime::currentDateTime().toTime_t())); + qrand(); // Skip first + + QUuid result; + uint *data = &(result.data1); + int chunks = 16 / sizeof(uint); + while (chunks--) { + uint randNumber = 0; + for (int filled = 0; filled < intbits; filled += randbits) + randNumber |= qrand()<<filled; + *(data+chunks) = randNumber; + } + + result.data4[0] = (result.data4[0] & 0x3F) | 0x80; // UV_DCE + result.data3 = (result.data3 & 0x0FFF) | 0x4000; // UV_Random + + return result; +} + +Session::Session(QObject* parent) : + QObject(parent) + , _shellProcess(0) + , _emulation(0) + , _monitorActivity(false) + , _monitorSilence(false) + , _notifiedActivity(false) + , _autoClose(true) + , _wantedClose(false) + , _silenceSeconds(10) + , _addToUtmp(true) + , _flowControl(true) + , _fullScripting(false) + , _sessionId(0) + , _sessionProcessInfo(0) + , _foregroundProcessInfo(0) + , _foregroundPid(0) + //, _zmodemBusy(false) + //, _zmodemProc(0) + //, _zmodemProgress(0) + , _hasDarkBackground(false) +{ + _uniqueIdentifier = createUuid(); + + //prepare DBus communication + //new SessionAdaptor(this); + _sessionId = ++lastSessionId; + + // JPS: commented out for lack of DBUS support by default on OSX + //QDBusConnection::sessionBus().registerObject(QLatin1String("/Sessions/")+QString::number(_sessionId), this); + + //create emulation backend + _emulation = new Vt102Emulation(); + + connect( _emulation, SIGNAL( titleChanged( int, const QString & ) ), + this, SLOT( setUserTitle( int, const QString & ) ) ); + connect( _emulation, SIGNAL( stateSet(int) ), + this, SLOT( activityStateSet(int) ) ); + //connect( _emulation, SIGNAL( zmodemDetected() ), this , + // SLOT( fireZModemDetected() ) ); + connect( _emulation, SIGNAL( changeTabTextColorRequest( int ) ), + this, SIGNAL( changeTabTextColorRequest( int ) ) ); + connect( _emulation, SIGNAL(profileChangeCommandReceived(const QString&)), + this, SIGNAL( profileChangeCommandReceived(const QString&)) ); + connect( _emulation, SIGNAL(flowControlKeyPressed(bool)) , this, + SLOT(updateFlowControlState(bool)) ); + + //create new teletype for I/O with shell process + openTeletype(-1); + + //setup timer for monitoring session activity + _monitorTimer = new QTimer(this); + _monitorTimer->setSingleShot(true); + connect(_monitorTimer, SIGNAL(timeout()), this, SLOT(monitorTimerDone())); +} + +void Session::openTeletype(int fd) +{ + if (_shellProcess && isRunning()) + { + kWarning() << "Attempted to open teletype in a running session."; + return; + } + + delete _shellProcess; + + if (fd < 0) + _shellProcess = new Pty(); + else + _shellProcess = new Pty(fd); + + _shellProcess->setUtf8Mode(_emulation->utf8()); + + //connect teletype to emulation backend + connect( _shellProcess,SIGNAL(receivedData(const char*,int)),this, + SLOT(onReceiveBlock(const char*,int)) ); + connect( _emulation,SIGNAL(sendData(const char*,int)),_shellProcess, + SLOT(sendData(const char*,int)) ); + connect( _emulation,SIGNAL(lockPtyRequest(bool)),_shellProcess,SLOT(lockPty(bool)) ); + connect( _emulation,SIGNAL(useUtf8Request(bool)),_shellProcess,SLOT(setUtf8Mode(bool)) ); + connect( _shellProcess,SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(done(int)) ); + connect( _emulation,SIGNAL(imageSizeChanged(int,int)),this,SLOT(updateWindowSize(int,int)) ); +} + +WId Session::windowId() const +{ + // Returns a window ID for this session which is used + // to set the WINDOWID environment variable in the shell + // process. + // + // Sessions can have multiple views or no views, which means + // that a single ID is not always going to be accurate. + // + // If there are no views, the window ID is just 0. If + // there are multiple views, then the window ID for the + // top-level window which contains the first view is + // returned + + if ( _views.count() == 0 ) + return 0; + else + { + QWidget* window = _views.first(); + + Q_ASSERT( window ); + + while ( window->parentWidget() != 0 ) + window = window->parentWidget(); + + return window->winId(); + } +} + +void Session::setDarkBackground(bool darkBackground) +{ + _hasDarkBackground = darkBackground; +} +bool Session::hasDarkBackground() const +{ + return _hasDarkBackground; +} +bool Session::isRunning() const +{ + return _shellProcess->state() == QProcess::Running; +} + +void Session::setCodec(QTextCodec* codec) +{ + emulation()->setCodec(codec); +} + +bool Session::setCodec(QByteArray name) +{ + QTextCodec *codec = QTextCodec::codecForName(name); + if (codec) { + setCodec(codec); + return true; + } + return false; +} + +QByteArray Session::codec() +{ + return _emulation->codec()->name(); +} + +void Session::setProgram(const QString& program) +{ + _program = ShellCommand::expand(program); +} +void Session::setInitialWorkingDirectory(const QString& dir) +{ + //_initialWorkingDir = KShell::tildeExpand(ShellCommand::expand(dir)); + _initialWorkingDir = ShellCommand::expand(dir); +} +void Session::setArguments(const QStringList& arguments) +{ + _arguments = ShellCommand::expand(arguments); +} + +QString Session::currentWorkingDirectory() +{ + // only returned cached value + if (_currentWorkingDir.isEmpty()) updateWorkingDirectory(); + return _currentWorkingDir; +} +ProcessInfo* Session::updateWorkingDirectory() +{ + ProcessInfo *process = getProcessInfo(); + _currentWorkingDir = process->validCurrentDir(); + return process; +} + +QList<TerminalDisplay*> Session::views() const +{ + return _views; +} + +void Session::addView(TerminalDisplay* widget) +{ + Q_ASSERT( !_views.contains(widget) ); + + _views.append(widget); + + if ( _emulation != 0 ) + { + // connect emulation - view signals and slots + connect( widget , SIGNAL(keyPressedSignal(QKeyEvent*)) , _emulation , + SLOT(sendKeyEvent(QKeyEvent*)) ); + connect( widget , SIGNAL(mouseSignal(int,int,int,int)) , _emulation , + SLOT(sendMouseEvent(int,int,int,int)) ); + connect( widget , SIGNAL(sendStringToEmu(const char*)) , _emulation , + SLOT(sendString(const char*)) ); + + // allow emulation to notify view when the foreground process + // indicates whether or not it is interested in mouse signals + connect( _emulation , SIGNAL(programUsesMouseChanged(bool)) , widget , + SLOT(setUsesMouse(bool)) ); + + widget->setUsesMouse( _emulation->programUsesMouse() ); + + widget->setScreenWindow(_emulation->createWindow()); + } + + //connect view signals and slots + QObject::connect( widget ,SIGNAL(changedContentSizeSignal(int,int)),this, + SLOT(onViewSizeChange(int,int))); + + QObject::connect( widget ,SIGNAL(destroyed(QObject*)) , this , + SLOT(viewDestroyed(QObject*)) ); +} + +void Session::viewDestroyed(QObject* view) +{ + TerminalDisplay* display = (TerminalDisplay*)view; + + Q_ASSERT( _views.contains(display) ); + + removeView(display); +} + +void Session::removeView(TerminalDisplay* widget) +{ + _views.removeAll(widget); + + disconnect(widget,0,this,0); + + if ( _emulation != 0 ) + { + // disconnect + // - key presses signals from widget + // - mouse activity signals from widget + // - string sending signals from widget + // + // ... and any other signals connected in addView() + disconnect( widget, 0, _emulation, 0); + + // disconnect state change signals emitted by emulation + disconnect( _emulation , 0 , widget , 0); + } + + // close the session automatically when the last view is removed + if ( _views.count() == 0 ) + { + close(); + } +} + +QString Session::checkProgram(const QString& program) const +{ + // Upon a KPty error, there is no description on what that error was... + // Check to see if the given program is executable. + QString exec = QFile::encodeName(program); + + if (exec.isEmpty()) + return QString(); + + // if 'exec' is not specified, fall back to default shell. if that + // is not set then fall back to /bin/sh + if ( exec.isEmpty() ) + exec = qgetenv("SHELL"); + if ( exec.isEmpty() ) + exec = "/bin/sh"; + + // JPS: commented out to get rid of KShell and KRun + /* + exec = KRun::binaryName(exec, false); + exec = KShell::tildeExpand(exec); + QString pexec = KGlobal::dirs()->findExe(exec); + if ( pexec.isEmpty() ) + { + kError() << i18n("Could not find binary: ") << exec; + return QString(); + } + + return exec; + */ + return program; +} + +void Session::terminalWarning(const QString& message) +{ + //static const QByteArray warningText = i18nc("@info:shell Alert the user with red color text", "Warning: ").toLocal8Bit(); + static const QByteArray warningText = i18nc("@info:shell Alert the user with red color text", "Warning: "); + QByteArray messageText = message.toLocal8Bit(); + + static const char redPenOn[] = "\033[1m\033[31m"; + static const char redPenOff[] = "\033[0m"; + + _emulation->receiveData(redPenOn,strlen(redPenOn)); + _emulation->receiveData("\n\r\n\r",4); + _emulation->receiveData(warningText.constData(),strlen(warningText.constData())); + _emulation->receiveData(messageText.constData(),strlen(messageText.constData())); + _emulation->receiveData("\n\r\n\r",4); + _emulation->receiveData(redPenOff,strlen(redPenOff)); +} + +QString Session::shellSessionId() const +{ + QString friendlyUuid(_uniqueIdentifier.toString()); + friendlyUuid.remove('-').remove('{').remove('}'); + + return friendlyUuid; +} + +void Session::run() +{ + //check that everything is in place to run the session + if (_program.isEmpty()) + { + kWarning() << "Session::run() - program to run not set."; + } + if (_arguments.isEmpty()) + { + kWarning() << "Session::run() - no command line arguments specified."; + } + if (_uniqueIdentifier.isNull()) + { + _uniqueIdentifier = createUuid(); + } + + const int CHOICE_COUNT = 3; + QString programs[CHOICE_COUNT] = {_program,qgetenv("SHELL"),"/bin/sh"}; + QString exec; + int choice = 0; + while (choice < CHOICE_COUNT) + { + exec = checkProgram(programs[choice]); + if (exec.isEmpty()) + choice++; + else + break; + } + + // if a program was specified via setProgram(), but it couldn't be found, print a warning + if (choice != 0 && choice < CHOICE_COUNT && !_program.isEmpty()) + { + terminalWarning(i18n("Could not find '%1', starting '%2' instead. Please check your profile settings.",_program.toLatin1().data(),exec.toLatin1().data())); + } + // if none of the choices are available, print a warning + else if (choice == CHOICE_COUNT) + { + terminalWarning(i18n("Could not find an interactive shell to start.")); + return; + } + + // if no arguments are specified, fall back to program name + QStringList arguments = _arguments.join(QChar(' ')).isEmpty() ? + QStringList() << exec : _arguments; + + // JPS: commented out for lack of DBUS support by default on OSX + QString dbusService = ""; //QDBusConnection::sessionBus().baseService(); + if (!_initialWorkingDir.isEmpty()) + _shellProcess->setWorkingDirectory(_initialWorkingDir); + else + _shellProcess->setWorkingDirectory(QDir::homePath()); + + _shellProcess->setFlowControlEnabled(_flowControl); + _shellProcess->setErase(_emulation->eraseChar()); + + // this is not strictly accurate use of the COLORFGBG variable. This does not + // tell the terminal exactly which colors are being used, but instead approximates + // the color scheme as "black on white" or "white on black" depending on whether + // the background color is deemed dark or not + QString backgroundColorHint = _hasDarkBackground ? "COLORFGBG=15;0" : "COLORFGBG=0;15"; + _environment << backgroundColorHint; + _environment << QString("SHELL_SESSION_ID=%1").arg(shellSessionId()); + + int result = _shellProcess->start(exec, + arguments, + _environment, + windowId(), + _addToUtmp, + dbusService, + (QLatin1String("/Sessions/") + + QString::number(_sessionId))); + + if (result < 0) + { + terminalWarning(i18n("Could not start program '%1' with arguments '%2'.", exec.toLatin1().data(), arguments.join(" ").toLatin1().data())); + std::cout << "ERROR: " << exec.toLatin1().data(); + return; + } + + _shellProcess->setWriteable(false); // We are reachable via kwrited. + + emit started(); +} + +void Session::setUserTitle( int what, const QString &caption ) +{ + //set to true if anything is actually changed (eg. old _nameTitle != new _nameTitle ) + bool modified = false; + + if ((what == IconNameAndWindowTitle) || (what == WindowTitle)) + { + if ( _userTitle != caption ) { + _userTitle = caption; + modified = true; + } + } + + if ((what == IconNameAndWindowTitle) || (what == IconName)) + { + if ( _iconText != caption ) { + _iconText = caption; + modified = true; + } + } + + if (what == TextColor || what == BackgroundColor) + { + QString colorString = caption.section(';',0,0); + QColor color = QColor(colorString); + if (color.isValid()) + { + if (what == TextColor) + emit changeForegroundColorRequest(color); + else + emit changeBackgroundColorRequest(color); + } + } + + if (what == SessionName) + { + if ( _nameTitle != caption ) { + setTitle(Session::NameRole,caption); + return; + } + } + + if (what == 31) + { + QString cwd=caption; + cwd=cwd.replace( QRegExp("^~"), QDir::homePath() ); + emit openUrlRequest(cwd); + } + + // change icon via \033]32;Icon\007 + if (what == 32) + { + if ( _iconName != caption ) { + _iconName = caption; + + modified = true; + } + } + + if (what == ProfileChange) + { + emit profileChangeCommandReceived(caption); + return; + } + + if ( modified ) + emit titleChanged(); +} + +QString Session::userTitle() const +{ + return _userTitle; +} +void Session::setTabTitleFormat(TabTitleContext context , const QString& format) +{ + if ( context == LocalTabTitle ) + _localTabTitleFormat = format; + else if ( context == RemoteTabTitle ) + _remoteTabTitleFormat = format; +} +QString Session::tabTitleFormat(TabTitleContext context) const +{ + if ( context == LocalTabTitle ) + return _localTabTitleFormat; + else if ( context == RemoteTabTitle ) + return _remoteTabTitleFormat; + + return QString(); +} + +void Session::monitorTimerDone() +{ + //FIXME: The idea here is that the notification popup will appear to tell the user than output from + //the terminal has stopped and the popup will disappear when the user activates the session. + // + //This breaks with the addition of multiple views of a session. The popup should disappear + //when any of the views of the session becomes active + + + //FIXME: Make message text for this notification and the activity notification more descriptive. + if (_monitorSilence) { + //KNotification::event("Silence", i18n("Silence in session '%1'", _nameTitle), QPixmap(), + // QApplication::activeWindow(), + // KNotification::CloseWhenWidgetActivated); + emit stateChanged(NOTIFYSILENCE); + } + else + { + emit stateChanged(NOTIFYNORMAL); + } + + _notifiedActivity=false; +} +void Session::updateFlowControlState(bool suspended) +{ + if (suspended) + { + if (flowControlEnabled()) + { + foreach(TerminalDisplay* display,_views) + { + if (display->flowControlWarningEnabled()) + display->outputSuspended(true); + } + } + } + else + { + foreach(TerminalDisplay* display,_views) + display->outputSuspended(false); + } +} +void Session::activityStateSet(int state) +{ + if (state==NOTIFYBELL) + { + emit bellRequest( i18n("Bell in session '%1'",_nameTitle.toLatin1().data()) ); + } + else if (state==NOTIFYACTIVITY) + { + if (_monitorSilence) { + _monitorTimer->start(_silenceSeconds*1000); + } + + if ( _monitorActivity ) { + //FIXME: See comments in Session::monitorTimerDone() + if (!_notifiedActivity) { + //KNotification::event("Activity", i18n("Activity in session '%1'", _nameTitle), QPixmap(), + // QApplication::activeWindow(), + //KNotification::CloseWhenWidgetActivated); + _notifiedActivity=true; + } + } + } + + if ( state==NOTIFYACTIVITY && !_monitorActivity ) + state = NOTIFYNORMAL; + if ( state==NOTIFYSILENCE && !_monitorSilence ) + state = NOTIFYNORMAL; + + emit stateChanged(state); +} + +void Session::onViewSizeChange(int /*height*/, int /*width*/) +{ + updateTerminalSize(); +} + +void Session::updateTerminalSize() +{ + QListIterator<TerminalDisplay*> viewIter(_views); + + int minLines = -1; + int minColumns = -1; + + // minimum number of lines and columns that views require for + // their size to be taken into consideration ( to avoid problems + // with new view widgets which haven't yet been set to their correct size ) + const int VIEW_LINES_THRESHOLD = 2; + const int VIEW_COLUMNS_THRESHOLD = 2; + + //select largest number of lines and columns that will fit in all visible views + while ( viewIter.hasNext() ) + { + TerminalDisplay* view = viewIter.next(); + if ( view->isHidden() == false && + view->lines() >= VIEW_LINES_THRESHOLD && + view->columns() >= VIEW_COLUMNS_THRESHOLD ) + { + minLines = (minLines == -1) ? view->lines() : qMin( minLines , view->lines() ); + minColumns = (minColumns == -1) ? view->columns() : qMin( minColumns , view->columns() ); + view->processFilters(); + } + } + + // backend emulation must have a _terminal of at least 1 column x 1 line in size + if ( minLines > 0 && minColumns > 0 ) + { + _emulation->setImageSize( minLines , minColumns ); + } +} +void Session::updateWindowSize(int lines, int columns) +{ + Q_ASSERT(lines > 0 && columns > 0); + _shellProcess->setWindowSize(lines,columns); +} +void Session::refresh() +{ + // attempt to get the shell process to redraw the display + // + // this requires the program running in the shell + // to cooperate by sending an update in response to + // a window size change + // + // the window size is changed twice, first made slightly larger and then + // resized back to its normal size so that there is actually a change + // in the window size (some shells do nothing if the + // new and old sizes are the same) + // + // if there is a more 'correct' way to do this, please + // send an email with method or patches to konsole-devel@kde.org + + const QSize existingSize = _shellProcess->windowSize(); + _shellProcess->setWindowSize(existingSize.height(),existingSize.width()+1); + _shellProcess->setWindowSize(existingSize.height(),existingSize.width()); +} + +bool Session::kill(int signal) +{ + int result = ::kill(_shellProcess->pid(),signal); + + if ( result == 0 ) + { + _shellProcess->waitForFinished(); + return true; + } + else + return false; +} + +void Session::close() +{ + _autoClose = true; + _wantedClose = true; + + if (!isRunning() || !kill(SIGHUP)) + { + if (isRunning()) + { + kWarning() << "Process" << _shellProcess->pid() << "did not respond to SIGHUP"; + + // close the pty and wait to see if the process finishes. If it does, + // the done() slot will have been called so we can return. Otherwise, + // emit the finished() signal regardless + _shellProcess->pty()->close(); + if (_shellProcess->waitForFinished(3000)) + return; + + kWarning() << "Unable to kill process" << _shellProcess->pid(); + } + + // Forced close. + QTimer::singleShot(1, this, SIGNAL(finished())); + } +} + +void Session::sendText(const QString &text) const +{ + _emulation->sendText(text); +} + +void Session::sendMouseEvent(int buttons, int column, int line, int eventType) +{ + _emulation->sendMouseEvent(buttons, column, line, eventType); +} + +Session::~Session() +{ + if (_foregroundProcessInfo) + delete _foregroundProcessInfo; + if (_sessionProcessInfo) + delete _sessionProcessInfo; + delete _emulation; + delete _shellProcess; + //delete _zmodemProc; +} + +void Session::done(int exitStatus) +{ + if (!_autoClose) + { + _userTitle = i18n("@info:shell This session is done", "Finished"); + emit titleChanged(); + return; + } + + QString message; + if (!_wantedClose || exitStatus != 0) + { + if (_shellProcess->exitStatus() == QProcess::NormalExit) + message = i18n("Program '%1' exited with status %2.", _program.toLatin1().data(), exitStatus); + else + message = i18n("Program '%1' crashed.", _program.toLatin1().data()); + + //FIXME: See comments in Session::monitorTimerDone() + //KNotification::event("Finished", message , QPixmap(), + // QApplication::activeWindow(), + // KNotification::CloseWhenWidgetActivated); + } + + if ( !_wantedClose && _shellProcess->exitStatus() != QProcess::NormalExit ) + terminalWarning(message); + else + emit finished(); +} + +Emulation* Session::emulation() const +{ + return _emulation; +} + +QString Session::keyBindings() const +{ + return _emulation->keyBindings(); +} + +QStringList Session::environment() const +{ + return _environment; +} + +void Session::setEnvironment(const QStringList& environment) +{ + _environment = environment; +} + +int Session::sessionId() const +{ + return _sessionId; +} + +void Session::setKeyBindings(const QString &id) +{ + _emulation->setKeyBindings(id); +} + +void Session::setTitle(TitleRole role , const QString& newTitle) +{ + if ( title(role) != newTitle ) + { + if ( role == NameRole ) + _nameTitle = newTitle; + else if ( role == DisplayedTitleRole ) + _displayTitle = newTitle; + + emit titleChanged(); + } +} + +QString Session::title(TitleRole role) const +{ + if ( role == NameRole ) + return _nameTitle; + else if ( role == DisplayedTitleRole ) + return _displayTitle; + else + return QString(); +} + +ProcessInfo* Session::getProcessInfo() +{ + ProcessInfo* process; + + if (isForegroundProcessActive()) + process = _foregroundProcessInfo; + else + { + updateSessionProcessInfo(); + process = _sessionProcessInfo; + } + + return process; +} + +void Session::updateSessionProcessInfo() +{ + Q_ASSERT(_shellProcess); + if (!_sessionProcessInfo) + { + _sessionProcessInfo = ProcessInfo::newInstance(processId()); + _sessionProcessInfo->setUserHomeDir(); + } + _sessionProcessInfo->update(); +} + +bool Session::updateForegroundProcessInfo() +{ + bool valid = (_foregroundProcessInfo != 0); + + // has foreground process changed? + Q_ASSERT(_shellProcess); + int pid = _shellProcess->foregroundProcessGroup(); + if (pid != _foregroundPid) + { + if (valid) + delete _foregroundProcessInfo; + _foregroundProcessInfo = ProcessInfo::newInstance(pid); + _foregroundPid = pid; + valid = true; + } + + if (valid) + { + _foregroundProcessInfo->update(); + valid = _foregroundProcessInfo->isValid(); + } + + return valid; +} + +bool Session::isRemote() +{ + ProcessInfo* process = getProcessInfo(); + + bool ok = false; + return ( process->name(&ok) == "ssh" && ok ); +} + +QString Session::getDynamicTitle() +{ + // update current directory from process + ProcessInfo* process = updateWorkingDirectory(); + + // format tab titles using process info + bool ok = false; + QString title; + if ( process->name(&ok) == "ssh" && ok ) + { + SSHProcessInfo sshInfo(*process); + title = sshInfo.format(tabTitleFormat(Session::RemoteTabTitle)); + } + else + title = process->format(tabTitleFormat(Session::LocalTabTitle)); + + return title; +} + +/* +KUrl Session::getUrl() +{ + QString path; + + updateSessionProcessInfo(); + if (_sessionProcessInfo->isValid()) + { + bool ok = false; + + // check if foreground process is bookmark-able + if (isForegroundProcessActive()) + { + // for remote connections, save the user and host + // bright ideas to get the directory at the other end are welcome :) + if (_foregroundProcessInfo->name(&ok) == "ssh" && ok) + { + SSHProcessInfo sshInfo(*_foregroundProcessInfo); + path = "ssh://" + sshInfo.userName() + '@' + sshInfo.host(); + } + else + { + path = _foregroundProcessInfo->currentDir(&ok); + if (!ok) + path.clear(); + } + } + else // otherwise use the current working directory of the shell process + { + path = _sessionProcessInfo->currentDir(&ok); + if (!ok) + path.clear(); + } + } + + return KUrl(path); +} +*/ + +void Session::setIconName(const QString& iconName) +{ + if ( iconName != _iconName ) + { + _iconName = iconName; + emit titleChanged(); + } +} + +void Session::setIconText(const QString& iconText) +{ + _iconText = iconText; +} + +QString Session::iconName() const +{ + return _iconName; +} + +QString Session::iconText() const +{ + return _iconText; +} + +void Session::setHistoryType(const HistoryType &hType) +{ + _emulation->setHistory(hType); +} + +const HistoryType& Session::historyType() const +{ + return _emulation->history(); +} + +void Session::clearHistory() +{ + _emulation->clearHistory(); +} + +QStringList Session::arguments() const +{ + return _arguments; +} + +QString Session::program() const +{ + return _program; +} + +// unused currently +bool Session::isMonitorActivity() const { return _monitorActivity; } +// unused currently +bool Session::isMonitorSilence() const { return _monitorSilence; } + +void Session::setMonitorActivity(bool _monitor) +{ + _monitorActivity=_monitor; + _notifiedActivity=false; + + activityStateSet(NOTIFYNORMAL); +} + +void Session::setMonitorSilence(bool _monitor) +{ + if (_monitorSilence==_monitor) + return; + + _monitorSilence=_monitor; + if (_monitorSilence) + { + _monitorTimer->start(_silenceSeconds*1000); + } + else + _monitorTimer->stop(); + + activityStateSet(NOTIFYNORMAL); +} + +void Session::setMonitorSilenceSeconds(int seconds) +{ + _silenceSeconds=seconds; + if (_monitorSilence) { + _monitorTimer->start(_silenceSeconds*1000); + } +} + +void Session::setAddToUtmp(bool set) +{ + _addToUtmp = set; +} + +void Session::setFlowControlEnabled(bool enabled) +{ + _flowControl = enabled; + + if (_shellProcess) + _shellProcess->setFlowControlEnabled(_flowControl); + emit flowControlEnabledChanged(enabled); +} +bool Session::flowControlEnabled() const +{ + if (_shellProcess) + return _shellProcess->flowControlEnabled(); + else + return _flowControl; +} + +/* +void Session::fireZModemDetected() +{ + if (!_zmodemBusy) + { + QTimer::singleShot(10, this, SIGNAL(zmodemDetected())); + _zmodemBusy = true; + } +} + +void Session::cancelZModem() +{ + _shellProcess->sendData("\030\030\030\030", 4); // Abort + _zmodemBusy = false; +} + +void Session::startZModem(const QString &zmodem, const QString &dir, const QStringList &list) +{ + _zmodemBusy = true; + _zmodemProc = new KProcess(); + _zmodemProc->setOutputChannelMode( KProcess::SeparateChannels ); + + *_zmodemProc << zmodem << "-v" << list; + + if (!dir.isEmpty()) + _zmodemProc->setWorkingDirectory(dir); + + connect(_zmodemProc,SIGNAL (readyReadStandardOutput()), + this, SLOT(zmodemReadAndSendBlock())); + connect(_zmodemProc,SIGNAL (readyReadStandardError()), + this, SLOT(zmodemReadStatus())); + connect(_zmodemProc,SIGNAL (finished(int,QProcess::ExitStatus)), + this, SLOT(zmodemFinished())); + + _zmodemProc->start(); + + disconnect( _shellProcess,SIGNAL(receivedData(const char*,int)), this, SLOT(onReceiveBlock(const char*,int)) ); + connect( _shellProcess,SIGNAL(receivedData(const char*,int)), this, SLOT(zmodemRcvBlock(const char*,int)) ); + + _zmodemProgress = new ZModemDialog(QApplication::activeWindow(), false, + i18n("ZModem Progress")); + + connect(_zmodemProgress, SIGNAL(user1Clicked()), + this, SLOT(zmodemFinished())); + + _zmodemProgress->show(); +} + +void Session::zmodemReadAndSendBlock() +{ + _zmodemProc->setReadChannel( QProcess::StandardOutput ); + QByteArray data = _zmodemProc->readAll(); + + if ( data.count() == 0 ) + return; + + _shellProcess->sendData(data.constData(),data.count()); +} + +void Session::zmodemReadStatus() +{ + _zmodemProc->setReadChannel( QProcess::StandardError ); + QByteArray msg = _zmodemProc->readAll(); + while(!msg.isEmpty()) + { + int i = msg.indexOf('\015'); + int j = msg.indexOf('\012'); + QByteArray txt; + if ((i != -1) && ((j == -1) || (i < j))) + { + msg = msg.mid(i+1); + } + else if (j != -1) + { + txt = msg.left(j); + msg = msg.mid(j+1); + } + else + { + txt = msg; + msg.truncate(0); + } + if (!txt.isEmpty()) + _zmodemProgress->addProgressText(QString::fromLocal8Bit(txt)); + } +} + +void Session::zmodemRcvBlock(const char *data, int len) +{ + QByteArray ba( data, len ); + + _zmodemProc->write( ba ); +} + +void Session::zmodemFinished() +{ + // zmodemFinished() is called by QProcess's finished() and + // ZModemDialog's user1Clicked(). Therefore, an invocation by + // user1Clicked() will recursively invoke this function again + // when the KProcess is deleted! + if (_zmodemProc) { + KProcess* process = _zmodemProc; + _zmodemProc = 0; // Set _zmodemProc to 0 avoid recursive invocations! + _zmodemBusy = false; + delete process; // Now, the KProcess may be disposed safely. + + disconnect( _shellProcess,SIGNAL(receivedData(const char*,int)), this ,SLOT(zmodemRcvBlock(const char*,int)) ); + connect( _shellProcess,SIGNAL(receivedData(const char*,int)), this, SLOT(onReceiveBlock(const char*,int)) ); + + _shellProcess->sendData("\030\030\030\030", 4); // Abort + _shellProcess->sendData("\001\013\n", 3); // Try to get prompt back + _zmodemProgress->transferDone(); + } +} +*/ + +void Session::onReceiveBlock( const char* buf, int len ) +{ + _emulation->receiveData( buf, len ); + emit receivedData( QString::fromLatin1( buf, len ) ); +} + +QSize Session::size() +{ + return _emulation->imageSize(); +} + +void Session::setSize(const QSize& size) +{ + if ((size.width() <= 1) || (size.height() <= 1)) + return; + + emit resizeRequest(size); +} +int Session::processId() const +{ + return _shellProcess->pid(); +} + +void Session::setTitle(int role , const QString& title) +{ + switch (role) { + case (0): + this->setTitle(Session::NameRole, title); + break; + case (1): + this->setTitle(Session::DisplayedTitleRole, title); + break; + } +} + +QString Session::title(int role) const +{ + switch (role) { + case (0): + return this->title(Session::NameRole); + case (1): + return this->title(Session::DisplayedTitleRole); + default: + return QString(); + } +} + +void Session::setTabTitleFormat(int context , const QString& format) +{ + switch (context) { + case (0): + this->setTabTitleFormat(Session::LocalTabTitle, format); + break; + case (1): + this->setTabTitleFormat(Session::RemoteTabTitle, format); + break; + } +} + +QString Session::tabTitleFormat(int context) const +{ + switch (context) { + case (0): + return this->tabTitleFormat(Session::LocalTabTitle); + case (1): + return this->tabTitleFormat(Session::RemoteTabTitle); + default: + return QString(); + } +} + +int Session::foregroundProcessId() +{ + int pid; + + bool ok = false; + pid = getProcessInfo()->pid(&ok); + if (!ok) + pid = -1; + + return pid; +} + +bool Session::isForegroundProcessActive() +{ + // foreground process info is always updated after this + return updateForegroundProcessInfo() && (processId() != _foregroundPid); +} + +QString Session::foregroundProcessName() +{ + QString name; + + if (updateForegroundProcessInfo()) + { + bool ok = false; + name = _foregroundProcessInfo->name(&ok); + if (!ok) + name.clear(); + } + + return name; +} + +/* +void Session::saveSession(KConfigGroup& group) +{ + group.writePathEntry("WorkingDir", currentWorkingDirectory()); + group.writeEntry("LocalTab", tabTitleFormat(LocalTabTitle)); + group.writeEntry("RemoteTab", tabTitleFormat(RemoteTabTitle)); + group.writeEntry("SessionGuid", _uniqueIdentifier.toString()); + group.writeEntry("Encoding", QString(codec())); +} + +void Session::restoreSession(KConfigGroup& group) +{ + QString value; + + value = group.readPathEntry("WorkingDir", QString()); + if (!value.isEmpty()) setInitialWorkingDirectory(value); + value = group.readEntry("LocalTab"); + if (!value.isEmpty()) setTabTitleFormat(LocalTabTitle, value); + value = group.readEntry("RemoteTab"); + if (!value.isEmpty()) setTabTitleFormat(RemoteTabTitle, value); + value = group.readEntry("SessionGuid"); + if (!value.isEmpty()) _uniqueIdentifier = QUuid(value); + value = group.readEntry("Encoding"); + if (!value.isEmpty()) setCodec(value.toUtf8()); +} +*/ + +SessionGroup::SessionGroup(QObject* parent) + : QObject(parent), _masterMode(0) +{ +} +SessionGroup::~SessionGroup() +{ +} +int SessionGroup::masterMode() const { return _masterMode; } +QList<Session*> SessionGroup::sessions() const { return _sessions.keys(); } +bool SessionGroup::masterStatus(Session* session) const { return _sessions[session]; } + +void SessionGroup::addSession(Session* session) +{ + connect(session,SIGNAL(finished()),this,SLOT(sessionFinished())); + _sessions.insert(session,false); +} +void SessionGroup::removeSession(Session* session) +{ + disconnect(session,SIGNAL(finished()),this,SLOT(sessionFinished())); + setMasterStatus(session,false); + _sessions.remove(session); +} +void SessionGroup::sessionFinished() +{ + Session* session = qobject_cast<Session*>(sender()); + Q_ASSERT(session); + removeSession(session); +} +void SessionGroup::setMasterMode(int mode) +{ + _masterMode = mode; +} +QList<Session*> SessionGroup::masters() const +{ + return _sessions.keys(true); +} +void SessionGroup::setMasterStatus(Session* session , bool master) +{ + const bool wasMaster = _sessions[session]; + + if (wasMaster == master) { + // No status change -> nothing to do. + return; + } + _sessions[session] = master; + + if(master) { + connect( session->emulation() , SIGNAL(sendData(const char*,int)) , this, + SLOT(forwardData(const char*,int)) ); + } + else { + disconnect( session->emulation() , SIGNAL(sendData(const char*,int)) , this, + SLOT(forwardData(const char*,int)) ); + } +} +void SessionGroup::forwardData(const char* data, int size) +{ + static bool _inForwardData = false; + if(_inForwardData) { // Avoid recursive calls among session groups! + // A recursive call happens when a master in group A calls forwardData() + // in group B. If one of the destination sessions in group B is also a + // master of a group including the master session of group A, this would + // again call forwardData() in group A, and so on. + return; + } + + _inForwardData = true; + QListIterator<Session*> iter(_sessions.keys()); + while(iter.hasNext()) { + Session* other = iter.next(); + if(!_sessions[other]) { + other->emulation()->sendString(data, size); + } + } + _inForwardData = false; +} + +#include "Session.moc" +
new file mode 100644 --- /dev/null +++ b/gui/konsole/Session.h @@ -0,0 +1,764 @@ +/* + This file is part of Konsole, an X terminal. + + Copyright 2007-2008 by Robert Knight <robertknight@gmail.com> + Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de> + Copyright 2009 by Thomas Dreibholz <dreibh@iem.uni-due.de> + + 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 SESSION_H +#define SESSION_H + +// Qt +#include <QtCore/QStringList> +#include <QtCore/QByteRef> +#include <QtCore/QSize> +#include <QUuid> +#include <QWidget> + +// KDE +//#include <KApplication> +//#include <KMainWindow> +#include "konsole_export.h" + +// Konsole +#include "History.h" + +class KProcess; +class KUrl; + +namespace Konsole +{ + +class Emulation; +class Pty; +class ProcessInfo; +class TerminalDisplay; + //class ZModemDialog; + +/** + * Represents a terminal session consisting of a pseudo-teletype and a terminal emulation. + * The pseudo-teletype (or PTY) handles I/O between the terminal process and Konsole. + * The terminal emulation ( Emulation and subclasses ) processes the output stream from the + * PTY and produces a character image which is then shown on views connected to the session. + * + * Each Session can be connected to one or more views by using the addView() method. + * The attached views can then display output from the program running in the terminal + * or send input to the program in the terminal in the form of keypresses and mouse + * activity. + */ +class KONSOLEPRIVATE_EXPORT Session : public QObject +{ +Q_OBJECT +Q_CLASSINFO("D-Bus Interface", "org.kde.konsole.Session") + +public: + Q_PROPERTY(QString name READ nameTitle) + Q_PROPERTY(int processId READ processId) + Q_PROPERTY(QString keyBindings READ keyBindings WRITE setKeyBindings) + Q_PROPERTY(QSize size READ size WRITE setSize) + + /** + * Constructs a new session. + * + * To start the terminal process, call the run() method, + * after specifying the program and arguments + * using setProgram() and setArguments() + * + * If no program or arguments are specified explicitly, the Session + * falls back to using the program specified in the SHELL environment + * variable. + */ + explicit Session(QObject* parent = 0); + ~Session(); + + /** + * Connect to an existing terminal. When a new Session() is constructed it + * automatically searches for and opens a new teletype. If you want to + * use an existing teletype (given its file descriptor) call this after + * constructing the session. + * + * Calling openTeletype() while a session is running has no effect. + * + * @param masterFd The file descriptor of the pseudo-teletype master (See KPtyProcess::KPtyProcess()) + */ + void openTeletype(int masterFd); + + /** + * Returns true if the session is currently running. This will be true + * after run() has been called successfully. + */ + bool isRunning() const; + + /** + * Adds a new view for this session. + * + * The viewing widget will display the output from the terminal and + * input from the viewing widget (key presses, mouse activity etc.) + * will be sent to the terminal. + * + * Views can be removed using removeView(). The session is automatically + * closed when the last view is removed. + */ + void addView(TerminalDisplay* widget); + /** + * Removes a view from this session. When the last view is removed, + * the session will be closed automatically. + * + * @p widget will no longer display output from or send input + * to the terminal + */ + void removeView(TerminalDisplay* widget); + + /** + * Returns the views connected to this session + */ + QList<TerminalDisplay*> views() const; + + /** + * Returns the terminal emulation instance being used to encode / decode + * characters to / from the process. + */ + Emulation* emulation() const; + + /** Returns the unique ID for this session. */ + int sessionId() const; + + /** + * This enum describes the contexts for which separate + * tab title formats may be specified. + */ + enum TabTitleContext + { + /** Default tab title format */ + LocalTabTitle, + /** + * Tab title format used session currently contains + * a connection to a remote computer (via SSH) + */ + RemoteTabTitle + }; + + /** + * Returns true if the session currently contains a connection to a + * remote computer. It currently supports ssh. + */ + bool isRemote(); + + /** + * Sets the format used by this session for tab titles. + * + * @param context The context whoose format should be set. + * @param format The tab title format. This may be a mixture + * of plain text and dynamic elements denoted by a '%' character + * followed by a letter. (eg. %d for directory). The dynamic + * elements available depend on the @p context + */ + void setTabTitleFormat(TabTitleContext context , const QString& format); + /** Returns the format used by this session for tab titles. */ + QString tabTitleFormat(TabTitleContext context) const; + + + /** Returns the arguments passed to the shell process when run() is called. */ + QStringList arguments() const; + /** Returns the program name of the shell process started when run() is called. */ + QString program() const; + + /** + * Sets the command line arguments which the session's program will be passed when + * run() is called. + */ + void setArguments(const QStringList& arguments); + /** Sets the program to be executed when run() is called. */ + void setProgram(const QString& program); + + /** Returns the session's current working directory. */ + QString initialWorkingDirectory() { return _initialWorkingDir; } + + /** + * Sets the initial working directory for the session when it is run + * This has no effect once the session has been started. + */ + void setInitialWorkingDirectory( const QString& dir ); + + /** + * Returns the current directory of the foreground process in the session + */ + QString currentWorkingDirectory(); + + /** + * Sets the type of history store used by this session. + * Lines of output produced by the terminal are added + * to the history store. The type of history store + * used affects the number of lines which can be + * remembered before they are lost and the storage + * (in memory, on-disk etc.) used. + */ + void setHistoryType(const HistoryType& type); + /** + * Returns the type of history store used by this session. + */ + const HistoryType& historyType() const; + /** + * Clears the history store used by this session. + */ + void clearHistory(); + + /** + * Sets the key bindings used by this session. The bindings + * specify how input key sequences are translated into + * the character stream which is sent to the terminal. + * + * @param id The name of the key bindings to use. The + * names of available key bindings can be determined using the + * KeyboardTranslatorManager class. + */ + void setKeyBindings(const QString& id); + /** Returns the name of the key bindings used by this session. */ + QString keyBindings() const; + + /** + * This enum describes the available title roles. + */ + enum TitleRole + { + /** The name of the session. */ + NameRole, + /** The title of the session which is displayed in tabs etc. */ + DisplayedTitleRole + }; + + /** + * Return the session title set by the user (ie. the program running + * in the terminal), or an empty string if the user has not set a custom title + */ + QString userTitle() const; + + /** Convenience method used to read the name property. Returns title(Session::NameRole). */ + QString nameTitle() const { return title(Session::NameRole); } + /** Returns a title generated from tab format and process information. */ + QString getDynamicTitle(); + + /** Sets the name of the icon associated with this session. */ + void setIconName(const QString& iconName); + /** Returns the name of the icon associated with this session. */ + QString iconName() const; + + /** Return URL for the session. */ + //KUrl getUrl(); + + /** Sets the text of the icon associated with this session. */ + void setIconText(const QString& iconText); + /** Returns the text of the icon associated with this session. */ + QString iconText() const; + + /** Sets the session's title for the specified @p role to @p title. */ + void setTitle(TitleRole role , const QString& title); + + /** Returns the session's title for the specified @p role. */ + QString title(TitleRole role) const; + + /** + * Specifies whether a utmp entry should be created for the pty used by this session. + * If true, KPty::login() is called when the session is started. + */ + void setAddToUtmp(bool); + + /** + * Specifies whether to close the session automatically when the terminal + * process terminates. + */ + void setAutoClose(bool b) { _autoClose = b; } + + /** Returns true if the user has started a program in the session. */ + bool isForegroundProcessActive(); + + /** Returns the name of the current foreground process. */ + QString foregroundProcessName(); + + /** Returns the terminal session's window size in lines and columns. */ + QSize size(); + /** + * Emits a request to resize the session to accommodate + * the specified window size. + * + * @param size The size in lines and columns to request. + */ + void setSize(const QSize& size); + + /** + * Sets whether the session has a dark background or not. The session + * uses this information to set the COLORFGBG variable in the process's + * environment, which allows the programs running in the terminal to determine + * whether the background is light or dark and use appropriate colors by default. + * + * This has no effect once the session is running. + */ + void setDarkBackground(bool darkBackground); + /** + * Returns true if the session has a dark background. + * See setDarkBackground() + */ + bool hasDarkBackground() const; + + /** + * Attempts to get the shell program to redraw the current display area. + * This can be used after clearing the screen, for example, to get the + * shell to redraw the prompt line. + */ + void refresh(); + + // void startZModem(const QString &rz, const QString &dir, const QStringList &list); + // void cancelZModem(); + // bool isZModemBusy() { return _zmodemBusy; } + + /** + * Possible values of the @p what parameter for setUserTitle() + * See "Operating System Controls" section on http://rtfm.etla.org/xterm/ctlseq.html + */ + enum UserTitleChange + { + IconNameAndWindowTitle = 0, + IconName = 1, + WindowTitle = 2, + TextColor = 10, + BackgroundColor = 11, + SessionName = 30, + ProfileChange = 50 // this clashes with Xterm's font change command + }; + + // Sets the text codec used by this sessions terminal emulation. + void setCodec(QTextCodec* codec); + + // session management + //void saveSession(KConfigGroup& group); + //void restoreSession(KConfigGroup& group); + +public slots: + + /** + * Starts the terminal session. + * + * This creates the terminal process and connects the teletype to it. + */ + void run(); + + /** + * Returns the environment of this session as a list of strings like + * VARIABLE=VALUE + */ + Q_SCRIPTABLE QStringList environment() const; + + /** + * Sets the environment for this session. + * @p environment should be a list of strings like + * VARIABLE=VALUE + */ + Q_SCRIPTABLE void setEnvironment(const QStringList& environment); + + /** + * Closes the terminal session. This sends a hangup signal + * (SIGHUP) to the terminal process and causes the finished() + * signal to be emitted. If the process does not respond to the SIGHUP signal + * then the terminal connection (the pty) is closed and Konsole waits for the + * process to exit. + */ + Q_SCRIPTABLE void close(); + + /** + * Changes the session title or other customizable aspects of the terminal + * emulation display. For a list of what may be changed see the + * Emulation::titleChanged() signal. + * + * @param what The feature being changed. Value is one of UserTitleChange + * @param caption The text part of the terminal command + */ + void setUserTitle( int what , const QString &caption ); + + /** + * Enables monitoring for activity in the session. + * This will cause notifySessionState() to be emitted + * with the NOTIFYACTIVITY state flag when output is + * received from the terminal. + */ + Q_SCRIPTABLE void setMonitorActivity(bool); + + /** Returns true if monitoring for activity is enabled. */ + Q_SCRIPTABLE bool isMonitorActivity() const; + + /** + * Enables monitoring for silence in the session. + * This will cause notifySessionState() to be emitted + * with the NOTIFYSILENCE state flag when output is not + * received from the terminal for a certain period of + * time, specified with setMonitorSilenceSeconds() + */ + Q_SCRIPTABLE void setMonitorSilence(bool); + + /** + * Returns true if monitoring for inactivity (silence) + * in the session is enabled. + */ + Q_SCRIPTABLE bool isMonitorSilence() const; + + /** See setMonitorSilence() */ + Q_SCRIPTABLE void setMonitorSilenceSeconds(int seconds); + + /** + * Sets whether flow control is enabled for this terminal + * session. + */ + Q_SCRIPTABLE void setFlowControlEnabled(bool enabled); + + /** Returns whether flow control is enabled for this terminal session. */ + Q_SCRIPTABLE bool flowControlEnabled() const; + + /** + * Sends @p text to the current foreground terminal program. + */ + Q_SCRIPTABLE void sendText(const QString& text) const; + + /** + * Sends a mouse event of type @p eventType emitted by button + * @p buttons on @p column/@p line to the current foreground + * terminal program + */ + Q_SCRIPTABLE void sendMouseEvent(int buttons, int column, int line, int eventType); + + /** + * Returns the process id of the terminal process. + * This is the id used by the system API to refer to the process. + */ + Q_SCRIPTABLE int processId() const; + + /** + * Returns the process id of the terminal's foreground process. + * This is initially the same as processId() but can change + * as the user starts other programs inside the terminal. + */ + Q_SCRIPTABLE int foregroundProcessId(); + + /** Sets the text codec used by this sessions terminal emulation. + * Overloaded to accept a QByteArray for convenience since DBus + * does not accept QTextCodec directky. + */ + Q_SCRIPTABLE bool setCodec(QByteArray codec); + + /** Returns the codec used to decode incoming characters in this + * terminal emulation + */ + Q_SCRIPTABLE QByteArray codec(); + + /** Sets the session's title for the specified @p role to @p title. + * This is an overloaded member function for setTitle(TitleRole, QString) + * provided for convenience since enum data types may not be + * exported directly through DBus + */ + Q_SCRIPTABLE void setTitle(int role, const QString& title); + + /** Returns the session's title for the specified @p role. + * This is an overloaded member function for setTitle(TitleRole) + * provided for convenience since enum data types may not be + * exported directly through DBus + */ + Q_SCRIPTABLE QString title(int role) const; + + /** Returns the "friendly" version of the QUuid of this session. + * This is a QUuid with the braces and dashes removed, so it cannot be + * used to construct a new QUuid. The same text appears in the + * SHELL_SESSION_ID environment variable. + */ + Q_SCRIPTABLE QString shellSessionId() const; + + /** Sets the session's tab title format for the specified @p context to @p format. + * This is an overloaded member function for setTabTitleFormat(TabTitleContext, QString) + * provided for convenience since enum data types may not be + * exported directly through DBus + */ + Q_SCRIPTABLE void setTabTitleFormat(int context, const QString& format); + + /** Returns the session's tab title format for the specified @p context. + * This is an overloaded member function for tabTitleFormat(TitleRole) + * provided for convenience since enum data types may not be + * exported directly through DBus + */ + Q_SCRIPTABLE QString tabTitleFormat(int context) const; + +signals: + + /** Emitted when the terminal process starts. */ + void started(); + + /** + * Emitted when the terminal process exits. + */ + void finished(); + + /** + * Emitted when output is received from the terminal process. + */ + void receivedData( const QString& text ); + + /** Emitted when the session's title has changed. */ + void titleChanged(); + + /** + * Emitted when the activity state of this session changes. + * + * @param state The new state of the session. This may be one + * of NOTIFYNORMAL, NOTIFYSILENCE or NOTIFYACTIVITY + */ + void stateChanged(int state); + + /** Emitted when a bell event occurs in the session. */ + void bellRequest( const QString& message ); + + /** + * Requests that the color the text for any tabs associated with + * this session should be changed; + * + * TODO: Document what the parameter does + */ + void changeTabTextColorRequest(int); + + /** + * Requests that the background color of views on this session + * should be changed. + */ + void changeBackgroundColorRequest(const QColor&); + /** + * Requests that the text color of views on this session should + * be changed to @p color. + */ + void changeForegroundColorRequest(const QColor&); + + /** TODO: Document me. */ + void openUrlRequest(const QString& url); + + /** TODO: Document me. */ + //void zmodemDetected(); + + /** + * Emitted when the terminal process requests a change + * in the size of the terminal window. + * + * @param size The requested window size in terms of lines and columns. + */ + void resizeRequest(const QSize& size); + + /** + * Emitted when a profile change command is received from the terminal. + * + * @param text The text of the command. This is a string of the form + * "PropertyName=Value;PropertyName=Value ..." + */ + void profileChangeCommandReceived(const QString& text); + + /** + * Emitted when the flow control state changes. + * + * @param enabled True if flow control is enabled or false otherwise. + */ + void flowControlEnabledChanged(bool enabled); + +private slots: + void done(int); + + // void fireZModemDetected(); + + void onReceiveBlock( const char* buffer, int len ); + void monitorTimerDone(); + + void onViewSizeChange(int height, int width); + + void activityStateSet(int); + + //automatically detach views from sessions when view is destroyed + void viewDestroyed(QObject* view); + + //void zmodemReadStatus(); + //void zmodemReadAndSendBlock(); + //void zmodemRcvBlock(const char *data, int len); + //void zmodemFinished(); + + void updateFlowControlState(bool suspended); + void updateWindowSize(int lines, int columns); +private: + + void updateTerminalSize(); + WId windowId() const; + bool kill(int signal); + // print a warning message in the terminal. This is used + // if the program fails to start, or if the shell exits in + // an unsuccessful manner + void terminalWarning(const QString& message); + // checks that the binary 'program' is available and can be executed + // returns the binary name if available or an empty string otherwise + QString checkProgram(const QString& program) const; + ProcessInfo* getProcessInfo(); + void updateSessionProcessInfo(); + bool updateForegroundProcessInfo(); + ProcessInfo* updateWorkingDirectory(); + + QUuid _uniqueIdentifier; // SHELL_SESSION_ID + + Pty* _shellProcess; + Emulation* _emulation; + + QList<TerminalDisplay*> _views; + + bool _monitorActivity; + bool _monitorSilence; + bool _notifiedActivity; + bool _masterMode; + bool _autoClose; + bool _wantedClose; + QTimer* _monitorTimer; + + int _silenceSeconds; + + QString _nameTitle; + QString _displayTitle; + QString _userTitle; + + QString _localTabTitleFormat; + QString _remoteTabTitleFormat; + + QString _iconName; + QString _iconText; // as set by: echo -en '\033]1;IconText\007 + bool _addToUtmp; + bool _flowControl; + bool _fullScripting; + + QString _program; + QStringList _arguments; + + QStringList _environment; + int _sessionId; + + QString _initialWorkingDir; + QString _currentWorkingDir; + + ProcessInfo* _sessionProcessInfo; + ProcessInfo* _foregroundProcessInfo; + int _foregroundPid; + + // ZModem + // bool _zmodemBusy; + // KProcess* _zmodemProc; + // ZModemDialog* _zmodemProgress; + + // Color/Font Changes by ESC Sequences + + QColor _modifiedBackground; // as set by: echo -en '\033]11;Color\007 + + QString _profileKey; + + bool _hasDarkBackground; + + static int lastSessionId; + +}; + +/** + * Provides a group of sessions which is divided into master and slave sessions. + * Activity in master sessions can be propagated to all sessions within the group. + * The type of activity which is propagated and method of propagation is controlled + * by the masterMode() flags. + */ +class SessionGroup : public QObject +{ +Q_OBJECT + +public: + /** Constructs an empty session group. */ + SessionGroup(QObject* parent); + /** Destroys the session group and removes all connections between master and slave sessions. */ + ~SessionGroup(); + + /** Adds a session to the group. */ + void addSession( Session* session ); + /** Removes a session from the group. */ + void removeSession( Session* session ); + + /** Returns the list of sessions currently in the group. */ + QList<Session*> sessions() const; + + /** + * Sets whether a particular session is a master within the group. + * Changes or activity in the group's master sessions may be propagated + * to all the sessions in the group, depending on the current masterMode() + * + * @param session The session whoose master status should be changed. + * @param master True to make this session a master or false otherwise + */ + void setMasterStatus( Session* session , bool master ); + /** Returns the master status of a session. See setMasterStatus() */ + bool masterStatus( Session* session ) const; + + /** + * This enum describes the options for propagating certain activity or + * changes in the group's master sessions to all sessions in the group. + */ + enum MasterMode + { + /** + * Any input key presses in the master sessions are sent to all + * sessions in the group. + */ + CopyInputToAll = 1 + }; + + /** + * Specifies which activity in the group's master sessions is propagated + * to all sessions in the group. + * + * @param mode A bitwise OR of MasterMode flags. + */ + void setMasterMode( int mode ); + /** + * Returns a bitwise OR of the active MasterMode flags for this group. + * See setMasterMode() + */ + int masterMode() const; + +private slots: + void sessionFinished(); + void forwardData(const char* data, int size); + +private: + QList<Session*> masters() const; + + // maps sessions to their master status + QHash<Session*,bool> _sessions; + + int _masterMode; +}; + +} + +#endif + +/* + Local Variables: + mode: c++ + c-file-style: "stroustrup" + indent-tabs-mode: nil + tab-width: 4 + End: +*/
new file mode 100644 --- /dev/null +++ b/gui/konsole/SessionController.cpp @@ -0,0 +1,1560 @@ +/* + Copyright 2006-2008 by Robert Knight <robertknight@gmail.com> + Copyright 2009 by Thomas Dreibholz <dreibh@iem.uni-due.de> + + 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 "SessionController.h" + +// Qt +#include <QtGui/QApplication> +#include <QMenu> + +// KDE +#include <KAction> +#include <KDebug> +#include <KIcon> +#include <KInputDialog> +#include <KLocale> +#include <KMenu> +#include <KMessageBox> +#include <KRun> +#include <kshell.h> +#include <KStandardDirs> +#include <KToggleAction> +#include <KUrl> +#include <KXmlGuiWindow> +#include <KXMLGUIFactory> +#include <KXMLGUIBuilder> +#include <kdebug.h> +#include <kcodecaction.h> +#include <kdeversion.h> + +// Konsole +#include "EditProfileDialog.h" +#include "CopyInputDialog.h" +#include "Emulation.h" +#include "Filter.h" +#include "History.h" +#include "IncrementalSearchBar.h" +#include "RenameTabsDialog.h" +#include "ScreenWindow.h" +#include "Session.h" +#include "ProfileList.h" +#include "TerminalDisplay.h" +#include "SessionManager.h" + +// for SaveHistoryTask +#include <KFileDialog> +#include <KIO/Job> +#include <KJob> +#include "TerminalCharacterDecoder.h" + + +using namespace Konsole; + +KIcon SessionController::_activityIcon; +KIcon SessionController::_silenceIcon; +QSet<SessionController*> SessionController::_allControllers; +QPointer<SearchHistoryThread> SearchHistoryTask::_thread; +int SessionController::_lastControllerId; + +SessionController::SessionController(Session* session , TerminalDisplay* view, QObject* parent) + : ViewProperties(parent) + , KXMLGUIClient() + , _session(session) + , _view(view) + , _copyToGroup(0) + , _profileList(0) + , _previousState(-1) + , _viewUrlFilter(0) + , _searchFilter(0) + , _searchToggleAction(0) + , _findNextAction(0) + , _findPreviousAction(0) + , _urlFilterUpdateRequired(false) + , _codecAction(0) + , _changeProfileMenu(0) + , _listenForScreenWindowUpdates(false) + , _preventClose(false) +{ + _allControllers.insert(this); + + Q_ASSERT( session ); + Q_ASSERT( view ); + + // handle user interface related to session (menus etc.) + if (isKonsolePart()) + setXMLFile("konsole/partui.rc"); + else + setXMLFile("konsole/sessionui.rc"); + + setupActions(); + actionCollection()->addAssociatedWidget(view); + foreach (QAction* action, actionCollection()->actions()) + action->setShortcutContext(Qt::WidgetWithChildrenShortcut); + + setIdentifier(++_lastControllerId); + sessionTitleChanged(); + + view->installEventFilter(this); + + // listen for session resize requests + connect( _session , SIGNAL(resizeRequest(const QSize&)) , this , + SLOT(sessionResizeRequest(const QSize&)) ); + + // listen for popup menu requests + connect( _view , SIGNAL(configureRequest(QPoint)) , this, + SLOT(showDisplayContextMenu(QPoint)) ); + + // move view to newest output when keystrokes occur + connect( _view , SIGNAL(keyPressedSignal(QKeyEvent*)) , this , + SLOT(trackOutput(QKeyEvent*)) ); + + // listen to activity / silence notifications from session + connect( _session , SIGNAL(stateChanged(int)) , this , + SLOT(sessionStateChanged(int) )); + // listen to title and icon changes + connect( _session , SIGNAL(titleChanged()) , this , SLOT(sessionTitleChanged()) ); + + // listen for color changes + connect( _session , SIGNAL(changeBackgroundColorRequest(QColor)) , _view , SLOT(setBackgroundColor(QColor)) ); + connect( _session , SIGNAL(changeForegroundColorRequest(QColor)) , _view , SLOT(setForegroundColor(QColor)) ); + + // update the title when the session starts + connect( _session , SIGNAL(started()) , this , SLOT(snapshot()) ); + + // listen for output changes to set activity flag + connect( _session->emulation() , SIGNAL(outputChanged()) , this , + SLOT(fireActivity()) ); + + // listen for detection of ZModem transfer + connect( _session , SIGNAL(zmodemDetected()) , this , SLOT(zmodemDownload()) ); + + // listen for flow control status changes + connect( _session , SIGNAL(flowControlEnabledChanged(bool)) , _view , + SLOT(setFlowControlWarningEnabled(bool)) ); + _view->setFlowControlWarningEnabled(_session->flowControlEnabled()); + + // take a snapshot of the session state every so often when + // user activity occurs + // + // the timer is owned by the session so that it will be destroyed along + // with the session + QTimer* activityTimer = new QTimer(_session); + activityTimer->setSingleShot(true); + activityTimer->setInterval(2000); + connect( _view , SIGNAL(keyPressedSignal(QKeyEvent*)) , activityTimer , SLOT(start()) ); + connect( activityTimer , SIGNAL(timeout()) , this , SLOT(snapshot()) ); +} + +void SessionController::updateSearchFilter() +{ + if ( _searchFilter ) + { + Q_ASSERT( searchBar() && searchBar()->isVisible() ); + + _view->processFilters(); + } +} + +SessionController::~SessionController() +{ + if ( _view ) + _view->setScreenWindow(0); + + _allControllers.remove(this); +} +void SessionController::trackOutput(QKeyEvent* event) +{ + Q_ASSERT( _view->screenWindow() ); + + // jump to the end of the history buffer unless the key pressed + // is one of the three main modifiers, as these are used to select + // the selection mode (eg. Ctrl+Alt+<Left Click> for column/block selection) + switch (event->key()) + { + case Qt::Key_Shift: + case Qt::Key_Control: + case Qt::Key_Alt: + break; + default: + _view->screenWindow()->setTrackOutput(true); + } +} +void SessionController::requireUrlFilterUpdate() +{ + // this method is called every time the screen window's output changes, so do not + // do anything expensive here. + + _urlFilterUpdateRequired = true; +} +void SessionController::snapshot() +{ + Q_ASSERT( _session != 0 ); + + QString title = _session->getDynamicTitle(); + title = title.simplified(); + + // Visualize that the session is broadcasting to others + if (_copyToGroup && _copyToGroup->sessions().count() > 1) { + title.append('*'); + } + updateSessionIcon(); + + // apply new title + if ( !title.isEmpty() ) + _session->setTitle(Session::DisplayedTitleRole,title); + else + _session->setTitle(Session::DisplayedTitleRole,_session->title(Session::NameRole)); +} + +QString SessionController::currentDir() const +{ + return _session->currentWorkingDirectory(); +} + +KUrl SessionController::url() const +{ + return _session->getUrl(); +} + +void SessionController::rename() +{ + renameSession(); +} + +void SessionController::openUrl( const KUrl& url ) +{ + // handle local paths + if ( url.isLocalFile() ) + { + QString path = url.toLocalFile(); + _session->emulation()->sendText("cd " + KShell::quoteArg(path) + '\r'); + } + else if ( url.protocol() == "ssh" ) + { + _session->emulation()->sendText("ssh "); + + if ( url.port() > -1 ) + _session->emulation()->sendText("-p " + QString::number(url.port()) + ' ' ); + if ( url.hasUser() ) + _session->emulation()->sendText(url.user() + '@'); + if ( url.hasHost() ) + _session->emulation()->sendText(url.host() + '\r'); + } + else if ( url.protocol() == "telnet" ) + { + _session->emulation()->sendText("telnet "); + + if ( url.hasUser() ) + _session->emulation()->sendText("-l " + url.user() + ' '); + if ( url.hasHost() ) + _session->emulation()->sendText(url.host() + ' '); + if ( url.port() > -1 ) + _session->emulation()->sendText(QString::number(url.port())); + _session->emulation()->sendText("\r"); + } + else + { + //TODO Implement handling for other Url types + + KMessageBox::sorry(_view->window(), + i18n("Konsole does not know how to open the bookmark: ") + + url.prettyUrl()); + + kWarning(1211) << "Unable to open bookmark at url" << url << ", I do not know" + << " how to handle the protocol " << url.protocol(); + } +} + +bool SessionController::eventFilter(QObject* watched , QEvent* event) +{ + if ( watched == _view ) + { + if ( event->type() == QEvent::FocusIn ) + { + // notify the world that the view associated with this session has been focused + // used by the view manager to update the title of the MainWindow widget containing the view + emit focused(this); + + // when the view is focused, set bell events from the associated session to be delivered + // by the focused view + + // first, disconnect any other views which are listening for bell signals from the session + disconnect( _session , SIGNAL(bellRequest(const QString&)) , 0 , 0 ); + // second, connect the newly focused view to listen for the session's bell signal + connect( _session , SIGNAL(bellRequest(const QString&)) , + _view , SLOT(bell(const QString&)) ); + + if(_copyToAllTabsAction->isChecked()) { + // A session with "Copy To All Tabs" has come into focus: + // Ensure that newly created sessions are included in _copyToGroup! + copyInputToAllTabs(); + } + } + // when a mouse move is received, create the URL filter and listen for output changes if + // it has not already been created. If it already exists, then update only if the output + // has changed since the last update ( _urlFilterUpdateRequired == true ) + // + // also check that no mouse buttons are pressed since the URL filter only applies when + // the mouse is hovering over the view + if ( event->type() == QEvent::MouseMove && + (!_viewUrlFilter || _urlFilterUpdateRequired) && + ((QMouseEvent*)event)->buttons() == Qt::NoButton ) + { + if ( _view->screenWindow() && !_viewUrlFilter ) + { + connect( _view->screenWindow() , SIGNAL(scrolled(int)) , this , + SLOT(requireUrlFilterUpdate()) ); + connect( _view->screenWindow() , SIGNAL(outputChanged()) , this , + SLOT(requireUrlFilterUpdate()) ); + + // install filter on the view to highlight URLs + _viewUrlFilter = new UrlFilter(); + _view->filterChain()->addFilter( _viewUrlFilter ); + } + + _view->processFilters(); + _urlFilterUpdateRequired = false; + } + } + + return false; +} + +void SessionController::removeSearchFilter() +{ + if (!_searchFilter) + return; + + _view->filterChain()->removeFilter(_searchFilter); + delete _searchFilter; + _searchFilter = 0; +} + +void SessionController::setSearchBar(IncrementalSearchBar* searchBar) +{ + // disconnect the existing search bar + if ( _searchBar ) + { + disconnect( this , 0 , _searchBar , 0 ); + disconnect( _searchBar , 0 , this , 0 ); + } + + // remove any existing search filter + removeSearchFilter(); + + // connect new search bar + _searchBar = searchBar; + if ( _searchBar ) + { + connect( _searchBar , SIGNAL(closeClicked()) , this , SLOT(searchClosed()) ); + connect( _searchBar , SIGNAL(findNextClicked()) , this , SLOT(findNextInHistory()) ); + connect( _searchBar , SIGNAL(findPreviousClicked()) , this , SLOT(findPreviousInHistory()) ); + connect( _searchBar , SIGNAL(highlightMatchesToggled(bool)) , this , SLOT(highlightMatches(bool)) ); + + // if the search bar was previously active + // then re-enter search mode + searchHistory( _searchToggleAction->isChecked() ); + } +} +IncrementalSearchBar* SessionController::searchBar() const +{ + return _searchBar; +} + +void SessionController::setShowMenuAction(QAction* action) +{ + actionCollection()->addAction("show-menubar",action); +} + +void SessionController::setupActions() +{ + KAction* action = 0; + KToggleAction* toggleAction = 0; + KActionCollection* collection = actionCollection(); + + // Close Session + action = collection->addAction("close-session", this, SLOT(closeSession())); + action->setIcon(KIcon("tab-close")); + action->setText(i18n("&Close Tab")); + action->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_W)); + + // Open Browser + action = collection->addAction("open-browser", this, SLOT(openBrowser())); + action->setText(i18n("Open File Manager")); + action->setIcon(KIcon("system-file-manager")); + + // Copy and Paste + action = KStandardAction::copy(this, SLOT(copy()), collection); + action->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_C)); + + action = KStandardAction::paste(this, SLOT(paste()), collection); + KShortcut pasteShortcut = action->shortcut(); + pasteShortcut.setPrimary(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_V)); + pasteShortcut.setAlternate(QKeySequence(Qt::SHIFT + Qt::Key_Insert)); + action->setShortcut(pasteShortcut); + + action = collection->addAction("paste-selection", this, SLOT(pasteSelection())); + action->setText(i18n("Paste Selection")); + action->setShortcut(QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_Insert)); + + // Rename Session + action = collection->addAction("rename-session", this, SLOT(renameSession())); + action->setText( i18n("&Rename Tab...") ); + action->setShortcut( QKeySequence(Qt::CTRL+Qt::ALT+Qt::Key_S) ); + + // Copy Input To -> All Tabs in Current Window + _copyToAllTabsAction = collection->addAction("copy-input-to-all-tabs", this, SLOT(copyInputToAllTabs())); + _copyToAllTabsAction->setText(i18n("&All Tabs in Current Window")); + _copyToAllTabsAction->setCheckable(true); + + // Copy Input To -> Select Tabs + _copyToSelectedAction = collection->addAction("copy-input-to-selected-tabs", this, SLOT(copyInputToSelectedTabs())); + _copyToSelectedAction->setShortcut( QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Period) ); + _copyToSelectedAction->setText(i18n("&Select Tabs...")); + _copyToSelectedAction->setCheckable(true); + + // Copy Input To -> None + _copyToNoneAction = collection->addAction("copy-input-to-none", this, SLOT(copyInputToNone())); + _copyToNoneAction->setText(i18nc("@action:inmenu Do not select any tabs", "&None")); + _copyToNoneAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Slash)); + _copyToNoneAction->setCheckable(true); + _copyToNoneAction->setChecked(true); + + action = collection->addAction("zmodem-upload", this, SLOT(zmodemUpload())); + action->setText(i18n("&ZModem Upload...")); + action->setIcon(KIcon("document-open")); + action->setShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_U)); + + // Monitor + toggleAction = new KToggleAction(i18n("Monitor for &Activity"),this); + toggleAction->setShortcut(QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_A)); + action = collection->addAction("monitor-activity", toggleAction); + connect(action, SIGNAL(toggled(bool)), this, SLOT(monitorActivity(bool))); + + toggleAction = new KToggleAction(i18n("Monitor for &Silence"),this); + toggleAction->setShortcut(QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_I)); + action = collection->addAction("monitor-silence", toggleAction); + connect(action, SIGNAL(toggled(bool)), this, SLOT(monitorSilence(bool))); + + // Character Encoding + _codecAction = new KCodecAction(i18n("Set &Encoding"),this); + _codecAction->setIcon(KIcon("character-set")); + collection->addAction("set-encoding", _codecAction); + connect(_codecAction->menu(), SIGNAL(aboutToShow()), this, SLOT(updateCodecAction())); + connect(_codecAction, SIGNAL(triggered(QTextCodec*)), this, SLOT(changeCodec(QTextCodec*))); + + // Text Size + action = collection->addAction("enlarge-font", this, SLOT(increaseTextSize())); + action->setText(i18n("Enlarge Font")); + action->setIcon(KIcon("format-font-size-more")); + action->setShortcut(KShortcut(Qt::CTRL | Qt::Key_Plus)); + + action = collection->addAction("shrink-font", this, SLOT(decreaseTextSize())); + action->setText(i18n("Shrink Font")); + action->setIcon(KIcon("format-font-size-less")); + action->setShortcut(KShortcut(Qt::CTRL | Qt::Key_Minus)); + + // History + _searchToggleAction = KStandardAction::find(this, NULL, collection); + _searchToggleAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_F)); + _searchToggleAction->setCheckable(true); + connect(_searchToggleAction, SIGNAL(toggled(bool)), this, SLOT(searchHistory(bool))); + + _findNextAction = KStandardAction::findNext(this, SLOT(findNextInHistory()), collection); + _findNextAction->setEnabled(false); + + _findPreviousAction = KStandardAction::findPrev(this, SLOT(findPreviousInHistory()), collection); + _findPreviousAction->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_F3)); + _findPreviousAction->setEnabled(false); + + action = KStandardAction::saveAs(this, SLOT(saveHistory()), collection); + action->setText(i18n("Save Output &As...")); + + action = collection->addAction("configure-history", this, SLOT(showHistoryOptions())); + action->setText(i18n("Configure Scrollback...")); + action->setIcon(KIcon("configure")); + + action = collection->addAction("clear-history", this, SLOT(clearHistory())); + action->setText(i18n("Clear Scrollback")); + action->setIcon(KIcon("edit-clear-history")); + + action = collection->addAction("clear-history-and-reset", this, SLOT(clearHistoryAndReset())); + action->setText(i18n("Clear Scrollback and Reset")); + action->setIcon(KIcon("edit-clear-history")); + action->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_X)); + + // Profile Options + action = collection->addAction("edit-current-profile", this, SLOT(editCurrentProfile())); + action->setText(i18n("Configure Current Profile...")); + action->setIcon(KIcon("document-properties") ); + + _changeProfileMenu = new KActionMenu(i18n("Change Profile"), _view); + collection->addAction("change-profile", _changeProfileMenu); + connect(_changeProfileMenu->menu(), SIGNAL(aboutToShow()), this, SLOT(prepareChangeProfileMenu())); +} + +void SessionController::changeProfile(Profile::Ptr profile) +{ + SessionManager::instance()->setSessionProfile(_session,profile); +} + +void SessionController::prepareChangeProfileMenu() +{ + if (_changeProfileMenu->menu()->isEmpty()) { + _profileList = new ProfileList(false,this); + connect(_profileList, SIGNAL(profileSelected(Profile::Ptr)), this, SLOT(changeProfile(Profile::Ptr))); + } + + _changeProfileMenu->menu()->clear(); + _changeProfileMenu->menu()->addActions(_profileList->actions()); +} +void SessionController::updateCodecAction() +{ + _codecAction->setCurrentCodec(QString(_session->emulation()->codec()->name())); +} + +void SessionController::changeCodec(QTextCodec* codec) +{ + _session->setCodec(codec); +} + +void SessionController::editCurrentProfile() +{ + EditProfileDialog* dialog = new EditProfileDialog( QApplication::activeWindow() ); + + dialog->setProfile(SessionManager::instance()->sessionProfile(_session)); + dialog->show(); +} + +void SessionController::renameSession() +{ + QScopedPointer<RenameTabsDialog> dialog(new RenameTabsDialog(QApplication::activeWindow())); + dialog->setTabTitleText(_session->tabTitleFormat(Session::LocalTabTitle)); + dialog->setRemoteTabTitleText(_session->tabTitleFormat(Session::RemoteTabTitle)); + + if (!_session->isRemote()) { + dialog->focusTabTitleText(); + } else { + dialog->focusRemoteTabTitleText(); + } + + QPointer<Session> guard(_session); + int result = dialog->exec(); + if (!guard) + return; + + if (result) + { + QString tabTitle = dialog->tabTitleText(); + QString remoteTabTitle = dialog->remoteTabTitleText(); + + _session->setTabTitleFormat(Session::LocalTabTitle, tabTitle); + _session->setTabTitleFormat(Session::RemoteTabTitle, remoteTabTitle); + + // trigger an update of the tab text + snapshot(); + } +} +void SessionController::saveSession() +{ + Q_ASSERT(0); // not implemented yet + + //SaveSessionDialog dialog(_view); + //int result = dialog.exec(); +} +bool SessionController::confirmClose() const +{ + if (_session->isForegroundProcessActive()) + { + QString title = _session->foregroundProcessName(); + + // hard coded for now. In future make it possible for the user to specify which programs + // are ignored when considering whether to display a confirmation + QStringList ignoreList; + ignoreList << QString(qgetenv("SHELL")).section('/',-1); + if (ignoreList.contains(title)) + return true; + + QString question; + if (title.isEmpty()) + question = i18n("A program is currently running in this session." + " Are you sure you want to close it?"); + else + question = i18n("The program '%1' is currently running in this session." + " Are you sure you want to close it?",title); + + int result = KMessageBox::warningYesNo(_view->window(),question,i18n("Confirm Close")); + return (result == KMessageBox::Yes) ? true : false; + } + return true; +} +void SessionController::closeSession() +{ + if (_preventClose) + return; + + if (confirmClose()) + _session->close(); +} + +void SessionController::openBrowser() +{ + new KRun(url(), QApplication::activeWindow()); +} + +void SessionController::copy() +{ + _view->copyClipboard(); +} + +void SessionController::paste() +{ + _view->pasteClipboard(); +} +void SessionController::pasteSelection() +{ + _view->pasteSelection(); +} +static const KXmlGuiWindow* findWindow(const QObject* object) +{ + // Walk up the QObject hierarchy to find a KXmlGuiWindow. + while(object != NULL) { + const KXmlGuiWindow* window = dynamic_cast<const KXmlGuiWindow*>(object); + if(window != NULL) { + return(window); + } + object = object->parent(); + } + return(NULL); +} + +static bool hasTerminalDisplayInSameWindow(const Session* session, const KXmlGuiWindow* window) +{ + // Iterate all TerminalDisplays of this Session ... + QListIterator<TerminalDisplay*> terminalDisplayIterator(session->views()); + while(terminalDisplayIterator.hasNext()) { + const TerminalDisplay* terminalDisplay = terminalDisplayIterator.next(); + // ... and check whether a TerminalDisplay has the same + // window as given in the parameter + if(window == findWindow(terminalDisplay)) { + return(true); + } + } + return(false); +} + +void SessionController::copyInputToAllTabs() +{ + if(!_copyToGroup) { + _copyToGroup = new SessionGroup(this); + } + + // Find our window ... + const KXmlGuiWindow* myWindow = findWindow(_view); + + QSet<Session*> group = + QSet<Session*>::fromList(SessionManager::instance()->sessions()); + for(QSet<Session*>::iterator iterator = group.begin(); + iterator != group.end(); ++iterator) { + Session* session = *iterator; + + // First, ensure that the session is removed + // (necessary to avoid duplicates on addSession()!) + _copyToGroup->removeSession(session); + + // Add current session if it is displayed our window + if(hasTerminalDisplayInSameWindow(session, myWindow)) { + _copyToGroup->addSession(session); + } + } + _copyToGroup->setMasterStatus(_session, true); + _copyToGroup->setMasterMode(SessionGroup::CopyInputToAll); + + snapshot(); + _copyToAllTabsAction->setChecked(true); + _copyToSelectedAction->setChecked(false); + _copyToNoneAction->setChecked(false); +} + +void SessionController::copyInputToSelectedTabs() +{ + if (!_copyToGroup) + { + _copyToGroup = new SessionGroup(this); + _copyToGroup->addSession(_session); + _copyToGroup->setMasterStatus(_session,true); + _copyToGroup->setMasterMode(SessionGroup::CopyInputToAll); + } + + CopyInputDialog* dialog = new CopyInputDialog(_view); + dialog->setMasterSession(_session); + + QSet<Session*> currentGroup = QSet<Session*>::fromList(_copyToGroup->sessions()); + currentGroup.remove(_session); + + dialog->setChosenSessions(currentGroup); + + QPointer<Session> guard(_session); + int result = dialog->exec(); + if (!guard) + return; + + if (result) + { + QSet<Session*> newGroup = dialog->chosenSessions(); + newGroup.remove(_session); + + QSet<Session*> completeGroup = newGroup | currentGroup; + foreach(Session* session, completeGroup) + { + if (newGroup.contains(session) && !currentGroup.contains(session)) + _copyToGroup->addSession(session); + else if (!newGroup.contains(session) && currentGroup.contains(session)) + _copyToGroup->removeSession(session); + } + + _copyToGroup->setMasterStatus(_session, true); + _copyToGroup->setMasterMode(SessionGroup::CopyInputToAll); + snapshot(); + } + + delete dialog; + _copyToAllTabsAction->setChecked(false); + _copyToSelectedAction->setChecked(true); + _copyToNoneAction->setChecked(false); +} + +void SessionController::copyInputToNone() +{ + if (!_copyToGroup) // No 'Copy To' is active + return; + + QSet<Session*> group = + QSet<Session*>::fromList(SessionManager::instance()->sessions()); + for(QSet<Session*>::iterator iterator = group.begin(); + iterator != group.end(); ++iterator) { + Session* session = *iterator; + + if(session != _session) { + _copyToGroup->removeSession(*iterator); + } + } + delete _copyToGroup; + _copyToGroup = NULL; + snapshot(); + + _copyToAllTabsAction->setChecked(false); + _copyToSelectedAction->setChecked(false); + _copyToNoneAction->setChecked(true); +} + +void SessionController::searchClosed() +{ + _searchToggleAction->toggle(); +} + +#if 0 +void SessionController::searchHistory() +{ + searchHistory(true); +} +#endif + +void SessionController::listenForScreenWindowUpdates() +{ + if (_listenForScreenWindowUpdates) + return; + + connect( _view->screenWindow() , SIGNAL(outputChanged()) , this , + SLOT(updateSearchFilter()) ); + connect( _view->screenWindow() , SIGNAL(scrolled(int)) , this , + SLOT(updateSearchFilter()) ); + + _listenForScreenWindowUpdates = true; +} + +// searchHistory() may be called either as a result of clicking a menu item or +// as a result of changing the search bar widget +void SessionController::searchHistory(bool showSearchBar) +{ + if ( _searchBar ) + { + _searchBar->setVisible(showSearchBar); + + if (showSearchBar) + { + removeSearchFilter(); + + listenForScreenWindowUpdates(); + + _searchFilter = new RegExpFilter(); + _view->filterChain()->addFilter(_searchFilter); + connect( _searchBar , SIGNAL(searchChanged(const QString&)) , this , + SLOT(searchTextChanged(const QString&)) ); + + // invoke search for matches for the current search text + const QString& currentSearchText = _searchBar->searchText(); + if (!currentSearchText.isEmpty()) + { + searchTextChanged(currentSearchText); + } + + setFindNextPrevEnabled(true); + } + else + { + setFindNextPrevEnabled(false); + + disconnect( _searchBar , SIGNAL(searchChanged(const QString&)) , this , + SLOT(searchTextChanged(const QString&)) ); + + removeSearchFilter(); + + _view->setFocus( Qt::ActiveWindowFocusReason ); + } + } +} +void SessionController::setFindNextPrevEnabled(bool enabled) +{ + _findNextAction->setEnabled(enabled); + _findPreviousAction->setEnabled(enabled); +} +void SessionController::searchTextChanged(const QString& text) +{ + Q_ASSERT( _view->screenWindow() ); + + if ( text.isEmpty() ) + _view->screenWindow()->clearSelection(); + + // update search. this is called even when the text is + // empty to clear the view's filters + beginSearch(text , SearchHistoryTask::ForwardsSearch); +} +void SessionController::searchCompleted(bool success) +{ + if ( _searchBar ) + _searchBar->setFoundMatch(success); +} + +void SessionController::beginSearch(const QString& text , int direction) +{ + Q_ASSERT( _searchBar ); + Q_ASSERT( _searchFilter ); + + Qt::CaseSensitivity caseHandling = _searchBar->matchCase() ? Qt::CaseSensitive : Qt::CaseInsensitive; + QRegExp::PatternSyntax syntax = _searchBar->matchRegExp() ? QRegExp::RegExp : QRegExp::FixedString; + + QRegExp regExp( text.trimmed() , caseHandling , syntax ); + _searchFilter->setRegExp(regExp); + + if ( !regExp.isEmpty() ) + { + SearchHistoryTask* task = new SearchHistoryTask(this); + + connect( task , SIGNAL(completed(bool)) , this , SLOT(searchCompleted(bool)) ); + + task->setRegExp(regExp); + task->setSearchDirection( (SearchHistoryTask::SearchDirection)direction ); + task->setAutoDelete(true); + task->addScreenWindow( _session , _view->screenWindow() ); + task->execute(); + } + + _view->processFilters(); +} +void SessionController::highlightMatches(bool highlight) +{ + if ( highlight ) + { + _view->filterChain()->addFilter(_searchFilter); + _view->processFilters(); + } + else + { + _view->filterChain()->removeFilter(_searchFilter); + } + + _view->update(); +} +void SessionController::findNextInHistory() +{ + Q_ASSERT( _searchBar ); + Q_ASSERT( _searchFilter ); + + beginSearch(_searchBar->searchText(),SearchHistoryTask::ForwardsSearch); +} +void SessionController::findPreviousInHistory() +{ + Q_ASSERT( _searchBar ); + Q_ASSERT( _searchFilter ); + + beginSearch(_searchBar->searchText(),SearchHistoryTask::BackwardsSearch); +} +void SessionController::showHistoryOptions() +{ + HistorySizeDialog* dialog = new HistorySizeDialog( QApplication::activeWindow() ); + const HistoryType& currentHistory = _session->historyType(); + + if ( currentHistory.isEnabled() ) + { + if ( currentHistory.isUnlimited() ) + dialog->setMode( HistorySizeDialog::UnlimitedHistory ); + else + { + dialog->setMode( HistorySizeDialog::FixedSizeHistory ); + dialog->setLineCount( currentHistory.maximumLineCount() ); + } + } + else + dialog->setMode( HistorySizeDialog::NoHistory ); + + connect( dialog , SIGNAL(optionsChanged(int,int,bool)) , + this , SLOT(scrollBackOptionsChanged(int,int,bool)) ); + + dialog->show(); +} +void SessionController::sessionResizeRequest(const QSize& size) +{ + //kDebug(1211) << "View resize requested to " << size; + _view->setSize(size.width(),size.height()); +} +void SessionController::scrollBackOptionsChanged(int mode, int lines, bool saveToCurrentProfile ) +{ + switch (mode) + { + case HistorySizeDialog::NoHistory: + _session->setHistoryType( HistoryTypeNone() ); + break; + case HistorySizeDialog::FixedSizeHistory: + _session->setHistoryType( CompactHistoryType(lines) ); + break; + case HistorySizeDialog::UnlimitedHistory: + _session->setHistoryType( HistoryTypeFile() ); + break; + } + if (saveToCurrentProfile) + { + Profile::Ptr profile = SessionManager::instance()->sessionProfile(_session); + + switch (mode) + { + case HistorySizeDialog::NoHistory: + profile->setProperty(Profile::HistoryMode , Profile::DisableHistory); + break; + case HistorySizeDialog::FixedSizeHistory: + profile->setProperty(Profile::HistoryMode , Profile::FixedSizeHistory); + profile->setProperty(Profile::HistorySize , lines); + break; + case HistorySizeDialog::UnlimitedHistory: + profile->setProperty(Profile::HistoryMode , Profile::UnlimitedHistory); + break; + } + SessionManager::instance()->changeProfile(profile, profile->setProperties()); + } +} + +void SessionController::saveHistory() +{ + SessionTask* task = new SaveHistoryTask(this); + task->setAutoDelete(true); + task->addSession( _session ); + task->execute(); +} + +void SessionController::clearHistory() +{ + _session->clearHistory(); + _view->updateImage(); // To reset view scrollbar +} + +void SessionController::clearHistoryAndReset() +{ + Emulation* emulation = _session->emulation(); + emulation->reset(); + _session->refresh(); + clearHistory(); +} + +void SessionController::increaseTextSize() +{ + QFont font = _view->getVTFont(); + font.setPointSizeF(font.pointSizeF()+1); + _view->setVTFont(font); + + //TODO - Save this setting as a session default +} + +void SessionController::decreaseTextSize() +{ + static const qreal MinimumFontSize = 6; + + QFont font = _view->getVTFont(); + font.setPointSizeF( qMax(font.pointSizeF()-1,MinimumFontSize) ); + _view->setVTFont(font); + + //TODO - Save this setting as a session default +} + +void SessionController::monitorActivity(bool monitor) +{ + _session->setMonitorActivity(monitor); +} +void SessionController::monitorSilence(bool monitor) +{ + _session->setMonitorSilence(monitor); +} +void SessionController::updateSessionIcon() +{ + // Visualize that the session is broadcasting to others + if (_copyToGroup && _copyToGroup->sessions().count() > 1) { + // Master Mode: set different icon, to warn the user to be careful + setIcon(KIcon("emblem-important")); + } + else { + // Not in Master Mode: use normal icon + setIcon( _sessionIcon ); + } +} +void SessionController::sessionTitleChanged() +{ + if ( _sessionIconName != _session->iconName() ) + { + _sessionIconName = _session->iconName(); + _sessionIcon = KIcon( _sessionIconName ); + updateSessionIcon(); + } + + QString title = _session->title(Session::DisplayedTitleRole); + + // special handling for the "%w" marker which is replaced with the + // window title set by the shell + title.replace("%w",_session->userTitle()); + // special handling for the "%#" marker which is replaced with the + // number of the shell + title.replace("%#",QString::number(_session->sessionId())); + + if ( title.isEmpty() ) + title = _session->title(Session::NameRole); + + setTitle( title ); +} + +void SessionController::showDisplayContextMenu(const QPoint& position) +{ + // needed to make sure the popup menu is available, even if a hosting + // application did not merge our GUI. + if (!factory()) + { + if (!clientBuilder()) + { + setClientBuilder(new KXMLGUIBuilder(_view)); + } + + KXMLGUIFactory* factory = new KXMLGUIFactory(clientBuilder(), this); + factory->addClient(this); + //kDebug(1211) << "Created xmlgui factory" << factory; + } + + QMenu* popup = qobject_cast<QMenu*>(factory()->container("session-popup-menu",this)); + if (popup) + { + // prepend content-specific actions such as "Open Link", "Copy Email Address" etc. + QList<QAction*> contentActions = _view->filterActions(position); + QAction* contentSeparator = new QAction(popup); + contentSeparator->setSeparator(true); + contentActions << contentSeparator; + + _preventClose = true; + + popup->insertActions(popup->actions().value(0,0),contentActions); + QAction* chosen = popup->exec( _view->mapToGlobal(position) ); + + // remove content-specific actions, unless the close action was chosen + // in which case the popup menu will be partially destroyed at this point + foreach(QAction* action,contentActions) + popup->removeAction(action); + delete contentSeparator; + + _preventClose = false; + + if (chosen && chosen->objectName() == "close-session") + chosen->trigger(); + } + else + { + kWarning() << "Unable to display popup menu for session" + << _session->title(Session::NameRole) + << ", no GUI factory available to build the popup."; + } +} + +void SessionController::sessionStateChanged(int state) +{ + if ( state == _previousState ) + return; + + _previousState = state; + + // TODO - Replace the icon choices below when suitable icons for silence and activity + // are available + if ( state == NOTIFYACTIVITY ) + { + if (_activityIcon.isNull()) + { + _activityIcon = KIcon("dialog-information"); + } + + setIcon(_activityIcon); + } + else if ( state == NOTIFYSILENCE ) + { + if (_silenceIcon.isNull()) + { + _silenceIcon = KIcon("dialog-information"); + } + + setIcon(_silenceIcon); + } + else if ( state == NOTIFYNORMAL ) + { + if ( _sessionIconName != _session->iconName() ) + { + _sessionIconName = _session->iconName(); + _sessionIcon = KIcon( _sessionIconName ); + } + + updateSessionIcon(); + } +} + +void SessionController::zmodemDownload() +{ + QString zmodem = KGlobal::dirs()->findExe("rz"); + if(zmodem.isEmpty()) { + zmodem = KGlobal::dirs()->findExe("lrz"); + } + if(!zmodem.isEmpty()) { + const QString path = KFileDialog::getExistingDirectory( + QString(), _view, + i18n("Save ZModem Download to...")); + + if(!path.isEmpty()) { + _session->startZModem(zmodem, path, QStringList()); + return; + } + } + else { + KMessageBox::error(_view, + i18n("<p>A ZModem file transfer attempt has been detected, " + "but no suitable ZModem software was found on this system.</p>" + "<p>You may wish to install the 'rzsz' or 'lrzsz' package.</p>")); + } + _session->cancelZModem(); + return; +} + +void SessionController::zmodemUpload() +{ + if(_session->isZModemBusy()) { + KMessageBox::sorry(_view, + i18n("<p>The current session already has a ZModem file transfer in progress.</p>")); + return; + } + QString zmodem = KGlobal::dirs()->findExe("sz"); + if(zmodem.isEmpty()) { + zmodem = KGlobal::dirs()->findExe("lsz"); + } + if(zmodem.isEmpty()) { + KMessageBox::sorry(_view, + i18n("<p>No suitable ZModem software was found on this system.</p>" + "<p>You may wish to install the 'rzsz' or 'lrzsz' package.</p>")); + return; + } + + QStringList files = KFileDialog::getOpenFileNames(KUrl(), QString(), _view, + i18n("Select Files for ZModem Upload")); + if(!files.isEmpty()) { + _session->startZModem(zmodem, QString(), files); + } +} + +bool SessionController::isKonsolePart() const +{ + // Check to see if we are being called from Konsole or a KPart + if (QString(qApp->metaObject()->className()) == "Konsole::Application") + return false; + else + return true; +} + +SessionTask::SessionTask(QObject* parent) + : QObject(parent) + , _autoDelete(false) +{ +} +void SessionTask::setAutoDelete(bool enable) +{ + _autoDelete = enable; +} +bool SessionTask::autoDelete() const +{ + return _autoDelete; +} +void SessionTask::addSession(Session* session) +{ + _sessions << session; +} +QList<SessionPtr> SessionTask::sessions() const +{ + return _sessions; +} + +SaveHistoryTask::SaveHistoryTask(QObject* parent) + : SessionTask(parent) +{ +} +SaveHistoryTask::~SaveHistoryTask() +{ +} + +void SaveHistoryTask::execute() +{ + QListIterator<SessionPtr> iter(sessions()); + + // TODO - think about the UI when saving multiple history sessions, if there are more than two or + // three then providing a URL for each one will be tedious + + // TODO - show a warning ( preferably passive ) if saving the history output fails + // + + KFileDialog* dialog = new KFileDialog( QString(":konsole") /* check this */, + QString(), QApplication::activeWindow() ); + dialog->setOperationMode(KFileDialog::Saving); + dialog->setConfirmOverwrite(true); + + QStringList mimeTypes; + mimeTypes << "text/plain"; + mimeTypes << "text/html"; + dialog->setMimeFilter(mimeTypes,"text/plain"); + + // iterate over each session in the task and display a dialog to allow the user to choose where + // to save that session's history. + // then start a KIO job to transfer the data from the history to the chosen URL + while ( iter.hasNext() ) + { + SessionPtr session = iter.next(); + + dialog->setCaption( i18n("Save Output From %1",session->title(Session::NameRole)) ); + + int result = dialog->exec(); + + if ( result != QDialog::Accepted ) + continue; + + KUrl url = dialog->selectedUrl(); + + if ( !url.isValid() ) + { // UI: Can we make this friendlier? + KMessageBox::sorry( 0 , i18n("%1 is an invalid URL, the output could not be saved.",url.url()) ); + continue; + } + + KIO::TransferJob* job = KIO::put( url, + -1, // no special permissions + // overwrite existing files + // do not resume an existing transfer + // show progress information only for remote + // URLs + KIO::Overwrite | (url.isLocalFile() ? KIO::HideProgressInfo : KIO::DefaultFlags) + // a better solution would be to show progress + // information after a certain period of time + // instead, since the overall speed of transfer + // depends on factors other than just the protocol + // used + ); + + + SaveJob jobInfo; + jobInfo.session = session; + jobInfo.lastLineFetched = -1; // when each request for data comes in from the KIO subsystem + // lastLineFetched is used to keep track of how much of the history + // has already been sent, and where the next request should continue + // from. + // this is set to -1 to indicate the job has just been started + + if ( dialog->currentMimeFilter() == "text/html" ) + jobInfo.decoder = new HTMLDecoder(); + else + jobInfo.decoder = new PlainTextDecoder(); + + _jobSession.insert(job,jobInfo); + + connect( job , SIGNAL(dataReq(KIO::Job*,QByteArray&)), + this, SLOT(jobDataRequested(KIO::Job*,QByteArray&)) ); + connect( job , SIGNAL(result(KJob*)), + this, SLOT(jobResult(KJob*)) ); + } + + dialog->deleteLater(); +} +void SaveHistoryTask::jobDataRequested(KIO::Job* job , QByteArray& data) +{ + // TODO - Report progress information for the job + + // PERFORMANCE: Do some tests and tweak this value to get faster saving + const int LINES_PER_REQUEST = 500; + + SaveJob& info = _jobSession[job]; + + // transfer LINES_PER_REQUEST lines from the session's history + // to the save location + if ( info.session ) + { + // note: when retrieving lines from the emulation, + // the first line is at index 0. + + int sessionLines = info.session->emulation()->lineCount(); + + if ( sessionLines-1 == info.lastLineFetched ) + return; // if there is no more data to transfer then stop the job + + int copyUpToLine = qMin( info.lastLineFetched + LINES_PER_REQUEST , + sessionLines-1 ); + + QTextStream stream(&data,QIODevice::ReadWrite); + info.decoder->begin(&stream); + info.session->emulation()->writeToStream( info.decoder , info.lastLineFetched+1 , copyUpToLine ); + info.decoder->end(); + + // if there are still more lines to process after this request + // then insert a new line character + // to ensure that the next block of lines begins on a new line + // + // FIXME - There is still an extra new-line at the end of the save data. + if ( copyUpToLine <= sessionLines-1 ) + { + stream << '\n'; + } + + + info.lastLineFetched = copyUpToLine; + } +} +void SaveHistoryTask::jobResult(KJob* job) +{ + if ( job->error() ) + { + KMessageBox::sorry( 0 , i18n("A problem occurred when saving the output.\n%1",job->errorString()) ); + } + + TerminalCharacterDecoder * decoder = _jobSession[job].decoder; + + _jobSession.remove(job); + + delete decoder; + + // notify the world that the task is done + emit completed(true); + + if ( autoDelete() ) + deleteLater(); +} +void SearchHistoryTask::addScreenWindow( Session* session , ScreenWindow* searchWindow ) +{ + _windows.insert(session,searchWindow); +} +void SearchHistoryTask::execute() +{ + QMapIterator< SessionPtr , ScreenWindowPtr > iter(_windows); + + while ( iter.hasNext() ) + { + iter.next(); + executeOnScreenWindow( iter.key() , iter.value() ); + } +} + +void SearchHistoryTask::executeOnScreenWindow( SessionPtr session , ScreenWindowPtr window ) +{ + Q_ASSERT( session ); + Q_ASSERT( window ); + + Emulation* emulation = session->emulation(); + + int selectionColumn = 0; + int selectionLine = 0; + + window->getSelectionEnd(selectionColumn , selectionLine); + + if ( !_regExp.isEmpty() ) + { + int pos = -1; + const bool forwards = ( _direction == ForwardsSearch ); + int startLine = selectionLine + window->currentLine() + ( forwards ? 1 : -1 ); + // Temporary fix for #205495 + if (startLine < 0) startLine = 0; + const int lastLine = window->lineCount() - 1; + QString string; + + //text stream to read history into string for pattern or regular expression searching + QTextStream searchStream(&string); + + PlainTextDecoder decoder; + decoder.setRecordLinePositions(true); + + //setup first and last lines depending on search direction + int line = startLine; + + //read through and search history in blocks of 10K lines. + //this balances the need to retrieve lots of data from the history each time + //(for efficient searching) + //without using silly amounts of memory if the history is very large. + const int maxDelta = qMin(window->lineCount(),10000); + int delta = forwards ? maxDelta : -maxDelta; + + int endLine = line; + bool hasWrapped = false; // set to true when we reach the top/bottom + // of the output and continue from the other + // end + + //loop through history in blocks of <delta> lines. + do + { + // ensure that application does not appear to hang + // if searching through a lengthy output + QApplication::processEvents(); + + // calculate lines to search in this iteration + if ( hasWrapped ) + { + if ( endLine == lastLine ) + line = 0; + else if ( endLine == 0 ) + line = lastLine; + + endLine += delta; + + if ( forwards ) + endLine = qMin( startLine , endLine ); + else + endLine = qMax( startLine , endLine ); + } + else + { + endLine += delta; + + if ( endLine > lastLine ) + { + hasWrapped = true; + endLine = lastLine; + } else if ( endLine < 0 ) + { + hasWrapped = true; + endLine = 0; + } + } + + decoder.begin(&searchStream); + emulation->writeToStream(&decoder, qMin(endLine,line) , qMax(endLine,line) ); + decoder.end(); + + // line number search below assumes that the buffer ends with a new-line + string.append('\n'); + + pos = -1; + if (forwards) + pos = string.indexOf(_regExp); + else + pos = string.lastIndexOf(_regExp); + + //if a match is found, position the cursor on that line and update the screen + if ( pos != -1 ) + { + int newLines = 0; + QList<int> linePositions = decoder.linePositions(); + while (newLines < linePositions.count() && linePositions[newLines] <= pos) + newLines++; + + // ignore the new line at the start of the buffer + newLines--; + + int findPos = qMin(line,endLine) + newLines; + + highlightResult(window,findPos); + + emit completed(true); + + return; + } + + //clear the current block of text and move to the next one + string.clear(); + line = endLine; + + } while ( startLine != endLine ); + + // if no match was found, clear selection to indicate this + window->clearSelection(); + window->notifyOutputChanged(); + } + + emit completed(false); +} +void SearchHistoryTask::highlightResult(ScreenWindowPtr window , int findPos) +{ + //work out how many lines into the current block of text the search result was found + //- looks a little painful, but it only has to be done once per search. + + //kDebug(1211) << "Found result at line " << findPos; + + //update display to show area of history containing selection + window->scrollTo(findPos); + window->setSelectionStart( 0 , findPos - window->currentLine() , false ); + window->setSelectionEnd( window->columnCount() , findPos - window->currentLine() ); + window->setTrackOutput(false); + window->notifyOutputChanged(); +} + +SearchHistoryTask::SearchHistoryTask(QObject* parent) + : SessionTask(parent) + , _direction(ForwardsSearch) +{ + +} +void SearchHistoryTask::setSearchDirection( SearchDirection direction ) +{ + _direction = direction; +} +SearchHistoryTask::SearchDirection SearchHistoryTask::searchDirection() const +{ + return _direction; +} +void SearchHistoryTask::setRegExp(const QRegExp& expression) +{ + _regExp = expression; +} +QRegExp SearchHistoryTask::regExp() const +{ + return _regExp; +} + +#include "SessionController.moc" +
new file mode 100644 --- /dev/null +++ b/gui/konsole/SessionController.h @@ -0,0 +1,452 @@ +/* + Copyright 2006-2008 by Robert Knight <robertknight@gmail.com> + Copyright 2009 by Thomas Dreibholz <dreibh@iem.uni-due.de> + + 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 SESSIONCONTROLLER_H +#define SESSIONCONTROLLER_H + +// Qt +#include <QtGui/QIcon> +#include <QtCore/QList> +#include <QtCore/QPointer> +#include <QtCore/QString> +#include <QtCore/QThread> +#include <QtCore/QHash> + +// KDE +#include <KActionCollection> +#include <KActionMenu> +#include <KIcon> +#include <KXMLGUIClient> + +// Konsole +#include "HistorySizeDialog.h" +#include "ViewProperties.h" +#include "Profile.h" + +namespace KIO +{ + class Job; +} + +class QAction; +class QTextCodec; +class KCodecAction; +class KUrl; +class KJob; + +namespace Konsole +{ + +class Session; +class SessionGroup; +class ScreenWindow; +class TerminalDisplay; +class IncrementalSearchBar; +class ProfileList; +class UrlFilter; +class RegExpFilter; + +// SaveHistoryTask +class TerminalCharacterDecoder; + + +typedef QPointer<Session> SessionPtr; + +/** + * Provides the menu actions to manipulate a single terminal session and view pair. + * The actions provided by this class are defined in the sessionui.rc XML file. + * + * SessionController monitors the session and provides access to basic information + * about the session such as title(), icon() and currentDir(). SessionController + * provides notifications of activity in the session via the activity() signal. + * + * When the controlled view receives the focus, the focused() signal is emitted + * with a pointer to the controller. This can be used by main application window + * which contains the view to plug the controller's actions into the menu when + * the view is focused. + */ +class KONSOLEPRIVATE_EXPORT SessionController : public ViewProperties , public KXMLGUIClient +{ +Q_OBJECT + +public: + /** + * Constructs a new SessionController which operates on @p session and @p view. + */ + SessionController(Session* session , TerminalDisplay* view, QObject* parent); + ~SessionController(); + + /** Returns the session associated with this controller */ + QPointer<Session> session() { return _session; } + /** Returns the view associated with this controller */ + QPointer<TerminalDisplay> view() { return _view; } + + /** + * Returns true if the controller is valid. + * A valid controller is one which has a non-null session() and view(). + * + * Equivalent to "!session().isNull() && !view().isNull()" + */ + bool isValid() const; + + /** + * Sets the widget used for searches through the session's output. + * + * When the user clicks on the "Search Output" menu action the @p searchBar 's + * show() method will be called. The SessionController will then connect to the search + * bar's signals to update the search when the widget's controls are pressed. + */ + void setSearchBar( IncrementalSearchBar* searchBar ); + /** + * see setSearchBar() + */ + IncrementalSearchBar* searchBar() const; + + /** + * Sets the action displayed in the session's context menu to hide or + * show the menu bar. + */ + void setShowMenuAction(QAction* action); + + // reimplemented + virtual KUrl url() const; + virtual QString currentDir() const; + virtual void rename(); + virtual bool confirmClose() const; + + // Reimplemented to watch for events happening to the view + virtual bool eventFilter(QObject* watched , QEvent* event); + + /** Returns the set of all controllers that exist. */ + static QSet<SessionController*> allControllers() + { return _allControllers; } + +signals: + /** + * Emitted when the view associated with the controller is focused. + * This can be used by other classes to plug the controller's actions into a window's + * menus. + */ + void focused( SessionController* controller ); + +public slots: + /** + * Issues a command to the session to navigate to the specified URL. + * This may not succeed if the foreground program does not understand + * the command sent to it ( 'cd path' for local URLs ) or is not + * responding to input. + * + * openUrl() currently supports urls for local paths and those + * using the 'ssh' protocol ( eg. "ssh://joebloggs@hostname" ) + */ + void openUrl( const KUrl& url ); + +private slots: + // menu item handlers + void openBrowser(); + void copy(); + void paste(); + void pasteSelection(); // shortcut only + void copyInputToAllTabs(); + void copyInputToSelectedTabs(); + void copyInputToNone(); + void editCurrentProfile(); + void changeCodec(QTextCodec* codec); + void searchHistory(bool showSearchBar); + void findNextInHistory(); + void findPreviousInHistory(); + void saveHistory(); + void showHistoryOptions(); + void clearHistory(); + void clearHistoryAndReset(); + void closeSession(); + void monitorActivity(bool monitor); + void monitorSilence(bool monitor); + void increaseTextSize(); + void decreaseTextSize(); + void renameSession(); + void saveSession(); + void changeProfile(Profile::Ptr profile); + + // other + void prepareChangeProfileMenu(); + void updateCodecAction(); + void showDisplayContextMenu(const QPoint& position); + void sessionStateChanged(int state); + void sessionTitleChanged(); + void searchTextChanged(const QString& text); + void searchCompleted(bool success); + void searchClosed(); // called when the user clicks on the + // history search bar's close button + + void snapshot(); // called periodically as the user types + // to take a snapshot of the state of the + // foreground process in the terminal + + void requireUrlFilterUpdate(); + void highlightMatches(bool highlight); + void scrollBackOptionsChanged(int mode , int lines, bool saveToCurrentProfile); + void sessionResizeRequest(const QSize& size); + void trackOutput(QKeyEvent* event); // move view to end of current output + // when a key press occurs in the + // display area + + void updateSearchFilter(); + + void zmodemDownload(); + void zmodemUpload(); + + /* Returns true if called within a KPart; false if called within Konsole. */ + bool isKonsolePart() const; + +private: + // begins the search + // text - pattern to search for + // direction - value from SearchHistoryTask::SearchDirection enum to specify + // the search direction + void beginSearch(const QString& text , int direction); + void setupActions(); + void removeSearchFilter(); // remove and delete the current search filter if set + void setFindNextPrevEnabled(bool enabled); + void listenForScreenWindowUpdates(); + +private: + void updateSessionIcon(); + + QPointer<Session> _session; + QPointer<TerminalDisplay> _view; + SessionGroup* _copyToGroup; + + ProfileList* _profileList; + + KIcon _sessionIcon; + QString _sessionIconName; + int _previousState; + + UrlFilter* _viewUrlFilter; + RegExpFilter* _searchFilter; + + KAction* _copyToAllTabsAction; + KAction* _copyToSelectedAction; + KAction* _copyToNoneAction; + + KAction* _searchToggleAction; + KAction* _findNextAction; + KAction* _findPreviousAction; + + + bool _urlFilterUpdateRequired; + + QPointer<IncrementalSearchBar> _searchBar; + + KCodecAction* _codecAction; + + KActionMenu* _changeProfileMenu; + + bool _listenForScreenWindowUpdates; + bool _preventClose; + + static QSet<SessionController*> _allControllers; + static int _lastControllerId; + static KIcon _activityIcon; + static KIcon _silenceIcon; +}; +inline bool SessionController::isValid() const +{ + return !_session.isNull() && !_view.isNull(); +} + +/** + * Abstract class representing a task which can be performed on a group of sessions. + * + * Create a new instance of the appropriate sub-class for the task you want to perform and + * call the addSession() method to add each session which needs to be processed. + * + * Finally, call the execute() method to perform the sub-class specific action on each + * of the sessions. + */ +class SessionTask : public QObject +{ +Q_OBJECT + +public: + SessionTask(QObject* parent = 0); + + /** + * Sets whether the task automatically deletes itself when the task has been finished. + * Depending on whether the task operates synchronously or asynchronously, the deletion + * may be scheduled immediately after execute() returns or it may happen some time later. + */ + void setAutoDelete(bool enable); + /** Returns true if the task automatically deletes itself. See setAutoDelete() */ + bool autoDelete() const; + + /** Adds a new session to the group */ + void addSession(Session* session); + + /** + * Executes the task on each of the sessions in the group. + * The completed() signal is emitted when the task is finished, depending on the specific sub-class + * execute() may be synchronous or asynchronous + */ + virtual void execute() = 0; + +signals: + /** + * Emitted when the task has completed. + * Depending on the task this may occur just before execute() returns, or it + * may occur later + * + * @param success Indicates whether the task completed successfully or not + */ + void completed(bool success); + +protected: + + /** Returns a list of sessions in the group */ + QList< SessionPtr > sessions() const; + +private: + + bool _autoDelete; + QList< SessionPtr > _sessions; +}; + +/** + * A task which prompts for a URL for each session and saves that session's output + * to the given URL + */ +class SaveHistoryTask : public SessionTask +{ +Q_OBJECT + +public: + /** Constructs a new task to save session output to URLs */ + SaveHistoryTask(QObject* parent = 0); + virtual ~SaveHistoryTask(); + + /** + * Opens a save file dialog for each session in the group and begins saving + * each session's history to the given URL. + * + * The data transfer is performed asynchronously and will continue after execute() returns. + */ + virtual void execute(); + +private slots: + void jobDataRequested(KIO::Job* job , QByteArray& data); + void jobResult(KJob* job); + +private: + class SaveJob // structure to keep information needed to process + // incoming data requests from jobs + { + public: + SessionPtr session; // the session associated with a history save job + int lastLineFetched; // the last line processed in the previous data request + // set this to -1 at the start of the save job + + TerminalCharacterDecoder* decoder; // decoder used to convert terminal characters + // into output + + }; + + QHash<KJob*,SaveJob> _jobSession; +}; + +class SearchHistoryThread; +/** + * A task which searches through the output of sessions for matches for a given regular expression. + * SearchHistoryTask operates on ScreenWindow instances rather than sessions added by addSession(). + * A screen window can be added to the list to search using addScreenWindow() + * + * When execute() is called, the search begins in the direction specified by searchDirection(), + * starting at the position of the current selection. + * + * FIXME - This is not a proper implementation of SessionTask, in that it ignores sessions specified + * with addSession() + * + * TODO - Implementation requirements: + * May provide progress feedback to the user when searching very large output logs. + */ +class SearchHistoryTask : public SessionTask +{ +Q_OBJECT + +public: + /** + * This enum describes the strategies available for searching through the + * session's output. + */ + enum SearchDirection + { + /** Searches forwards through the output, starting at the current selection. */ + ForwardsSearch, + /** Searches backwars through the output, starting at the current selection. */ + BackwardsSearch + }; + + /** + * Constructs a new search task. + */ + explicit SearchHistoryTask(QObject* parent = 0); + + /** Adds a screen window to the list to search when execute() is called. */ + void addScreenWindow( Session* session , ScreenWindow* searchWindow); + + /** Sets the regular expression which is searched for when execute() is called */ + void setRegExp(const QRegExp& regExp); + /** Returns the regular expression which is searched for when execute() is called */ + QRegExp regExp() const; + + /** Specifies the direction to search in when execute() is called. */ + void setSearchDirection( SearchDirection direction ); + /** Returns the current search direction. See setSearchDirection(). */ + SearchDirection searchDirection() const; + + /** + * Performs a search through the session's history, starting at the position + * of the current selection, in the direction specified by setSearchDirection(). + * + * If it finds a match, the ScreenWindow specified in the constructor is + * scrolled to the position where the match occurred and the selection + * is set to the matching text. execute() then returns immediately. + * + * To continue the search looking for further matches, call execute() again. + */ + virtual void execute(); + +private: + typedef QPointer<ScreenWindow> ScreenWindowPtr; + + void executeOnScreenWindow( SessionPtr session , ScreenWindowPtr window ); + void highlightResult( ScreenWindowPtr window , int position); + + QMap< SessionPtr , ScreenWindowPtr > _windows; + QRegExp _regExp; + SearchDirection _direction; + + static QPointer<SearchHistoryThread> _thread; +}; + +} + +#endif //SESSIONCONTROLLER_H +
new file mode 100644 --- /dev/null +++ b/gui/konsole/SessionManager.cpp @@ -0,0 +1,978 @@ +/* + This source file is part of Konsole, a terminal emulator. + + 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 "SessionManager.h" + +// Qt +#include <QtCore/QDir> +#include <QtCore/QFileInfo> +#include <QtCore/QList> +#include <QtCore/QSignalMapper> +#include <QtCore/QString> +#include <QtCore/QTextCodec> + +// KDE +#include <klocale.h> +#include <kicon.h> +#include <krun.h> +#include <kshell.h> +#include <kconfig.h> +#include <kglobal.h> +#include <kdebug.h> +#include <kconfiggroup.h> +#include <kstandarddirs.h> +#include <kdesktopfile.h> + +// Konsole +#include "ColorScheme.h" +#include "Session.h" +#include "History.h" +#include "ShellCommand.h" + +using namespace Konsole; + +#if 0 + +bool Profile::isAvailable() const +{ + //TODO: Is it necessary to cache the result of the search? + + QString binary = KRun::binaryName( command(true) , false ); + binary = KShell::tildeExpand(binary); + + QString fullBinaryPath = KGlobal::dirs()->findExe(binary); + + if ( fullBinaryPath.isEmpty() ) + return false; + else + return true; +} +#endif + +bool profileIndexLessThan(const Profile::Ptr &p1, const Profile::Ptr &p2) +{ + return p1->menuIndexAsInt() <= p2->menuIndexAsInt(); +} + +bool profileNameLessThan(const Profile::Ptr &p1, const Profile::Ptr &p2) +{ + return QString::localeAwareCompare(p1->name(), p2->name()) <= 0; +} + +static void sortByIndexProfileList(QList<Profile::Ptr> &list) +{ + qStableSort(list.begin(), list.end(), profileIndexLessThan); +} + +static void sortByNameProfileList(QList<Profile::Ptr> &list) +{ + qStableSort(list.begin(), list.end(), profileNameLessThan); +} + +SessionManager::SessionManager() + : _loadedAllProfiles(false) + , _loadedFavorites(false) +{ + //map finished() signals from sessions + _sessionMapper = new QSignalMapper(this); + connect( _sessionMapper , SIGNAL(mapped(QObject*)) , this , + SLOT(sessionTerminated(QObject*)) ); + + //load fallback profile + _fallbackProfile = Profile::Ptr(new FallbackProfile); + addProfile(_fallbackProfile); + + //locate and load default profile + KSharedConfigPtr appConfig = KSharedConfig::openConfig("konsolerc"); + const KConfigGroup group = appConfig->group( "Desktop Entry" ); + QString defaultSessionFilename = group.readEntry("DefaultProfile","Shell.profile"); + + QString path = KGlobal::dirs()->findResource("data","konsole/"+defaultSessionFilename); + if (!path.isEmpty()) + { + Profile::Ptr profile = loadProfile(path); + if ( profile ) + _defaultProfile = profile; + } + + Q_ASSERT( _types.count() > 0 ); + Q_ASSERT( _defaultProfile ); + + // get shortcuts and paths of profiles associated with + // them - this doesn't load the shortcuts themselves, + // that is done on-demand. + loadShortcuts(); +} +Profile::Ptr SessionManager::loadProfile(const QString& shortPath) +{ + // the fallback profile has a 'special' path name, "FALLBACK/" + if (shortPath == _fallbackProfile->property<QString>(Profile::Path)) + return _fallbackProfile; + + QString path = shortPath; + + // add a suggested suffix and relative prefix if missing + QFileInfo fileInfo(path); + if ( fileInfo.suffix().isEmpty() ) + path.append(".profile"); + if ( fileInfo.path().isEmpty() || fileInfo.path() == "." ) + path.prepend(QString("konsole")+QDir::separator()); + + // if the file is not an absolute path, look it up + if ( !fileInfo.isAbsolute() ) + path = KStandardDirs::locate("data",path); + + // check that we have not already loaded this profile + QSetIterator<Profile::Ptr> iter(_types); + while ( iter.hasNext() ) + { + Profile::Ptr profile = iter.next(); + if ( profile->path() == path ) + return profile; + } + + // guard to prevent problems if a profile specifies itself as its parent + // or if there is recursion in the "inheritance" chain + // (eg. two profiles, A and B, specifying each other as their parents) + static QStack<QString> recursionGuard; + PopStackOnExit<QString> popGuardOnExit(recursionGuard); + + if (recursionGuard.contains(path)) + { + kWarning() << "Ignoring attempt to load profile recursively from" << path; + return _fallbackProfile; + } + else + recursionGuard.push(path); + + // load the profile + ProfileReader* reader = 0; + if ( path.endsWith(QLatin1String(".desktop")) ) + reader = 0; // new KDE3ProfileReader; + else + reader = new KDE4ProfileReader; + + if (!reader) + { + kWarning() << "Could not create loader to read profile from" << path; + return Profile::Ptr(); + } + + Profile::Ptr newProfile = Profile::Ptr(new Profile(defaultProfile())); + newProfile->setProperty(Profile::Path,path); + + QString parentProfilePath; + bool result = reader->readProfile(path,newProfile,parentProfilePath); + + if ( !parentProfilePath.isEmpty() ) + { + Profile::Ptr parentProfile = loadProfile(parentProfilePath); + newProfile->setParent(parentProfile); + } + + delete reader; + + if (!result) + { + kWarning() << "Could not load profile from " << path; + return Profile::Ptr(); + } + else + { + addProfile(newProfile); + return newProfile; + } +} +QStringList SessionManager::availableProfilePaths() const +{ + KDE3ProfileReader kde3Reader; + KDE4ProfileReader kde4Reader; + + QStringList profiles; + profiles += kde3Reader.findProfiles(); + profiles += kde4Reader.findProfiles(); + + return profiles; +} + +void SessionManager::loadAllProfiles() +{ + if ( _loadedAllProfiles ) + return; + + QStringList profiles = availableProfilePaths(); + + QListIterator<QString> iter(profiles); + while (iter.hasNext()) + loadProfile(iter.next()); + + _loadedAllProfiles = true; +} + +void SessionManager::sortProfiles(QList<Profile::Ptr> &list) +{ + + QList<Profile::Ptr> lackingIndices; + QList<Profile::Ptr> havingIndices; + + for (int i = 0; i < list.size(); ++i) + { + // dis-regard the fallback profile + if (list.at(i)->path() == _fallbackProfile->property<QString>(Profile::Path)) + continue; + + if (list.at(i)->menuIndexAsInt() == 0) + lackingIndices.append(list.at(i)); + else + havingIndices.append(list.at(i)); + } + + // sort by index + sortByIndexProfileList(havingIndices); + + // sort alphabetically those w/o an index + sortByNameProfileList(lackingIndices); + + // Put those with indices in sequential order w/o any gaps + int i = 0; + for (i = 0; i < havingIndices.size(); ++i) + { + Profile::Ptr tempProfile = havingIndices.at(i); + tempProfile->setProperty(Profile::MenuIndex, QString::number(i+1)); + havingIndices.replace(i, tempProfile); + } + // Put those w/o indices in sequential order + for (int j = 0; j < lackingIndices.size(); ++j) + { + Profile::Ptr tempProfile = lackingIndices.at(j); + tempProfile->setProperty(Profile::MenuIndex, QString::number(j+1+i)); + lackingIndices.replace(j, tempProfile); + } + + // combine the 2 list: first those who had indices + list.clear(); + list.append(havingIndices); + list.append(lackingIndices); +} + +void SessionManager::saveState() +{ + // save default profile + setDefaultProfile( _defaultProfile ); + + // save shortcuts + saveShortcuts(); + + // save favorites + saveFavorites(); +} +void SessionManager::closeAll() +{ + // close remaining sessions + foreach( Session* session , _sessions ) + { + session->close(); + } + _sessions.clear(); +} +SessionManager::~SessionManager() +{ + if (_sessions.count() > 0) + { + kWarning() << "Konsole SessionManager destroyed with sessions still alive"; + // ensure that the Session doesn't later try to call back and do things to the + // SessionManager + foreach(Session* session , _sessions) + disconnect(session , 0 , this , 0); + } +} + +const QList<Session*> SessionManager::sessions() +{ + return _sessions; +} + +void SessionManager::updateSession(Session* session) +{ + Profile::Ptr info = _sessionProfiles[session]; + + // Temp fix for crashes when changing profiles 256357, 246054 + if (!info) + info = defaultProfile(); + + Q_ASSERT( info ); + + applyProfile(session,info,false); + + // FIXME - This may update a lot more than just the session + // of interest. + emit sessionUpdated(session); +} + +Session* SessionManager::createSession(Profile::Ptr info) +{ + Session* session = 0; + + if (!info) + info = defaultProfile(); + + if (!_types.contains(info)) + addProfile(info); + + //configuration information found, create a new session based on this + session = new Session(); + applyProfile(session,info,false); + + connect( session , SIGNAL(profileChangeCommandReceived(QString)) , this , + SLOT(sessionProfileCommandReceived(QString)) ); + + //ask for notification when session dies + _sessionMapper->setMapping(session,session); + connect( session , SIGNAL(finished()) , _sessionMapper , + SLOT(map()) ); + + //add session to active list + _sessions << session; + _sessionProfiles.insert(session,info); + + Q_ASSERT( session ); + + return session; +} + +void SessionManager::sessionTerminated(QObject* sessionObject) +{ + Session* session = qobject_cast<Session*>(sessionObject); + + Q_ASSERT( session ); + + _sessions.removeAll(session); + session->deleteLater(); +} + +QList<Profile::Ptr> SessionManager::sortedFavorites() +{ + QList<Profile::Ptr> favorites = findFavorites().toList(); + + sortProfiles(favorites); + return favorites; +} + +QList<Profile::Ptr> SessionManager::loadedProfiles() const +{ + return _types.toList(); +} + +Profile::Ptr SessionManager::defaultProfile() const +{ + return _defaultProfile; +} +Profile::Ptr SessionManager::fallbackProfile() const +{ return _fallbackProfile; } + +QString SessionManager::saveProfile(Profile::Ptr info) +{ + ProfileWriter* writer = new KDE4ProfileWriter; + + QString newPath = writer->getPath(info); + + writer->writeProfile(newPath,info); + + delete writer; + + return newPath; +} + +void SessionManager::changeProfile(Profile::Ptr info , + QHash<Profile::Property,QVariant> propertyMap, bool persistant) +{ + Q_ASSERT(info); + + // insert the changes into the existing Profile instance + QListIterator<Profile::Property> iter(propertyMap.keys()); + while ( iter.hasNext() ) + { + const Profile::Property property = iter.next(); + info->setProperty(property,propertyMap[property]); + } + + // when changing a group, iterate through the profiles + // in the group and call changeProfile() on each of them + // + // this is so that each profile in the group, the profile is + // applied, a change notification is emitted and the profile + // is saved to disk + ProfileGroup::Ptr group = info->asGroup(); + if (group) + { + foreach(const Profile::Ptr &profile, group->profiles()) + changeProfile(profile,propertyMap,persistant); + return; + } + + // apply the changes to existing sessions + applyProfile(info,true); + + // notify the world about the change + emit profileChanged(info); + + // save changes to disk, unless the profile is hidden, in which case + // it has no file on disk + if ( persistant && !info->isHidden() ) + { + info->setProperty(Profile::Path,saveProfile(info)); + } +} +void SessionManager::applyProfile(Profile::Ptr info , bool modifiedPropertiesOnly) +{ + QListIterator<Session*> iter(_sessions); + while ( iter.hasNext() ) + { + Session* next = iter.next(); + if ( _sessionProfiles[next] == info ) + applyProfile(next,info,modifiedPropertiesOnly); + } +} +Profile::Ptr SessionManager::sessionProfile(Session* session) const +{ + return _sessionProfiles[session]; +} +void SessionManager::setSessionProfile(Session* session, Profile::Ptr profile) +{ + _sessionProfiles[session] = profile; + updateSession(session); +} +void SessionManager::applyProfile(Session* session, const Profile::Ptr info , bool modifiedPropertiesOnly) +{ + Q_ASSERT(info); + + _sessionProfiles[session] = info; + + ShouldApplyProperty apply(info,modifiedPropertiesOnly); + + // Basic session settings + if ( apply.shouldApply(Profile::Name) ) + session->setTitle(Session::NameRole,info->name()); + + if ( apply.shouldApply(Profile::Command) ) + session->setProgram(info->command()); + + if ( apply.shouldApply(Profile::Arguments) ) + session->setArguments(info->arguments()); + + if ( apply.shouldApply(Profile::Directory) ) + session->setInitialWorkingDirectory(info->defaultWorkingDirectory()); + + if ( apply.shouldApply(Profile::Environment) ) + { + // add environment variable containing home directory of current profile + // (if specified) + QStringList environment = info->property<QStringList>(Profile::Environment); + environment << QString("PROFILEHOME=%1").arg(info->defaultWorkingDirectory()); + + session->setEnvironment(environment); + } + + if ( apply.shouldApply(Profile::Icon) ) + session->setIconName(info->icon()); + + // Key bindings + if ( apply.shouldApply(Profile::KeyBindings) ) + session->setKeyBindings(info->property<QString>(Profile::KeyBindings)); + + // Tab formats + if ( apply.shouldApply(Profile::LocalTabTitleFormat) ) + session->setTabTitleFormat( Session::LocalTabTitle , + info->property<QString>(Profile::LocalTabTitleFormat)); + if ( apply.shouldApply(Profile::RemoteTabTitleFormat) ) + session->setTabTitleFormat( Session::RemoteTabTitle , + info->property<QString>(Profile::RemoteTabTitleFormat)); + + // History + if ( apply.shouldApply(Profile::HistoryMode) || apply.shouldApply(Profile::HistorySize) ) + { + int mode = info->property<int>(Profile::HistoryMode); + switch ((Profile::HistoryModeEnum)mode) + { + case Profile::DisableHistory: + session->setHistoryType( HistoryTypeNone() ); + break; + case Profile::FixedSizeHistory: + { + int lines = info->property<int>(Profile::HistorySize); + session->setHistoryType( CompactHistoryType(lines) ); + } + break; + case Profile::UnlimitedHistory: + session->setHistoryType( HistoryTypeFile() ); + break; + } + } + + // Terminal features + if ( apply.shouldApply(Profile::FlowControlEnabled) ) + session->setFlowControlEnabled( info->property<bool>(Profile::FlowControlEnabled) ); + + // Encoding + if ( apply.shouldApply(Profile::DefaultEncoding) ) + { + QByteArray name = info->property<QString>(Profile::DefaultEncoding).toUtf8(); + session->setCodec( QTextCodec::codecForName(name) ); + } +} + +void SessionManager::addProfile(Profile::Ptr type) +{ + if ( _types.isEmpty() ) + _defaultProfile = type; + + _types.insert(type); + + emit profileAdded(type); +} + +bool SessionManager::deleteProfile(Profile::Ptr type) +{ + bool wasDefault = ( type == defaultProfile() ); + + if ( type ) + { + // try to delete the config file + if ( type->isPropertySet(Profile::Path) && QFile::exists(type->path()) ) + { + if (!QFile::remove(type->path())) + { + kWarning() << "Could not delete profile: " << type->path() + << "The file is most likely in a directory which is read-only."; + + return false; + } + } + + // remove from favorites, profile list, shortcut list etc. + setFavorite(type,false); + setShortcut(type,QKeySequence()); + _types.remove(type); + + // mark the profile as hidden so that it does not show up in the + // Manage Profiles dialog and is not saved to disk + type->setHidden(true); + } + + // if we just deleted the default session type, + // replace it with a random type from the list + if ( wasDefault ) + { + setDefaultProfile( _types.toList().first() ); + } + + emit profileRemoved(type); + + return true; +} +void SessionManager::setDefaultProfile(Profile::Ptr info) +{ + Q_ASSERT ( _types.contains(info) ); + + _defaultProfile = info; + + QString path = info->path(); + + if ( path.isEmpty() ) + path = KDE4ProfileWriter().getPath(info); + + QFileInfo fileInfo(path); + + KSharedConfigPtr config = KGlobal::config(); + KConfigGroup group = config->group("Desktop Entry"); + group.writeEntry("DefaultProfile",fileInfo.fileName()); +} +QSet<Profile::Ptr> SessionManager::findFavorites() +{ + if (!_loadedFavorites) + loadFavorites(); + + return _favorites; +} +void SessionManager::setFavorite(Profile::Ptr info , bool favorite) +{ + if (!_types.contains(info)) + addProfile(info); + + if ( favorite && !_favorites.contains(info) ) + { + _favorites.insert(info); + emit favoriteStatusChanged(info,favorite); + } + else if ( !favorite && _favorites.contains(info) ) + { + _favorites.remove(info); + emit favoriteStatusChanged(info,favorite); + } +} +void SessionManager::loadShortcuts() +{ + KSharedConfigPtr appConfig = KGlobal::config(); + KConfigGroup shortcutGroup = appConfig->group("Profile Shortcuts"); + + QMap<QString,QString> entries = shortcutGroup.entryMap(); + + QMapIterator<QString,QString> iter(entries); + while ( iter.hasNext() ) + { + iter.next(); + + QKeySequence shortcut = QKeySequence::fromString(iter.key()); + QString profilePath = iter.value(); + + ShortcutData data; + data.profilePath = profilePath; + + _shortcuts.insert(shortcut,data); + } +} +void SessionManager::saveShortcuts() +{ + KSharedConfigPtr appConfig = KGlobal::config(); + KConfigGroup shortcutGroup = appConfig->group("Profile Shortcuts"); + shortcutGroup.deleteGroup(); + + QMapIterator<QKeySequence,ShortcutData> iter(_shortcuts); + while ( iter.hasNext() ) + { + iter.next(); + + QString shortcutString = iter.key().toString(); + + shortcutGroup.writeEntry(shortcutString, + iter.value().profilePath); + } +} +void SessionManager::setShortcut(Profile::Ptr info , + const QKeySequence& keySequence ) +{ + QKeySequence existingShortcut = shortcut(info); + _shortcuts.remove(existingShortcut); + + if (keySequence.isEmpty()) + return; + + ShortcutData data; + data.profileKey = info; + data.profilePath = info->path(); + // TODO - This won't work if the profile doesn't + // have a path yet + _shortcuts.insert(keySequence,data); + + emit shortcutChanged(info,keySequence); +} +void SessionManager::loadFavorites() +{ + KSharedConfigPtr appConfig = KGlobal::config(); + KConfigGroup favoriteGroup = appConfig->group("Favorite Profiles"); + + QSet<QString> favoriteSet; + + if ( favoriteGroup.hasKey("Favorites") ) + { + QStringList list = favoriteGroup.readEntry("Favorites", QStringList()); + favoriteSet = QSet<QString>::fromList(list); + } + else + { + // if there is no favorites key at all, mark the + // supplied 'Shell.profile' as the only favorite + favoriteSet << "Shell.profile"; + } + + // look for favorites amongst those already loaded + QSetIterator<Profile::Ptr> iter(_types); + while ( iter.hasNext() ) + { + Profile::Ptr profile = iter.next(); + const QString& path = profile->path(); + if ( favoriteSet.contains( path ) ) + { + _favorites.insert( profile ); + favoriteSet.remove(path); + } + } + // load any remaining favorites + QSetIterator<QString> unloadedFavoriteIter(favoriteSet); + while ( unloadedFavoriteIter.hasNext() ) + { + Profile::Ptr profile = loadProfile(unloadedFavoriteIter.next()); + if (profile) + _favorites.insert(profile); + } + + _loadedFavorites = true; +} +void SessionManager::saveFavorites() +{ + KSharedConfigPtr appConfig = KGlobal::config(); + KConfigGroup favoriteGroup = appConfig->group("Favorite Profiles"); + + QStringList paths; + QSetIterator<Profile::Ptr> keyIter(_favorites); + while ( keyIter.hasNext() ) + { + Profile::Ptr profile = keyIter.next(); + + Q_ASSERT( _types.contains(profile) && profile ); + + paths << profile->path(); + } + + favoriteGroup.writeEntry("Favorites",paths); +} + +QList<QKeySequence> SessionManager::shortcuts() +{ + return _shortcuts.keys(); +} + +Profile::Ptr SessionManager::findByShortcut(const QKeySequence& shortcut) +{ + Q_ASSERT( _shortcuts.contains(shortcut) ); + + if ( !_shortcuts[shortcut].profileKey ) + { + Profile::Ptr key = loadProfile(_shortcuts[shortcut].profilePath); + if (!key) + { + _shortcuts.remove(shortcut); + return Profile::Ptr(); + } + _shortcuts[shortcut].profileKey = key; + } + + return _shortcuts[shortcut].profileKey; +} + +void SessionManager::sessionProfileCommandReceived(const QString& text) +{ + // FIXME: This is inefficient, it creates a new profile instance for + // each set of changes applied. Instead a new profile should be created + // only the first time changes are applied to a session + + Session* session = qobject_cast<Session*>(sender()); + Q_ASSERT( session ); + + ProfileCommandParser parser; + QHash<Profile::Property,QVariant> changes = parser.parse(text); + + Profile::Ptr newProfile = Profile::Ptr(new Profile(_sessionProfiles[session])); + + QHashIterator<Profile::Property,QVariant> iter(changes); + while ( iter.hasNext() ) + { + iter.next(); + newProfile->setProperty(iter.key(),iter.value()); + } + + _sessionProfiles[session] = newProfile; + applyProfile(newProfile,true); + emit sessionUpdated(session); +} + +QKeySequence SessionManager::shortcut(Profile::Ptr info) const +{ + QMapIterator<QKeySequence,ShortcutData> iter(_shortcuts); + while (iter.hasNext()) + { + iter.next(); + if ( iter.value().profileKey == info + || iter.value().profilePath == info->path() ) + return iter.key(); + } + + return QKeySequence(); +} + +void SessionManager::saveSessions(KConfig* config) +{ + // The session IDs can't be restored. + // So we need to map the old ID to the future new ID. + int n = 1; + _restoreMapping.clear(); + + foreach(Session* session, _sessions) + { + QString name = QLatin1String("Session") + QString::number(n); + KConfigGroup group(config, name); + + group.writePathEntry("Profile", + _sessionProfiles.value(session)->path()); + session->saveSession(group); + _restoreMapping.insert(session, n); + n++; + } + + KConfigGroup group(config, "Number"); + group.writeEntry("NumberOfSessions", _sessions.count()); +} + +int SessionManager::getRestoreId(Session* session) +{ + return _restoreMapping.value(session); +} + +void SessionManager::restoreSessions(KConfig* config) +{ + KConfigGroup group(config, "Number"); + int sessions; + + // Any sessions saved? + if ((sessions = group.readEntry("NumberOfSessions", 0)) > 0) + { + for (int n = 1; n <= sessions; n++) + { + QString name = QLatin1String("Session") + QString::number(n); + KConfigGroup sessionGroup(config, name); + + QString profile = sessionGroup.readPathEntry("Profile", QString()); + Profile::Ptr ptr = defaultProfile(); + if (!profile.isEmpty()) ptr = loadProfile(profile); + + Session* session = createSession(ptr); + session->restoreSession(sessionGroup); + } + } +} + +Session* SessionManager::idToSession(int id) +{ + Q_ASSERT(id); + foreach(Session* session, _sessions) + if (session->sessionId() == id) + return session; + + // this should not happen + Q_ASSERT(0); + return 0; +} + +K_GLOBAL_STATIC( SessionManager , theSessionManager ) +SessionManager* SessionManager::instance() +{ + return theSessionManager; +} + +SessionListModel::SessionListModel(QObject* parent) +: QAbstractListModel(parent) +{ +} + +void SessionListModel::setSessions(const QList<Session*>& sessions) +{ + _sessions = sessions; + + foreach(Session* session, sessions) + connect(session,SIGNAL(finished()),this,SLOT(sessionFinished())); + + reset(); +} +QVariant SessionListModel::data(const QModelIndex& index, int role) const +{ + Q_ASSERT(index.isValid()); + + int row = index.row(); + int column = index.column(); + + Q_ASSERT( row >= 0 && row < _sessions.count() ); + Q_ASSERT( column >= 0 && column < 2 ); + + switch (role) + { + case Qt::DisplayRole: + if (column == 1) + return _sessions[row]->title(Session::DisplayedTitleRole); + else if (column == 0) + return _sessions[row]->sessionId(); + break; + case Qt::DecorationRole: + if (column == 1) + return KIcon(_sessions[row]->iconName()); + else + return QVariant(); + } + + return QVariant(); +} +QVariant SessionListModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Vertical) + return QVariant(); + else + { + switch (section) + { + case 0: + return i18n("Number"); + case 1: + return i18n("Title"); + default: + return QVariant(); + } + } +} + +int SessionListModel::columnCount(const QModelIndex&) const +{ + return 2; +} +int SessionListModel::rowCount(const QModelIndex&) const +{ + return _sessions.count(); +} +QModelIndex SessionListModel::parent(const QModelIndex&) const +{ + return QModelIndex(); +} +void SessionListModel::sessionFinished() +{ + Session* session = qobject_cast<Session*>(sender()); + int row = _sessions.indexOf(session); + + if (row != -1) + { + beginRemoveRows(QModelIndex(),row,row); + sessionRemoved(session); + _sessions.removeAt(row); + endRemoveRows(); + } +} +QModelIndex SessionListModel::index(int row, int column, const QModelIndex& parent) const +{ + if (hasIndex(row,column,parent)) + return createIndex(row,column,_sessions[row]); + else + return QModelIndex(); +} + +#include "SessionManager.moc" +
new file mode 100644 --- /dev/null +++ b/gui/konsole/SessionManager.h @@ -0,0 +1,448 @@ +/* + This source file is part of Konsole, a terminal emulator. + + 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. +*/ + +#ifndef SESSIONMANAGER_H +#define SESSIONMANAGER_H + +// Qt +#include <QtGui/QFont> +#include <QtGui/QKeySequence> + +#include <QtCore/QAbstractListModel> +#include <QtCore/QHash> +#include <QtCore/QList> +#include <QtCore/QSet> +#include <QtCore/QStringList> +#include <QtCore/QPair> +#include <QtCore/QPointer> +#include <QtCore/QVariant> +#include <QtCore/QStack> + +// Konsole +#include "Profile.h" + +class QSignalMapper; + + +namespace Konsole +{ + +class Session; + +/** + * Manages running terminal sessions and the profiles which specify various + * settings for terminal sessions and their displays. + * + * Profiles in the manager have a concept of favorite status, which can be used + * by widgets and dialogs in the application decide which sessions to list and + * how to display them. The favorite status of a profile can be altered using + * setFavorite() and retrieved using isFavorite() + */ +class KONSOLEPRIVATE_EXPORT SessionManager : public QObject +{ +Q_OBJECT + +public: + /** + * Constructs a new session manager and loads information about the available + * profiles. + */ + SessionManager(); + void setMenuOrder(); + + /** + * Destroys the SessionManager. All running sessions should be closed (via closeAll()) and the + * SessionManager's state should be saved via saveState() before the SessionManager is destroyed. + */ + virtual ~SessionManager(); + + /** Kill all running sessions. */ + void closeAll(); + + /** Saves state information (favorites, shortcuts, default profile etc.) to disk. */ + void saveState(); + + /** + * Returns a list of profiles which have been loaded. + * Initially only the profile currently set as the default is loaded. + * + * Favorite profiles are loaded automatically when findFavorites() is called. + * + * All other profiles can be loaded by calling loadAllProfiles(). This may + * involves opening, reading and parsing all profiles from disk, and + * should only be done when necessary. + */ + QList<Profile::Ptr> loadedProfiles() const; + + QList<Profile::Ptr> sortedFavorites(); + + /* + * Sorts the profile list by menuindex; those without an menuindex, sort by name. + * The menuindex list is first and then the non-menuindex list. + * + * @param list The profile list to sort + */ + void sortProfiles(QList<Profile::Ptr> &list); + + /** + * Searches for available profiles on-disk and returns a list + * of paths of profiles which can be loaded. + */ + QStringList availableProfilePaths() const; + + + /** + * Loads a profile from the specified path and registers + * it with the SessionManager. + * + * @p path may be relative or absolute. The path may just be the + * base name of the profile to load (eg. if the profile's full path + * is "<konsole data dir>/My Profile.profile" then both + * "konsole/My Profile.profile" , "My Profile.profile" and + * "My Profile" will be accepted) + * + * @return Pointer to a profile which can be passed to createSession() + * to create a new session using this profile. + */ + Profile::Ptr loadProfile(const QString& path); + + /** + * Updates a @p profile with the changes specified in @p propertyMap. + * + * All sessions currently using the profile will be updated to reflect the new settings. + * + * After the profile is updated, the profileChanged() signal will be emitted. + * + * @param profile The profile to change + * @param propertyMap A map between profile properties and values describing the changes + * @param persistant If true, the changes are saved to the profile's configuration file, + * set this to false if you want to preview possible changes to a profile but do not + * wish to make them permanent. + */ + void changeProfile(Profile::Ptr profile , QHash<Profile::Property,QVariant> propertyMap, + bool persistant = true); + + /** + * Returns a Profile object describing the default type of session, which is used + * if createSession() is called with an empty configPath argument. + */ + Profile::Ptr defaultProfile() const; + /** + * Returns a Profile object with hard-coded settings which is always available. + * This can be used as a parent for new profiles which provides suitable default settings + * for all properties. + */ + Profile::Ptr fallbackProfile() const; + + /** + * Creates a new session using the settings specified by the specified + * profile. + * + * The new session has no views associated with it. A new TerminalDisplay view + * must be created in order to display the output from the terminal session and + * send keyboard or mouse input to it. + * + * @param profile A profile containing the settings for the new session. If @p profile + * is null the default profile (see defaultProfile()) will be used. + */ + Session* createSession(Profile::Ptr profile = Profile::Ptr()); + + /** Returns the profile associated with a session. */ + Profile::Ptr sessionProfile(Session* session) const; + /** Sets the profile associated with a session. */ + void setSessionProfile(Session* session, Profile::Ptr profile); + + /** + * Updates a session's properties to match its current profile. + */ + void updateSession(Session* session); + + /** + * Returns a list of active sessions. + */ + const QList<Session*> sessions(); + + /** + * Deletes the configuration file used to store a profile. + * The profile will continue to exist while sessions are still using it. The profile + * will be marked as hidden (see Profile::setHidden() ) so that it does not show + * up in profile lists and future changes to the profile are not stored to disk. + * + * Returns true if the profile was successfully deleted or false otherwise. + */ + bool deleteProfile(Profile::Ptr profile); + + /** + * Sets the @p profile as the default profile for new sessions created + * with createSession() + */ + void setDefaultProfile(Profile::Ptr profile); + + /** + * Returns the set of the user's favorite profiles. + */ + QSet<Profile::Ptr> findFavorites(); + + /** + * Returns the list of shortcut key sequences which + * can be used to create new sessions based on + * existing profiles + * + * When one of the shortcuts is activated, + * use findByShortcut() to load the profile associated + * with the shortcut. + */ + QList<QKeySequence> shortcuts(); + + /** + * Finds and loads the profile associated with + * the specified @p shortcut key sequence and returns a pointer to it. + */ + Profile::Ptr findByShortcut(const QKeySequence& shortcut); + + /** + * Associates a shortcut with a particular profile. + */ + void setShortcut(Profile::Ptr profile , const QKeySequence& shortcut); + + /** Returns the shortcut associated with a particular profile. */ + QKeySequence shortcut(Profile::Ptr profile) const; + + /** + * Registers a new type of session. + * The favorite status of the session ( as returned by isFavorite() ) is set to false by default. + */ + void addProfile(Profile::Ptr type); + + /** + * Specifies whether a profile should be included in the user's + * list of favorite sessions. + */ + void setFavorite(Profile::Ptr profile , bool favorite); + + /** + * Loads all available profiles. This involves reading each + * profile configuration file from disk and parsing it. + * Therefore it should only be done when necessary. + */ + void loadAllProfiles(); + + /** + * Sets the global session manager instance. + */ + static void setInstance(SessionManager* instance); + /** + * Returns the session manager instance. + */ + static SessionManager* instance(); + + // session management + void saveSessions(KConfig* config); + int getRestoreId(Session* session); + void restoreSessions(KConfig* config); + Session *idToSession(int id); + +signals: + /** Emitted when a profile is added to the manager. */ + void profileAdded(Profile::Ptr ptr); + /** Emitted when a profile is removed from the manager. */ + void profileRemoved(Profile::Ptr ptr); + /** Emitted when a profile's properties are modified. */ + void profileChanged(Profile::Ptr ptr); + + /** + * Emitted when a session's settings are updated to match + * its current profile. + */ + void sessionUpdated(Session* session); + + /** + * Emitted when the favorite status of a profile changes. + * + * @param profile The profile to change + * @param favorite Specifies whether the session is a favorite or not + */ + void favoriteStatusChanged(Profile::Ptr profile , bool favorite); + + /** + * Emitted when the shortcut for a profile is changed. + * + * @param profile The profile whoose status was changed + * @param newShortcut The new shortcut key sequence for the profile + */ + void shortcutChanged(Profile::Ptr profile , const QKeySequence& newShortcut); + +protected Q_SLOTS: + + /** + * Called to inform the manager that a session has finished executing. + * + * @param session The Session which has finished executing. + */ + void sessionTerminated( QObject* session ); + +private slots: + void sessionProfileCommandReceived(const QString& text); + +private: + + + // loads the mappings between shortcut key sequences and + // profile paths + void loadShortcuts(); + // saves the mappings between shortcut key sequences and + // profile paths + void saveShortcuts(); + + //loads the set of favorite sessions + void loadFavorites(); + //saves the set of favorite sessions + void saveFavorites(); + // saves a profile to a file + // returns the path to which the profile was saved, which will + // be the same as the path property of profile if valid or a newly generated path + // otherwise + QString saveProfile(Profile::Ptr profile); + + // applies updates to a profile + // to all sessions currently using that profile + // if modifiedPropertiesOnly is true, only properties which + // are set in the profile @p key are updated + void applyProfile(Profile::Ptr ptr , bool modifiedPropertiesOnly); + // apples updates to the profile @p info to the session @p session + // if modifiedPropertiesOnly is true, only properties which + // are set in @p info are update ( ie. properties for which info->isPropertySet(<property>) + // returns true ) + void applyProfile(Session* session , const Profile::Ptr info , bool modifiedPropertiesOnly); + + QSet<Profile::Ptr> _types; + QHash<Session*,Profile::Ptr> _sessionProfiles; + QHash<Session*,int> _restoreMapping; + + struct ShortcutData + { + Profile::Ptr profileKey; + QString profilePath; + }; + QMap<QKeySequence,ShortcutData> _shortcuts; // shortcut keys -> profile path + + QList<Session*> _sessions; // list of running sessions + + Profile::Ptr _defaultProfile; + Profile::Ptr _fallbackProfile; + + QSet<Profile::Ptr> _favorites; // list of favorite profiles + + bool _loadedAllProfiles; // set to true after loadAllProfiles has been called + bool _loadedFavorites; // set to true after loadFavorites has been called + QSignalMapper* _sessionMapper; +}; + +/** Utility class to simplify code in SessionManager::applyProfile(). */ +class ShouldApplyProperty +{ +public: + ShouldApplyProperty(const Profile::Ptr profile , bool modifiedOnly) : + _profile(profile) , _modifiedPropertiesOnly(modifiedOnly) {} + + bool shouldApply(Profile::Property property) const + { + return !_modifiedPropertiesOnly || _profile->isPropertySet(property); + } +private: + const Profile::Ptr _profile; + bool _modifiedPropertiesOnly; +}; + +/** + * PopStackOnExit is a utility to remove all values from a QStack which are added during + * the lifetime of a PopStackOnExit instance. + * + * When a PopStackOnExit instance is destroyed, elements are removed from the stack + * until the stack count is reduced the value when the PopStackOnExit instance was created. + */ +template <class T> +class PopStackOnExit +{ +public: + PopStackOnExit(QStack<T>& stack) : _stack(stack) , _count(stack.count()) {} + ~PopStackOnExit() + { + while (_stack.count() > _count) + _stack.pop(); + } +private: + QStack<T>& _stack; + int _count; +}; +/** + * Item-view model which contains a flat list of sessions. + * After constructing the model, call setSessions() to set the sessions displayed + * in the list. When a session ends (after emitting the finished() signal) it is + * automatically removed from the list. + * + * The internal pointer for each item in the model (index.internalPointer()) is the + * associated Session* + */ +class SessionListModel : public QAbstractListModel +{ +Q_OBJECT + +public: + SessionListModel(QObject* parent = 0); + + /** + * Sets the list of sessions displayed in the model. + * To display all sessions that are currently running in the list, + * call setSessions(SessionManager::instance()->sessions()) + */ + void setSessions(const QList<Session*>& sessions); + + // reimplemented from QAbstractItemModel + virtual QModelIndex index(int row, int column, const QModelIndex& parent) const; + virtual QVariant data(const QModelIndex& index, int role) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, + int role) const; + virtual int columnCount(const QModelIndex& parent) const; + virtual int rowCount(const QModelIndex& parent) const; + virtual QModelIndex parent(const QModelIndex& index) const; + +protected: + virtual void sessionRemoved(Session*) {} + +private slots: + void sessionFinished(); + +private: + QList<Session*> _sessions; +}; + +} +#endif //SESSIONMANAGER_H + +/* + Local Variables: + mode: c++ + c-file-style: "stroustrup" + indent-tabs-mode: nil + tab-width: 4 + End: +*/
new file mode 100644 --- /dev/null +++ b/gui/konsole/ShellCommand.cpp @@ -0,0 +1,179 @@ +/* + Copyright 2007-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 "ShellCommand.h" + +// Qt +//#include <KShell> +//#include <KDebug> + +using namespace Konsole; + +// expands environment variables in 'text' +// function copied from kdelibs/kio/kio/kurlcompletion.cpp +static bool expandEnv(QString& text); + +ShellCommand::ShellCommand(const QString& fullCommand) +{ + //_arguments = KShell::splitArgs(fullCommand); + bool inQuotes = false; + + QString builder; + + for ( int i = 0 ; i < fullCommand.count() ; i++ ) + { + QChar ch = fullCommand[i]; + + const bool isLastChar = ( i == fullCommand.count() - 1 ); + const bool isQuote = ( ch == '\'' || ch == '\"' ); + + if ( !isLastChar && isQuote ) + inQuotes = !inQuotes; + else + { + if ( (!ch.isSpace() || inQuotes) && !isQuote ) + builder.append(ch); + + if ( (ch.isSpace() && !inQuotes) || ( i == fullCommand.count()-1 ) ) + { + _arguments << builder; + builder.clear(); + } + } + } + +} +ShellCommand::ShellCommand(const QString& command , const QStringList& arguments) +{ + _arguments = arguments; + + if ( !_arguments.isEmpty() ) + _arguments[0] == command; +} +QString ShellCommand::fullCommand() const +{ + QStringList quotedArgs(_arguments); + for (int i=0;i<quotedArgs.count();i++) + { + QString arg = quotedArgs.at(i); + bool hasSpace = false; + for (int j=0;j<arg.count();j++) + if (arg[j].isSpace()) + hasSpace = true; + if (hasSpace) + quotedArgs[i] = '\"' + arg + '\"'; + } + return quotedArgs.join(QChar(' ')); +} +QString ShellCommand::command() const +{ + if ( !_arguments.isEmpty() ) + return _arguments[0]; + else + return QString(); +} +QStringList ShellCommand::arguments() const +{ + return _arguments; +} +bool ShellCommand::isRootCommand() const +{ + Q_ASSERT(0); // not implemented yet + return false; +} +bool ShellCommand::isAvailable() const +{ + Q_ASSERT(0); // not implemented yet + return false; +} +QStringList ShellCommand::expand(const QStringList& items) +{ + QStringList result; + + foreach( const QString &item , items ) + result << expand(item); + + return result; +} +QString ShellCommand::expand(const QString& text) +{ + QString result = text; + expandEnv(result); + return result; +} + +/* + * expandEnv + * + * Expand environment variables in text. Escaped '$' characters are ignored. + * Return true if any variables were expanded + */ +static bool expandEnv( QString &text ) +{ + // Find all environment variables beginning with '$' + // + int pos = 0; + + bool expanded = false; + + while ( (pos = text.indexOf(QLatin1Char('$'), pos)) != -1 ) { + + // Skip escaped '$' + // + if ( pos > 0 && text.at(pos-1) == QLatin1Char('\\') ) { + pos++; + } + // Variable found => expand + // + else { + // Find the end of the variable = next '/' or ' ' + // + int pos2 = text.indexOf( QLatin1Char(' '), pos+1 ); + int pos_tmp = text.indexOf( QLatin1Char('/'), pos+1 ); + + if ( pos2 == -1 || (pos_tmp != -1 && pos_tmp < pos2) ) + pos2 = pos_tmp; + + if ( pos2 == -1 ) + pos2 = text.length(); + + // Replace if the variable is terminated by '/' or ' ' + // and defined + // + if ( pos2 >= 0 ) { + int len = pos2 - pos; + QString key = text.mid( pos+1, len-1); + QString value = + QString::fromLocal8Bit( qgetenv(key.toLocal8Bit()) ); + + if ( !value.isEmpty() ) { + expanded = true; + text.replace( pos, len, value ); + pos = pos + value.length(); + } + else { + pos = pos2; + } + } + } + } + + return expanded; +}
new file mode 100644 --- /dev/null +++ b/gui/konsole/ShellCommand.h @@ -0,0 +1,92 @@ +/* + Copyright 2007-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. +*/ + +#ifndef SHELLCOMMAND_H +#define SHELLCOMMAND_H + +// Qt +#include <QtCore/QStringList> + +namespace Konsole +{ + +/** + * A class to parse and extract information about shell commands. + * + * ShellCommand can be used to: + * + * <ul> + * <li>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") + * </li> + * <li>Take a command and a list of arguments and combine them to + * form a complete command line. + * </li> + * <li>Determine whether the binary specified by a command exists in the + * user's PATH. + * </li> + * <li>Determine whether a command-line specifies the execution of + * another command as the root user using su/sudo etc. + * </li> + * </ul> + */ +class ShellCommand +{ +public: + /** + * Constructs a ShellCommand from a command line. + * + * @param fullCommand The command line to parse. + */ + ShellCommand(const QString& fullCommand); + /** + * Constructs a ShellCommand with the specified @p command and @p arguments. + */ + ShellCommand(const QString& command , const QStringList& arguments); + + /** 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 +
new file mode 100644 --- /dev/null +++ b/gui/konsole/TabTitleFormatAction.cpp @@ -0,0 +1,104 @@ +/* + Copyright 2007-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 "TabTitleFormatAction.h" + +// Qt +#include <QList> +#include <QMenu> + +// KDE +#include <KLocale> + +using namespace Konsole; + +const TabTitleFormatAction::Element TabTitleFormatAction::_localElements[] = +{ + { "%n" , I18N_NOOP("Program Name") }, + { "%d" , I18N_NOOP("Current Directory (Short)") }, + { "%D" , I18N_NOOP("Current Directory (Long)") }, + { "%w" , I18N_NOOP("Window Title Set by Shell") }, + { "%#" , I18N_NOOP("Session Number") }, + { "%u" , I18N_NOOP("User Name") } +}; +const int TabTitleFormatAction::_localElementCount = 6; +const TabTitleFormatAction::Element TabTitleFormatAction::_remoteElements[] = +{ + { "%u" , I18N_NOOP("User Name") }, + { "%h" , I18N_NOOP("Remote Host (Short)") }, + { "%H" , I18N_NOOP("Remote Host (Long)") }, + { "%w" , I18N_NOOP("Window Title Set by Shell") }, + { "%#" , I18N_NOOP("Session Number") } +}; +const int TabTitleFormatAction::_remoteElementCount = 5; + +TabTitleFormatAction::TabTitleFormatAction(QObject* parent) + : QAction(parent) + , _context(Session::LocalTabTitle) +{ + setMenu( new QMenu() ); + connect( menu() , SIGNAL(triggered(QAction*)) , this , SLOT(fireElementSelected(QAction*)) ); +} +TabTitleFormatAction::~TabTitleFormatAction() +{ + menu()->deleteLater(); +} +void TabTitleFormatAction::fireElementSelected(QAction* action) +{ + emit dynamicElementSelected(action->data().value<QString>()); +} +void TabTitleFormatAction::setContext(Session::TabTitleContext context) +{ + _context = context; + + menu()->clear(); + + QList<QAction*> list; + + int count = 0; + const Element* array = 0; + + if ( context == Session::LocalTabTitle ) + { + count = _localElementCount; + array = _localElements; + } + else if ( context == Session::RemoteTabTitle ) + { + count = _remoteElementCount; + array = _remoteElements; + } + + for ( int i = 0 ; i < count ; i++ ) + { + QAction* action = new QAction(i18n(array[i].description),this); + action->setData(array[i].element); + list << action; + } + + menu()->addActions(list); +} +Session::TabTitleContext TabTitleFormatAction::context() const +{ + return _context; +} + +#include "TabTitleFormatAction.moc" +
new file mode 100644 --- /dev/null +++ b/gui/konsole/TabTitleFormatAction.h @@ -0,0 +1,66 @@ +/* + Copyright 2007-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. +*/ + +#ifndef TABTITLEFORMATACTION_H +#define TABTITLEFORMATACTION_H + +// Qt +#include <QAction> + +// Konsole +#include "Session.h" + +namespace Konsole +{ + +class TabTitleFormatAction : public QAction +{ +Q_OBJECT + +public: + TabTitleFormatAction(QObject* parent); + ~TabTitleFormatAction(); + + void setContext(Session::TabTitleContext context); + Session::TabTitleContext context() const; + +signals: + void dynamicElementSelected(const QString&); + +private slots: + void fireElementSelected(QAction*); + +private: + Session::TabTitleContext _context; + + struct Element + { + QString element; + const char *description; + }; + static const Element _localElements[]; + static const int _localElementCount; + static const Element _remoteElements[]; + static const int _remoteElementCount; + +}; + +} + +#endif // TABTITLEFORMATACTION_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/TerminalCharacterDecoder.cpp @@ -0,0 +1,251 @@ +/* + This file is part of Konsole, an X terminal. + + 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 Lesser 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 Lesser 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 "TerminalCharacterDecoder.h" + +// Qt +#include <QtCore/QTextStream> + +// KDE +//#include <kdebug.h> + +// Konsole +#include "konsole_wcwidth.h" + +using namespace Konsole; +PlainTextDecoder::PlainTextDecoder() + : _output(0) + , _includeTrailingWhitespace(true) + , _recordLinePositions(false) +{ + +} +void PlainTextDecoder::setTrailingWhitespace(bool enable) +{ + _includeTrailingWhitespace = enable; +} +bool PlainTextDecoder::trailingWhitespace() const +{ + return _includeTrailingWhitespace; +} +void PlainTextDecoder::begin(QTextStream* output) +{ + _output = output; + if (!_linePositions.isEmpty()) + _linePositions.clear(); +} +void PlainTextDecoder::end() +{ + _output = 0; +} + +void PlainTextDecoder::setRecordLinePositions(bool record) +{ + _recordLinePositions = record; +} +QList<int> PlainTextDecoder::linePositions() const +{ + return _linePositions; +} +void PlainTextDecoder::decodeLine(const Character* const characters, int count, LineProperty /*properties*/ + ) +{ + Q_ASSERT( _output ); + + if (_recordLinePositions && _output->string()) + { + int pos = _output->string()->count(); + _linePositions << pos; + } + + //TODO should we ignore or respect the LINE_WRAPPED line property? + + //note: we build up a QString and send it to the text stream rather writing into the text + //stream a character at a time because it is more efficient. + //(since QTextStream always deals with QStrings internally anyway) + QString plainText; + plainText.reserve(count); + + int outputCount = count; + + // if inclusion of trailing whitespace is disabled then find the end of the + // line + if ( !_includeTrailingWhitespace ) + { + for (int i = count-1 ; i >= 0 ; i--) + { + if ( characters[i].character != ' ' ) + break; + else + outputCount--; + } + } + + for (int i=0;i<outputCount;) + { + plainText.append( QChar(characters[i].character) ); + i += qMax(1,konsole_wcwidth(characters[i].character)); + } + *_output << plainText; +} + +HTMLDecoder::HTMLDecoder() : + _output(0) + ,_colorTable(base_color_table) + ,_innerSpanOpen(false) + ,_lastRendition(DEFAULT_RENDITION) +{ + +} + +void HTMLDecoder::begin(QTextStream* output) +{ + _output = output; + + QString text; + + //open monospace span + openSpan(text,"font-family:monospace"); + + *output << text; +} + +void HTMLDecoder::end() +{ + Q_ASSERT( _output ); + + QString text; + + closeSpan(text); + + *_output << text; + + _output = 0; + +} + +//TODO: Support for LineProperty (mainly double width , double height) +void HTMLDecoder::decodeLine(const Character* const characters, int count, LineProperty /*properties*/ + ) +{ + Q_ASSERT( _output ); + + QString text; + + int spaceCount = 0; + + for (int i=0;i<count;i++) + { + QChar ch(characters[i].character); + + //check if appearance of character is different from previous char + if ( characters[i].rendition != _lastRendition || + characters[i].foregroundColor != _lastForeColor || + characters[i].backgroundColor != _lastBackColor ) + { + if ( _innerSpanOpen ) + closeSpan(text); + + _lastRendition = characters[i].rendition; + _lastForeColor = characters[i].foregroundColor; + _lastBackColor = characters[i].backgroundColor; + + //build up style string + QString style; + + bool useBold; + ColorEntry::FontWeight weight = characters[i].fontWeight(_colorTable); + if (weight == ColorEntry::UseCurrentFormat) + useBold = _lastRendition & RE_BOLD; + else + useBold = weight == ColorEntry::Bold; + + if (useBold) + style.append("font-weight:bold;"); + + if ( _lastRendition & RE_UNDERLINE ) + style.append("font-decoration:underline;"); + + //colours - a colour table must have been defined first + if ( _colorTable ) + { + style.append( QString("color:%1;").arg(_lastForeColor.color(_colorTable).name() ) ); + + if (!characters[i].isTransparent(_colorTable)) + { + style.append( QString("background-color:%1;").arg(_lastBackColor.color(_colorTable).name() ) ); + } + } + + //open the span with the current style + openSpan(text,style); + _innerSpanOpen = true; + } + + //handle whitespace + if (ch.isSpace()) + spaceCount++; + else + spaceCount = 0; + + + //output current character + if (spaceCount < 2) + { + //escape HTML tag characters and just display others as they are + if ( ch == '<' ) + text.append("<"); + else if (ch == '>') + text.append(">"); + else + text.append(ch); + } + else + { + text.append(" "); //HTML truncates multiple spaces, so use a space marker instead + } + + } + + //close any remaining open inner spans + if ( _innerSpanOpen ) + closeSpan(text); + + //start new line + text.append("<br>"); + + *_output << text; +} +void HTMLDecoder::openSpan(QString& text , const QString& style) +{ + text.append( QString("<span style=\"%1\">").arg(style) ); +} + +void HTMLDecoder::closeSpan(QString& text) +{ + text.append("</span>"); +} + +void HTMLDecoder::setColorTable(const ColorEntry* table) +{ + _colorTable = table; +}
new file mode 100644 --- /dev/null +++ b/gui/konsole/TerminalCharacterDecoder.h @@ -0,0 +1,150 @@ +/* + This file is part of Konsole, an X terminal. + + 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 Lesser 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 Lesser 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 TERMINAL_CHARACTER_DECODER_H +#define TERMINAL_CHARACTER_DECODER_H + +#include "Character.h" + +#include <QList> + +class QTextStream; + +namespace Konsole +{ + +/** + * Base class for terminal character decoders + * + * The decoder converts lines of terminal characters which consist of a unicode character, foreground + * and background colours and other appearance-related properties into text strings. + * + * Derived classes may produce either plain text with no other colour or appearance information, or + * they may produce text which incorporates these additional properties. + */ +class TerminalCharacterDecoder +{ +public: + virtual ~TerminalCharacterDecoder() {} + + /** Begin decoding characters. The resulting text is appended to @p output. */ + virtual void begin(QTextStream* output) = 0; + /** End decoding. */ + virtual void end() = 0; + + /** + * Converts a line of terminal characters with associated properties into a text string + * and writes the string into an output QTextStream. + * + * @param characters An array of characters of length @p count. + * @param count The number of characters + * @param properties Additional properties which affect all characters in the line + */ + virtual void decodeLine(const Character* const characters, + int count, + LineProperty properties) = 0; +}; + +/** + * A terminal character decoder which produces plain text, ignoring colours and other appearance-related + * properties of the original characters. + */ +class PlainTextDecoder : public TerminalCharacterDecoder +{ +public: + PlainTextDecoder(); + + /** + * Set whether trailing whitespace at the end of lines should be included + * in the output. + * Defaults to true. + */ + void setTrailingWhitespace(bool enable); + /** + * Returns whether trailing whitespace at the end of lines is included + * in the output. + */ + bool trailingWhitespace() const; + /** + * Returns of character positions in the output stream + * at which new lines where added. Returns an empty if setTrackLinePositions() is false or if + * the output device is not a string. + */ + QList<int> linePositions() const; + /** Enables recording of character positions at which new lines are added. See linePositions() */ + void setRecordLinePositions(bool record); + + virtual void begin(QTextStream* output); + virtual void end(); + + virtual void decodeLine(const Character* const characters, + int count, + LineProperty properties); + + +private: + QTextStream* _output; + bool _includeTrailingWhitespace; + + bool _recordLinePositions; + QList<int> _linePositions; +}; + +/** + * A terminal character decoder which produces pretty HTML markup + */ +class HTMLDecoder : public TerminalCharacterDecoder +{ +public: + /** + * Constructs an HTML decoder using a default black-on-white color scheme. + */ + HTMLDecoder(); + + /** + * Sets the colour table which the decoder uses to produce the HTML colour codes in its + * output + */ + void setColorTable( const ColorEntry* table ); + + virtual void decodeLine(const Character* const characters, + int count, + LineProperty properties); + + virtual void begin(QTextStream* output); + virtual void end(); + +private: + void openSpan(QString& text , const QString& style); + void closeSpan(QString& text); + + QTextStream* _output; + const ColorEntry* _colorTable; + bool _innerSpanOpen; + quint8 _lastRendition; + CharacterColor _lastForeColor; + CharacterColor _lastBackColor; + +}; + +} + +#endif
new file mode 100644 --- /dev/null +++ b/gui/konsole/TerminalDisplay.cpp @@ -0,0 +1,2987 @@ +/* + This file is part of Konsole, a terminal emulator for KDE. + + Copyright 2006-2008 by Robert Knight <robertknight@gmail.com> + Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de> + + 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 "TerminalDisplay.h" + +// Qt +#include <QtGui/QApplication> +#include <QtGui/QBoxLayout> +#include <QtGui/QClipboard> +#include <QtGui/QKeyEvent> +#include <QtCore/QEvent> +#include <QtCore/QTime> +#include <QtCore/QFile> +#include <QtGui/QGridLayout> +#include <QtGui/QLabel> +#include <QtGui/QLayout> +#include <QtGui/QPainter> +#include <QtGui/QPixmap> +#include <QtGui/QScrollBar> +#include <QtGui/QStyle> +#include <QtCore/QTimer> +#include <QtGui/QToolTip> +#include <QtCore/QTextStream> + +// KDE +//#include <kshell.h> +//#include <KColorScheme> +//#include <KCursor> +//#include <kdebug.h> +//#include <KLocale> +//#include <KMenu> +//#include <KNotification> +//#include <KGlobalSettings> +//#include <KShortcut> +//#include <KIO/NetAccess> + +// Konsole +//#include <config-apps.h> +#include "Filter.h" +#include "konsole_wcwidth.h" +#include "ScreenWindow.h" +#include "TerminalCharacterDecoder.h" + +using namespace Konsole; + +#ifndef loc +#define loc(X,Y) ((Y)*_columns+(X)) +#endif + +#define yMouseScroll 1 + +#define REPCHAR "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ + "abcdefgjijklmnopqrstuvwxyz" \ + "0123456789./+@" + +const ColorEntry Konsole::base_color_table[TABLE_COLORS] = +// The following are almost IBM standard color codes, with some slight +// gamma correction for the dim colors to compensate for bright X screens. +// It contains the 8 ansiterm/xterm colors in 2 intensities. +{ + // Fixme: could add faint colors here, also. + // normal + ColorEntry(QColor(0x00,0x00,0x00), 0), ColorEntry( QColor(0xB2,0xB2,0xB2), 1), // Dfore, Dback + ColorEntry(QColor(0x00,0x00,0x00), 0), ColorEntry( QColor(0xB2,0x18,0x18), 0), // Black, Red + ColorEntry(QColor(0x18,0xB2,0x18), 0), ColorEntry( QColor(0xB2,0x68,0x18), 0), // Green, Yellow + ColorEntry(QColor(0x18,0x18,0xB2), 0), ColorEntry( QColor(0xB2,0x18,0xB2), 0), // Blue, Magenta + ColorEntry(QColor(0x18,0xB2,0xB2), 0), ColorEntry( QColor(0xB2,0xB2,0xB2), 0), // Cyan, White + // intensiv + ColorEntry(QColor(0x00,0x00,0x00), 0), ColorEntry( QColor(0xFF,0xFF,0xFF), 1), + ColorEntry(QColor(0x68,0x68,0x68), 0), ColorEntry( QColor(0xFF,0x54,0x54), 0), + ColorEntry(QColor(0x54,0xFF,0x54), 0), ColorEntry( QColor(0xFF,0xFF,0x54), 0), + ColorEntry(QColor(0x54,0x54,0xFF), 0), ColorEntry( QColor(0xFF,0x54,0xFF), 0), + ColorEntry(QColor(0x54,0xFF,0xFF), 0), ColorEntry( QColor(0xFF,0xFF,0xFF), 0) +}; + +// scroll increment used when dragging selection at top/bottom of window. + +// static +bool TerminalDisplay::_antialiasText = true; +bool TerminalDisplay::HAVE_TRANSPARENCY = false; + +// we use this to force QPainter to display text in LTR mode +// more information can be found in: http://unicode.org/reports/tr9/ +const QChar LTR_OVERRIDE_CHAR( 0x202D ); + +/* ------------------------------------------------------------------------- */ +/* */ +/* Colors */ +/* */ +/* ------------------------------------------------------------------------- */ + +/* Note that we use ANSI color order (bgr), while IBMPC color order is (rgb) + + Code 0 1 2 3 4 5 6 7 + ----------- ------- ------- ------- ------- ------- ------- ------- ------- + ANSI (bgr) Black Red Green Yellow Blue Magenta Cyan White + IBMPC (rgb) Black Blue Green Cyan Red Magenta Yellow White +*/ + +ScreenWindow* TerminalDisplay::screenWindow() const +{ + return _screenWindow; +} +void TerminalDisplay::setScreenWindow(ScreenWindow* window) +{ + // disconnect existing screen window if any + if ( _screenWindow ) + { + disconnect( _screenWindow , 0 , this , 0 ); + } + + _screenWindow = window; + + if ( window ) + { + +// TODO: Determine if this is an issue. +//#warning "The order here is not specified - does it matter whether updateImage or updateLineProperties comes first?" + connect( _screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateLineProperties()) ); + connect( _screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateImage()) ); + window->setWindowLines(_lines); + } +} + +const ColorEntry* TerminalDisplay::colorTable() const +{ + return _colorTable; +} +void TerminalDisplay::setBackgroundColor(const QColor& color) +{ + _colorTable[DEFAULT_BACK_COLOR].color = color; + QPalette p = palette(); + p.setColor( backgroundRole(), color ); + setPalette( p ); + + // Avoid propagating the palette change to the scroll bar + _scrollBar->setPalette( QApplication::palette() ); + + update(); +} +void TerminalDisplay::setForegroundColor(const QColor& color) +{ + _colorTable[DEFAULT_FORE_COLOR].color = color; + + update(); +} +void TerminalDisplay::setColorTable(const ColorEntry table[]) +{ + for (int i = 0; i < TABLE_COLORS; i++) + _colorTable[i] = table[i]; + + setBackgroundColor(_colorTable[DEFAULT_BACK_COLOR].color); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Font */ +/* */ +/* ------------------------------------------------------------------------- */ + +/* + The VT100 has 32 special graphical characters. The usual vt100 extended + xterm fonts have these at 0x00..0x1f. + + QT's iso mapping leaves 0x00..0x7f without any changes. But the graphicals + come in here as proper unicode characters. + + We treat non-iso10646 fonts as VT100 extended and do the requiered mapping + from unicode to 0x00..0x1f. The remaining translation is then left to the + QCodec. +*/ + +static inline bool isLineChar(quint16 c) { return ((c & 0xFF80) == 0x2500);} +static inline bool isLineCharString(const QString& string) +{ + return (string.length() > 0) && (isLineChar(string.at(0).unicode())); +} + + +// assert for i in [0..31] : vt100extended(vt100_graphics[i]) == i. + +unsigned short Konsole::vt100_graphics[32] = +{ // 0/8 1/9 2/10 3/11 4/12 5/13 6/14 7/15 + 0x0020, 0x25C6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, + 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, + 0xF800, 0xF801, 0x2500, 0xF803, 0xF804, 0x251c, 0x2524, 0x2534, + 0x252c, 0x2502, 0x2264, 0x2265, 0x03C0, 0x2260, 0x00A3, 0x00b7 +}; + +void TerminalDisplay::fontChange(const QFont&) +{ + QFontMetrics fm(font()); + _fontHeight = fm.height() + _lineSpacing; + + // waba TerminalDisplay 1.123: + // "Base character width on widest ASCII character. This prevents too wide + // characters in the presence of double wide (e.g. Japanese) characters." + // Get the width from representative normal width characters + _fontWidth = qRound((double)fm.width(REPCHAR)/(double)strlen(REPCHAR)); + + _fixedFont = true; + + int fw = fm.width(REPCHAR[0]); + for(unsigned int i=1; i< strlen(REPCHAR); i++) + { + if (fw != fm.width(REPCHAR[i])) + { + _fixedFont = false; + break; + } + } + + if (_fontWidth < 1) + _fontWidth=1; + + _fontAscent = fm.ascent(); + + emit changedFontMetricSignal( _fontHeight, _fontWidth ); + propagateSize(); + update(); +} + +void TerminalDisplay::setVTFont(const QFont& f) +{ + QFont font = f; + + QFontMetrics metrics(font); + + if ( !QFontInfo(font).fixedPitch() ) + { + kWarning() << "Using an unsupported variable-width font in the terminal. This may produce display errors."; + } + + if ( metrics.height() < height() && metrics.maxWidth() < width() ) + { + // hint that text should be drawn without anti-aliasing. + // depending on the user's font configuration, this may not be respected + if (!_antialiasText) + font.setStyleStrategy( QFont::NoAntialias ); + + // experimental optimization. Konsole assumes that the terminal is using a + // mono-spaced font, in which case kerning information should have an effect. + // Disabling kerning saves some computation when rendering text. + font.setKerning(false); + + QWidget::setFont(font); + fontChange(font); + } +} + +void TerminalDisplay::setFont(const QFont &) +{ + // ignore font change request if not coming from konsole itself +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Constructor / Destructor */ +/* */ +/* ------------------------------------------------------------------------- */ + +TerminalDisplay::TerminalDisplay(QWidget *parent) +:QWidget(parent) +,_screenWindow(0) +,_allowBell(true) +,_gridLayout(0) +,_fontHeight(1) +,_fontWidth(1) +,_fontAscent(1) +,_boldIntense(true) +,_lines(1) +,_columns(1) +,_usedLines(1) +,_usedColumns(1) +,_contentHeight(1) +,_contentWidth(1) +,_image(0) +,_randomSeed(0) +,_resizing(false) +,_terminalSizeHint(false) +,_terminalSizeStartup(true) +,_bidiEnabled(false) +,_actSel(0) +,_wordSelectionMode(false) +,_lineSelectionMode(false) +,_preserveLineBreaks(false) +,_columnSelectionMode(false) +,_scrollbarLocation(NoScrollBar) +,_wordCharacters(":@-./_~") +,_bellMode(SystemBeepBell) +,_blinking(false) +,_hasBlinker(false) +,_cursorBlinking(false) +,_hasBlinkingCursor(false) +,_allowBlinkingText(true) +,_ctrlDrag(true) +,_tripleClickMode(SelectWholeLine) +,_isFixedSize(false) +,_possibleTripleClick(false) +,_resizeWidget(0) +,_resizeTimer(0) +,_flowControlWarningEnabled(false) +,_outputSuspendedLabel(0) +,_lineSpacing(0) +,_colorsInverted(false) +,_blendColor(qRgba(0,0,0,0xff)) +,_filterChain(new TerminalImageFilterChain()) +,_cursorShape(BlockCursor) +{ + // terminal applications are not designed with Right-To-Left in mind, + // so the layout is forced to Left-To-Right + setLayoutDirection(Qt::LeftToRight); + + // The offsets are not yet calculated. + // Do not calculate these too often to be more smoothly when resizing + // konsole in opaque mode. + _topMargin = DEFAULT_TOP_MARGIN; + _leftMargin = DEFAULT_LEFT_MARGIN; + + // create scroll bar for scrolling output up and down + // set the scroll bar's slider to occupy the whole area of the scroll bar initially + _scrollBar = new QScrollBar(this); + setScroll(0,0); + _scrollBar->setCursor( Qt::ArrowCursor ); + connect(_scrollBar, SIGNAL(valueChanged(int)), this, + SLOT(scrollBarPositionChanged(int))); + + // setup timers for blinking cursor and text + _blinkTimer = new QTimer(this); + connect(_blinkTimer, SIGNAL(timeout()), this, SLOT(blinkEvent())); + _blinkCursorTimer = new QTimer(this); + connect(_blinkCursorTimer, SIGNAL(timeout()), this, SLOT(blinkCursorEvent())); + + //KCursor::setAutoHideCursor( this, true ); + + setUsesMouse(true); + setColorTable(base_color_table); + setMouseTracking(true); + + // Enable drag and drop + setAcceptDrops(true); // attempt + dragInfo.state = diNone; + + setFocusPolicy( Qt::WheelFocus ); + + // enable input method support + setAttribute(Qt::WA_InputMethodEnabled, true); + + // this is an important optimization, it tells Qt + // that TerminalDisplay will handle repainting its entire area. + setAttribute(Qt::WA_OpaquePaintEvent); + + _gridLayout = new QGridLayout(this); + _gridLayout->setContentsMargins(0, 0, 0, 0); + + setLayout( _gridLayout ); + + new AutoScrollHandler(this); +} + +TerminalDisplay::~TerminalDisplay() +{ + disconnect(_blinkTimer); + disconnect(_blinkCursorTimer); + qApp->removeEventFilter( this ); + + delete[] _image; + + delete _gridLayout; + delete _outputSuspendedLabel; + delete _filterChain; +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Display Operations */ +/* */ +/* ------------------------------------------------------------------------- */ + +/** + A table for emulating the simple (single width) unicode drawing chars. + It represents the 250x - 257x glyphs. If it's zero, we can't use it. + if it's not, it's encoded as follows: imagine a 5x5 grid where the points are numbered + 0 to 24 left to top, top to bottom. Each point is represented by the corresponding bit. + + Then, the pixels basically have the following interpretation: + _|||_ + -...- + -...- + -...- + _|||_ + +where _ = none + | = vertical line. + - = horizontal line. + */ + + +enum LineEncode +{ + TopL = (1<<1), + TopC = (1<<2), + TopR = (1<<3), + + LeftT = (1<<5), + Int11 = (1<<6), + Int12 = (1<<7), + Int13 = (1<<8), + RightT = (1<<9), + + LeftC = (1<<10), + Int21 = (1<<11), + Int22 = (1<<12), + Int23 = (1<<13), + RightC = (1<<14), + + LeftB = (1<<15), + Int31 = (1<<16), + Int32 = (1<<17), + Int33 = (1<<18), + RightB = (1<<19), + + BotL = (1<<21), + BotC = (1<<22), + BotR = (1<<23) +}; + +#include "LineFont.h" + +static void drawLineChar(QPainter& paint, int x, int y, int w, int h, uchar code) +{ + //Calculate cell midpoints, end points. + int cx = x + w/2; + int cy = y + h/2; + int ex = x + w - 1; + int ey = y + h - 1; + + quint32 toDraw = LineChars[code]; + + //Top _lines: + if (toDraw & TopL) + paint.drawLine(cx-1, y, cx-1, cy-2); + if (toDraw & TopC) + paint.drawLine(cx, y, cx, cy-2); + if (toDraw & TopR) + paint.drawLine(cx+1, y, cx+1, cy-2); + + //Bot _lines: + if (toDraw & BotL) + paint.drawLine(cx-1, cy+2, cx-1, ey); + if (toDraw & BotC) + paint.drawLine(cx, cy+2, cx, ey); + if (toDraw & BotR) + paint.drawLine(cx+1, cy+2, cx+1, ey); + + //Left _lines: + if (toDraw & LeftT) + paint.drawLine(x, cy-1, cx-2, cy-1); + if (toDraw & LeftC) + paint.drawLine(x, cy, cx-2, cy); + if (toDraw & LeftB) + paint.drawLine(x, cy+1, cx-2, cy+1); + + //Right _lines: + if (toDraw & RightT) + paint.drawLine(cx+2, cy-1, ex, cy-1); + if (toDraw & RightC) + paint.drawLine(cx+2, cy, ex, cy); + if (toDraw & RightB) + paint.drawLine(cx+2, cy+1, ex, cy+1); + + //Intersection points. + if (toDraw & Int11) + paint.drawPoint(cx-1, cy-1); + if (toDraw & Int12) + paint.drawPoint(cx, cy-1); + if (toDraw & Int13) + paint.drawPoint(cx+1, cy-1); + + if (toDraw & Int21) + paint.drawPoint(cx-1, cy); + if (toDraw & Int22) + paint.drawPoint(cx, cy); + if (toDraw & Int23) + paint.drawPoint(cx+1, cy); + + if (toDraw & Int31) + paint.drawPoint(cx-1, cy+1); + if (toDraw & Int32) + paint.drawPoint(cx, cy+1); + if (toDraw & Int33) + paint.drawPoint(cx+1, cy+1); + +} + +void TerminalDisplay::drawLineCharString( QPainter& painter, int x, int y, const QString& str, + const Character* attributes) +{ + const QPen& currentPen = painter.pen(); + + if ( (attributes->rendition & RE_BOLD) && _boldIntense ) + { + QPen boldPen(currentPen); + boldPen.setWidth(3); + painter.setPen( boldPen ); + } + + for (int i=0 ; i < str.length(); i++) + { + uchar code = str[i].cell(); + if (LineChars[code]) + drawLineChar(painter, x + (_fontWidth*i), y, _fontWidth, _fontHeight, code); + } + + painter.setPen( currentPen ); +} + +void TerminalDisplay::setKeyboardCursorShape(KeyboardCursorShape shape) +{ + _cursorShape = shape; +} +TerminalDisplay::KeyboardCursorShape TerminalDisplay::keyboardCursorShape() const +{ + return _cursorShape; +} +void TerminalDisplay::setKeyboardCursorColor(bool useForegroundColor, const QColor& color) +{ + if (useForegroundColor) + _cursorColor = QColor(); // an invalid color means that + // the foreground color of the + // current character should + // be used + + else + _cursorColor = color; +} +QColor TerminalDisplay::keyboardCursorColor() const +{ + return _cursorColor; +} + +void TerminalDisplay::setOpacity(qreal opacity) +{ + QColor color(_blendColor); + color.setAlphaF(opacity); + + // enable automatic background filling to prevent the display + // flickering if there is no transparency + /*if ( color.alpha() == 255 ) + { + setAutoFillBackground(true); + } + else + { + setAutoFillBackground(false); + }*/ + + _blendColor = color.rgba(); +} + +void TerminalDisplay::drawBackground(QPainter& painter, const QRect& rect, const QColor& backgroundColor, bool useOpacitySetting ) +{ + // the area of the widget showing the contents of the terminal display is drawn + // using the background color from the color scheme set with setColorTable() + // + // the area of the widget behind the scroll-bar is drawn using the background + // brush from the scroll-bar's palette, to give the effect of the scroll-bar + // being outside of the terminal display and visual consistency with other KDE + // applications. + // + QRect scrollBarArea = _scrollBar->isVisible() ? + rect.intersected(_scrollBar->geometry()) : + QRect(); + QRegion contentsRegion = QRegion(rect).subtracted(scrollBarArea); + QRect contentsRect = contentsRegion.boundingRect(); + + if ( HAVE_TRANSPARENCY && qAlpha(_blendColor) < 0xff && useOpacitySetting ) + { + QColor color(backgroundColor); + color.setAlpha(qAlpha(_blendColor)); + + painter.save(); + painter.setCompositionMode(QPainter::CompositionMode_Source); + painter.fillRect(contentsRect, color); + painter.restore(); + } + else + painter.fillRect(contentsRect, backgroundColor); + + painter.fillRect(scrollBarArea,_scrollBar->palette().background()); +} + +void TerminalDisplay::drawCursor(QPainter& painter, + const QRect& rect, + const QColor& foregroundColor, + const QColor& /*backgroundColor*/, + bool& invertCharacterColor) +{ + QRect cursorRect = rect; + cursorRect.setHeight(_fontHeight - _lineSpacing - 1); + + if (!_cursorBlinking) + { + if ( _cursorColor.isValid() ) + painter.setPen(_cursorColor); + else + painter.setPen(foregroundColor); + + if ( _cursorShape == BlockCursor ) + { + // draw the cursor outline, adjusting the area so that + // it is draw entirely inside 'rect' + int penWidth = qMax(1,painter.pen().width()); + + painter.drawRect(cursorRect.adjusted(penWidth/2, + penWidth/2, + - penWidth/2 - penWidth%2, + - penWidth/2 - penWidth%2)); + if ( hasFocus() ) + { + painter.fillRect(cursorRect, _cursorColor.isValid() ? _cursorColor : foregroundColor); + + if ( !_cursorColor.isValid() ) + { + // invert the colour used to draw the text to ensure that the character at + // the cursor position is readable + invertCharacterColor = true; + } + } + } + else if ( _cursorShape == UnderlineCursor ) + painter.drawLine(cursorRect.left(), + cursorRect.bottom(), + cursorRect.right(), + cursorRect.bottom()); + else if ( _cursorShape == IBeamCursor ) + painter.drawLine(cursorRect.left(), + cursorRect.top(), + cursorRect.left(), + cursorRect.bottom()); + + } +} + +void TerminalDisplay::drawCharacters(QPainter& painter, + const QRect& rect, + const QString& text, + const Character* style, + bool invertCharacterColor) +{ + // don't draw text which is currently blinking + if ( _blinking && (style->rendition & RE_BLINK) ) + return; + + // setup bold and underline + bool useBold; + ColorEntry::FontWeight weight = style->fontWeight(_colorTable); + if (weight == ColorEntry::UseCurrentFormat) + useBold = ((style->rendition & RE_BOLD) && _boldIntense) || font().bold(); + else + useBold = (weight == ColorEntry::Bold) ? true : false; + bool useUnderline = style->rendition & RE_UNDERLINE || font().underline(); + + QFont font = painter.font(); + if ( font.bold() != useBold + || font.underline() != useUnderline ) + { + font.setBold(useBold); + font.setUnderline(useUnderline); + painter.setFont(font); + } + + // setup pen + const CharacterColor& textColor = ( invertCharacterColor ? style->backgroundColor : style->foregroundColor ); + const QColor color = textColor.color(_colorTable); + QPen pen = painter.pen(); + if ( pen.color() != color ) + { + pen.setColor(color); + painter.setPen(color); + } + + // draw text + if ( isLineCharString(text) ) + drawLineCharString(painter,rect.x(),rect.y(),text,style); + else + { + // the drawText(rect,flags,string) overload is used here with null flags + // instead of drawText(rect,string) because the (rect,string) overload causes + // the application's default layout direction to be used instead of + // the widget-specific layout direction, which should always be + // Qt::LeftToRight for this widget + // This was discussed in: http://lists.kde.org/?t=120552223600002&r=1&w=2 + if (_bidiEnabled) + painter.drawText(rect,0,text); + else + painter.drawText(rect,0,LTR_OVERRIDE_CHAR+text); + } +} + +void TerminalDisplay::drawTextFragment(QPainter& painter , + const QRect& rect, + const QString& text, + const Character* style) +{ + painter.save(); + + // setup painter + const QColor foregroundColor = style->foregroundColor.color(_colorTable); + const QColor backgroundColor = style->backgroundColor.color(_colorTable); + + // draw background if different from the display's background color + if ( backgroundColor != palette().background().color() ) + drawBackground(painter,rect,backgroundColor, + false /* do not use transparency */); + + // draw cursor shape if the current character is the cursor + // this may alter the foreground and background colors + bool invertCharacterColor = false; + if ( style->rendition & RE_CURSOR ) + drawCursor(painter,rect,foregroundColor,backgroundColor,invertCharacterColor); + + // draw text + drawCharacters(painter,rect,text,style,invertCharacterColor); + + painter.restore(); +} + +void TerminalDisplay::setRandomSeed(uint randomSeed) { _randomSeed = randomSeed; } +uint TerminalDisplay::randomSeed() const { return _randomSeed; } + +#if 0 +/*! + Set XIM Position +*/ +void TerminalDisplay::setCursorPos(const int curx, const int cury) +{ + QPoint tL = contentsRect().topLeft(); + int tLx = tL.x(); + int tLy = tL.y(); + + int xpos, ypos; + ypos = _topMargin + tLy + _fontHeight*(cury-1) + _fontAscent; + xpos = _leftMargin + tLx + _fontWidth*curx; + //setMicroFocusHint(xpos, ypos, 0, _fontHeight); //### ??? + // fprintf(stderr, "x/y = %d/%d\txpos/ypos = %d/%d\n", curx, cury, xpos, ypos); + _cursorLine = cury; + _cursorCol = curx; +} +#endif + +// scrolls the image by 'lines', down if lines > 0 or up otherwise. +// +// the terminal emulation keeps track of the scrolling of the character +// image as it receives input, and when the view is updated, it calls scrollImage() +// with the final scroll amount. this improves performance because scrolling the +// display is much cheaper than re-rendering all the text for the +// part of the image which has moved up or down. +// Instead only new lines have to be drawn +void TerminalDisplay::scrollImage(int lines , const QRect& screenWindowRegion) +{ + // if the flow control warning is enabled this will interfere with the + // scrolling optimizations and cause artifacts. the simple solution here + // is to just disable the optimization whilst it is visible + if ( _outputSuspendedLabel && _outputSuspendedLabel->isVisible() ) + return; + + // constrain the region to the display + // the bottom of the region is capped to the number of lines in the display's + // internal image - 2, so that the height of 'region' is strictly less + // than the height of the internal image. + QRect region = screenWindowRegion; + region.setBottom( qMin(region.bottom(),this->_lines-2) ); + + // return if there is nothing to do + if ( lines == 0 + || _image == 0 + || !region.isValid() + || (region.top() + abs(lines)) >= region.bottom() + || this->_lines <= region.height() ) return; + + // hide terminal size label to prevent it being scrolled + if (_resizeWidget && _resizeWidget->isVisible()) + _resizeWidget->hide(); + + // Note: With Qt 4.4 the left edge of the scrolled area must be at 0 + // to get the correct (newly exposed) part of the widget repainted. + // + // The right edge must be before the left edge of the scroll bar to + // avoid triggering a repaint of the entire widget, the distance is + // given by SCROLLBAR_CONTENT_GAP + // + // Set the QT_FLUSH_PAINT environment variable to '1' before starting the + // application to monitor repainting. + // + int scrollBarWidth = _scrollBar->isHidden() ? 0 : _scrollBar->width(); + const int SCROLLBAR_CONTENT_GAP = 1; + QRect scrollRect; + if ( _scrollbarLocation == ScrollBarLeft ) + { + scrollRect.setLeft(scrollBarWidth+SCROLLBAR_CONTENT_GAP); + scrollRect.setRight(width()); + } + else + { + scrollRect.setLeft(0); + scrollRect.setRight(width() - scrollBarWidth - SCROLLBAR_CONTENT_GAP); + } + void* firstCharPos = &_image[ region.top() * this->_columns ]; + void* lastCharPos = &_image[ (region.top() + abs(lines)) * this->_columns ]; + + int top = _topMargin + (region.top() * _fontHeight); + int linesToMove = region.height() - abs(lines); + int bytesToMove = linesToMove * + this->_columns * + sizeof(Character); + + Q_ASSERT( linesToMove > 0 ); + Q_ASSERT( bytesToMove > 0 ); + + //scroll internal image + if ( lines > 0 ) + { + // check that the memory areas that we are going to move are valid + Q_ASSERT( (char*)lastCharPos + bytesToMove < + (char*)(_image + (this->_lines * this->_columns)) ); + + Q_ASSERT( (lines*this->_columns) < _imageSize ); + + //scroll internal image down + memmove( firstCharPos , lastCharPos , bytesToMove ); + + //set region of display to scroll + scrollRect.setTop(top); + } + else + { + // check that the memory areas that we are going to move are valid + Q_ASSERT( (char*)firstCharPos + bytesToMove < + (char*)(_image + (this->_lines * this->_columns)) ); + + //scroll internal image up + memmove( lastCharPos , firstCharPos , bytesToMove ); + + //set region of the display to scroll + scrollRect.setTop(top + abs(lines) * _fontHeight); + } + scrollRect.setHeight(linesToMove * _fontHeight ); + + Q_ASSERT(scrollRect.isValid() && !scrollRect.isEmpty()); + + //scroll the display vertically to match internal _image + scroll( 0 , _fontHeight * (-lines) , scrollRect ); +} + +QRegion TerminalDisplay::hotSpotRegion() const +{ + QRegion region; + foreach( Filter::HotSpot* hotSpot , _filterChain->hotSpots() ) + { + QRect r; + if (hotSpot->startLine()==hotSpot->endLine()) { + r.setLeft(hotSpot->startColumn()); + r.setTop(hotSpot->startLine()); + r.setRight(hotSpot->endColumn()); + r.setBottom(hotSpot->endLine()); + region |= imageToWidget(r);; + } else { + r.setLeft(hotSpot->startColumn()); + r.setTop(hotSpot->startLine()); + r.setRight(_columns); + r.setBottom(hotSpot->startLine()); + region |= imageToWidget(r);; + for ( int line = hotSpot->startLine()+1 ; line < hotSpot->endLine() ; line++ ) { + r.setLeft(0); + r.setTop(line); + r.setRight(_columns); + r.setBottom(line); + region |= imageToWidget(r);; + } + r.setLeft(0); + r.setTop(hotSpot->endLine()); + r.setRight(hotSpot->endColumn()); + r.setBottom(hotSpot->endLine()); + region |= imageToWidget(r);; + } + } + return region; +} + +void TerminalDisplay::processFilters() +{ + if (!_screenWindow) + return; + + QRegion preUpdateHotSpots = hotSpotRegion(); + + // use _screenWindow->getImage() here rather than _image because + // other classes may call processFilters() when this display's + // ScreenWindow emits a scrolled() signal - which will happen before + // updateImage() is called on the display and therefore _image is + // out of date at this point + _filterChain->setImage( _screenWindow->getImage(), + _screenWindow->windowLines(), + _screenWindow->windowColumns(), + _screenWindow->getLineProperties() ); + _filterChain->process(); + + QRegion postUpdateHotSpots = hotSpotRegion(); + + update( preUpdateHotSpots | postUpdateHotSpots ); +} + +void TerminalDisplay::updateImage() +{ + if ( !_screenWindow ) + return; + + // optimization - scroll the existing image where possible and + // avoid expensive text drawing for parts of the image that + // can simply be moved up or down + scrollImage( _screenWindow->scrollCount() , + _screenWindow->scrollRegion() ); + _screenWindow->resetScrollCount(); + + if (!_image) { + // Create _image. + // The emitted changedContentSizeSignal also leads to getImage being recreated, so do this first. + updateImageSize(); + } + + Character* const newimg = _screenWindow->getImage(); + int lines = _screenWindow->windowLines(); + int columns = _screenWindow->windowColumns(); + + setScroll( _screenWindow->currentLine() , _screenWindow->lineCount() ); + + Q_ASSERT( this->_usedLines <= this->_lines ); + Q_ASSERT( this->_usedColumns <= this->_columns ); + + int y,x,len; + + QPoint tL = contentsRect().topLeft(); + int tLx = tL.x(); + int tLy = tL.y(); + _hasBlinker = false; + + CharacterColor cf; // undefined + CharacterColor _clipboard; // undefined + int cr = -1; // undefined + + const int linesToUpdate = qMin(this->_lines, qMax(0,lines )); + const int columnsToUpdate = qMin(this->_columns,qMax(0,columns)); + + QChar *disstrU = new QChar[columnsToUpdate]; + char *dirtyMask = new char[columnsToUpdate+2]; + QRegion dirtyRegion; + + // debugging variable, this records the number of lines that are found to + // be 'dirty' ( ie. have changed from the old _image to the new _image ) and + // which therefore need to be repainted + int dirtyLineCount = 0; + + for (y = 0; y < linesToUpdate; ++y) + { + const Character* currentLine = &_image[y*this->_columns]; + const Character* const newLine = &newimg[y*columns]; + + bool updateLine = false; + + // The dirty mask indicates which characters need repainting. We also + // mark surrounding neighbours dirty, in case the character exceeds + // its cell boundaries + memset(dirtyMask, 0, columnsToUpdate+2); + + for( x = 0 ; x < columnsToUpdate ; ++x) + { + if ( newLine[x] != currentLine[x] ) + { + dirtyMask[x] = true; + } + } + + if (!_resizing) // not while _resizing, we're expecting a paintEvent + for (x = 0; x < columnsToUpdate; ++x) + { + _hasBlinker |= (newLine[x].rendition & RE_BLINK); + + // Start drawing if this character or the next one differs. + // We also take the next one into account to handle the situation + // where characters exceed their cell width. + if (dirtyMask[x]) + { + quint16 c = newLine[x+0].character; + if ( !c ) + continue; + int p = 0; + disstrU[p++] = c; //fontMap(c); + bool lineDraw = isLineChar(c); + bool doubleWidth = (x+1 == columnsToUpdate) ? false : (newLine[x+1].character == 0); + cr = newLine[x].rendition; + _clipboard = newLine[x].backgroundColor; + if (newLine[x].foregroundColor != cf) cf = newLine[x].foregroundColor; + int lln = columnsToUpdate - x; + for (len = 1; len < lln; ++len) + { + const Character& ch = newLine[x+len]; + + if (!ch.character) + continue; // Skip trailing part of multi-col chars. + + bool nextIsDoubleWidth = (x+len+1 == columnsToUpdate) ? false : (newLine[x+len+1].character == 0); + + if ( ch.foregroundColor != cf || + ch.backgroundColor != _clipboard || + ch.rendition != cr || + !dirtyMask[x+len] || + isLineChar(c) != lineDraw || + nextIsDoubleWidth != doubleWidth ) + break; + + disstrU[p++] = c; //fontMap(c); + } + + QString unistr(disstrU, p); + + bool saveFixedFont = _fixedFont; + if (lineDraw) + _fixedFont = false; + if (doubleWidth) + _fixedFont = false; + + updateLine = true; + + _fixedFont = saveFixedFont; + x += len - 1; + } + + } + + //both the top and bottom halves of double height _lines must always be redrawn + //although both top and bottom halves contain the same characters, only + //the top one is actually + //drawn. + if (_lineProperties.count() > y) + updateLine |= (_lineProperties[y] & LINE_DOUBLEHEIGHT); + + // if the characters on the line are different in the old and the new _image + // then this line must be repainted. + if (updateLine) + { + dirtyLineCount++; + + // add the area occupied by this line to the region which needs to be + // repainted + QRect dirtyRect = QRect( _leftMargin+tLx , + _topMargin+tLy+_fontHeight*y , + _fontWidth * columnsToUpdate , + _fontHeight ); + + dirtyRegion |= dirtyRect; + } + + // replace the line of characters in the old _image with the + // current line of the new _image + memcpy((void*)currentLine,(const void*)newLine,columnsToUpdate*sizeof(Character)); + } + + // if the new _image is smaller than the previous _image, then ensure that the area + // outside the new _image is cleared + if ( linesToUpdate < _usedLines ) + { + dirtyRegion |= QRect( _leftMargin+tLx , + _topMargin+tLy+_fontHeight*linesToUpdate , + _fontWidth * this->_columns , + _fontHeight * (_usedLines-linesToUpdate) ); + } + _usedLines = linesToUpdate; + + if ( columnsToUpdate < _usedColumns ) + { + dirtyRegion |= QRect( _leftMargin+tLx+columnsToUpdate*_fontWidth , + _topMargin+tLy , + _fontWidth * (_usedColumns-columnsToUpdate) , + _fontHeight * this->_lines ); + } + _usedColumns = columnsToUpdate; + + dirtyRegion |= _inputMethodData.previousPreeditRect; + + // update the parts of the display which have changed + update(dirtyRegion); + + if ( _hasBlinker && !_blinkTimer->isActive()) _blinkTimer->start( TEXT_BLINK_DELAY ); + if (!_hasBlinker && _blinkTimer->isActive()) { _blinkTimer->stop(); _blinking = false; } + delete[] dirtyMask; + delete[] disstrU; + +} + +void TerminalDisplay::showResizeNotification() +{ + if (_terminalSizeHint && isVisible()) + { + if (_terminalSizeStartup) { + _terminalSizeStartup=false; + return; + } + if (!_resizeWidget) + { + _resizeWidget = new QLabel(i18n("Size: XXX x XXX"), this); + _resizeWidget->setMinimumWidth(_resizeWidget->fontMetrics().width(i18n("Size: XXX x XXX"))); + _resizeWidget->setMinimumHeight(_resizeWidget->sizeHint().height()); + _resizeWidget->setAlignment(Qt::AlignCenter); + + _resizeWidget->setStyleSheet("background-color:palette(window);border-style:solid;border-width:1px;border-color:palette(dark)"); + + _resizeTimer = new QTimer(this); + _resizeTimer->setSingleShot(true); + connect(_resizeTimer, SIGNAL(timeout()), _resizeWidget, SLOT(hide())); + } + QString sizeStr = i18n("Size: %1 x %2", _columns, _lines); + _resizeWidget->setText(sizeStr); + _resizeWidget->move((width()-_resizeWidget->width())/2, + (height()-_resizeWidget->height())/2+20); + _resizeWidget->show(); + _resizeTimer->start(1000); + } +} + +void TerminalDisplay::setBlinkingCursor(bool blink) +{ + _hasBlinkingCursor=blink; + + if (blink && !_blinkCursorTimer->isActive()) + _blinkCursorTimer->start(QApplication::cursorFlashTime() / 2); + + if (!blink && _blinkCursorTimer->isActive()) + { + _blinkCursorTimer->stop(); + if (_cursorBlinking) + blinkCursorEvent(); + else + _cursorBlinking = false; + } +} + +void TerminalDisplay::setBlinkingTextEnabled(bool blink) +{ + _allowBlinkingText = blink; + + if (blink && !_blinkTimer->isActive()) + _blinkTimer->start(TEXT_BLINK_DELAY); + + if (!blink && _blinkTimer->isActive()) + { + _blinkTimer->stop(); + _blinking = false; + } +} + +void TerminalDisplay::focusOutEvent(QFocusEvent*) +{ + // trigger a repaint of the cursor so that it is both visible (in case + // it was hidden during blinking) + // and drawn in a focused out state + _cursorBlinking = false; + updateCursor(); + + _blinkCursorTimer->stop(); + if (_blinking) + blinkEvent(); + + _blinkTimer->stop(); +} +void TerminalDisplay::focusInEvent(QFocusEvent*) +{ + if (_hasBlinkingCursor) + { + _blinkCursorTimer->start(); + } + updateCursor(); + + if (_hasBlinker) + _blinkTimer->start(); +} + +void TerminalDisplay::paintEvent( QPaintEvent* pe ) +{ + QPainter paint(this); + + foreach (const QRect &rect, (pe->region() & contentsRect()).rects()) + { + drawBackground(paint,rect,palette().background().color(), + true /* use opacity setting */); + drawContents(paint, rect); + } + drawInputMethodPreeditString(paint,preeditRect()); + paintFilters(paint); +} + +QPoint TerminalDisplay::cursorPosition() const +{ + if (_screenWindow) + return _screenWindow->cursorPosition(); + else + return QPoint(0,0); +} + +QRect TerminalDisplay::preeditRect() const +{ + const int preeditLength = string_width(_inputMethodData.preeditString); + + if ( preeditLength == 0 ) + return QRect(); + + return QRect(_leftMargin + _fontWidth*cursorPosition().x(), + _topMargin + _fontHeight*cursorPosition().y(), + _fontWidth*preeditLength, + _fontHeight); +} + +void TerminalDisplay::drawInputMethodPreeditString(QPainter& painter , const QRect& rect) +{ + if ( _inputMethodData.preeditString.isEmpty() ) + return; + + const QPoint cursorPos = cursorPosition(); + + bool invertColors = false; + const QColor background = _colorTable[DEFAULT_BACK_COLOR].color; + const QColor foreground = _colorTable[DEFAULT_FORE_COLOR].color; + const Character* style = &_image[loc(cursorPos.x(),cursorPos.y())]; + + drawBackground(painter,rect,background,true); + drawCursor(painter,rect,foreground,background,invertColors); + drawCharacters(painter,rect,_inputMethodData.preeditString,style,invertColors); + + _inputMethodData.previousPreeditRect = rect; +} + +FilterChain* TerminalDisplay::filterChain() const +{ + return _filterChain; +} + +void TerminalDisplay::paintFilters(QPainter& painter) +{ + // get color of character under mouse and use it to draw + // lines for filters + QPoint cursorPos = mapFromGlobal(QCursor::pos()); + int cursorLine; + int cursorColumn; + int scrollBarWidth = (_scrollbarLocation == ScrollBarLeft) ? _scrollBar->width() : 0; + + getCharacterPosition( cursorPos , cursorLine , cursorColumn ); + Character cursorCharacter = _image[loc(cursorColumn,cursorLine)]; + + painter.setPen( QPen(cursorCharacter.foregroundColor.color(colorTable())) ); + + // iterate over hotspots identified by the display's currently active filters + // and draw appropriate visuals to indicate the presence of the hotspot + + QList<Filter::HotSpot*> spots = _filterChain->hotSpots(); + QListIterator<Filter::HotSpot*> iter(spots); + while (iter.hasNext()) + { + Filter::HotSpot* spot = iter.next(); + + QRegion region; + if ( spot->type() == Filter::HotSpot::Link ) { + QRect r; + if (spot->startLine()==spot->endLine()) { + r.setCoords( spot->startColumn()*_fontWidth + 1 + scrollBarWidth, + spot->startLine()*_fontHeight + 1, + (spot->endColumn()-1)*_fontWidth - 1 + scrollBarWidth, + (spot->endLine()+1)*_fontHeight - 1 ); + region |= r; + } else { + r.setCoords( spot->startColumn()*_fontWidth + 1 + scrollBarWidth, + spot->startLine()*_fontHeight + 1, + (_columns-1)*_fontWidth - 1 + scrollBarWidth, + (spot->startLine()+1)*_fontHeight - 1 ); + region |= r; + for ( int line = spot->startLine()+1 ; line < spot->endLine() ; line++ ) { + r.setCoords( 0*_fontWidth + 1 + scrollBarWidth, + line*_fontHeight + 1, + (_columns-1)*_fontWidth - 1 + scrollBarWidth, + (line+1)*_fontHeight - 1 ); + region |= r; + } + r.setCoords( 0*_fontWidth + 1 + scrollBarWidth, + spot->endLine()*_fontHeight + 1, + (spot->endColumn()-1)*_fontWidth - 1 + scrollBarWidth, + (spot->endLine()+1)*_fontHeight - 1 ); + region |= r; + } + } + + for ( int line = spot->startLine() ; line <= spot->endLine() ; line++ ) + { + int startColumn = 0; + int endColumn = _columns-1; // TODO use number of _columns which are actually + // occupied on this line rather than the width of the + // display in _columns + + // ignore whitespace at the end of the lines + while ( QChar(_image[loc(endColumn,line)].character).isSpace() && endColumn > 0 ) + endColumn--; + + // increment here because the column which we want to set 'endColumn' to + // is the first whitespace character at the end of the line + endColumn++; + + if ( line == spot->startLine() ) + startColumn = spot->startColumn(); + if ( line == spot->endLine() ) + endColumn = spot->endColumn(); + + // subtract one pixel from + // the right and bottom so that + // we do not overdraw adjacent + // hotspots + // + // subtracting one pixel from all sides also prevents an edge case where + // moving the mouse outside a link could still leave it underlined + // because the check below for the position of the cursor + // finds it on the border of the target area + QRect r; + r.setCoords( startColumn*_fontWidth + 1 + scrollBarWidth, + line*_fontHeight + 1, + endColumn*_fontWidth - 1 + scrollBarWidth, + (line+1)*_fontHeight - 1 ); + // Underline link hotspots + if ( spot->type() == Filter::HotSpot::Link ) + { + QFontMetrics metrics(font()); + + // find the baseline (which is the invisible line that the characters in the font sit on, + // with some having tails dangling below) + int baseline = r.bottom() - metrics.descent(); + // find the position of the underline below that + int underlinePos = baseline + metrics.underlinePos(); + if ( region.contains( mapFromGlobal(QCursor::pos()) ) ){ + painter.drawLine( r.left() , underlinePos , + r.right() , underlinePos ); + } + } + // Marker hotspots simply have a transparent rectanglular shape + // drawn on top of them + else if ( spot->type() == Filter::HotSpot::Marker ) + { + //TODO - Do not use a hardcoded colour for this + painter.fillRect(r,QBrush(QColor(255,0,0,120))); + } + } + } +} +void TerminalDisplay::drawContents(QPainter &paint, const QRect &rect) +{ + QPoint tL = contentsRect().topLeft(); + int tLx = tL.x(); + int tLy = tL.y(); + + int lux = qMin(_usedColumns-1, qMax(0,(rect.left() - tLx - _leftMargin ) / _fontWidth)); + int luy = qMin(_usedLines-1, qMax(0,(rect.top() - tLy - _topMargin ) / _fontHeight)); + int rlx = qMin(_usedColumns-1, qMax(0,(rect.right() - tLx - _leftMargin ) / _fontWidth)); + int rly = qMin(_usedLines-1, qMax(0,(rect.bottom() - tLy - _topMargin ) / _fontHeight)); + + const int bufferSize = _usedColumns; + QString unistr; + unistr.reserve(bufferSize); + for (int y = luy; y <= rly; y++) + { + quint16 c = _image[loc(lux,y)].character; + int x = lux; + if(!c && x) + x--; // Search for start of multi-column character + for (; x <= rlx; x++) + { + int len = 1; + int p = 0; + + // reset our buffer to the maximal size + unistr.resize(bufferSize); + QChar *disstrU = unistr.data(); + + // is this a single character or a sequence of characters ? + if ( _image[loc(x,y)].rendition & RE_EXTENDED_CHAR ) + { + // sequence of characters + ushort extendedCharLength = 0; + ushort* chars = ExtendedCharTable::instance + .lookupExtendedChar(_image[loc(x,y)].charSequence,extendedCharLength); + for ( int index = 0 ; index < extendedCharLength ; index++ ) + { + Q_ASSERT( p < bufferSize ); + disstrU[p++] = chars[index]; + } + } + else + { + // single character + c = _image[loc(x,y)].character; + if (c) + { + Q_ASSERT( p < bufferSize ); + disstrU[p++] = c; //fontMap(c); + } + } + + bool lineDraw = isLineChar(c); + bool doubleWidth = (_image[ qMin(loc(x,y)+1,_imageSize) ].character == 0); + CharacterColor currentForeground = _image[loc(x,y)].foregroundColor; + CharacterColor currentBackground = _image[loc(x,y)].backgroundColor; + quint8 currentRendition = _image[loc(x,y)].rendition; + + while (x+len <= rlx && + _image[loc(x+len,y)].foregroundColor == currentForeground && + _image[loc(x+len,y)].backgroundColor == currentBackground && + _image[loc(x+len,y)].rendition == currentRendition && + (_image[ qMin(loc(x+len,y)+1,_imageSize) ].character == 0) == doubleWidth && + isLineChar( c = _image[loc(x+len,y)].character) == lineDraw) // Assignment! + { + if (c) + disstrU[p++] = c; //fontMap(c); + if (doubleWidth) // assert((_image[loc(x+len,y)+1].character == 0)), see above if condition + len++; // Skip trailing part of multi-column character + len++; + } + if ((x+len < _usedColumns) && (!_image[loc(x+len,y)].character)) + len++; // Adjust for trailing part of multi-column character + + bool save__fixedFont = _fixedFont; + if (lineDraw) + _fixedFont = false; + if (doubleWidth) + _fixedFont = false; + unistr.resize(p); + + // Create a text scaling matrix for double width and double height lines. + QMatrix textScale; + + if (y < _lineProperties.size()) + { + if (_lineProperties[y] & LINE_DOUBLEWIDTH) + textScale.scale(2,1); + + if (_lineProperties[y] & LINE_DOUBLEHEIGHT) + textScale.scale(1,2); + } + + //Apply text scaling matrix. + paint.setWorldMatrix(textScale, true); + + //calculate the area in which the text will be drawn + QRect textArea = QRect( _leftMargin+tLx+_fontWidth*x , _topMargin+tLy+_fontHeight*y , _fontWidth*len , _fontHeight); + + //move the calculated area to take account of scaling applied to the painter. + //the position of the area from the origin (0,0) is scaled + //by the opposite of whatever + //transformation has been applied to the painter. this ensures that + //painting does actually start from textArea.topLeft() + //(instead of textArea.topLeft() * painter-scale) + textArea.moveTopLeft( textScale.inverted().map(textArea.topLeft()) ); + + //paint text fragment + drawTextFragment( paint, + textArea, + unistr, + &_image[loc(x,y)] ); //, + //0, + //!_isPrinting ); + + _fixedFont = save__fixedFont; + + //reset back to single-width, single-height _lines + paint.setWorldMatrix(textScale.inverted(), true); + + if (y < _lineProperties.size()-1) + { + //double-height _lines are represented by two adjacent _lines + //containing the same characters + //both _lines will have the LINE_DOUBLEHEIGHT attribute. + //If the current line has the LINE_DOUBLEHEIGHT attribute, + //we can therefore skip the next line + if (_lineProperties[y] & LINE_DOUBLEHEIGHT) + y++; + } + + x += len - 1; + } + } +} + +void TerminalDisplay::blinkEvent() +{ + if (!_allowBlinkingText) return; + + _blinking = !_blinking; + + //TODO: Optimize to only repaint the areas of the widget + // where there is blinking text + // rather than repainting the whole widget. + update(); +} + +QRect TerminalDisplay::imageToWidget(const QRect& imageArea) const +{ + QRect result; + result.setLeft( _leftMargin + _fontWidth * imageArea.left() ); + result.setTop( _topMargin + _fontHeight * imageArea.top() ); + result.setWidth( _fontWidth * imageArea.width() ); + result.setHeight( _fontHeight * imageArea.height() ); + + return result; +} + +void TerminalDisplay::updateCursor() +{ + QRect cursorRect = imageToWidget( QRect(cursorPosition(),QSize(1,1)) ); + update(cursorRect); +} + +void TerminalDisplay::blinkCursorEvent() +{ + _cursorBlinking = !_cursorBlinking; + updateCursor(); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Resizing */ +/* */ +/* ------------------------------------------------------------------------- */ + +void TerminalDisplay::resizeEvent(QResizeEvent*) +{ + updateImageSize(); +} + +void TerminalDisplay::propagateSize() +{ + if (_isFixedSize) + { + setSize(_columns, _lines); + QWidget::setFixedSize(sizeHint()); + parentWidget()->adjustSize(); + parentWidget()->setFixedSize(parentWidget()->sizeHint()); + return; + } + if (_image) + updateImageSize(); +} + +void TerminalDisplay::updateImageSize() +{ + Character* oldimg = _image; + int oldlin = _lines; + int oldcol = _columns; + + makeImage(); + + // copy the old image to reduce flicker + int lines = qMin(oldlin,_lines); + int columns = qMin(oldcol,_columns); + + if (oldimg) + { + for (int line = 0; line < lines; line++) + { + memcpy((void*)&_image[_columns*line], + (void*)&oldimg[oldcol*line],columns*sizeof(Character)); + } + delete[] oldimg; + } + + if (_screenWindow) + _screenWindow->setWindowLines(_lines); + + _resizing = (oldlin!=_lines) || (oldcol!=_columns); + + if ( _resizing ) + { + showResizeNotification(); + emit changedContentSizeSignal(_contentHeight, _contentWidth); // expose resizeEvent + } + + _resizing = false; +} + +//showEvent and hideEvent are reimplemented here so that it appears to other classes that the +//display has been resized when the display is hidden or shown. +// +//TODO: Perhaps it would be better to have separate signals for show and hide instead of using +//the same signal as the one for a content size change +void TerminalDisplay::showEvent(QShowEvent*) +{ + emit changedContentSizeSignal(_contentHeight,_contentWidth); +} +void TerminalDisplay::hideEvent(QHideEvent*) +{ + emit changedContentSizeSignal(_contentHeight,_contentWidth); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Scrollbar */ +/* */ +/* ------------------------------------------------------------------------- */ + +void TerminalDisplay::scrollBarPositionChanged(int) +{ + if ( !_screenWindow ) + return; + + _screenWindow->scrollTo( _scrollBar->value() ); + + // if the thumb has been moved to the bottom of the _scrollBar then set + // the display to automatically track new output, + // that is, scroll down automatically + // to how new _lines as they are added + const bool atEndOfOutput = (_scrollBar->value() == _scrollBar->maximum()); + _screenWindow->setTrackOutput( atEndOfOutput ); + + updateImage(); +} + +void TerminalDisplay::setScroll(int cursor, int slines) +{ + // update _scrollBar if the range or value has changed, + // otherwise return + // + // setting the range or value of a _scrollBar will always trigger + // a repaint, so it should be avoided if it is not necessary + if ( _scrollBar->minimum() == 0 && + _scrollBar->maximum() == (slines - _lines) && + _scrollBar->value() == cursor ) + { + return; + } + + disconnect(_scrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollBarPositionChanged(int))); + _scrollBar->setRange(0,slines - _lines); + _scrollBar->setSingleStep(1); + _scrollBar->setPageStep(_lines); + _scrollBar->setValue(cursor); + connect(_scrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollBarPositionChanged(int))); +} + +void TerminalDisplay::setScrollBarPosition(ScrollBarPosition position) +{ + if (_scrollbarLocation == position) + return; + + if ( position == NoScrollBar ) + _scrollBar->hide(); + else + _scrollBar->show(); + + _topMargin = _leftMargin = 1; + _scrollbarLocation = position; + + propagateSize(); + update(); +} + +void TerminalDisplay::mousePressEvent(QMouseEvent* ev) +{ + if ( _possibleTripleClick && (ev->button()==Qt::LeftButton) ) { + mouseTripleClickEvent(ev); + return; + } + + if ( !contentsRect().contains(ev->pos()) ) return; + + if ( !_screenWindow ) return; + + int charLine; + int charColumn; + getCharacterPosition(ev->pos(),charLine,charColumn); + QPoint pos = QPoint(charColumn,charLine); + + if ( ev->button() == Qt::LeftButton) + { + _lineSelectionMode = false; + _wordSelectionMode = false; + + emit isBusySelecting(true); // Keep it steady... + // Drag only when the Control key is hold + bool selected = false; + + // The receiver of the testIsSelected() signal will adjust + // 'selected' accordingly. + //emit testIsSelected(pos.x(), pos.y(), selected); + + selected = _screenWindow->isSelected(pos.x(),pos.y()); + + if ((!_ctrlDrag || ev->modifiers() & Qt::ControlModifier) && selected ) { + // The user clicked inside selected text + dragInfo.state = diPending; + dragInfo.start = ev->pos(); + } + else { + // No reason to ever start a drag event + dragInfo.state = diNone; + + _preserveLineBreaks = !( ( ev->modifiers() & Qt::ControlModifier ) && !(ev->modifiers() & Qt::AltModifier) ); + _columnSelectionMode = (ev->modifiers() & Qt::AltModifier) && (ev->modifiers() & Qt::ControlModifier); + + if (_mouseMarks || (ev->modifiers() & Qt::ShiftModifier)) + { + _screenWindow->clearSelection(); + + //emit clearSelectionSignal(); + pos.ry() += _scrollBar->value(); + _iPntSel = _pntSel = pos; + _actSel = 1; // left mouse button pressed but nothing selected yet. + + } + else + { + emit mouseSignal( 0, charColumn + 1, charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , 0); + } + } + } + else if ( ev->button() == Qt::MidButton ) + { + if ( _mouseMarks || (!_mouseMarks && (ev->modifiers() & Qt::ShiftModifier)) ) + emitSelection(true,ev->modifiers() & Qt::ControlModifier); + else + emit mouseSignal( 1, charColumn +1, charLine +1 +_scrollBar->value() -_scrollBar->maximum() , 0); + } + else if ( ev->button() == Qt::RightButton ) + { + if (_mouseMarks || (ev->modifiers() & Qt::ShiftModifier)) + emit configureRequest(ev->pos()); + else + emit mouseSignal( 2, charColumn +1, charLine +1 +_scrollBar->value() -_scrollBar->maximum() , 0); + } +} + +QList<QAction*> TerminalDisplay::filterActions(const QPoint& position) +{ + int charLine, charColumn; + getCharacterPosition(position,charLine,charColumn); + + Filter::HotSpot* spot = _filterChain->hotSpotAt(charLine,charColumn); + + return spot ? spot->actions() : QList<QAction*>(); +} + +void TerminalDisplay::mouseMoveEvent(QMouseEvent* ev) +{ + int charLine = 0; + int charColumn = 0; + int scrollBarWidth = (_scrollbarLocation == ScrollBarLeft) ? _scrollBar->width() : 0; + + getCharacterPosition(ev->pos(),charLine,charColumn); + + // handle filters + // change link hot-spot appearance on mouse-over + Filter::HotSpot* spot = _filterChain->hotSpotAt(charLine,charColumn); + if ( spot && spot->type() == Filter::HotSpot::Link) + { + QRegion previousHotspotArea = _mouseOverHotspotArea; + _mouseOverHotspotArea = QRegion(); + QRect r; + if (spot->startLine()==spot->endLine()) { + r.setCoords( spot->startColumn()*_fontWidth + scrollBarWidth, + spot->startLine()*_fontHeight, + spot->endColumn()*_fontWidth + scrollBarWidth, + (spot->endLine()+1)*_fontHeight - 1 ); + _mouseOverHotspotArea |= r; + } else { + r.setCoords( spot->startColumn()*_fontWidth + scrollBarWidth, + spot->startLine()*_fontHeight, + _columns*_fontWidth - 1 + scrollBarWidth, + (spot->startLine()+1)*_fontHeight ); + _mouseOverHotspotArea |= r; + for ( int line = spot->startLine()+1 ; line < spot->endLine() ; line++ ) { + r.setCoords( 0*_fontWidth + scrollBarWidth, + line*_fontHeight, + _columns*_fontWidth + scrollBarWidth, + (line+1)*_fontHeight ); + _mouseOverHotspotArea |= r; + } + r.setCoords( 0*_fontWidth + scrollBarWidth, + spot->endLine()*_fontHeight, + spot->endColumn()*_fontWidth + scrollBarWidth, + (spot->endLine()+1)*_fontHeight ); + _mouseOverHotspotArea |= r; + } + // display tooltips when mousing over links + // TODO: Extend this to work with filter types other than links + const QString& tooltip = spot->tooltip(); + if ( !tooltip.isEmpty() ) + { + QToolTip::showText( mapToGlobal(ev->pos()) , tooltip , this , _mouseOverHotspotArea.boundingRect() ); + } + + update( _mouseOverHotspotArea | previousHotspotArea ); + } + else if ( !_mouseOverHotspotArea.isEmpty() ) + { + update( _mouseOverHotspotArea ); + // set hotspot area to an invalid rectangle + _mouseOverHotspotArea = QRegion(); + } + + // for auto-hiding the cursor, we need mouseTracking + if (ev->buttons() == Qt::NoButton ) return; + + // if the terminal is interested in mouse movements + // then emit a mouse movement signal, unless the shift + // key is being held down, which overrides this. + if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) + { + int button = 3; + if (ev->buttons() & Qt::LeftButton) + button = 0; + if (ev->buttons() & Qt::MidButton) + button = 1; + if (ev->buttons() & Qt::RightButton) + button = 2; + + + emit mouseSignal( button, + charColumn + 1, + charLine + 1 +_scrollBar->value() -_scrollBar->maximum(), + 1 ); + + return; + } + + if (dragInfo.state == diPending) + { + // we had a mouse down, but haven't confirmed a drag yet + // if the mouse has moved sufficiently, we will confirm + + int distance = 10; //KGlobalSettings::dndEventDelay(); + if ( ev->x() > dragInfo.start.x() + distance || ev->x() < dragInfo.start.x() - distance || + ev->y() > dragInfo.start.y() + distance || ev->y() < dragInfo.start.y() - distance) + { + // we've left the drag square, we can start a real drag operation now + emit isBusySelecting(false); // Ok.. we can breath again. + + _screenWindow->clearSelection(); + doDrag(); + } + return; + } + else if (dragInfo.state == diDragging) + { + // this isn't technically needed because mouseMoveEvent is suppressed during + // Qt drag operations, replaced by dragMoveEvent + return; + } + + if (_actSel == 0) return; + + // don't extend selection while pasting + if (ev->buttons() & Qt::MidButton) return; + + extendSelection( ev->pos() ); +} + +void TerminalDisplay::extendSelection( const QPoint& position ) +{ + QPoint pos = position; + + if ( !_screenWindow ) + return; + + //if ( !contentsRect().contains(ev->pos()) ) return; + QPoint tL = contentsRect().topLeft(); + int tLx = tL.x(); + int tLy = tL.y(); + int scroll = _scrollBar->value(); + + // we're in the process of moving the mouse with the left button pressed + // the mouse cursor will kept caught within the bounds of the text in + // this widget. + + int linesBeyondWidget = 0; + + QRect textBounds(tLx + _leftMargin, + tLy + _topMargin, + _usedColumns*_fontWidth-1, + _usedLines*_fontHeight-1); + + // Adjust position within text area bounds. + QPoint oldpos = pos; + + pos.setX( qBound(textBounds.left(),pos.x(),textBounds.right()) ); + pos.setY( qBound(textBounds.top(),pos.y(),textBounds.bottom()) ); + + if ( oldpos.y() > textBounds.bottom() ) + { + linesBeyondWidget = (oldpos.y()-textBounds.bottom()) / _fontHeight; + _scrollBar->setValue(_scrollBar->value()+linesBeyondWidget+1); // scrollforward + } + if ( oldpos.y() < textBounds.top() ) + { + linesBeyondWidget = (textBounds.top()-oldpos.y()) / _fontHeight; + _scrollBar->setValue(_scrollBar->value()-linesBeyondWidget-1); // history + } + + int charColumn = 0; + int charLine = 0; + getCharacterPosition(pos,charLine,charColumn); + + QPoint here = QPoint(charColumn,charLine); //QPoint((pos.x()-tLx-_leftMargin+(_fontWidth/2))/_fontWidth,(pos.y()-tLy-_topMargin)/_fontHeight); + QPoint ohere; + QPoint _iPntSelCorr = _iPntSel; + _iPntSelCorr.ry() -= _scrollBar->value(); + QPoint _pntSelCorr = _pntSel; + _pntSelCorr.ry() -= _scrollBar->value(); + bool swapping = false; + + if ( _wordSelectionMode ) + { + // Extend to word boundaries + int i; + QChar selClass; + + bool left_not_right = ( here.y() < _iPntSelCorr.y() || + ( here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x() ) ); + bool old_left_not_right = ( _pntSelCorr.y() < _iPntSelCorr.y() || + ( _pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x() ) ); + swapping = left_not_right != old_left_not_right; + + // Find left (left_not_right ? from here : from start) + QPoint left = left_not_right ? here : _iPntSelCorr; + i = loc(left.x(),left.y()); + if (i>=0 && i<=_imageSize) { + selClass = charClass(_image[i].character); + while ( ((left.x()>0) || (left.y()>0 && (_lineProperties[left.y()-1] & LINE_WRAPPED) )) + && charClass(_image[i-1].character) == selClass ) + { i--; if (left.x()>0) left.rx()--; else {left.rx()=_usedColumns-1; left.ry()--;} } + } + + // Find left (left_not_right ? from start : from here) + QPoint right = left_not_right ? _iPntSelCorr : here; + i = loc(right.x(),right.y()); + if (i>=0 && i<=_imageSize) { + selClass = charClass(_image[i].character); + while( ((right.x()<_usedColumns-1) || (right.y()<_usedLines-1 && (_lineProperties[right.y()] & LINE_WRAPPED) )) + && charClass(_image[i+1].character) == selClass ) + { i++; if (right.x()<_usedColumns-1) right.rx()++; else {right.rx()=0; right.ry()++; } } + } + + // Pick which is start (ohere) and which is extension (here) + if ( left_not_right ) + { + here = left; ohere = right; + } + else + { + here = right; ohere = left; + } + ohere.rx()++; + } + + if ( _lineSelectionMode ) + { + // Extend to complete line + bool above_not_below = ( here.y() < _iPntSelCorr.y() ); + + QPoint above = above_not_below ? here : _iPntSelCorr; + QPoint below = above_not_below ? _iPntSelCorr : here; + + while (above.y()>0 && (_lineProperties[above.y()-1] & LINE_WRAPPED) ) + above.ry()--; + while (below.y()<_usedLines-1 && (_lineProperties[below.y()] & LINE_WRAPPED) ) + below.ry()++; + + above.setX(0); + below.setX(_usedColumns-1); + + // Pick which is start (ohere) and which is extension (here) + if ( above_not_below ) + { + here = above; ohere = below; + } + else + { + here = below; ohere = above; + } + + QPoint newSelBegin = QPoint( ohere.x(), ohere.y() ); + swapping = !(_tripleSelBegin==newSelBegin); + _tripleSelBegin = newSelBegin; + + ohere.rx()++; + } + + int offset = 0; + if ( !_wordSelectionMode && !_lineSelectionMode ) + { + int i; + QChar selClass; + + bool left_not_right = ( here.y() < _iPntSelCorr.y() || + ( here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x() ) ); + bool old_left_not_right = ( _pntSelCorr.y() < _iPntSelCorr.y() || + ( _pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x() ) ); + swapping = left_not_right != old_left_not_right; + + // Find left (left_not_right ? from here : from start) + QPoint left = left_not_right ? here : _iPntSelCorr; + + // Find left (left_not_right ? from start : from here) + QPoint right = left_not_right ? _iPntSelCorr : here; + if ( right.x() > 0 && !_columnSelectionMode ) + { + i = loc(right.x(),right.y()); + if (i>=0 && i<=_imageSize) { + selClass = charClass(_image[i-1].character); + /* if (selClass == ' ') + { + while ( right.x() < _usedColumns-1 && charClass(_image[i+1].character) == selClass && (right.y()<_usedLines-1) && + !(_lineProperties[right.y()] & LINE_WRAPPED)) + { i++; right.rx()++; } + if (right.x() < _usedColumns-1) + right = left_not_right ? _iPntSelCorr : here; + else + right.rx()++; // will be balanced later because of offset=-1; + }*/ + } + } + + // Pick which is start (ohere) and which is extension (here) + if ( left_not_right ) + { + here = left; ohere = right; offset = 0; + } + else + { + here = right; ohere = left; offset = -1; + } + } + + if ((here == _pntSelCorr) && (scroll == _scrollBar->value())) return; // not moved + + if (here == ohere) return; // It's not left, it's not right. + + if ( _actSel < 2 || swapping ) + { + if ( _columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode ) + { + _screenWindow->setSelectionStart( ohere.x() , ohere.y() , true ); + } + else + { + _screenWindow->setSelectionStart( ohere.x()-1-offset , ohere.y() , false ); + } + + } + + _actSel = 2; // within selection + _pntSel = here; + _pntSel.ry() += _scrollBar->value(); + + if ( _columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode ) + { + _screenWindow->setSelectionEnd( here.x() , here.y() ); + } + else + { + _screenWindow->setSelectionEnd( here.x()+offset , here.y() ); + } + +} + +void TerminalDisplay::mouseReleaseEvent(QMouseEvent* ev) +{ + if ( !_screenWindow ) + return; + + int charLine; + int charColumn; + getCharacterPosition(ev->pos(),charLine,charColumn); + + if ( ev->button() == Qt::LeftButton) + { + emit isBusySelecting(false); + if(dragInfo.state == diPending) + { + // We had a drag event pending but never confirmed. Kill selection + _screenWindow->clearSelection(); + //emit clearSelectionSignal(); + } + else + { + if ( _actSel > 1 ) + { + setSelection( _screenWindow->selectedText(_preserveLineBreaks) ); + } + + _actSel = 0; + + //FIXME: emits a release event even if the mouse is + // outside the range. The procedure used in `mouseMoveEvent' + // applies here, too. + + if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) + emit mouseSignal( 3, // release + charColumn + 1, + charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , 0); + } + dragInfo.state = diNone; + } + + + if ( !_mouseMarks && + ((ev->button() == Qt::RightButton && !(ev->modifiers() & Qt::ShiftModifier)) + || ev->button() == Qt::MidButton) ) + { + emit mouseSignal( 3, + charColumn + 1, + charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , + 0); + } +} + +void TerminalDisplay::getCharacterPosition(const QPoint& widgetPoint,int& line,int& column) const +{ + column = (widgetPoint.x() + _fontWidth/2 -contentsRect().left()-_leftMargin) / _fontWidth; + line = (widgetPoint.y()-contentsRect().top()-_topMargin) / _fontHeight; + + if ( line < 0 ) + line = 0; + if ( column < 0 ) + column = 0; + + if ( line >= _usedLines ) + line = _usedLines-1; + + // the column value returned can be equal to _usedColumns, which + // is the position just after the last character displayed in a line. + // + // this is required so that the user can select characters in the right-most + // column (or left-most for right-to-left input) + if ( column > _usedColumns ) + column = _usedColumns; +} + +void TerminalDisplay::updateLineProperties() +{ + if ( !_screenWindow ) + return; + + _lineProperties = _screenWindow->getLineProperties(); +} + +void TerminalDisplay::mouseDoubleClickEvent(QMouseEvent* ev) +{ + if ( ev->button() != Qt::LeftButton) return; + if ( !_screenWindow ) return; + + int charLine = 0; + int charColumn = 0; + + getCharacterPosition(ev->pos(),charLine,charColumn); + + QPoint pos(charColumn,charLine); + + // pass on double click as two clicks. + if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) + { + // Send just _ONE_ click event, since the first click of the double click + // was already sent by the click handler + emit mouseSignal( 0, + pos.x()+1, + pos.y()+1 +_scrollBar->value() -_scrollBar->maximum(), + 0 ); // left button + return; + } + + _screenWindow->clearSelection(); + QPoint bgnSel = pos; + QPoint endSel = pos; + int i = loc(bgnSel.x(),bgnSel.y()); + _iPntSel = bgnSel; + _iPntSel.ry() += _scrollBar->value(); + + _wordSelectionMode = true; + + // find word boundaries... + QChar selClass = charClass(_image[i].character); + { + // find the start of the word + int x = bgnSel.x(); + while ( ((x>0) || (bgnSel.y()>0 && (_lineProperties[bgnSel.y()-1] & LINE_WRAPPED) )) + && charClass(_image[i-1].character) == selClass ) + { + i--; + if (x>0) + x--; + else + { + x=_usedColumns-1; + bgnSel.ry()--; + } + } + + bgnSel.setX(x); + _screenWindow->setSelectionStart( bgnSel.x() , bgnSel.y() , false ); + + // find the end of the word + i = loc( endSel.x(), endSel.y() ); + x = endSel.x(); + while( ((x<_usedColumns-1) || (endSel.y()<_usedLines-1 && (_lineProperties[endSel.y()] & LINE_WRAPPED) )) + && charClass(_image[i+1].character) == selClass ) + { + i++; + if (x<_usedColumns-1) + x++; + else + { + x=0; + endSel.ry()++; + } + } + + endSel.setX(x); + + // In word selection mode don't select @ (64) if at end of word. + if ( ( QChar( _image[i].character ) == '@' ) && ( ( endSel.x() - bgnSel.x() ) > 0 ) ) + endSel.setX( x - 1 ); + + + _actSel = 2; // within selection + + _screenWindow->setSelectionEnd( endSel.x() , endSel.y() ); + + setSelection( _screenWindow->selectedText(_preserveLineBreaks) ); + } + + _possibleTripleClick=true; + + QTimer::singleShot(QApplication::doubleClickInterval(),this, + SLOT(tripleClickTimeout())); +} + +void TerminalDisplay::wheelEvent( QWheelEvent* ev ) +{ + if (ev->orientation() != Qt::Vertical) + return; + + // if the terminal program is not interested mouse events + // then send the event to the scrollbar if the slider has room to move + // or otherwise send simulated up / down key presses to the terminal program + // for the benefit of programs such as 'less' + if ( _mouseMarks ) + { + bool canScroll = _scrollBar->maximum() > 0; + if (canScroll) + _scrollBar->event(ev); + else + { + // assume that each Up / Down key event will cause the terminal application + // to scroll by one line. + // + // to get a reasonable scrolling speed, scroll by one line for every 5 degrees + // of mouse wheel rotation. Mouse wheels typically move in steps of 15 degrees, + // giving a scroll of 3 lines + int key = ev->delta() > 0 ? Qt::Key_Up : Qt::Key_Down; + + // QWheelEvent::delta() gives rotation in eighths of a degree + int wheelDegrees = ev->delta() / 8; + int linesToScroll = abs(wheelDegrees) / 5; + + QKeyEvent keyScrollEvent(QEvent::KeyPress,key,Qt::NoModifier); + + for (int i=0;i<linesToScroll;i++) + emit keyPressedSignal(&keyScrollEvent); + } + } + else + { + // terminal program wants notification of mouse activity + + int charLine; + int charColumn; + getCharacterPosition( ev->pos() , charLine , charColumn ); + + emit mouseSignal( ev->delta() > 0 ? 4 : 5, + charColumn + 1, + charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , + 0); + } +} + +void TerminalDisplay::tripleClickTimeout() +{ + _possibleTripleClick=false; +} + +void TerminalDisplay::mouseTripleClickEvent(QMouseEvent* ev) +{ + if ( !_screenWindow ) return; + + int charLine; + int charColumn; + getCharacterPosition(ev->pos(),charLine,charColumn); + _iPntSel = QPoint(charColumn,charLine); + + _screenWindow->clearSelection(); + + _lineSelectionMode = true; + _wordSelectionMode = false; + + _actSel = 2; // within selection + emit isBusySelecting(true); // Keep it steady... + + while (_iPntSel.y()>0 && (_lineProperties[_iPntSel.y()-1] & LINE_WRAPPED) ) + _iPntSel.ry()--; + + if (_tripleClickMode == SelectForwardsFromCursor) { + // find word boundary start + int i = loc(_iPntSel.x(),_iPntSel.y()); + QChar selClass = charClass(_image[i].character); + int x = _iPntSel.x(); + + while ( ((x>0) || + (_iPntSel.y()>0 && (_lineProperties[_iPntSel.y()-1] & LINE_WRAPPED) ) + ) + && charClass(_image[i-1].character) == selClass ) + { + i--; + if (x>0) + x--; + else + { + x=_columns-1; + _iPntSel.ry()--; + } + } + + _screenWindow->setSelectionStart( x , _iPntSel.y() , false ); + _tripleSelBegin = QPoint( x, _iPntSel.y() ); + } + else if (_tripleClickMode == SelectWholeLine) { + _screenWindow->setSelectionStart( 0 , _iPntSel.y() , false ); + _tripleSelBegin = QPoint( 0, _iPntSel.y() ); + } + + while (_iPntSel.y()<_lines-1 && (_lineProperties[_iPntSel.y()] & LINE_WRAPPED) ) + _iPntSel.ry()++; + + _screenWindow->setSelectionEnd( _columns - 1 , _iPntSel.y() ); + + setSelection(_screenWindow->selectedText(_preserveLineBreaks)); + + _iPntSel.ry() += _scrollBar->value(); +} + + +bool TerminalDisplay::focusNextPrevChild( bool next ) +{ + if (next) + return false; // This disables changing the active part in konqueror + // when pressing Tab + return QWidget::focusNextPrevChild( next ); +} + + +QChar TerminalDisplay::charClass(QChar qch) const +{ + if ( qch.isSpace() ) return ' '; + + if ( qch.isLetterOrNumber() || _wordCharacters.contains(qch, Qt::CaseInsensitive ) ) + return 'a'; + + return qch; +} + +void TerminalDisplay::setWordCharacters(const QString& wc) +{ + _wordCharacters = wc; +} + +void TerminalDisplay::setUsesMouse(bool on) +{ + _mouseMarks = on; + setCursor( _mouseMarks ? Qt::IBeamCursor : Qt::ArrowCursor ); +} +bool TerminalDisplay::usesMouse() const +{ + return _mouseMarks; +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Clipboard */ +/* */ +/* ------------------------------------------------------------------------- */ + +#undef KeyPress + +void TerminalDisplay::emitSelection(bool useXselection,bool appendReturn) +{ + if ( !_screenWindow ) + return; + + // Paste Clipboard by simulating keypress events + QString text = QApplication::clipboard()->text(useXselection ? QClipboard::Selection : + QClipboard::Clipboard); + if(appendReturn) + text.append("\r"); + if ( ! text.isEmpty() ) + { + text.replace('\n', '\r'); + QKeyEvent e(QEvent::KeyPress, 0, Qt::NoModifier, text); + emit keyPressedSignal(&e); // expose as a big fat keypress event + + _screenWindow->clearSelection(); + } +} + +void TerminalDisplay::setSelection(const QString& t) +{ + QApplication::clipboard()->setText(t, QClipboard::Selection); +} + +void TerminalDisplay::copyClipboard() +{ + if ( !_screenWindow ) + return; + + QString text = _screenWindow->selectedText(_preserveLineBreaks); + if (!text.isEmpty()) + QApplication::clipboard()->setText(text); +} + +void TerminalDisplay::pasteClipboard() +{ + emitSelection(false,false); +} + +void TerminalDisplay::pasteSelection() +{ + emitSelection(true,false); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Keyboard */ +/* */ +/* ------------------------------------------------------------------------- */ + +void TerminalDisplay::setFlowControlWarningEnabled( bool enable ) +{ + _flowControlWarningEnabled = enable; + + // if the dialog is currently visible and the flow control warning has + // been disabled then hide the dialog + if (!enable) + outputSuspended(false); +} + +void TerminalDisplay::keyPressEvent( QKeyEvent* event ) +{ + bool emitKeyPressSignal = true; + + // Keyboard-based navigation + if ( event->modifiers() == Qt::ShiftModifier ) + { + bool update = true; + + if ( event->key() == Qt::Key_PageUp ) + { + _screenWindow->scrollBy( ScreenWindow::ScrollPages , -1 ); + } + else if ( event->key() == Qt::Key_PageDown ) + { + _screenWindow->scrollBy( ScreenWindow::ScrollPages , 1 ); + } + else if ( event->key() == Qt::Key_Up ) + { + _screenWindow->scrollBy( ScreenWindow::ScrollLines , -1 ); + } + else if ( event->key() == Qt::Key_Down ) + { + _screenWindow->scrollBy( ScreenWindow::ScrollLines , 1 ); + } + else + update = false; + + if ( update ) + { + _screenWindow->setTrackOutput( _screenWindow->atEndOfOutput() ); + + updateLineProperties(); + updateImage(); + + // do not send key press to terminal + emitKeyPressSignal = false; + } + } + + _actSel=0; // Key stroke implies a screen update, so TerminalDisplay won't + // know where the current selection is. + + if (_hasBlinkingCursor) + { + _blinkCursorTimer->start(QApplication::cursorFlashTime() / 2); + if (_cursorBlinking) + blinkCursorEvent(); + else + _cursorBlinking = false; + } + + if ( emitKeyPressSignal ) + emit keyPressedSignal(event); + + event->accept(); +} + +void TerminalDisplay::inputMethodEvent( QInputMethodEvent* event ) +{ + QKeyEvent keyEvent(QEvent::KeyPress,0,Qt::NoModifier,event->commitString()); + emit keyPressedSignal(&keyEvent); + + _inputMethodData.preeditString = event->preeditString(); + update(preeditRect() | _inputMethodData.previousPreeditRect); + + event->accept(); +} +QVariant TerminalDisplay::inputMethodQuery( Qt::InputMethodQuery query ) const +{ + const QPoint cursorPos = _screenWindow ? _screenWindow->cursorPosition() : QPoint(0,0); + switch ( query ) + { + case Qt::ImMicroFocus: + return imageToWidget(QRect(cursorPos.x(),cursorPos.y(),1,1)); + break; + case Qt::ImFont: + return font(); + break; + case Qt::ImCursorPosition: + // return the cursor position within the current line + return cursorPos.x(); + break; + case Qt::ImSurroundingText: + { + // return the text from the current line + QString lineText; + QTextStream stream(&lineText); + PlainTextDecoder decoder; + decoder.begin(&stream); + decoder.decodeLine(&_image[loc(0,cursorPos.y())],_usedColumns,_lineProperties[cursorPos.y()]); + decoder.end(); + return lineText; + } + break; + case Qt::ImCurrentSelection: + return QString(); + break; + default: + break; + } + + return QVariant(); +} + +bool TerminalDisplay::handleShortcutOverrideEvent(QKeyEvent* keyEvent) +{ + int modifiers = keyEvent->modifiers(); + + // When a possible shortcut combination is pressed, + // emit the overrideShortcutCheck() signal to allow the host + // to decide whether the terminal should override it or not. + if (modifiers != Qt::NoModifier) + { + int modifierCount = 0; + unsigned int currentModifier = Qt::ShiftModifier; + + while (currentModifier <= Qt::KeypadModifier) + { + if (modifiers & currentModifier) + modifierCount++; + currentModifier <<= 1; + } + if (modifierCount < 2) + { + bool override = false; + emit overrideShortcutCheck(keyEvent,override); + if (override) + { + keyEvent->accept(); + return true; + } + } + } + + // Override any of the following shortcuts because + // they are needed by the terminal + int keyCode = keyEvent->key() | modifiers; + switch ( keyCode ) + { + // list is taken from the QLineEdit::event() code + case Qt::Key_Tab: + case Qt::Key_Delete: + case Qt::Key_Home: + case Qt::Key_End: + case Qt::Key_Backspace: + case Qt::Key_Left: + case Qt::Key_Right: + keyEvent->accept(); + return true; + } + return false; +} + +bool TerminalDisplay::event(QEvent* event) +{ + bool eventHandled = false; + switch (event->type()) + { + case QEvent::ShortcutOverride: + eventHandled = handleShortcutOverrideEvent((QKeyEvent*)event); + break; + case QEvent::PaletteChange: + case QEvent::ApplicationPaletteChange: + _scrollBar->setPalette( QApplication::palette() ); + break; + default: + break; + } + return eventHandled ? true : QWidget::event(event); +} + +void TerminalDisplay::setBellMode(int mode) +{ + _bellMode=mode; +} + +void TerminalDisplay::enableBell() +{ + _allowBell = true; +} + +void TerminalDisplay::bell(const QString& message) +{ + if (_bellMode==NoBell) return; + + //limit the rate at which bells can occur + //...mainly for sound effects where rapid bells in sequence + //produce a horrible noise + if ( _allowBell ) + { + _allowBell = false; + QTimer::singleShot(500,this,SLOT(enableBell())); + + if (_bellMode==SystemBeepBell) + { + // TODO: This will need added back in at some point + //KNotification::beep(); + } + else if (_bellMode==NotifyBell) + { + // TODO: This will need added back in at some point + //KNotification::event("BellVisible", message,QPixmap(),this); + } + else if (_bellMode==VisualBell) + { + swapColorTable(); + QTimer::singleShot(200,this,SLOT(swapColorTable())); + } + } +} + +void TerminalDisplay::swapColorTable() +{ + ColorEntry color = _colorTable[1]; + _colorTable[1]=_colorTable[0]; + _colorTable[0]= color; + _colorsInverted = !_colorsInverted; + update(); +} + +void TerminalDisplay::clearImage() +{ + // We initialize _image[_imageSize] too. See makeImage() + for (int i = 0; i <= _imageSize; i++) + { + _image[i].character = ' '; + _image[i].foregroundColor = CharacterColor(COLOR_SPACE_DEFAULT, + DEFAULT_FORE_COLOR); + _image[i].backgroundColor = CharacterColor(COLOR_SPACE_DEFAULT, + DEFAULT_BACK_COLOR); + _image[i].rendition = DEFAULT_RENDITION; + } +} + +void TerminalDisplay::calcGeometry() +{ + _scrollBar->resize(_scrollBar->sizeHint().width(), contentsRect().height()); + switch(_scrollbarLocation) + { + case NoScrollBar : + _leftMargin = DEFAULT_LEFT_MARGIN; + _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN; + break; + case ScrollBarLeft : + _leftMargin = DEFAULT_LEFT_MARGIN + _scrollBar->width(); + _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN - _scrollBar->width(); + _scrollBar->move(contentsRect().topLeft()); + break; + case ScrollBarRight: + _leftMargin = DEFAULT_LEFT_MARGIN; + _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN - _scrollBar->width(); + _scrollBar->move(contentsRect().topRight() - QPoint(_scrollBar->width()-1,0)); + break; + } + + _topMargin = DEFAULT_TOP_MARGIN; + _contentHeight = contentsRect().height() - 2 * DEFAULT_TOP_MARGIN + /* mysterious */ 1; + + if (!_isFixedSize) + { + // ensure that display is always at least one column wide + _columns = qMax(1,_contentWidth / _fontWidth); + _usedColumns = qMin(_usedColumns,_columns); + + // ensure that display is always at least one line high + _lines = qMax(1,_contentHeight / _fontHeight); + _usedLines = qMin(_usedLines,_lines); + } +} + +void TerminalDisplay::makeImage() +{ + calcGeometry(); + + // confirm that array will be of non-zero size, since the painting code + // assumes a non-zero array length + Q_ASSERT( _lines > 0 && _columns > 0 ); + Q_ASSERT( _usedLines <= _lines && _usedColumns <= _columns ); + + _imageSize=_lines*_columns; + + // We over-commit one character so that we can be more relaxed in dealing with + // certain boundary conditions: _image[_imageSize] is a valid but unused position + _image = new Character[_imageSize+1]; + + clearImage(); +} + +// calculate the needed size, this must be synced with calcGeometry() +void TerminalDisplay::setSize(int columns, int lines) +{ + int scrollBarWidth = _scrollBar->isHidden() ? 0 : _scrollBar->sizeHint().width(); + int horizontalMargin = 2 * DEFAULT_LEFT_MARGIN; + int verticalMargin = 2 * DEFAULT_TOP_MARGIN; + + QSize newSize = QSize( horizontalMargin + scrollBarWidth + (columns * _fontWidth) , + verticalMargin + (lines * _fontHeight) ); + + if ( newSize != size() ) + { + _size = newSize; + updateGeometry(); + } +} + +void TerminalDisplay::setFixedSize(int cols, int lins) +{ + _isFixedSize = true; + + //ensure that display is at least one line by one column in size + _columns = qMax(1,cols); + _lines = qMax(1,lins); + _usedColumns = qMin(_usedColumns,_columns); + _usedLines = qMin(_usedLines,_lines); + + if (_image) + { + delete[] _image; + makeImage(); + } + setSize(cols, lins); + QWidget::setFixedSize(_size); +} + +QSize TerminalDisplay::sizeHint() const +{ + return _size; +} + + +/* --------------------------------------------------------------------- */ +/* */ +/* Drag & Drop */ +/* */ +/* --------------------------------------------------------------------- */ + +void TerminalDisplay::dragEnterEvent(QDragEnterEvent* event) +{ + if (event->mimeData()->hasFormat("text/plain")) + event->acceptProposedAction(); +} + +void TerminalDisplay::dropEvent(QDropEvent* event) +{ + //KUrl::List urls = KUrl::List::fromMimeData(event->mimeData()); + + QString dropText; + /* + if (!urls.isEmpty()) + { + for ( int i = 0 ; i < urls.count() ; i++ ) + { + KUrl url = KIO::NetAccess::mostLocalUrl( urls[i] , 0 ); + QString urlText; + + if (url.isLocalFile()) + urlText = url.path(); + else + urlText = url.url(); + + // in future it may be useful to be able to insert file names with drag-and-drop + // without quoting them (this only affects paths with spaces in) + urlText = KShell::quoteArg(urlText); + + dropText += urlText; + + if ( i != urls.count()-1 ) + dropText += ' '; + } + } + else + { + dropText = event->mimeData()->text(); + } + */ + + if(event->mimeData()->hasFormat("text/plain")) + { + emit sendStringToEmu(dropText.toLocal8Bit()); + } +} + +void TerminalDisplay::doDrag() +{ + dragInfo.state = diDragging; + dragInfo.dragObject = new QDrag(this); + QMimeData *mimeData = new QMimeData; + mimeData->setText(QApplication::clipboard()->text(QClipboard::Selection)); + dragInfo.dragObject->setMimeData(mimeData); + dragInfo.dragObject->start(Qt::CopyAction); + // Don't delete the QTextDrag object. Qt will delete it when it's done with it. +} + +void TerminalDisplay::outputSuspended(bool suspended) +{ + //create the label when this function is first called + if (!_outputSuspendedLabel) + { + //This label includes a link to an English language website + //describing the 'flow control' (Xon/Xoff) feature found in almost + //all terminal emulators. + //If there isn't a suitable article available in the target language the link + //can simply be removed. + _outputSuspendedLabel = new QLabel( i18n("<qt>Output has been " + "<a href=\"http://en.wikipedia.org/wiki/Flow_control\">suspended</a>" + " by pressing Ctrl+S." + " Press <b>Ctrl+Q</b> to resume.</qt>"), + this ); + + QPalette palette(_outputSuspendedLabel->palette()); + //KColorScheme::adjustBackground(palette,KColorScheme::NeutralBackground); + _outputSuspendedLabel->setPalette(palette); + _outputSuspendedLabel->setAutoFillBackground(true); + _outputSuspendedLabel->setBackgroundRole(QPalette::Base); + _outputSuspendedLabel->setFont(QApplication::font()); + _outputSuspendedLabel->setContentsMargins(5, 5, 5, 5); + + //enable activation of "Xon/Xoff" link in label + _outputSuspendedLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse | + Qt::LinksAccessibleByKeyboard); + _outputSuspendedLabel->setOpenExternalLinks(true); + _outputSuspendedLabel->setVisible(false); + + _gridLayout->addWidget(_outputSuspendedLabel); + _gridLayout->addItem( new QSpacerItem(0,0,QSizePolicy::Expanding, + QSizePolicy::Expanding), + 1,0); + + } + + _outputSuspendedLabel->setVisible(suspended); +} + +uint TerminalDisplay::lineSpacing() const +{ + return _lineSpacing; +} + +void TerminalDisplay::setLineSpacing(uint i) +{ + _lineSpacing = i; + setVTFont(font()); // Trigger an update. +} + +AutoScrollHandler::AutoScrollHandler(QWidget* parent) +: QObject(parent) +, _timerId(0) +{ + parent->installEventFilter(this); +} +void AutoScrollHandler::timerEvent(QTimerEvent* event) +{ + if (event->timerId() != _timerId) + return; + + QMouseEvent mouseEvent( QEvent::MouseMove, + widget()->mapFromGlobal(QCursor::pos()), + Qt::NoButton, + Qt::LeftButton, + Qt::NoModifier); + + QApplication::sendEvent(widget(),&mouseEvent); +} +bool AutoScrollHandler::eventFilter(QObject* watched,QEvent* event) +{ + Q_ASSERT( watched == parent() ); + Q_UNUSED( watched ); + + QMouseEvent* mouseEvent = (QMouseEvent*)event; + switch (event->type()) + { + case QEvent::MouseMove: + { + bool mouseInWidget = widget()->rect().contains(mouseEvent->pos()); + + if (mouseInWidget) + { + if (_timerId) + killTimer(_timerId); + _timerId = 0; + } + else + { + if (!_timerId && (mouseEvent->buttons() & Qt::LeftButton)) + _timerId = startTimer(100); + } + break; + } + case QEvent::MouseButtonRelease: + if (_timerId && (mouseEvent->buttons() & ~Qt::LeftButton)) + { + killTimer(_timerId); + _timerId = 0; + } + break; + default: + break; + }; + + return false; +} + +#include "TerminalDisplay.moc"
new file mode 100644 --- /dev/null +++ b/gui/konsole/TerminalDisplay.h @@ -0,0 +1,821 @@ +/* + Copyright 2007-2008 by Robert Knight <robertknight@gmail.com> + Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de> + + 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 TERMINALDISPLAY_H +#define TERMINALDISPLAY_H + +// Qt +#include <QtGui/QColor> +#include <QtCore/QPointer> +#include <QtGui/QWidget> + +// Konsole +#include "Filter.h" +#include "Character.h" +#include "konsole_export.h" + +class QDrag; +class QDragEnterEvent; +class QDropEvent; +class QLabel; +class QTimer; +class QEvent; +class QGridLayout; +class QKeyEvent; +class QScrollBar; +class QShowEvent; +class QHideEvent; +class QTimerEvent; +class QWidget; + +class KMenu; + +namespace Konsole +{ + +extern unsigned short vt100_graphics[32]; + +class ScreenWindow; + +/** + * A widget which displays output from a terminal emulation and sends input keypresses and mouse activity + * to the terminal. + * + * When the terminal emulation receives new output from the program running in the terminal, + * it will update the display by calling updateImage(). + * + * TODO More documentation + */ +class KONSOLEPRIVATE_EXPORT TerminalDisplay : public QWidget +{ + Q_OBJECT + +public: + /** Constructs a new terminal display widget with the specified parent. */ + TerminalDisplay(QWidget *parent=0); + virtual ~TerminalDisplay(); + + /** Returns the terminal color palette used by the display. */ + const ColorEntry* colorTable() const; + /** Sets the terminal color palette used by the display. */ + void setColorTable(const ColorEntry table[]); + /** + * Sets the seed used to generate random colors for the display + * (in color schemes that support them). + */ + void setRandomSeed(uint seed); + /** + * Returns the seed used to generate random colors for the display + * (in color schemes that support them). + */ + uint randomSeed() const; + + /** Sets the opacity of the terminal display. */ + void setOpacity(qreal opacity); + + /** + * This enum describes the location where the scroll bar is positioned in the display widget. + */ + enum ScrollBarPosition + { + /** Do not show the scroll bar. */ + NoScrollBar=0, + /** Show the scroll bar on the left side of the display. */ + ScrollBarLeft=1, + /** Show the scroll bar on the right side of the display. */ + ScrollBarRight=2 + }; + /** + * Specifies whether the terminal display has a vertical scroll bar, and if so whether it + * is shown on the left or right side of the display. + */ + void setScrollBarPosition(ScrollBarPosition position); + + /** + * Sets the current position and range of the display's scroll bar. + * + * @param cursor The position of the scroll bar's thumb. + * @param lines The maximum value of the scroll bar. + */ + void setScroll(int cursor, int lines); + + /** + * Returns the display's filter chain. When the image for the display is updated, + * the text is passed through each filter in the chain. Each filter can define + * hotspots which correspond to certain strings (such as URLs or particular words). + * Depending on the type of the hotspots created by the filter ( returned by Filter::Hotspot::type() ) + * the view will draw visual cues such as underlines on mouse-over for links or translucent + * rectangles for markers. + * + * To add a new filter to the view, call: + * viewWidget->filterChain()->addFilter( filterObject ); + */ + FilterChain* filterChain() const; + + /** + * Updates the filters in the display's filter chain. This will cause + * the hotspots to be updated to match the current image. + * + * WARNING: This function can be expensive depending on the + * image size and number of filters in the filterChain() + * + * TODO - This API does not really allow efficient usage. Revise it so + * that the processing can be done in a better way. + * + * eg: + * - Area of interest may be known ( eg. mouse cursor hovering + * over an area ) + */ + void processFilters(); + + /** + * Returns a list of menu actions created by the filters for the content + * at the given @p position. + */ + QList<QAction*> filterActions(const QPoint& position); + + /** Returns true if the cursor is set to blink or false otherwise. */ + bool blinkingCursor() { return _hasBlinkingCursor; } + /** Specifies whether or not the cursor blinks. */ + void setBlinkingCursor(bool blink); + + /** Specifies whether or not text can blink. */ + void setBlinkingTextEnabled(bool blink); + + void setCtrlDrag(bool enable) { _ctrlDrag=enable; } + bool ctrlDrag() { return _ctrlDrag; } + + /** + * This enum describes the methods for selecting text when + * the user triple-clicks within the display. + */ + enum TripleClickMode + { + /** Select the whole line underneath the cursor. */ + SelectWholeLine, + /** Select from the current cursor position to the end of the line. */ + SelectForwardsFromCursor + }; + /** Sets how the text is selected when the user triple clicks within the display. */ + void setTripleClickMode(TripleClickMode mode) { _tripleClickMode = mode; } + /** See setTripleClickSelectionMode() */ + TripleClickMode tripleClickMode() { return _tripleClickMode; } + + void setLineSpacing(uint); + uint lineSpacing() const; + + void emitSelection(bool useXselection,bool appendReturn); + + /** + * This enum describes the available shapes for the keyboard cursor. + * See setKeyboardCursorShape() + */ + enum KeyboardCursorShape + { + /** A rectangular block which covers the entire area of the cursor character. */ + BlockCursor, + /** + * A single flat line which occupies the space at the bottom of the cursor + * character's area. + */ + UnderlineCursor, + /** + * An cursor shaped like the capital letter 'I', similar to the IBeam + * cursor used in Qt/KDE text editors. + */ + IBeamCursor + }; + /** + * Sets the shape of the keyboard cursor. This is the cursor drawn + * at the position in the terminal where keyboard input will appear. + * + * In addition the terminal display widget also has a cursor for + * the mouse pointer, which can be set using the QWidget::setCursor() + * method. + * + * Defaults to BlockCursor + */ + void setKeyboardCursorShape(KeyboardCursorShape shape); + /** + * Returns the shape of the keyboard cursor. See setKeyboardCursorShape() + */ + KeyboardCursorShape keyboardCursorShape() const; + + /** + * Sets the color used to draw the keyboard cursor. + * + * The keyboard cursor defaults to using the foreground color of the character + * underneath it. + * + * @param useForegroundColor If true, the cursor color will change to match + * the foreground color of the character underneath it as it is moved, in this + * case, the @p color parameter is ignored and the color of the character + * under the cursor is inverted to ensure that it is still readable. + * @param color The color to use to draw the cursor. This is only taken into + * account if @p useForegroundColor is false. + */ + void setKeyboardCursorColor(bool useForegroundColor , const QColor& color); + + /** + * Returns the color of the keyboard cursor, or an invalid color if the keyboard + * cursor color is set to change according to the foreground color of the character + * underneath it. + */ + QColor keyboardCursorColor() const; + + /** + * Returns the number of lines of text which can be displayed in the widget. + * + * This will depend upon the height of the widget and the current font. + * See fontHeight() + */ + int lines() { return _lines; } + /** + * Returns the number of characters of text which can be displayed on + * each line in the widget. + * + * This will depend upon the width of the widget and the current font. + * See fontWidth() + */ + int columns() { return _columns; } + + /** + * Returns the height of the characters in the font used to draw the text in the display. + */ + int fontHeight() { return _fontHeight; } + /** + * Returns the width of the characters in the display. + * This assumes the use of a fixed-width font. + */ + int fontWidth() { return _fontWidth; } + + void setSize(int cols, int lins); + void setFixedSize(int cols, int lins); + + // reimplemented + QSize sizeHint() const; + + /** + * Sets which characters, in addition to letters and numbers, + * are regarded as being part of a word for the purposes + * of selecting words in the display by double clicking on them. + * + * The word boundaries occur at the first and last characters which + * are either a letter, number, or a character in @p wc + * + * @param wc An array of characters which are to be considered parts + * of a word ( in addition to letters and numbers ). + */ + void setWordCharacters(const QString& wc); + /** + * Returns the characters which are considered part of a word for the + * purpose of selecting words in the display with the mouse. + * + * @see setWordCharacters() + */ + QString wordCharacters() { return _wordCharacters; } + + /** + * Sets the type of effect used to alert the user when a 'bell' occurs in the + * terminal session. + * + * The terminal session can trigger the bell effect by calling bell() with + * the alert message. + */ + void setBellMode(int mode); + /** + * Returns the type of effect used to alert the user when a 'bell' occurs in + * the terminal session. + * + * See setBellMode() + */ + int bellMode() { return _bellMode; } + + /** + * This enum describes the different types of sounds and visual effects which + * can be used to alert the user when a 'bell' occurs in the terminal + * session. + */ + enum BellMode + { + /** A system beep. */ + SystemBeepBell=0, + /** + * KDE notification. This may play a sound, show a passive popup + * or perform some other action depending on the user's settings. + */ + NotifyBell=1, + /** A silent, visual bell (eg. inverting the display's colors briefly) */ + VisualBell=2, + /** No bell effects */ + NoBell=3 + }; + + void setSelection(const QString &t); + + /** + * Reimplemented. Has no effect. Use setVTFont() to change the font + * used to draw characters in the display. + */ + virtual void setFont(const QFont &); + + /** Returns the font used to draw characters in the display */ + QFont getVTFont() { return font(); } + + /** + * Sets the font used to draw the display. Has no effect if @p font + * is larger than the size of the display itself. + */ + void setVTFont(const QFont& font); + + /** + * Specified whether anti-aliasing of text in the terminal display + * is enabled or not. Defaults to enabled. + */ + static void setAntialias( bool antialias ) { _antialiasText = antialias; } + /** + * Returns true if anti-aliasing of text in the terminal is enabled. + */ + static bool antialias() { return _antialiasText; } + + /** + * Specifies whether characters with intense colors should be rendered + * as bold. Defaults to true. + */ + void setBoldIntense(bool value) { _boldIntense = value; } + /** + * Returns true if characters with intense colors are rendered in bold. + */ + bool getBoldIntense() { return _boldIntense; } + + /** + * Sets whether or not the current height and width of the + * terminal in lines and columns is displayed whilst the widget + * is being resized. + */ + void setTerminalSizeHint(bool on) { _terminalSizeHint=on; } + /** + * Returns whether or not the current height and width of + * the terminal in lines and columns is displayed whilst the widget + * is being resized. + */ + bool terminalSizeHint() { return _terminalSizeHint; } + /** + * Sets whether the terminal size display is shown briefly + * after the widget is first shown. + * + * See setTerminalSizeHint() , isTerminalSizeHint() + */ + void setTerminalSizeStartup(bool on) { _terminalSizeStartup=on; } + + /** + * Sets the status of the BiDi rendering inside the terminal display. + * Defaults to disabled. + */ + void setBidiEnabled(bool set) { _bidiEnabled=set; } + /** + * Returns the status of the BiDi rendering in this widget. + */ + bool isBidiEnabled() { return _bidiEnabled; } + + /** + * Sets the terminal screen section which is displayed in this widget. + * When updateImage() is called, the display fetches the latest character image from the + * the associated terminal screen window. + * + * In terms of the model-view paradigm, the ScreenWindow is the model which is rendered + * by the TerminalDisplay. + */ + void setScreenWindow( ScreenWindow* window ); + /** Returns the terminal screen section which is displayed in this widget. See setScreenWindow() */ + ScreenWindow* screenWindow() const; + + static bool HAVE_TRANSPARENCY; + +public slots: + + /** + * Causes the terminal display to fetch the latest character image from the associated + * terminal screen ( see setScreenWindow() ) and redraw the display. + */ + void updateImage(); + /** + * Causes the terminal display to fetch the latest line status flags from the + * associated terminal screen ( see setScreenWindow() ). + */ + void updateLineProperties(); + + /** Copies the selected text to the clipboard. */ + void copyClipboard(); + /** + * Pastes the content of the clipboard into the + * display. + */ + void pasteClipboard(); + /** + * Pastes the content of the selection into the + * display. + */ + void pasteSelection(); + + /** + * Changes whether the flow control warning box should be shown when the flow control + * stop key (Ctrl+S) are pressed. + */ + void setFlowControlWarningEnabled(bool enabled); + /** + * Returns true if the flow control warning box is enabled. + * See outputSuspended() and setFlowControlWarningEnabled() + */ + bool flowControlWarningEnabled() const + { return _flowControlWarningEnabled; } + + /** + * Causes the widget to display or hide a message informing the user that terminal + * output has been suspended (by using the flow control key combination Ctrl+S) + * + * @param suspended True if terminal output has been suspended and the warning message should + * be shown or false to indicate that terminal output has been resumed and that + * the warning message should disappear. + */ + void outputSuspended(bool suspended); + + /** + * Sets whether the program whoose output is being displayed in the view + * is interested in mouse events. + * + * If this is set to true, mouse signals will be emitted by the view when the user clicks, drags + * or otherwise moves the mouse inside the view. + * The user interaction needed to create selections will also change, and the user will be required + * to hold down the shift key to create a selection or perform other mouse activities inside the + * view area - since the program running in the terminal is being allowed to handle normal mouse + * events itself. + * + * @param usesMouse Set to true if the program running in the terminal is interested in mouse events + * or false otherwise. + */ + void setUsesMouse(bool usesMouse); + + /** See setUsesMouse() */ + bool usesMouse() const; + + /** + * Shows a notification that a bell event has occurred in the terminal. + * TODO: More documentation here + */ + void bell(const QString& message); + + /** + * Sets the background of the display to the specified color. + * @see setColorTable(), setForegroundColor() + */ + void setBackgroundColor(const QColor& color); + + /** + * Sets the text of the display to the specified color. + * @see setColorTable(), setBackgroundColor() + */ + void setForegroundColor(const QColor& color); + +signals: + + /** + * Emitted when the user presses a key whilst the terminal widget has focus. + */ + void keyPressedSignal(QKeyEvent *e); + + /** + * A mouse event occurred. + * @param button The mouse button (0 for left button, 1 for middle button, 2 for right button, 3 for release) + * @param column The character column where the event occurred + * @param line The character row where the event occurred + * @param eventType The type of event. 0 for a mouse press / release or 1 for mouse motion + */ + void mouseSignal(int button, int column, int line, int eventType); + void changedFontMetricSignal(int height, int width); + void changedContentSizeSignal(int height, int width); + + /** + * Emitted when the user right clicks on the display, or right-clicks with the Shift + * key held down if usesMouse() is true. + * + * This can be used to display a context menu. + */ + void configureRequest(const QPoint& position); + + /** + * When a shortcut which is also a valid terminal key sequence is pressed while + * the terminal widget has focus, this signal is emitted to allow the host to decide + * whether the shortcut should be overridden. + * When the shortcut is overridden, the key sequence will be sent to the terminal emulation instead + * and the action associated with the shortcut will not be triggered. + * + * @p override is set to false by default and the shortcut will be triggered as normal. + */ + void overrideShortcutCheck(QKeyEvent* keyEvent,bool& override); + + void isBusySelecting(bool); + void sendStringToEmu(const char*); + +protected: + virtual bool event( QEvent * ); + + virtual void paintEvent( QPaintEvent * ); + + virtual void showEvent(QShowEvent*); + virtual void hideEvent(QHideEvent*); + virtual void resizeEvent(QResizeEvent*); + + virtual void fontChange(const QFont &font); + virtual void focusInEvent(QFocusEvent* event); + virtual void focusOutEvent(QFocusEvent* event); + virtual void keyPressEvent(QKeyEvent* event); + virtual void mouseDoubleClickEvent(QMouseEvent* ev); + virtual void mousePressEvent( QMouseEvent* ); + virtual void mouseReleaseEvent( QMouseEvent* ); + virtual void mouseMoveEvent( QMouseEvent* ); + virtual void extendSelection( const QPoint& pos ); + virtual void wheelEvent( QWheelEvent* ); + + virtual bool focusNextPrevChild( bool next ); + + // drag and drop + virtual void dragEnterEvent(QDragEnterEvent* event); + virtual void dropEvent(QDropEvent* event); + void doDrag(); + enum DragState { diNone, diPending, diDragging }; + + struct _dragInfo { + DragState state; + QPoint start; + QDrag *dragObject; + } dragInfo; + + // classifies the 'ch' into one of three categories + // and returns a character to indicate which category it is in + // + // - A space (returns ' ') + // - Part of a word (returns 'a') + // - Other characters (returns the input character) + QChar charClass(QChar ch) const; + + void clearImage(); + + void mouseTripleClickEvent(QMouseEvent* ev); + + // reimplemented + virtual void inputMethodEvent ( QInputMethodEvent* event ); + virtual QVariant inputMethodQuery( Qt::InputMethodQuery query ) const; + +protected slots: + + void scrollBarPositionChanged(int value); + void blinkEvent(); + void blinkCursorEvent(); + + //Renables bell noises and visuals. Used to disable further bells for a short period of time + //after emitting the first in a sequence of bell events. + void enableBell(); + +private slots: + + void swapColorTable(); + void tripleClickTimeout(); // resets possibleTripleClick + +private: + + // -- Drawing helpers -- + + // divides the part of the display specified by 'rect' into + // fragments according to their colors and styles and calls + // drawTextFragment() to draw the fragments + void drawContents(QPainter &paint, const QRect &rect); + // draws a section of text, all the text in this section + // has a common color and style + void drawTextFragment(QPainter& painter, const QRect& rect, + const QString& text, const Character* style); + // draws the background for a text fragment + // if useOpacitySetting is true then the color's alpha value will be set to + // the display's transparency (set with setOpacity()), otherwise the background + // will be drawn fully opaque + void drawBackground(QPainter& painter, const QRect& rect, const QColor& color, + bool useOpacitySetting); + // draws the cursor character + void drawCursor(QPainter& painter, const QRect& rect , const QColor& foregroundColor, + const QColor& backgroundColor , bool& invertColors); + // draws the characters or line graphics in a text fragment + void drawCharacters(QPainter& painter, const QRect& rect, const QString& text, + const Character* style, bool invertCharacterColor); + // draws a string of line graphics + void drawLineCharString(QPainter& painter, int x, int y, + const QString& str, const Character* attributes); + + // draws the preedit string for input methods + void drawInputMethodPreeditString(QPainter& painter , const QRect& rect); + + // -- + + // maps an area in the character image to an area on the widget + QRect imageToWidget(const QRect& imageArea) const; + + // maps a point on the widget to the position ( ie. line and column ) + // of the character at that point. + void getCharacterPosition(const QPoint& widgetPoint,int& line,int& column) const; + + // the area where the preedit string for input methods will be draw + QRect preeditRect() const; + + // shows a notification window in the middle of the widget indicating the terminal's + // current size in columns and lines + void showResizeNotification(); + + // scrolls the image by a number of lines. + // 'lines' may be positive ( to scroll the image down ) + // or negative ( to scroll the image up ) + // 'region' is the part of the image to scroll - currently only + // the top, bottom and height of 'region' are taken into account, + // the left and right are ignored. + void scrollImage(int lines , const QRect& region); + + void calcGeometry(); + void propagateSize(); + void updateImageSize(); + void makeImage(); + + void paintFilters(QPainter& painter); + + // returns a region covering all of the areas of the widget which contain + // a hotspot + QRegion hotSpotRegion() const; + + // returns the position of the cursor in columns and lines + QPoint cursorPosition() const; + + // redraws the cursor + void updateCursor(); + + bool handleShortcutOverrideEvent(QKeyEvent* event); + + // the window onto the terminal screen which this display + // is currently showing. + QPointer<ScreenWindow> _screenWindow; + + bool _allowBell; + + QGridLayout* _gridLayout; + + bool _fixedFont; // has fixed pitch + int _fontHeight; // height + int _fontWidth; // width + int _fontAscent; // ascend + bool _boldIntense; // Whether intense colors should be rendered with bold font + + int _leftMargin; // offset + int _topMargin; // offset + + int _lines; // the number of lines that can be displayed in the widget + int _columns; // the number of columns that can be displayed in the widget + + int _usedLines; // the number of lines that are actually being used, this will be less + // than 'lines' if the character image provided with setImage() is smaller + // than the maximum image size which can be displayed + + int _usedColumns; // the number of columns that are actually being used, this will be less + // than 'columns' if the character image provided with setImage() is smaller + // than the maximum image size which can be displayed + + int _contentHeight; + int _contentWidth; + Character* _image; // [lines][columns] + // only the area [usedLines][usedColumns] in the image contains valid data + + int _imageSize; + QVector<LineProperty> _lineProperties; + + ColorEntry _colorTable[TABLE_COLORS]; + uint _randomSeed; + + bool _resizing; + bool _terminalSizeHint; + bool _terminalSizeStartup; + bool _bidiEnabled; + bool _mouseMarks; + + QPoint _iPntSel; // initial selection point + QPoint _pntSel; // current selection point + QPoint _tripleSelBegin; // help avoid flicker + int _actSel; // selection state + bool _wordSelectionMode; + bool _lineSelectionMode; + bool _preserveLineBreaks; + bool _columnSelectionMode; + + QClipboard* _clipboard; + QScrollBar* _scrollBar; + ScrollBarPosition _scrollbarLocation; + QString _wordCharacters; + int _bellMode; + + bool _blinking; // hide text in paintEvent + bool _hasBlinker; // has characters to blink + bool _cursorBlinking; // hide cursor in paintEvent + bool _hasBlinkingCursor; // has blinking cursor enabled + bool _allowBlinkingText; // allow text to blink + bool _ctrlDrag; // require Ctrl key for drag + TripleClickMode _tripleClickMode; + bool _isFixedSize; //Columns / lines are locked. + QTimer* _blinkTimer; // active when hasBlinker + QTimer* _blinkCursorTimer; // active when hasBlinkingCursor + + KMenu* _drop; + QString _dropText; + int _dndFileCount; + + bool _possibleTripleClick; // is set in mouseDoubleClickEvent and deleted + // after QApplication::doubleClickInterval() delay + + + QLabel* _resizeWidget; + QTimer* _resizeTimer; + + bool _flowControlWarningEnabled; + + //widgets related to the warning message that appears when the user presses Ctrl+S to suspend + //terminal output - informing them what has happened and how to resume output + QLabel* _outputSuspendedLabel; + + uint _lineSpacing; + + bool _colorsInverted; // true during visual bell + + QSize _size; + + QRgb _blendColor; + + // list of filters currently applied to the display. used for links and + // search highlight + TerminalImageFilterChain* _filterChain; + QRegion _mouseOverHotspotArea; + + KeyboardCursorShape _cursorShape; + + // custom cursor color. if this is invalid then the foreground + // color of the character under the cursor is used + QColor _cursorColor; + + + struct InputMethodData + { + QString preeditString; + QRect previousPreeditRect; + }; + InputMethodData _inputMethodData; + + static bool _antialiasText; // do we antialias or not + + //the delay in milliseconds between redrawing blinking text + static const int TEXT_BLINK_DELAY = 500; + static const int DEFAULT_LEFT_MARGIN = 1; + static const int DEFAULT_TOP_MARGIN = 1; + +public: + static void setTransparencyEnabled(bool enable) + { + HAVE_TRANSPARENCY = enable; + } +}; + +class AutoScrollHandler : public QObject +{ +Q_OBJECT + +public: + AutoScrollHandler(QWidget* parent); +protected: + virtual void timerEvent(QTimerEvent* event); + virtual bool eventFilter(QObject* watched,QEvent* event); +private: + QWidget* widget() const { return static_cast<QWidget*>(parent()); } + int _timerId; +}; + +} + +#endif // TERMINALDISPLAY_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/ViewContainer.cpp @@ -0,0 +1,851 @@ +/* + This file is part of the Konsole Terminal. + + Copyright 2006-2008 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. +*/ +//krazy:excludeall=qclasses + +// Own +#include "ViewContainer.h" + +// Qt +#include <QtCore/QHash> +#include <QtGui/QLabel> +#include <QtGui/QLineEdit> +#include <QtGui/QBrush> +#include <QtGui/QListWidget> +#include <QtGui/QSplitter> +#include <QtGui/QStackedWidget> +#include <QtGui/QTabBar> +#include <QtGui/QToolButton> +#include <QtGui/QWidgetAction> + +#include <QtGui/QDrag> +#include <QtGui/QDragMoveEvent> +#include <QMimeData> + +// KDE +#include <KColorDialog> +#include <kcolorscheme.h> +#include <kcolorutils.h> +#include <kdebug.h> +#include <kconfiggroup.h> +#include <KLocale> +#include <KMenu> +#include <KColorCollection> +#include <KTabWidget> + +// Konsole +#include "IncrementalSearchBar.h" +#include "ViewProperties.h" + +// TODO Perhaps move everything which is Konsole-specific into different files +#include "ProfileListWidget.h" + +using namespace Konsole; + +ViewContainer::ViewContainer(NavigationPosition position , QObject* parent) + : QObject(parent) + , _navigationDisplayMode(AlwaysShowNavigation) + , _navigationPosition(position) + , _searchBar(0) +{ +} + +ViewContainer::~ViewContainer() +{ + foreach( QWidget* view , _views ) + { + disconnect(view,SIGNAL(destroyed(QObject*)),this,SLOT(viewDestroyed(QObject*))); + } + + if (_searchBar) { + _searchBar->deleteLater(); + } + + emit destroyed(this); +} +void ViewContainer::moveViewWidget( int , int ) {} +void ViewContainer::setFeatures(Features features) +{ _features = features; } +ViewContainer::Features ViewContainer::features() const +{ return _features; } +void ViewContainer::moveActiveView( MoveDirection direction ) +{ + const int currentIndex = _views.indexOf( activeView() ) ; + int newIndex = -1; + + switch ( direction ) + { + case MoveViewLeft: + newIndex = qMax( currentIndex-1 , 0 ); + break; + case MoveViewRight: + newIndex = qMin( currentIndex+1 , _views.count() -1 ); + break; + } + + Q_ASSERT( newIndex != -1 ); + + moveViewWidget( currentIndex , newIndex ); + + _views.swap(currentIndex,newIndex); + + setActiveView( _views[newIndex] ); +} + +void ViewContainer::setNavigationDisplayMode(NavigationDisplayMode mode) +{ + _navigationDisplayMode = mode; + navigationDisplayModeChanged(mode); +} +ViewContainer::NavigationPosition ViewContainer::navigationPosition() const +{ + return _navigationPosition; +} +void ViewContainer::setNavigationPosition(NavigationPosition position) +{ + // assert that this position is supported + Q_ASSERT( supportedNavigationPositions().contains(position) ); + + _navigationPosition = position; + + navigationPositionChanged(position); +} +QList<ViewContainer::NavigationPosition> ViewContainer::supportedNavigationPositions() const +{ + return QList<NavigationPosition>() << NavigationPositionTop; +} +ViewContainer::NavigationDisplayMode ViewContainer::navigationDisplayMode() const +{ + return _navigationDisplayMode; +} +void ViewContainer::addView(QWidget* view , ViewProperties* item, int index) +{ + if (index == -1) + _views.append(view); + else + _views.insert(index,view); + + _navigation[view] = item; + + connect( view , SIGNAL(destroyed(QObject*)) , this , SLOT( viewDestroyed(QObject*) ) ); + + addViewWidget(view,index); + + emit viewAdded(view,item); +} +void ViewContainer::viewDestroyed(QObject* object) +{ + QWidget* widget = static_cast<QWidget*>(object); + + _views.removeAll(widget); + _navigation.remove(widget); + + // FIXME This can result in ViewContainerSubClass::removeViewWidget() being + // called after the widget's parent has been deleted or partially deleted + // in the ViewContainerSubClass instance's destructor. + // + // Currently deleteLater() is used to remove child widgets in the subclass + // constructors to get around the problem, but this is a hack and needs + // to be fixed. + removeViewWidget(widget); + + emit viewRemoved(widget); + + if (_views.count() == 0) + emit empty(this); +} +void ViewContainer::removeView(QWidget* view) +{ + _views.removeAll(view); + _navigation.remove(view); + + disconnect( view , SIGNAL(destroyed(QObject*)) , this , SLOT( viewDestroyed(QObject*) ) ); + + removeViewWidget(view); + + emit viewRemoved(view); + + if (_views.count() == 0) + emit empty(this); + +} + +const QList<QWidget*> ViewContainer::views() +{ + return _views; +} + +IncrementalSearchBar* ViewContainer::searchBar() +{ + if (!_searchBar) { + _searchBar = new IncrementalSearchBar( IncrementalSearchBar::AllFeatures , 0); + _searchBar->setVisible(false); + connect(_searchBar, SIGNAL(destroyed(QObject*)), this, SLOT(searchBarDestroyed())); + } + return _searchBar; +} + +void ViewContainer::searchBarDestroyed() +{ + _searchBar = 0; +} + +void ViewContainer::activateNextView() +{ + QWidget* active = activeView(); + + int index = _views.indexOf(active); + + if ( index == -1 ) + return; + + if ( index == _views.count() - 1 ) + index = 0; + else + index++; + + setActiveView( _views.at(index) ); +} + +void ViewContainer::activatePreviousView() +{ + QWidget* active = activeView(); + + int index = _views.indexOf(active); + + if ( index == -1 ) + return; + + if ( index == 0 ) + index = _views.count() - 1; + else + index--; + + setActiveView( _views.at(index) ); +} + +ViewProperties* ViewContainer::viewProperties( QWidget* widget ) +{ + Q_ASSERT( _navigation.contains(widget) ); + + return _navigation[widget]; +} + +QList<QWidget*> ViewContainer::widgetsForItem(ViewProperties* item) const +{ + return _navigation.keys(item); +} + +ViewContainerTabBar::ViewContainerTabBar(QWidget* parent,TabbedViewContainer* container) + : KTabBar(parent) + , _container(container) + , _dropIndicator(0) + , _dropIndicatorIndex(-1) + , _drawIndicatorDisabled(false) +{ + setStyleSheet("QTabBar::tab { min-width: 2em; max-width: 25em }"); + setElideMode(Qt::ElideLeft); +} +void ViewContainerTabBar::setDropIndicator(int index, bool drawDisabled) +{ + if (!parentWidget() || _dropIndicatorIndex == index) + return; + + _dropIndicatorIndex = index; + const int ARROW_SIZE = 32; + bool north = shape() == QTabBar::RoundedNorth || shape() == QTabBar::TriangularNorth; + + if (!_dropIndicator || _drawIndicatorDisabled != drawDisabled) + { + if (!_dropIndicator) + { + _dropIndicator = new QLabel(parentWidget()); + _dropIndicator->resize(ARROW_SIZE,ARROW_SIZE); + } + + QIcon::Mode drawMode = drawDisabled ? QIcon::Disabled : QIcon::Normal; + const QString iconName = north ? "arrow-up" : "arrow-down"; + _dropIndicator->setPixmap(KIcon(iconName).pixmap(ARROW_SIZE,ARROW_SIZE,drawMode)); + _drawIndicatorDisabled = drawDisabled; + } + + if (index < 0) + { + _dropIndicator->hide(); + return; + } + + const QRect rect = tabRect(index < count() ? index : index-1); + + QPoint pos; + if (index < count()) + pos = rect.topLeft(); + else + pos = rect.topRight(); + + if (north) + pos.ry() += ARROW_SIZE; + else + pos.ry() -= ARROW_SIZE; + + pos.rx() -= ARROW_SIZE/2; + + _dropIndicator->move(mapTo(parentWidget(),pos)); + _dropIndicator->show(); + +} +void ViewContainerTabBar::dragLeaveEvent(QDragLeaveEvent*) +{ + setDropIndicator(-1); +} +void ViewContainerTabBar::dragEnterEvent(QDragEnterEvent* event) +{ + if (event->mimeData()->hasFormat(ViewProperties::mimeType()) && + event->source() != 0) + event->acceptProposedAction(); +} +void ViewContainerTabBar::dragMoveEvent(QDragMoveEvent* event) +{ + if (event->mimeData()->hasFormat(ViewProperties::mimeType()) + && event->source() != 0) + { + int index = dropIndex(event->pos()); + if (index == -1) + index = count(); + + setDropIndicator(index,proposedDropIsSameTab(event)); + + event->acceptProposedAction(); + } +} +int ViewContainerTabBar::dropIndex(const QPoint& pos) const +{ + int tab = tabAt(pos); + if (tab < 0) + return tab; + + // pick the closest tab boundary + QRect rect = tabRect(tab); + if ( (pos.x()-rect.left()) > (rect.width()/2) ) + tab++; + + if (tab == count()) + return -1; + + return tab; +} +bool ViewContainerTabBar::proposedDropIsSameTab(const QDropEvent* event) const +{ + int index = dropIndex(event->pos()); + int droppedId = ViewProperties::decodeMimeData(event->mimeData()); + bool sameTabBar = event->source() == this; + + if (!sameTabBar) + return false; + + const QList<QWidget*> viewList = _container->views(); + int sourceIndex = -1; + for (int i=0;i<count();i++) + { + int idAtIndex = _container->viewProperties(viewList[i])->identifier(); + if (idAtIndex == droppedId) + sourceIndex = i; + } + + bool sourceAndDropAreLast = sourceIndex == count()-1 && index == -1; + if (sourceIndex == index || sourceIndex == index-1 || sourceAndDropAreLast) + return true; + else + return false; +} +void ViewContainerTabBar::dropEvent(QDropEvent* event) +{ + setDropIndicator(-1); + + if ( !event->mimeData()->hasFormat(ViewProperties::mimeType()) + || proposedDropIsSameTab(event) ) + { + event->ignore(); + return; + } + + int index = dropIndex(event->pos()); + int droppedId = ViewProperties::decodeMimeData(event->mimeData()); + bool result = false; + emit _container->moveViewRequest(index,droppedId,result); + + if (result) + event->accept(); + else + event->ignore(); +} + +QPixmap ViewContainerTabBar::dragDropPixmap(int tab) +{ + Q_ASSERT(tab >= 0 && tab < count()); + + // TODO - grabWidget() works except that it includes part + // of the tab bar outside the tab itself if the tab has + // curved corners + const QRect rect = tabRect(tab); + const int borderWidth = 1; + + QPixmap tabPixmap(rect.width()+borderWidth, + rect.height()+borderWidth); + QPainter painter(&tabPixmap); + painter.drawPixmap(0,0,QPixmap::grabWidget(this,rect)); + QPen borderPen; + borderPen.setBrush(palette().dark()); + borderPen.setWidth(borderWidth); + painter.setPen(borderPen); + painter.drawRect(0,0,rect.width(),rect.height()); + painter.end(); + + return tabPixmap; +} +TabbedViewContainer::TabbedViewContainer(NavigationPosition position , QObject* parent) +: ViewContainer(position,parent) + , _contextMenuTabIndex(0) +{ + _containerWidget = new QWidget; + _stackWidget = new QStackedWidget(); + _tabBar = new ViewContainerTabBar(_containerWidget,this); + _tabBar->setDrawBase(true); + _tabBar->setDocumentMode(true); + _tabBar->setFocusPolicy(Qt::NoFocus); + _tabBar->setSelectionBehaviorOnRemove(QTabBar::SelectPreviousTab); + + _newTabButton = new QToolButton(_containerWidget); + _newTabButton->setIcon(KIcon("tab-new")); + _newTabButton->adjustSize(); + // new tab button is initially hidden, it will be shown when setFeatures() is called + // with the QuickNewView flag enabled + _newTabButton->setHidden(true); + + _closeTabButton = new QToolButton(_containerWidget); + _closeTabButton->setIcon(KIcon("tab-close")); + _closeTabButton->adjustSize(); + _closeTabButton->setHidden(true); + _closeTabButton->setEnabled(false); + + connect( _tabBar , SIGNAL(currentChanged(int)) , this , SLOT(currentTabChanged(int)) ); + connect( _tabBar , SIGNAL(tabDoubleClicked(int)) , this , SLOT(tabDoubleClicked(int)) ); + connect( _tabBar , SIGNAL(newTabRequest()) , this , SIGNAL(newViewRequest()) ); + connect( _tabBar , SIGNAL(wheelDelta(int)) , this , SLOT(wheelScrolled(int)) ); + connect( _tabBar , SIGNAL(tabCloseRequested(int)) , this , SLOT(closeTab(int)) ); + connect( _tabBar , SIGNAL(initiateDrag(int)) , this , SLOT(startTabDrag(int)) ); + connect( _tabBar, SIGNAL(contextMenu(int, const QPoint&)), this, + SLOT(openTabContextMenu(int, const QPoint&)) ); + + connect( _newTabButton , SIGNAL(clicked()) , this , SIGNAL(newViewRequest()) ); + connect( _closeTabButton , SIGNAL(clicked()) , this , SLOT(closeCurrentTab()) ); + + _layout = new TabbedViewContainerLayout; + _layout->setSpacing(0); + _layout->setContentsMargins(0, 0, 0, 0); + _tabBarLayout = new QHBoxLayout; + _tabBarLayout->setSpacing(0); + _tabBarLayout->setContentsMargins(0, 0, 0, 0); + _tabBarLayout->addWidget(_newTabButton); + _tabBarLayout->addWidget(_tabBar); + _tabBarLayout->addWidget(_closeTabButton); + + _layout->addWidget(_stackWidget); + searchBar()->setParent(_containerWidget); + if ( position == NavigationPositionTop ) + { + _layout->insertLayout(0,_tabBarLayout); + _layout->insertWidget(-1,searchBar()); + _tabBar->setShape(QTabBar::RoundedNorth); + } + else if ( position == NavigationPositionBottom ) + { + _layout->insertWidget(-1,searchBar()); + _layout->insertLayout(-1,_tabBarLayout); + _tabBar->setShape(QTabBar::RoundedSouth); + } + else + Q_ASSERT(false); // position not supported + + _containerWidget->setLayout(_layout); + + _contextPopupMenu = new KMenu(_tabBar); + + _contextPopupMenu->addAction(KIcon("tab-detach"), + i18nc("@action:inmenu", "&Detach Tab"), this, + SLOT(tabContextMenuDetachTab())); + + _contextPopupMenu->addAction(KIcon(), + i18nc("@action:inmenu", "&Rename Tab..."), this, + SLOT(tabContextMenuRenameTab())); + +// _contextPopupMenu->addAction(KIcon("tab-close"), +// i18nc("@action:inmenu", "&Close Tab"), this, +// SLOT(tabContextMenuCloseTab())); + +} +void TabbedViewContainer::setNewViewMenu(QMenu* menu) +{ + _newTabButton->setMenu(menu); +} +ViewContainer::Features TabbedViewContainer::supportedFeatures() const +{ + return QuickNewView|QuickCloseView; +} +void TabbedViewContainer::setFeatures(Features features) +{ + ViewContainer::setFeatures(features); + + const bool tabBarHidden = _tabBar->isHidden(); + _newTabButton->setVisible(!tabBarHidden && (features & QuickNewView)); + _closeTabButton->setVisible(!tabBarHidden && (features & QuickCloseView)); +} +void TabbedViewContainer::closeCurrentTab() +{ + if (_stackWidget->currentIndex() != -1) + { + closeTab(_stackWidget->currentIndex()); + } +} +void TabbedViewContainer::closeTab(int tab) +{ + Q_ASSERT(tab >= 0 && tab < _stackWidget->count()); + + if (viewProperties(_stackWidget->widget(tab))->confirmClose()) + removeView(_stackWidget->widget(tab)); +} +void TabbedViewContainer::setTabBarVisible(bool visible) +{ + _tabBar->setVisible(visible); + _newTabButton->setVisible(visible && (features() & QuickNewView)); + _closeTabButton->setVisible(visible && (features() & QuickCloseView)); +} +QList<ViewContainer::NavigationPosition> TabbedViewContainer::supportedNavigationPositions() const +{ + return QList<NavigationPosition>() << NavigationPositionTop << NavigationPositionBottom; +} +void TabbedViewContainer::navigationPositionChanged(NavigationPosition position) +{ + // this method assumes that there are only two items + // in the layout + Q_ASSERT( _layout->count() == 3 ); + + // index of stack widget in the layout when tab bar is at the bottom + const int StackIndexWithTabBottom = 0; + + if ( position == NavigationPositionTop + && _layout->indexOf(_stackWidget) == StackIndexWithTabBottom ) + { + _layout->removeItem(_tabBarLayout); + _layout->removeWidget(searchBar()); + + _layout->insertLayout(0,_tabBarLayout); + _layout->insertWidget(-1,searchBar()); + _tabBar->setShape(QTabBar::RoundedNorth); + } + else if ( position == NavigationPositionBottom + && _layout->indexOf(_stackWidget) != StackIndexWithTabBottom ) + { + _layout->removeItem(_tabBarLayout); + _layout->removeWidget(searchBar()); + + _layout->insertWidget(-1,searchBar()); + _layout->insertLayout(-1,_tabBarLayout); + _tabBar->setShape(QTabBar::RoundedSouth); + } +} +void TabbedViewContainer::navigationDisplayModeChanged(NavigationDisplayMode mode) +{ + if ( mode == AlwaysShowNavigation && _tabBar->isHidden() ) + setTabBarVisible(true); + + if ( mode == AlwaysHideNavigation && !_tabBar->isHidden() ) + setTabBarVisible(false); + + if ( mode == ShowNavigationAsNeeded ) + dynamicTabBarVisibility(); +} +void TabbedViewContainer::dynamicTabBarVisibility() +{ + if ( _tabBar->count() > 1 && _tabBar->isHidden() ) + setTabBarVisible(true); + + if ( _tabBar->count() < 2 && !_tabBar->isHidden() ) + setTabBarVisible(false); +} +TabbedViewContainer::~TabbedViewContainer() +{ + if (!_containerWidget.isNull()) + _containerWidget->deleteLater(); +} + +void TabbedViewContainer::startTabDrag(int tab) +{ + QDrag* drag = new QDrag(_tabBar); + const QRect tabRect = _tabBar->tabRect(tab); + QPixmap tabPixmap = _tabBar->dragDropPixmap(tab); + + drag->setPixmap(tabPixmap); + + // offset the tab position so the tab will follow the cursor exactly + // where it was clicked (as opposed to centering on the origin of the pixmap) + QPoint mappedPos = _tabBar->mapFromGlobal(QCursor::pos()); + mappedPos.rx() -= tabRect.x(); + + drag->setHotSpot(mappedPos); + + int id = viewProperties(views()[tab])->identifier(); + QWidget* view = views()[tab]; + drag->setMimeData(ViewProperties::createMimeData(id)); + + // start drag, if drag-and-drop is successful the view at 'tab' will be + // deleted + // + // if the tab was dragged onto another application + // which blindly accepted the drop then ignore it + if (drag->exec() == Qt::MoveAction && drag->target() != 0) + { + // Deleting the view may cause the view container to be deleted, which + // will also delete the QDrag object. + // This can cause a crash if Qt's internal drag-and-drop handling + // tries to delete it later. + // + // For now set the QDrag's parent to 0 so that it won't be deleted if + // this view container is destroyed. + // + // FIXME: Resolve this properly + drag->setParent(0); + removeView(view); + } +} + +void TabbedViewContainer::tabDoubleClicked(int index) +{ + renameTab(index); +} + +void TabbedViewContainer::renameTab(int index) +{ + viewProperties(views()[index])->rename(); +} + +void TabbedViewContainer::openTabContextMenu(int index, const QPoint& pos) +{ + _contextMenuTabIndex = index; + + _contextPopupMenu->exec(pos); +} + +void TabbedViewContainer::tabContextMenuCloseTab() +{ + closeTab(_contextMenuTabIndex); +} + +void TabbedViewContainer::tabContextMenuDetachTab() +{ + emit detachTab(this, _stackWidget->widget(_contextMenuTabIndex)); +} + +void TabbedViewContainer::tabContextMenuRenameTab() +{ + renameTab(_contextMenuTabIndex); +} + +void TabbedViewContainer::moveViewWidget( int fromIndex , int toIndex ) +{ + QString text = _tabBar->tabText(fromIndex); + QIcon icon = _tabBar->tabIcon(fromIndex); + + // FIXME - This will lose properties of the tab other than + // their text and icon when moving them + + _tabBar->removeTab(fromIndex); + _tabBar->insertTab(toIndex,icon,text); + + QWidget* widget = _stackWidget->widget(fromIndex); + _stackWidget->removeWidget(widget); + _stackWidget->insertWidget(toIndex,widget); +} +void TabbedViewContainer::currentTabChanged(int index) +{ + _stackWidget->setCurrentIndex(index); + if (_stackWidget->widget(index)) + emit activeViewChanged(_stackWidget->widget(index)); + + // clear activity indicators + setTabActivity(index,false); +} + +void TabbedViewContainer::wheelScrolled(int delta) +{ + if ( delta < 0 ) + activateNextView(); + else + activatePreviousView(); +} + +QWidget* TabbedViewContainer::containerWidget() const +{ + return _containerWidget; +} +QWidget* TabbedViewContainer::activeView() const +{ + return _stackWidget->currentWidget(); +} +void TabbedViewContainer::setActiveView(QWidget* view) +{ + const int index = _stackWidget->indexOf(view); + + Q_ASSERT( index != -1 ); + + _stackWidget->setCurrentWidget(view); + _tabBar->setCurrentIndex(index); +} +void TabbedViewContainer::addViewWidget( QWidget* view , int index) +{ + _stackWidget->insertWidget(index,view); + _stackWidget->updateGeometry(); + + ViewProperties* item = viewProperties(view); + connect( item , SIGNAL(titleChanged(ViewProperties*)) , this , + SLOT(updateTitle(ViewProperties*))); + connect( item , SIGNAL(iconChanged(ViewProperties*) ) , this , + SLOT(updateIcon(ViewProperties*))); + connect( item , SIGNAL(activity(ViewProperties*)) , this , + SLOT(updateActivity(ViewProperties*))); + + _tabBar->insertTab( index , item->icon() , item->title() ); + + if ( navigationDisplayMode() == ShowNavigationAsNeeded ) + dynamicTabBarVisibility(); +} +void TabbedViewContainer::removeViewWidget( QWidget* view ) +{ + if (!_stackWidget) + return; + const int index = _stackWidget->indexOf(view); + + Q_ASSERT( index != -1 ); + + _stackWidget->removeWidget(view); + _tabBar->removeTab(index); + + if ( navigationDisplayMode() == ShowNavigationAsNeeded ) + dynamicTabBarVisibility(); +} + +void TabbedViewContainer::setTabActivity(int index , bool activity) +{ + const QPalette& palette = _tabBar->palette(); + KColorScheme colorScheme(palette.currentColorGroup()); + const QColor colorSchemeActive = colorScheme.foreground(KColorScheme::ActiveText).color(); + + const QColor normalColor = palette.text().color(); + const QColor activityColor = KColorUtils::mix(normalColor,colorSchemeActive); + + QColor color = activity ? activityColor : QColor(); + + if ( color != _tabBar->tabTextColor(index) ) + _tabBar->setTabTextColor(index,color); +} + +void TabbedViewContainer::updateActivity(ViewProperties* item) +{ + QListIterator<QWidget*> iter(widgetsForItem(item)); + while ( iter.hasNext() ) + { + const int index = _stackWidget->indexOf(iter.next()); + + if ( index != _stackWidget->currentIndex() ) + { + setTabActivity(index,true); + } + } +} + +void TabbedViewContainer::updateTitle(ViewProperties* item) +{ + QListIterator<QWidget*> iter(widgetsForItem(item)); + while ( iter.hasNext() ) + { + const int index = _stackWidget->indexOf( iter.next() ); + QString tabText = item->title(); + + _tabBar->setTabText( index , tabText ); + _tabBar->setTabToolTip( index , tabText ); + } +} +void TabbedViewContainer::updateIcon(ViewProperties* item) +{ + QListIterator<QWidget*> iter(widgetsForItem(item)); + while ( iter.hasNext() ) + { + const int index = _stackWidget->indexOf( iter.next() ); + _tabBar->setTabIcon( index , item->icon() ); + } +} + +StackedViewContainer::StackedViewContainer(QObject* parent) +: ViewContainer(NavigationPositionTop,parent) +{ + _containerWidget = new QWidget; + QVBoxLayout *layout = new QVBoxLayout(_containerWidget); + + _stackWidget = new QStackedWidget(_containerWidget); + + searchBar()->setParent(_containerWidget); + layout->addWidget(searchBar()); + layout->addWidget(_stackWidget); + layout->setContentsMargins(0, 0, 0, 0); +} +StackedViewContainer::~StackedViewContainer() +{ + if (!_containerWidget.isNull()) + _containerWidget->deleteLater(); +} +QWidget* StackedViewContainer::containerWidget() const +{ + return _containerWidget; +} +QWidget* StackedViewContainer::activeView() const +{ + return _stackWidget->currentWidget(); +} +void StackedViewContainer::setActiveView(QWidget* view) +{ + _stackWidget->setCurrentWidget(view); +} +void StackedViewContainer::addViewWidget( QWidget* view , int ) +{ + _stackWidget->addWidget(view); +} +void StackedViewContainer::removeViewWidget( QWidget* view ) +{ + if (!_stackWidget) + return; + const int index = _stackWidget->indexOf(view); + + Q_ASSERT( index != -1); + Q_UNUSED(index); + + _stackWidget->removeWidget(view); +} + +#include "ViewContainer.moc"
new file mode 100644 --- /dev/null +++ b/gui/konsole/ViewContainer.h @@ -0,0 +1,483 @@ +/* + This file is part of the Konsole Terminal. + + Copyright 2006-2008 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. +*/ + +#ifndef VIEWCONTAINER_H +#define VIEWCONTAINER_H + +// Qt +#include <QtCore/QObject> +#include <QtCore/QPointer> +#include <QtCore/QHash> +#include <QtCore/QList> +#include <QtGui/QBoxLayout> + +// KDE +#include <KTabBar> + +class QStackedWidget; +class QWidget; +class QLabel; + +// TabbedViewContainer + // Qt + class QPoint; + class QToolButton; + class QMenu; + + // KDE + class KMenu; + +// ListViewContainer + +namespace Konsole +{ + class IncrementalSearchBar; + class ViewProperties; +/** + * An interface for container widgets which can hold one or more views. + * + * The container widget typically displays a list of the views which + * it has and provides a means of switching between them. + * + * Subclasses should reimplement the addViewWidget() and removeViewWidget() functions + * to actually add or remove view widgets from the container widget, as well + * as updating any navigation aids. + */ +class ViewContainer : public QObject +{ +Q_OBJECT + +public: + + /** + * This enum describes the options for positioning the + * container's navigation widget. + */ + enum NavigationPosition + { + /** Position the navigation widget above the views. */ + NavigationPositionTop, + /** Position the navigation widget below the views. */ + NavigationPositionBottom, + /** Position the navigation widget to the left of the views. */ + NavigationPositionLeft, + /** Position the navigation widget to the right of the views. */ + NavigationPositionRight + }; + + /** + * Constructs a new view container with the specified parent. + * + * @param position The initial position of the navigation widget + * @param parent The parent object of the container + */ + ViewContainer(NavigationPosition position , QObject* parent); + + /** + * Called when the ViewContainer is destroyed. When reimplementing this in + * subclasses, use object->deleteLater() to delete any widgets or other objects + * instead of 'delete object'. + */ + virtual ~ViewContainer(); + + /** Returns the widget which contains the view widgets */ + virtual QWidget* containerWidget() const = 0; + + /** + * This enum describes the options for showing or hiding the + * container's navigation widget. + */ + enum NavigationDisplayMode + { + /** Always show the navigation widget. */ + AlwaysShowNavigation, + /** Always hide the navigation widget. */ + AlwaysHideNavigation, + /** Show the navigation widget only when the container has more than one view. */ + ShowNavigationAsNeeded + }; + /* + * Sets the visibility of the view container's navigation widget. + * + * The ViewContainer sub-class is responsible for ensuring that this + * setting is respected as views are added or removed from the + * container. + * + * ViewContainer sub-classes should reimplement the + * navigationDisplayModeChanged() method to respond to changes + * of this property. + */ + void setNavigationDisplayMode(NavigationDisplayMode mode); + /** + * Returns the current mode for controlling the visibility of the + * the view container's navigation widget. + */ + NavigationDisplayMode navigationDisplayMode() const; + + /** + * Sets the position of the navigation widget with + * respect to the main content area. + * + * Depending on the ViewContainer subclass, not all + * positions from the NavigationPosition enum may be + * supported. A list of supported positions can be + * obtained by calling supportedNavigationPositions() + * + * ViewContainer sub-classes should re-implement the + * navigationPositionChanged() method to respond + * to changes of this property. + */ + void setNavigationPosition(NavigationPosition position); + + /** + * Returns the position of the navigation widget with + * respect to the main content area. + */ + NavigationPosition navigationPosition() const; + + /** + * Returns the list of supported navigation positions. + * The supported positions will depend upon the type of the + * navigation widget used by the ViewContainer subclass. + * + * The base implementation returns one item, NavigationPositionTop + */ + virtual QList<NavigationPosition> supportedNavigationPositions() const; + + /** Adds a new view to the container widget */ + void addView(QWidget* view , ViewProperties* navigationItem, int index = -1); + + /** Removes a view from the container */ + void removeView(QWidget* view); + + /** Returns the ViewProperties instance associated with a particular view in the container */ + ViewProperties* viewProperties( QWidget* view ); + + /** Returns a list of the contained views */ + const QList<QWidget*> views(); + + /** + * Returns the view which currently has the focus or 0 if none + * of the child views have the focus. + */ + virtual QWidget* activeView() const = 0; + + /** + * Changes the focus to the specified view and updates + * navigation aids to reflect the change. + */ + virtual void setActiveView(QWidget* widget) = 0; + + /** + * @return the search widget for this view + */ + IncrementalSearchBar* searchBar(); + + /** Changes the active view to the next view */ + void activateNextView(); + + /** Changes the active view to the previous view */ + void activatePreviousView(); + + /** + * This enum describes the directions + * in which views can be re-arranged within the container + * using the moveActiveView() method. + */ + enum MoveDirection + { + /** Moves the view to the left. */ + MoveViewLeft, + /** Moves the view to the right. */ + MoveViewRight + }; + + /** + * Moves the active view within the container and + * updates the order in which the views are shown + * in the container's navigation widget. + * + * The default implementation does nothing. + */ + void moveActiveView( MoveDirection direction ); + + /** Enum describing extra UI features which can be + * provided by the container. */ + enum Feature + { + /** Provides a button which can be clicked to create new views quickly. + * When the button is clicked, a newViewRequest() signal is emitted. */ + QuickNewView = 1, + /** Provides a button which can be clicked to close views quickly. */ + QuickCloseView = 2 + }; + Q_DECLARE_FLAGS(Features,Feature) + /** + * Sets which additional features are enabled in this container. + * The default implementation does thing. Sub-classes should re-implement this + * to hide or show the relevant parts of their UI + */ + virtual void setFeatures(Features features); + /** Returns a bitwise-OR of enabled extra UI features. See setFeatures() */ + Features features() const; + /** Returns a bitwise-OR of supported extra UI features. The default + * implementation returns 0 (no extra features) */ + virtual Features supportedFeatures() const + { return 0; } + /** Sets the menu to be shown when the new view button is clicked. + * Only valid if the QuickNewView feature is enabled. + * The default implementation does nothing. */ + virtual void setNewViewMenu(QMenu* menu) { Q_UNUSED(menu); } + +signals: + /** Emitted when the container is deleted */ + void destroyed(ViewContainer* container); + + /** Emitted when the container has no more children */ + void empty(ViewContainer* container); + + /** Emitted when the user requests to duplicate a view */ + void duplicateRequest( ViewProperties* properties ); + + /** Emitted when the user requests to close a view */ + void closeRequest(QWidget* activeView); + + /** Emitted when the user requests to open a new view */ + void newViewRequest(); + + /** + * Emitted when the user requests to move a view from another container + * into this container. If 'success' is set to true by a connected slot + * then the original view will be removed. + * + * @param index Index at which to insert the new view in the container or -1 + * to append it. This index should be passed to addView() when the new view + * has been created. + * @param id The identifier of the view. + * @param success The slot handling this signal should set this to true if the + * new view was successfully created. + */ + void moveViewRequest(int index,int id,bool& success); + + /** Emitted when the active view changes */ + void activeViewChanged( QWidget* view ); + + /** Emitted when a view is added to the container. */ + void viewAdded(QWidget* view , ViewProperties* properties); + + /** Emitted when a view is removed from the container. */ + void viewRemoved(QWidget* view); + +protected: + /** + * Performs the task of adding the view widget + * to the container widget. + */ + virtual void addViewWidget(QWidget* view,int index) = 0; + /** + * Performs the task of removing the view widget + * from the container widget. + */ + virtual void removeViewWidget(QWidget* view) = 0; + + /** + * Called when the navigation display mode changes. + * See setNavigationDisplayMode + */ + virtual void navigationDisplayModeChanged(NavigationDisplayMode) {} + + /** + * Called when the navigation position changes to re-layout + * the container and place the navigation widget in the + * specified position. + */ + virtual void navigationPositionChanged(NavigationPosition) {} + + /** Returns the widgets which are associated with a particular navigation item */ + QList<QWidget*> widgetsForItem( ViewProperties* item ) const; + + /** + * Rearranges the order of widgets in the container. + * + * @param fromIndex Current index of the widget to move + * @param toIndex New index for the widget + */ + virtual void moveViewWidget( int fromIndex , int toIndex ); + +private slots: + void viewDestroyed(QObject* view); + void searchBarDestroyed(); + +private: + NavigationDisplayMode _navigationDisplayMode; + NavigationPosition _navigationPosition; + QList<QWidget*> _views; + QHash<QWidget*,ViewProperties*> _navigation; + Features _features; + IncrementalSearchBar* _searchBar; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(ViewContainer::Features) + +class TabbedViewContainer; + +// internal class, +// to allow for tweaks to the tab bar required by TabbedViewContainer. +class ViewContainerTabBar : public KTabBar +{ +Q_OBJECT + +public: + ViewContainerTabBar(QWidget* parent,TabbedViewContainer* container); + + // returns a pixmap image of a tab for use with QDrag + QPixmap dragDropPixmap(int tab); + +protected: + virtual void dragEnterEvent(QDragEnterEvent* event); + virtual void dragLeaveEvent(QDragLeaveEvent* event); + virtual void dragMoveEvent(QDragMoveEvent* event); + virtual void dropEvent(QDropEvent* event); + +private: + // show the indicator arrow which shows where a dropped tab will + // be inserted at 'index' + void setDropIndicator(int index, bool drawDisabled = false); + // returns the index at which a tab will be inserted if the mouse + // in a drag-drop operation is released at 'pos' + int dropIndex(const QPoint& pos) const; + // returns true if the tab to be dropped in a drag-drop operation + // is the same as the tab at the drop location + bool proposedDropIsSameTab(const QDropEvent* event) const; + + TabbedViewContainer* _container; + QLabel* _dropIndicator; + int _dropIndicatorIndex; + bool _drawIndicatorDisabled; +}; + +// internal +// this class provides a work-around for a problem in Qt 4.x +// where the insertItem() method only has protected access - +// and the TabbedViewContainer class needs to call it. +// +// and presumably for binary compatibility reasons will +// not be fixed until Qt 5. +class TabbedViewContainerLayout : public QVBoxLayout +{ +public: + void insertItemAt( int index , QLayoutItem* item ) + { + insertItem(index,item); + } +}; + +/** + * An alternative tabbed view container which uses a QTabBar and QStackedWidget + * combination for navigation instead of QTabWidget + */ +class TabbedViewContainer : public ViewContainer +{ + Q_OBJECT + +friend class ViewContainerTabBar; + +public: + /** + * Constructs a new tabbed view container. Supported positions + * are NavigationPositionTop and NavigationPositionBottom. + */ + TabbedViewContainer(NavigationPosition position , QObject* parent); + virtual ~TabbedViewContainer(); + + virtual QWidget* containerWidget() const; + virtual QWidget* activeView() const; + virtual void setActiveView(QWidget* view); + virtual QList<NavigationPosition> supportedNavigationPositions() const; + virtual void setFeatures(Features features); + virtual Features supportedFeatures() const; + virtual void setNewViewMenu(QMenu* menu); + +protected: + virtual void addViewWidget(QWidget* view , int index); + virtual void removeViewWidget(QWidget* view); + virtual void navigationDisplayModeChanged(NavigationDisplayMode mode); + virtual void navigationPositionChanged(NavigationPosition position); + virtual void moveViewWidget( int fromIndex , int toIndex ); + +private slots: + void updateTitle(ViewProperties* item); + void updateIcon(ViewProperties* item); + void updateActivity(ViewProperties* item); + void currentTabChanged(int index); + void closeTab(int index); + void closeCurrentTab(); + void wheelScrolled(int delta); + + void tabDoubleClicked(int index); + void openTabContextMenu(int index, const QPoint& point); + void tabContextMenuCloseTab(); + void tabContextMenuRenameTab(); + void tabContextMenuDetachTab(); + void startTabDrag(int index); + +signals: + void detachTab(ViewContainer * self, QWidget * activeView); + +private: + void dynamicTabBarVisibility(); + void setTabBarVisible(bool visible); + void setTabActivity(int index,bool activity); + void renameTab(int index); + + ViewContainerTabBar* _tabBar; + QPointer<QStackedWidget> _stackWidget; + QPointer<QWidget> _containerWidget; + TabbedViewContainerLayout* _layout; + QHBoxLayout* _tabBarLayout; + QToolButton* _newTabButton; + QToolButton* _closeTabButton; + int _contextMenuTabIndex; + KMenu* _contextPopupMenu; + +}; + +/** A plain view container with no navigation display */ +class StackedViewContainer : public ViewContainer +{ +public: + StackedViewContainer(QObject* parent); + virtual ~StackedViewContainer(); + + virtual QWidget* containerWidget() const; + virtual QWidget* activeView() const; + virtual void setActiveView(QWidget* view); + +protected: + virtual void addViewWidget( QWidget* view , int index); + virtual void removeViewWidget( QWidget* view ); + +private: + QPointer<QWidget> _containerWidget; + QPointer<QStackedWidget> _stackWidget; +}; + +} +#endif //VIEWCONTAINER_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/ViewManager.cpp @@ -0,0 +1,1092 @@ +/* + 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 "ViewManager.h" + +// System +#include <assert.h> + +// Qt +#include <QtCore/QDateTime> +#include <QtCore/QSignalMapper> +#include <QtGui/QMenu> + +// KDE +#include <kdebug.h> +#include <KAcceleratorManager> +#include <KGlobal> +#include <KLocale> +#include <KToggleAction> +#include <KXMLGUIFactory> +#include <QStringList> +#include <KConfigGroup> + +// Konsole +#include "ColorScheme.h" +#include "ProfileList.h" +#include "Session.h" +#include "TerminalDisplay.h" +#include "SessionController.h" +#include "SessionManager.h" +#include "ViewContainer.h" +#include "ViewSplitter.h" +#include "konsoleadaptor.h" +#include "Profile.h" + +using namespace Konsole; + +ViewManager::ViewManager(QObject* parent , KActionCollection* collection) + : QObject(parent) + , _viewSplitter(0) + , _actionCollection(collection) + , _containerSignalMapper(new QSignalMapper(this)) + , _navigationMethod(TabbedNavigation) + , _newViewMenu(0) +{ + // create main view area + _viewSplitter = new ViewSplitter(0); + KAcceleratorManager::setNoAccel(_viewSplitter); + + // the ViewSplitter class supports both recursive and non-recursive splitting, + // in non-recursive mode, all containers are inserted into the same top-level splitter + // widget, and all the divider lines between the containers have the same orientation + // + // the ViewManager class is not currently able to handle a ViewSplitter in recursive-splitting + // mode + _viewSplitter->setRecursiveSplitting(false); + _viewSplitter->setFocusPolicy(Qt::NoFocus); + + // setup actions which relating to the view + setupActions(); + + // emit a signal when all of the views held by this view manager are destroyed + connect( _viewSplitter , SIGNAL(allContainersEmpty()) , this , SIGNAL(empty()) ); + connect( _viewSplitter , SIGNAL(empty(ViewSplitter*)) , this , SIGNAL(empty()) ); + + // listen for addition or removal of views from associated containers + connect( _containerSignalMapper , SIGNAL(mapped(QObject*)) , this , + SLOT(containerViewsChanged(QObject*)) ); + + // listen for profile changes + connect( SessionManager::instance() , SIGNAL(profileChanged(Profile::Ptr)) , this, + SLOT(profileChanged(Profile::Ptr)) ); + connect( SessionManager::instance() , SIGNAL(sessionUpdated(Session*)) , this, + SLOT(updateViewsForSession(Session*)) ); + + //prepare DBus communication + new KonsoleAdaptor(this); + QDBusConnection::sessionBus().registerObject(QLatin1String("/Konsole"), this); + +} + +ViewManager::~ViewManager() +{ + delete _newViewMenu; +} +QMenu* ViewManager::createNewViewMenu() +{ + if (_newViewMenu) + return _newViewMenu; + + _newViewMenu = new QMenu(0); + ProfileList* newViewProfiles = new ProfileList(false,_newViewMenu); + newViewProfiles->syncWidgetActions(_newViewMenu,true); + connect(newViewProfiles,SIGNAL(profileSelected(Profile::Ptr)),this, + SIGNAL(newViewRequest(Profile::Ptr))); + + return _newViewMenu; +} +QWidget* ViewManager::activeView() const +{ + ViewContainer* container = _viewSplitter->activeContainer(); + if ( container ) + { + return container->activeView(); + } + else + { + return 0; + } +} + +QWidget* ViewManager::widget() const +{ + return _viewSplitter; +} + +void ViewManager::setupActions() +{ + KActionCollection* collection = _actionCollection; + + KAction* nextViewAction = new KAction( i18n("Next View") , this ); + KAction* previousViewAction = new KAction( i18n("Previous View") , this ); + KAction* nextContainerAction = new KAction( i18n("Next View Container") , this); + + KAction* moveViewLeftAction = new KAction( i18n("Move View Left") , this ); + KAction* moveViewRightAction = new KAction( i18n("Move View Right") , this ); + + // list of actions that should only be enabled when there are multiple view + // containers open + QList<QAction*> multiViewOnlyActions; + multiViewOnlyActions << nextContainerAction; + + if ( collection ) + { + KAction* splitLeftRightAction = new KAction( KIcon("view-split-left-right"), + i18nc("@action:inmenu", "Split View Left/Right"), + this ); + splitLeftRightAction->setShortcut( QKeySequence(Qt::CTRL+Qt::Key_ParenLeft) ); + collection->addAction("split-view-left-right",splitLeftRightAction); + connect( splitLeftRightAction , SIGNAL(triggered()) , this , SLOT(splitLeftRight()) ); + + KAction* splitTopBottomAction = new KAction( KIcon("view-split-top-bottom") , + i18nc("@action:inmenu", "Split View Top/Bottom"),this); + splitTopBottomAction->setShortcut( QKeySequence(Qt::CTRL+Qt::Key_ParenRight) ); + collection->addAction("split-view-top-bottom",splitTopBottomAction); + connect( splitTopBottomAction , SIGNAL(triggered()) , this , SLOT(splitTopBottom())); + + KAction* closeActiveAction = new KAction( i18nc("@action:inmenu Close Active View", "Close Active") , this ); + closeActiveAction->setIcon(KIcon("view-close")); + closeActiveAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_S) ); + closeActiveAction->setEnabled(false); + collection->addAction("close-active-view",closeActiveAction); + connect( closeActiveAction , SIGNAL(triggered()) , this , SLOT(closeActiveView()) ); + + multiViewOnlyActions << closeActiveAction; + + KAction* closeOtherAction = new KAction( i18nc("@action:inmenu Close Other Views", "Close Others") , this ); + closeOtherAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_O) ); + closeOtherAction->setEnabled(false); + collection->addAction("close-other-views",closeOtherAction); + connect( closeOtherAction , SIGNAL(triggered()) , this , SLOT(closeOtherViews()) ); + + multiViewOnlyActions << closeOtherAction; + + KAction* detachViewAction = collection->addAction("detach-view"); + detachViewAction->setIcon(KIcon("tab-detach")); + detachViewAction->setText(i18n("D&etach Current Tab")); + // Ctrl+Shift+D is not used as a shortcut by default because it is too close + // to Ctrl+D - which will terminate the session in many cases + detachViewAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_H)); + + connect( this , SIGNAL(splitViewToggle(bool)) , this , SLOT(updateDetachViewState()) ); + connect( detachViewAction , SIGNAL(triggered()) , this , SLOT(detachActiveView()) ); + + // Expand & Shrink Active View + KAction* expandActiveAction = new KAction( i18nc("@action:inmenu", "Expand View") , this ); + expandActiveAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_BracketRight) ); + collection->addAction("expand-active-view",expandActiveAction); + connect( expandActiveAction , SIGNAL(triggered()) , this , SLOT(expandActiveView()) ); + + multiViewOnlyActions << expandActiveAction; + + KAction* shrinkActiveAction = new KAction( i18nc("@action:inmenu", "Shrink View") , this ); + shrinkActiveAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_BracketLeft) ); + collection->addAction("shrink-active-view",shrinkActiveAction); + connect( shrinkActiveAction , SIGNAL(triggered()) , this , SLOT(shrinkActiveView()) ); + + multiViewOnlyActions << shrinkActiveAction; + + // Next / Previous View , Next Container + collection->addAction("next-view",nextViewAction); + collection->addAction("previous-view",previousViewAction); + collection->addAction("next-container",nextContainerAction); + collection->addAction("move-view-left",moveViewLeftAction); + collection->addAction("move-view-right",moveViewRightAction); + + // Switch to tab N shortcuts + const int SWITCH_TO_TAB_COUNT = 10; + QSignalMapper* switchToTabMapper = new QSignalMapper(this); + connect(switchToTabMapper,SIGNAL(mapped(int)),this,SLOT(switchToView(int))); + for (int i=0;i < SWITCH_TO_TAB_COUNT;i++) + { + KAction* switchToTabAction = new KAction(i18n("Switch to Tab %1",i+1),this); + switchToTabMapper->setMapping(switchToTabAction,i); + connect(switchToTabAction,SIGNAL(triggered()),switchToTabMapper, + SLOT(map())); + collection->addAction(QString("switch-to-tab-%1").arg(i),switchToTabAction); + } + } + + QListIterator<QAction*> iter(multiViewOnlyActions); + while ( iter.hasNext() ) + { + connect( this , SIGNAL(splitViewToggle(bool)) , iter.next() , SLOT(setEnabled(bool)) ); + } + + // keyboard shortcut only actions + KShortcut nextViewShortcut = nextViewAction->shortcut(); + nextViewShortcut.setPrimary( QKeySequence(Qt::SHIFT+Qt::Key_Right) ); + nextViewShortcut.setAlternate( QKeySequence(Qt::CTRL+Qt::Key_PageDown) ); + nextViewAction->setShortcut(nextViewShortcut); + connect( nextViewAction, SIGNAL(triggered()) , this , SLOT(nextView()) ); + _viewSplitter->addAction(nextViewAction); + + KShortcut previousViewShortcut = previousViewAction->shortcut(); + previousViewShortcut.setPrimary( QKeySequence(Qt::SHIFT+Qt::Key_Left) ); + previousViewShortcut.setAlternate( QKeySequence(Qt::CTRL+Qt::Key_PageUp) ); + previousViewAction->setShortcut(previousViewShortcut); + connect( previousViewAction, SIGNAL(triggered()) , this , SLOT(previousView()) ); + _viewSplitter->addAction(previousViewAction); + + nextContainerAction->setShortcut( QKeySequence(Qt::SHIFT+Qt::Key_Tab) ); + connect( nextContainerAction , SIGNAL(triggered()) , this , SLOT(nextContainer()) ); + _viewSplitter->addAction(nextContainerAction); + + moveViewLeftAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_Left) ); + connect( moveViewLeftAction , SIGNAL(triggered()) , this , SLOT(moveActiveViewLeft()) ); + _viewSplitter->addAction(moveViewLeftAction); + moveViewRightAction->setShortcut( QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_Right) ); + connect( moveViewRightAction , SIGNAL(triggered()) , this , SLOT(moveActiveViewRight()) ); + _viewSplitter->addAction(moveViewRightAction); +} +void ViewManager::switchToView(int index) +{ + Q_ASSERT(index >= 0); + ViewContainer* container = _viewSplitter->activeContainer(); + Q_ASSERT( container ); + QList<QWidget*> containerViews = container->views(); + if (index >= containerViews.count()) + return; + container->setActiveView(containerViews.at(index)); +} +void ViewManager::updateDetachViewState() +{ + if (!_actionCollection) + return; + + + bool splitView = _viewSplitter->containers().count() >= 2; + bool shouldEnable = splitView || _viewSplitter->activeContainer()->views().count() >= 2; + + QAction* detachAction = _actionCollection->action("detach-view"); + + if ( detachAction && shouldEnable != detachAction->isEnabled() ) + detachAction->setEnabled(shouldEnable); +} +void ViewManager::moveActiveViewLeft() +{ + ViewContainer* container = _viewSplitter->activeContainer(); + Q_ASSERT( container ); + container->moveActiveView( ViewContainer::MoveViewLeft ); +} +void ViewManager::moveActiveViewRight() +{ + ViewContainer* container = _viewSplitter->activeContainer(); + Q_ASSERT( container ); + container->moveActiveView( ViewContainer::MoveViewRight ); +} +void ViewManager::nextContainer() +{ + _viewSplitter->activateNextContainer(); +} + +void ViewManager::nextView() +{ + ViewContainer* container = _viewSplitter->activeContainer(); + + Q_ASSERT( container ); + + container->activateNextView(); +} + +void ViewManager::previousView() +{ + ViewContainer* container = _viewSplitter->activeContainer(); + + Q_ASSERT( container ); + + container->activatePreviousView(); +} + +void ViewManager::detachActiveView() +{ + // find the currently active view and remove it from its container + ViewContainer* container = _viewSplitter->activeContainer(); + + detachView(container, container->activeView()); +} + +void ViewManager::detachView(ViewContainer* container, QWidget* widgetView) +{ + TerminalDisplay * viewToDetach = + dynamic_cast<TerminalDisplay*>(widgetView); + + if (!viewToDetach) + return; + + emit viewDetached(_sessionMap[viewToDetach]); + + _sessionMap.remove(viewToDetach); + + // remove the view from this window + container->removeView(viewToDetach); + viewToDetach->deleteLater(); + + // if the container from which the view was removed is now empty then it can be deleted, + // unless it is the only container in the window, in which case it is left empty + // so that there is always an active container + if ( _viewSplitter->containers().count() > 1 && + container->views().count() == 0 ) + { + removeContainer(container); + } + +} + +void ViewManager::sessionFinished() +{ + // if this slot is called after the view manager's main widget + // has been destroyed, do nothing + if (!_viewSplitter) + return; + + Session* session = qobject_cast<Session*>(sender()); + + // We're using setSelectionBehaviorOnRemove(QTabBar::SelectPreviousTab) + // so no need to manually select next tab. + + Q_ASSERT(session); + + // close attached views + QList<TerminalDisplay*> children = _viewSplitter->findChildren<TerminalDisplay*>(); + + foreach ( TerminalDisplay* view , children ) + { + if ( _sessionMap[view] == session ) + { + _sessionMap.remove(view); + view->deleteLater(); + } + } + + // This is needed to remove this controller from factory() in + // order to prevent BUG: 185466 - disappearing menu popup + if (_pluggedController) + emit unplugController(_pluggedController); +} + +void ViewManager::focusActiveView() +{ + // give the active view in a container the focus. this ensures + // that controller associated with that view is activated and the session-specific + // menu items are replaced with the ones for the newly focused view + + // see the viewFocused() method + + ViewContainer* container = _viewSplitter->activeContainer(); + if ( container ) + { + QWidget* activeView = container->activeView(); + if ( activeView ) + { + activeView->setFocus(Qt::MouseFocusReason); + } + } +} + + +void ViewManager::viewActivated( QWidget* view ) +{ + Q_ASSERT( view != 0 ); + + // focus the activated view, this will cause the SessionController + // to notify the world that the view has been focused and the appropriate UI + // actions will be plugged in. + view->setFocus(Qt::OtherFocusReason); +} + +void ViewManager::splitLeftRight() +{ + splitView(Qt::Horizontal); +} +void ViewManager::splitTopBottom() +{ + splitView(Qt::Vertical); +} + +void ViewManager::splitView(Qt::Orientation orientation) +{ + // iterate over each session which has a view in the current active + // container and create a new view for that session in a new container + QListIterator<QWidget*> existingViewIter(_viewSplitter->activeContainer()->views()); + + ViewContainer* container = 0; + + while (existingViewIter.hasNext()) + { + Session* session = _sessionMap[(TerminalDisplay*)existingViewIter.next()]; + TerminalDisplay* display = createTerminalDisplay(session); + const Profile::Ptr info = SessionManager::instance()->sessionProfile(session); + applyProfile(display, info, false); + ViewProperties* properties = createController(session,display); + + _sessionMap[display] = session; + + // create a container using settings from the first + // session in the previous container + if ( !container ) + container = createContainer(info); + + int tabBarMode = info->property<int>(Profile::TabBarMode); + if ( tabBarMode == Profile::AlwaysHideTabBar ) + container->setNavigationDisplayMode(ViewContainer::AlwaysHideNavigation); + else if ( tabBarMode == Profile::AlwaysShowTabBar ) + container->setNavigationDisplayMode(ViewContainer::AlwaysShowNavigation); + else if ( tabBarMode == Profile::ShowTabBarAsNeeded ) + container->setNavigationDisplayMode(ViewContainer::ShowNavigationAsNeeded); + container->addView(display,properties); + session->addView( display ); + } + + _viewSplitter->addContainer(container,orientation); + emit splitViewToggle(_viewSplitter->containers().count() > 0); + + // focus the new container + container->containerWidget()->setFocus(); + + // ensure that the active view is focused after the split / unsplit + ViewContainer* activeContainer = _viewSplitter->activeContainer(); + QWidget* activeView = activeContainer ? activeContainer->activeView() : 0; + + if ( activeView ) + activeView->setFocus(Qt::OtherFocusReason); +} +void ViewManager::removeContainer(ViewContainer* container) +{ + // remove session map entries for views in this container + foreach( QWidget* view , container->views() ) + { + TerminalDisplay* display = qobject_cast<TerminalDisplay*>(view); + Q_ASSERT(display); + _sessionMap.remove(display); + } + + _viewSplitter->removeContainer(container); + container->deleteLater(); + + emit splitViewToggle( _viewSplitter->containers().count() > 1 ); +} +void ViewManager::expandActiveView() +{ + _viewSplitter->adjustContainerSize(_viewSplitter->activeContainer(),10); +} +void ViewManager::shrinkActiveView() +{ + _viewSplitter->adjustContainerSize(_viewSplitter->activeContainer(),-10); +} +void ViewManager::closeActiveView() +{ + // only do something if there is more than one container active + if ( _viewSplitter->containers().count() > 1 ) + { + ViewContainer* container = _viewSplitter->activeContainer(); + + removeContainer(container); + + // focus next container so that user can continue typing + // without having to manually focus it themselves + nextContainer(); + } +} +void ViewManager::closeOtherViews() +{ + ViewContainer* active = _viewSplitter->activeContainer(); + + QListIterator<ViewContainer*> iter(_viewSplitter->containers()); + while ( iter.hasNext() ) + { + ViewContainer* next = iter.next(); + if ( next != active ) + removeContainer(next); + } +} + +SessionController* ViewManager::createController(Session* session , TerminalDisplay* view) +{ + // create a new controller for the session, and ensure that this view manager + // is notified when the view gains the focus + SessionController* controller = new SessionController(session,view,this); + connect( controller , SIGNAL(focused(SessionController*)) , this , SLOT(controllerChanged(SessionController*)) ); + connect( session , SIGNAL(destroyed()) , controller , SLOT(deleteLater()) ); + connect( view , SIGNAL(destroyed()) , controller , SLOT(deleteLater()) ); + + // if this is the first controller created then set it as the active controller + if (!_pluggedController) + controllerChanged(controller); + + return controller; +} + +void ViewManager::controllerChanged(SessionController* controller) +{ + if ( controller == _pluggedController ) + return; + + _viewSplitter->setFocusProxy(controller->view()); + + _pluggedController = controller; + emit activeViewChanged(controller); +} + +SessionController* ViewManager::activeViewController() const +{ + return _pluggedController; +} + +IncrementalSearchBar* ViewManager::searchBar() const +{ + return _viewSplitter->activeSplitter()->activeContainer()->searchBar(); +} + +void ViewManager::createView(Session* session, ViewContainer* container, int index) +{ + // notify this view manager when the session finishes so that its view + // can be deleted + // + // TODO - Find a more efficient a way to avoid multiple connections + disconnect( session , SIGNAL(finished()) , this , SLOT(sessionFinished()) ); + connect( session , SIGNAL(finished()) , this , SLOT(sessionFinished()) ); + + bool isFirst = _sessionMap.isEmpty(); + TerminalDisplay* display = createTerminalDisplay(session); + applyProfile(display,SessionManager::instance()->sessionProfile(session),isFirst); + + // set initial size + display->setSize(80,40); + + ViewProperties* properties = createController(session,display); + + _sessionMap[display] = session; + container->addView(display,properties,index); + session->addView(display); + + // tell the session whether it has a light or dark background + const Profile::Ptr profile = SessionManager::instance()->sessionProfile(session); + session->setDarkBackground( colorSchemeForProfile(profile)->hasDarkBackground() ); + + if ( container == _viewSplitter->activeContainer() ) + { + container->setActiveView(display); + display->setFocus( Qt::OtherFocusReason ); + } + + updateDetachViewState(); +} + +void ViewManager::createView(Session* session) +{ + // create the default container + if (_viewSplitter->containers().count() == 0) + { + _viewSplitter->addContainer( createContainer(SessionManager::instance()->sessionProfile(session)) , + Qt::Vertical ); + emit splitViewToggle(false); + } + + + // iterate over the view containers owned by this view manager + // and create a new terminal display for the session in each of them, along with + // a controller for the session/display pair + QListIterator<ViewContainer*> containerIter(_viewSplitter->containers()); + + while ( containerIter.hasNext() ) + { + ViewContainer* container = containerIter.next(); + createView(session,container,-1); + } + +} + +ViewContainer* ViewManager::createContainer(const Profile::Ptr info) +{ + Q_ASSERT( info ); + + const int tabPosition = info->property<int>(Profile::TabBarPosition); + + ViewContainer::NavigationPosition position = ( tabPosition == Profile::TabBarTop ) ? + ViewContainer::NavigationPositionTop : + ViewContainer::NavigationPositionBottom; + + ViewContainer* container = 0; + + switch ( _navigationMethod ) + { + case TabbedNavigation: + { + container = + new TabbedViewContainer(position,_viewSplitter); + + connect(container, + SIGNAL(detachTab(ViewContainer*, QWidget*)), + this, + SLOT(detachView(ViewContainer*, QWidget*)) + ); + } + break; + case NoNavigation: + default: + container = new StackedViewContainer(_viewSplitter); + } + + // connect signals and slots + connect( container , SIGNAL(viewAdded(QWidget*,ViewProperties*)) , _containerSignalMapper , + SLOT(map()) ); + connect( container , SIGNAL(viewRemoved(QWidget*)) , _containerSignalMapper , + SLOT(map()) ); + _containerSignalMapper->setMapping(container,container); + + connect( container, SIGNAL(newViewRequest()), this, SIGNAL(newViewRequest()) ); + connect( container, SIGNAL(moveViewRequest(int,int,bool&)), + this , SLOT(containerMoveViewRequest(int,int,bool&)) ); + connect( container , SIGNAL(viewRemoved(QWidget*)) , this , SLOT(viewCloseRequest(QWidget*)) ); + connect( container , SIGNAL(closeRequest(QWidget*)) , this , SLOT(viewCloseRequest(QWidget*)) ); + connect( container , SIGNAL(activeViewChanged(QWidget*)) , this , SLOT(viewActivated(QWidget*))); + + return container; +} +void ViewManager::containerMoveViewRequest(int index, int id, bool& moved) +{ + ViewContainer* container = qobject_cast<ViewContainer*>(sender()); + SessionController* controller = qobject_cast<SessionController*>(ViewProperties::propertiesById(id)); + + if (!controller) + return; + + createView(controller->session(),container,index); + controller->session()->refresh(); + moved = true; +} +void ViewManager::setNavigationMethod(NavigationMethod method) +{ + _navigationMethod = method; + + KActionCollection* collection = _actionCollection; + + if ( collection ) + { + // FIXME: The following disables certain actions for the KPart that it + // doesn't actually have a use for, to avoid polluting the action/shortcut + // namespace of an application using the KPart (otherwise, a shortcut may + // be in use twice, and the user gets to see an "ambiguous shortcut over- + // load" error dialog). However, this approach sucks - it's the inverse of + // what it should be. Rather than disabling actions not used by the KPart, + // a method should be devised to only enable those that are used, perhaps + // by using a separate action collection. + + QAction* action; + + action = collection->action( "next-view" ); + if ( action ) action->setEnabled( _navigationMethod != NoNavigation ); + + action = collection->action( "previous-view" ); + if ( action ) action->setEnabled( _navigationMethod != NoNavigation ); + + action = collection->action( "split-view-left-right" ); + if ( action ) action->setEnabled( _navigationMethod != NoNavigation ); + + action = collection->action( "split-view-top-bottom" ); + if ( action ) action->setEnabled( _navigationMethod != NoNavigation ); + + action = collection->action( "rename-session" ); + if ( action ) action->setEnabled( _navigationMethod != NoNavigation ); + + action = collection->action( "move-view-left" ); + if ( action ) action->setEnabled( _navigationMethod != NoNavigation ); + + action = collection->action( "move-view-right" ); + if ( action ) action->setEnabled( _navigationMethod != NoNavigation ); + } +} + +ViewManager::NavigationMethod ViewManager::navigationMethod() const { return _navigationMethod; } + +void ViewManager::containerViewsChanged(QObject* container) +{ + if (_viewSplitter && container == _viewSplitter->activeContainer() ) + { + emit viewPropertiesChanged( viewProperties() ); + } +} + +void ViewManager::viewCloseRequest(QWidget* view) +{ + //FIXME Check that this cast is actually legal + TerminalDisplay* display = (TerminalDisplay*)view; + + Q_ASSERT(display); + + // 1. detach view from session + // 2. if the session has no views left, close it + Session* session = _sessionMap[ display ]; + _sessionMap.remove(display); + if ( session ) + { + display->deleteLater(); + + if ( session->views().count() == 0 ) + session->close(); + } + //we only update the focus if the splitter is still alive + if (_viewSplitter) { + focusActiveView(); + updateDetachViewState(); + } + // The below causes the menus to be messed up + // Only happenss when using the tab bar close button +// if (_pluggedController) +// emit unplugController(_pluggedController); +} + +TerminalDisplay* ViewManager::createTerminalDisplay(Session* session) +{ + TerminalDisplay* display = new TerminalDisplay(0); + + //TODO Temporary settings used here + display->setBellMode(TerminalDisplay::NotifyBell); + display->setTerminalSizeHint(true); + display->setTripleClickMode(TerminalDisplay::SelectWholeLine); + display->setTerminalSizeStartup(true); + display->setScrollBarPosition(TerminalDisplay::ScrollBarRight); + display->setRandomSeed(session->sessionId() * 31); + + return display; +} + +const ColorScheme* ViewManager::colorSchemeForProfile(const Profile::Ptr info) const +{ + const ColorScheme* colorScheme = ColorSchemeManager::instance()-> + findColorScheme(info->colorScheme()); + if ( !colorScheme ) + colorScheme = ColorSchemeManager::instance()->defaultColorScheme(); + Q_ASSERT( colorScheme ); + + return colorScheme; +} + +void ViewManager::applyProfile(TerminalDisplay* view , const Profile::Ptr info, + bool applyContainerSettings) +{ + Q_ASSERT( info ); + + const ColorScheme* colorScheme = colorSchemeForProfile(info); + + // menu bar visibility + emit setMenuBarVisibleRequest( info->property<bool>(Profile::ShowMenuBar) ); + + emit setSaveGeometryOnExitRequest( info->property<bool>(Profile::SaveGeometryOnExit) ); + + // tab bar visibility + if (applyContainerSettings) + { + ViewContainer* container = _viewSplitter->activeContainer(); + int tabBarMode = info->property<int>(Profile::TabBarMode); + int tabBarPosition = info->property<int>(Profile::TabBarPosition); + bool showNewCloseButtons = info->property<bool>(Profile::ShowNewAndCloseTabButtons); + + if ( tabBarMode == Profile::AlwaysHideTabBar ) + container->setNavigationDisplayMode(ViewContainer::AlwaysHideNavigation); + else if ( tabBarMode == Profile::AlwaysShowTabBar ) + container->setNavigationDisplayMode(ViewContainer::AlwaysShowNavigation); + else if ( tabBarMode == Profile::ShowTabBarAsNeeded ) + container->setNavigationDisplayMode(ViewContainer::ShowNavigationAsNeeded); + + ViewContainer::NavigationPosition position = container->navigationPosition(); + + if ( tabBarPosition == Profile::TabBarTop ) + position = ViewContainer::NavigationPositionTop; + else if ( tabBarPosition == Profile::TabBarBottom ) + position = ViewContainer::NavigationPositionBottom; + + if ( container->supportedNavigationPositions().contains(position) ) + container->setNavigationPosition(position); + + if (showNewCloseButtons) + { + container->setFeatures(container->features() + | ViewContainer::QuickNewView | ViewContainer::QuickCloseView); + container->setNewViewMenu(createNewViewMenu()); + } + else + container->setFeatures(container->features() + & ~ViewContainer::QuickNewView & ~ViewContainer::QuickCloseView); + } + + // load colour scheme + ColorEntry table[TABLE_COLORS]; + + colorScheme->getColorTable(table , view->randomSeed() ); + view->setColorTable(table); + view->setOpacity(colorScheme->opacity()); + + // load font + view->setAntialias(info->property<bool>(Profile::AntiAliasFonts)); + view->setBoldIntense(info->property<bool>(Profile::BoldIntense)); + view->setVTFont(info->font()); + + // set scroll-bar position + int scrollBarPosition = info->property<int>(Profile::ScrollBarPosition); + + if ( scrollBarPosition == Profile::ScrollBarHidden ) + view->setScrollBarPosition(TerminalDisplay::NoScrollBar); + else if ( scrollBarPosition == Profile::ScrollBarLeft ) + view->setScrollBarPosition(TerminalDisplay::ScrollBarLeft); + else if ( scrollBarPosition == Profile::ScrollBarRight ) + view->setScrollBarPosition(TerminalDisplay::ScrollBarRight); + + // terminal features + bool blinkingCursor = info->property<bool>(Profile::BlinkingCursorEnabled); + view->setBlinkingCursor(blinkingCursor); + + bool blinkingText = info->property<bool>(Profile::BlinkingTextEnabled); + view->setBlinkingTextEnabled(blinkingText); + + bool tripleClickMode = info->property<bool>(Profile::TripleClickMode); + view->setTripleClickMode(tripleClickMode ? TerminalDisplay::SelectForwardsFromCursor : TerminalDisplay::SelectWholeLine); + + bool bidiEnabled = info->property<bool>(Profile::BidiRenderingEnabled); + view->setBidiEnabled(bidiEnabled); + + // cursor shape + int cursorShape = info->property<int>(Profile::CursorShape); + + if ( cursorShape == Profile::BlockCursor ) + view->setKeyboardCursorShape(TerminalDisplay::BlockCursor); + else if ( cursorShape == Profile::IBeamCursor ) + view->setKeyboardCursorShape(TerminalDisplay::IBeamCursor); + else if ( cursorShape == Profile::UnderlineCursor ) + view->setKeyboardCursorShape(TerminalDisplay::UnderlineCursor); + + // cursor color + bool useCustomColor = info->property<bool>(Profile::UseCustomCursorColor); + const QColor& cursorColor = info->property<QColor>(Profile::CustomCursorColor); + + view->setKeyboardCursorColor(!useCustomColor,cursorColor); + + // word characters + view->setWordCharacters( info->property<QString>(Profile::WordCharacters) ); +} + +void ViewManager::updateViewsForSession(Session* session) +{ + QListIterator<TerminalDisplay*> iter(_sessionMap.keys(session)); + while ( iter.hasNext() ) + { + applyProfile(iter.next(),SessionManager::instance()->sessionProfile(session),false); + } +} + +void ViewManager::profileChanged(Profile::Ptr profile) +{ + QHashIterator<TerminalDisplay*,Session*> iter(_sessionMap); + + while ( iter.hasNext() ) + { + iter.next(); + + // if session uses this profile, update the display + if ( iter.key() != 0 && + iter.value() != 0 && + SessionManager::instance()->sessionProfile(iter.value()) == profile ) + { + applyProfile(iter.key(),profile,true); + } + } +} + +QList<ViewProperties*> ViewManager::viewProperties() const +{ + QList<ViewProperties*> list; + + ViewContainer* container = _viewSplitter->activeContainer(); + + Q_ASSERT( container ); + + QListIterator<QWidget*> viewIter(container->views()); + while ( viewIter.hasNext() ) + { + ViewProperties* properties = container->viewProperties(viewIter.next()); + Q_ASSERT( properties ); + list << properties; + } + + return list; +} + +void ViewManager::saveSessions(KConfigGroup& group) +{ + // find all unique session restore IDs + QList<int> ids; + QHash<Session*,int> unique; + + // first: sessions in the active container, preserving the order + ViewContainer* container = _viewSplitter->activeContainer(); + Q_ASSERT(container); + TerminalDisplay* activeview = dynamic_cast<TerminalDisplay*>(container->activeView()); + + QListIterator<QWidget*> viewIter(container->views()); + int tab = 1; + while (viewIter.hasNext()) + { + TerminalDisplay *view = dynamic_cast<TerminalDisplay*>(viewIter.next()); + Q_ASSERT(view); + Session *session = _sessionMap[view]; + ids << SessionManager::instance()->getRestoreId(session); + if (view == activeview) group.writeEntry("Active", tab); + unique.insert(session, 1); + tab++; + } + + // second: all other sessions, in random order + // we don't want to have sessions restored that are not connected + foreach(Session* session, _sessionMap) + if (!unique.contains(session)) + { + ids << SessionManager::instance()->getRestoreId(session); + unique.insert(session, 1); + } + + group.writeEntry("Sessions", ids); +} + +void ViewManager::restoreSessions(const KConfigGroup& group) +{ + QList<int> ids = group.readEntry("Sessions", QList<int>()); + int activeTab = group.readEntry("Active", 0); + TerminalDisplay *display = 0; + + int tab = 1; + foreach(int id, ids) + { + Session *session = SessionManager::instance()->idToSession(id); + createView(session); + if (!session->isRunning()) + session->run(); + if (tab++ == activeTab) + display = dynamic_cast<TerminalDisplay*>(activeView()); + } + + if (display) + { + _viewSplitter->activeContainer()->setActiveView(display); + display->setFocus(Qt::OtherFocusReason); + } + + if (ids.isEmpty()) // Session file is unusable, start default Profile + { + Profile::Ptr profile = SessionManager::instance()->defaultProfile(); + Session* session = SessionManager::instance()->createSession(profile); + createView(session); + if (!session->isRunning()) + session->run(); + } +} + +uint qHash(QPointer<TerminalDisplay> display) +{ + return qHash((TerminalDisplay*)display); +} + +int ViewManager::sessionCount() +{ + return this->_sessionMap.size(); +} + +int ViewManager::currentSession() +{ + QHash<TerminalDisplay*,Session*>::iterator i; + for (i = this->_sessionMap.begin(); i != this->_sessionMap.end(); ++i) + if (i.key()->isVisible()) + return i.value()->sessionId(); + return -1; +} + +int ViewManager::newSession() +{ + Profile::Ptr profile = SessionManager::instance()->defaultProfile(); + Session* session = SessionManager::instance()->createSession(profile); + + this->createView(session); + session->run(); + + return session->sessionId(); +} + +int ViewManager::newSession(QString profile, QString directory) +{ + QList<Profile::Ptr> profilelist = SessionManager::instance()->loadedProfiles(); + QList<Profile::Ptr>::iterator i = profilelist.begin(); + + Profile::Ptr profileptr = SessionManager::instance()->defaultProfile(); + + while (i != profilelist.end() ) + { + Profile::Ptr ptr = *i; + if ( ptr->name().compare(profile) == 0) + profileptr = ptr; + i++; + } + + Session* session = SessionManager::instance()->createSession(profileptr); + session->setInitialWorkingDirectory(directory); + + this->createView(session); + session->run(); + + return session->sessionId(); +} + +QStringList ViewManager::profileList() +{ + QList<Profile::Ptr> profilelist = SessionManager::instance()->loadedProfiles(); + QList<Profile::Ptr>::iterator i = profilelist.begin(); + QStringList list; + while (i != profilelist.end() ) + { + Profile::Ptr ptr = *i; + list.push_back(ptr->name()); + i++; + } + + return list; +} + +void ViewManager::nextSession() +{ + this->nextView(); +} + +void ViewManager::prevSession() +{ + this->previousView(); +} + +void ViewManager::moveSessionLeft() +{ + this->moveActiveViewLeft(); +} + +void ViewManager::moveSessionRight() +{ + this->moveActiveViewRight(); +} + +#include "ViewManager.moc" +
new file mode 100644 --- /dev/null +++ b/gui/konsole/ViewManager.h @@ -0,0 +1,373 @@ +/* + 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. +*/ + +#ifndef VIEWMANAGER_H +#define VIEWMANAGER_H + +// Qt +#include <QtCore/QHash> +#include <QtCore/QObject> +#include <QtCore/QPointer> + +// Konsole +#include "Profile.h" + +class QSignalMapper; +class QMenu; +class KActionCollection; +class KConfigGroup; + +namespace Konsole +{ + +class ColorScheme; +class IncrementalSearchBar; +class Session; +class TerminalDisplay; +class Profile; + +class SessionController; +class ViewProperties; +class ViewContainer; +class ViewSplitter; + +/** + * Manages the terminal display widgets in a Konsole window or part. + * + * When a view manager is created, it constructs a splitter widget ( accessed via + * widget() ) to hold one or more view containers. Each view container holds + * one or more terminal displays and a navigation widget ( eg. tabs or a list ) + * to allow the user to navigate between the displays in that container. + * + * The view manager provides menu actions ( defined in the 'konsoleui.rc' XML file ) + * to manipulate the views and view containers - for example, actions to split the view + * left/right or top/bottom, detach a view from the current window and navigate between + * views and containers. These actions are added to the collection specified in the + * ViewManager's constructor. + * + * The view manager provides facilities to construct display widgets for a terminal + * session and also to construct the SessionController which provides the menus and other + * user interface elements specific to each display/session pair. + * + */ +class KONSOLEPRIVATE_EXPORT ViewManager : public QObject +{ +Q_OBJECT +Q_CLASSINFO("D-Bus Interface", "org.kde.konsole.Konsole") + +public: + /** + * Constructs a new view manager with the specified @p parent. + * View-related actions defined in 'konsoleui.rc' are created + * and added to the specified @p collection. + */ + ViewManager(QObject* parent , KActionCollection* collection); + ~ViewManager(); + + /** + * Creates a new view to display the outout from and deliver input to @p session. + * Constructs a new container to hold the views if no container has yet been created. + */ + void createView(Session* session); + + /** + * Applies the view-specific settings associated with specified @p profile + * to the terminal display @p view. If @p applyContainerSettings is true then + * tab bar settings in the profile will also be applied + */ + void applyProfile(TerminalDisplay* view , const Profile::Ptr profile + , bool applyContainerSettings); + + /** + * Return the main widget for the view manager which + * holds all of the views managed by this ViewManager instance. + */ + QWidget* widget() const; + + /** + * Returns the view manager's active view. + */ + QWidget* activeView() const; + + /** + * Returns the list of view properties for views in the active container. + * Each view widget is associated with a ViewProperties instance which + * provides access to basic information about the session being + * displayed in the view, such as title, current directory and + * associated icon. + */ + QList<ViewProperties*> viewProperties() const; + + /** + * This enum describes the available types of navigation widget + * which newly created containers can provide to allow navigation + * between open sessions. + */ + enum NavigationMethod + { + /** + * Each container has a row of tabs (one per session) which the user + * can click on to navigate between open sessions. + */ + TabbedNavigation, + /** The container has no navigation widget. */ + NoNavigation + }; + + /** + * Sets the type of widget provided to navigate between open sessions + * in a container. The changes will only apply to newly created containers. + * + * The default method is TabbedNavigation. To disable navigation widgets, call + * setNavigationMethod(ViewManager::NoNavigation) before creating any sessions. + */ + void setNavigationMethod(NavigationMethod method); + + /** + * Returns the type of navigation widget created in new containers. + * See setNavigationMethod() + */ + NavigationMethod navigationMethod() const; + + /** + * Returns the controller for the active view. activeViewChanged() is + * emitted when this changes. + */ + SessionController* activeViewController() const; + + /** + * Returns the search bar. + */ + IncrementalSearchBar* searchBar() const; + + /** + * Session management + */ + void saveSessions(KConfigGroup& group); + void restoreSessions(const KConfigGroup& group); + +signals: + /** Emitted when the last view is removed from the view manager */ + void empty(); + + /** Emitted when a session is detached from a view owned by this ViewManager */ + void viewDetached(Session* session); + + /** + * Emitted when the active view changes. + * @param controller The controller associated with the active view + */ + void activeViewChanged(SessionController* controller); + + /** + * Emitted when the current session needs unplugged from factory(). + * @param controller The controller associated with the active view + */ + void unplugController(SessionController* controller); + + /** + * Emitted when the list of view properties ( as returned by viewProperties() ) changes. + * This occurs when views are added to or removed from the active container, or + * if the active container is changed. + */ + void viewPropertiesChanged(const QList<ViewProperties*>& propertiesList); + + /** + * Emitted when the number of views containers changes. This is used to disable or + * enable menu items which can only be used when there are one or multiple containers + * visible. + * + * @param multipleViews True if there are multiple view containers open or false if there is + * just a single view. + */ + void splitViewToggle(bool multipleViews); + + /** + * Emitted when menu bar visibility changes because a profile that requires so is + * activated. + */ + void setMenuBarVisibleRequest(bool); + void setSaveGeometryOnExitRequest(bool); + + /** Requests creation of a new view with the default profile. */ + void newViewRequest(); + /** Requests creation of a new view, with the selected profile. */ + void newViewRequest(Profile::Ptr); + +public slots: + /** DBus slot that returns the number of sessions in the current view. */ + int sessionCount(); + + /** DBus slot that returns the current (active) session window */ + int currentSession(); + + /** DBus slot that creates a new session in the current view. + * @param profile the name of the profile to be used + * @param directory the working directory where the session is + * started. + */ + int newSession(QString profile, QString directory); + + // DBus slot that returns a string list of defined (known) profiles + QStringList profileList(); + + /** DBus slot that creates a new session in the current view with the associated + * default profile and the default working directory + */ + int newSession(); + + /** DBus slot that changes the view port to the next session */ + void nextSession(); + + /** DBus slot that changes the view port to the previous session */ + void prevSession(); + + /** DBus slot that switches the current session (as returned by + * currentSession()) with the left (or previous) one in the + * navigation tab. + */ + void moveSessionLeft(); + + /** DBus slot that Switches the current session (as returned by + * currentSession()) with the right (or next) one in the navigation + * tab. + */ + void moveSessionRight(); + +private slots: + // called when the "Split View Left/Right" menu item is selected + void splitLeftRight(); + void splitTopBottom(); + void closeActiveView(); + void closeOtherViews(); + void expandActiveView(); + void shrinkActiveView(); + + // called when the "Detach View" menu item is selected + void detachActiveView(); + void updateDetachViewState(); + + // called when a session terminates - the view manager will delete any + // views associated with the session + void sessionFinished(); + // called when the container requests to close a particular view + void viewCloseRequest(QWidget* widget); + + // controller detects when an associated view is given the focus + // and emits a signal. ViewManager listens for that signal + // and then plugs the action into the UI + //void viewFocused( SessionController* controller ); + + // called when the active view in a ViewContainer changes, so + // that we can plug the appropriate actions into the UI + void viewActivated( QWidget* view ); + + // called when "Next View" shortcut is activated + void nextView(); + + // called when "Previous View" shortcut is activated + void previousView(); + + // called when "Next View Container" shortcut is activated + void nextContainer(); + + // called when the views in a container owned by this view manager + // changes + void containerViewsChanged(QObject* container); + + // called when a profile changes + void profileChanged(Profile::Ptr profile); + + void updateViewsForSession(Session* session); + + // moves active view to the left + void moveActiveViewLeft(); + // moves active view to the right + void moveActiveViewRight(); + // switches to the view at visual position 'index' + // in the current container + void switchToView(int index); + + // called when a SessionController gains focus + void controllerChanged(SessionController* controller); + + // called when a ViewContainer requests a view be + // moved + void containerMoveViewRequest(int index, int id, bool& success); + + void detachView(ViewContainer* container, QWidget* view); + +private: + void createView(Session* session, ViewContainer* container, int index); + const ColorScheme* colorSchemeForProfile(const Profile::Ptr profile) const; + + void setupActions(); + void focusActiveView(); + void registerView(TerminalDisplay* view); + void unregisterView(TerminalDisplay* view); + + // takes a view from a view container owned by a different manager and places it in + // newContainer owned by this manager + void takeView(ViewManager* otherManager , ViewContainer* otherContainer, ViewContainer* newContainer, TerminalDisplay* view); + void splitView(Qt::Orientation orientation); + + // creates a new container which can hold terminal displays + // 'profile' specifies the profile to use to get initial + // settings (eg. navigation position) for the container + ViewContainer* createContainer(const Profile::Ptr profile); + // removes a container and emits appropriate signals + void removeContainer(ViewContainer* container); + + // creates a new terminal display + // the 'session' is used so that the terminal display's random seed + // can be set to something which depends uniquely on that session + TerminalDisplay* createTerminalDisplay(Session* session = 0); + + // creates a new controller for a session/display pair which provides the menu + // actions associated with that view, and exposes basic information + // about the session ( such as title and associated icon ) to the display. + SessionController* createController(Session* session , TerminalDisplay* display); + + // create menu for 'new tab' button + QMenu* createNewViewMenu(); +private: + QPointer<ViewSplitter> _viewSplitter; + QPointer<SessionController> _pluggedController; + + QHash<TerminalDisplay*,Session*> _sessionMap; + + KActionCollection* _actionCollection; + QSignalMapper* _containerSignalMapper; + NavigationMethod _navigationMethod; + + QMenu* _newViewMenu; +}; + +} + +#endif + +/* + Local Variables: + mode: c++ + c-file-style: "stroustrup" + indent-tabs-mode: nil + tab-width: 4 + End: +*/
new file mode 100644 --- /dev/null +++ b/gui/konsole/ViewProperties.cpp @@ -0,0 +1,101 @@ +/* + Copyright 2007-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 "ViewProperties.h" + +// Qt + +using namespace Konsole; + +QHash<int,ViewProperties*> ViewProperties::_viewProperties; +QString ViewProperties::_mimeType = "application/x-konsole-view-id"; + +ViewProperties::ViewProperties(QObject* parent) +: QObject(parent) +, _id(0) +//, _flags(0) +{ +} +ViewProperties::~ViewProperties() +{ + _viewProperties.remove(_id); +} +ViewProperties* ViewProperties::propertiesById(int id) +{ + return _viewProperties[id]; +} +KUrl ViewProperties::url() const +{ + return KUrl(); +} +QString ViewProperties::currentDir() const +{ + return QString(); +} +void ViewProperties::fireActivity() +{ + emit activity(this); +} + +void ViewProperties::rename() +{ +} + +void ViewProperties::setTitle(const QString& title) +{ + if ( title != _title ) + { + _title = title; + emit titleChanged(this); + } +} +void ViewProperties::setIcon(const QIcon& icon) +{ + // the icon's cache key is used to determine whether this icon is the same + // as the old one. if so no signal is emitted. + + if ( icon.cacheKey() != _icon.cacheKey() ) + { + _icon = icon; + emit iconChanged(this); + } +} +void ViewProperties::setIdentifier(int id) +{ + if (_viewProperties.contains(_id)) + _viewProperties.remove(_id); + + _id = id; + + _viewProperties.insert(id,this); +} +QString ViewProperties::title() const +{ + return _title; +} +QIcon ViewProperties::icon() const +{ + return _icon; +} +int ViewProperties::identifier() const +{ + return _id; +} +#include "ViewProperties.moc"
new file mode 100644 --- /dev/null +++ b/gui/konsole/ViewProperties.h @@ -0,0 +1,156 @@ +/* + Copyright 2007-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. +*/ + +#ifndef VIEWPROPERTIES_H +#define VIEWPROPERTIES_H + +// Qt +#include <QtGui/QIcon> +#include <QtCore/QObject> +#include <QtCore/QHash> +#include <QtCore/QMimeData> + +// KDE +#include <KUrl> + +// Konsole +#include "konsole_export.h" + +namespace Konsole +{ + +/** + * Encapsulates user-visible information about the terminal session currently being displayed in a view, + * such as the associated title and icon. + * + * This can be used by navigation widgets in a ViewContainer sub-class to provide a tab, label or other + * item for switching between views. + */ +class KONSOLEPRIVATE_EXPORT ViewProperties : public QObject +{ +Q_OBJECT + +public: + ViewProperties(QObject* parent); + virtual ~ViewProperties(); + + /** Returns the icon associated with a view */ + QIcon icon() const; + /** Returns the title associated with a view */ + QString title() const; + + /** + * Returns the URL current associated with a view. + * The default implementation returns an empty URL. + */ + virtual KUrl url() const; + + /** + * Returns the current directory associated with a view. + * This may be the same as url() + * The default implementation returns an empty string. + */ + virtual QString currentDir() const; + + /** + * A unique identifier associated with this + * ViewProperties instance. + */ + int identifier() const; + + /** + * Sub-classes may re-implement this method to display a message to the user + * to allow them to confirm whether to close a view. + * The default implementation always returns true + */ + virtual bool confirmClose() const + { return true; } + + /** Finds a ViewProperties instance given its numeric identifier. */ + static ViewProperties* propertiesById(int id); + + /** Name of mime format to use in drag-and-drop operations. */ + static QString mimeType() + { return _mimeType; } + + /** Returns a new QMimeData instance which represents the view with the given @p id + * (See identifier()). The QMimeData instance returned must be deleted by the caller. + */ + static QMimeData* createMimeData(int id) + { + QMimeData* mimeData = new QMimeData; + QByteArray data((char*)&id,sizeof(int)); + mimeData->setData(mimeType(),data); + return mimeData; + } + /** Decodes a QMimeData instance created with createMimeData() and returns the identifier + * of the associated view. The associated ViewProperties instance can then be retrieved by + * calling propertiesById() + * + * The QMimeData instance must support the mime format returned by mimeType() + */ + static int decodeMimeData(const QMimeData* mimeData) + { + return *(int*)(mimeData->data(ViewProperties::mimeType()).constData()); + } + +signals: + /** Emitted when the icon for a view changes */ + void iconChanged(ViewProperties* properties); + /** Emitted when the title for a view changes */ + void titleChanged(ViewProperties* properties); + /** Emitted when activity has occurred in this view. */ + void activity(ViewProperties* item); + +public slots: + /** + * Requests the renaming of this view. + * The default implementation does nothing. + */ + virtual void rename(); + +protected slots: + /** Emits the activity() signal. */ + void fireActivity(); + +protected: + /** + * Subclasses may call this method to change the title. This causes + * a titleChanged() signal to be emitted + */ + void setTitle(const QString& title); + /** + * Subclasses may call this method to change the icon. This causes + * an iconChanged() signal to be emitted + */ + void setIcon(const QIcon& icon); + /** Subclasses may call this method to change the identifier. */ + void setIdentifier(int id); +private: + QIcon _icon; + QString _title; + int _id; + + static QHash<int,ViewProperties*> _viewProperties; + static QString _mimeType; +}; + +} + +#endif //VIEWPROPERTIES_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/ViewSplitter.cpp @@ -0,0 +1,287 @@ +/* + This file is part of the Konsole Terminal. + + Copyright 2006-2008 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 "ViewSplitter.h" + +// Qt +#include <QtGui/QKeyEvent> + +// KDE +#include "kdebug.h" + +// Konsole +#include "ViewContainer.h" + +using namespace Konsole; + +ViewSplitter::ViewSplitter(QWidget* parent) + : QSplitter(parent) + , _recursiveSplitting(true) +{ +} + +void ViewSplitter::childEmpty(ViewSplitter* splitter) +{ + delete splitter; + + if ( count() == 0 ) + emit empty(this); +} + +void ViewSplitter::adjustContainerSize(ViewContainer* container , int percentage) +{ + int containerIndex = indexOf(container->containerWidget()); + + Q_ASSERT( containerIndex != -1 ); + + QList<int> containerSizes = sizes(); + + int oldSize = containerSizes[containerIndex]; + int newSize = (int)(oldSize * ( 1.0 + percentage/100.0 )); + + int perContainerDelta = (count() == 1 ) ? 0 : ( (newSize-oldSize) / (count()-1) ) * (-1); + + for ( int i = 0 ; i < containerSizes.count() ; i++ ) + { + if ( i == containerIndex ) + containerSizes[i] = newSize; + else + containerSizes[i] = containerSizes[i] + perContainerDelta; + } + + setSizes(containerSizes); +} + +ViewSplitter* ViewSplitter::activeSplitter() +{ + QWidget* widget = focusWidget() ? focusWidget() : this; + + ViewSplitter* splitter = 0; + + while ( !splitter && widget ) + { + splitter = dynamic_cast<ViewSplitter*>(widget); + widget = widget->parentWidget(); + } + + Q_ASSERT( splitter ); + return splitter; +} + +void ViewSplitter::registerContainer( ViewContainer* container ) +{ + _containers << container; + connect( container , SIGNAL(destroyed(ViewContainer*)) , this , SLOT( containerDestroyed(ViewContainer*) ) ); + connect( container , SIGNAL(empty(ViewContainer*)) , this , SLOT( containerEmpty(ViewContainer*) ) ); +} + +void ViewSplitter::unregisterContainer( ViewContainer* container ) +{ + _containers.removeAll(container); + disconnect( container , 0 , this , 0 ); +} + +void ViewSplitter::updateSizes() +{ + int space; + + if ( orientation() == Qt::Horizontal ) + { + space = width() / count(); + } + else + { + space = height() / count(); + } + + QList<int> widgetSizes; + for (int i=0;i<count();i++) + widgetSizes << space; + + setSizes(widgetSizes); +} + +void ViewSplitter::setRecursiveSplitting(bool recursive) +{ + _recursiveSplitting = recursive; +} +bool ViewSplitter::recursiveSplitting() const +{ + return _recursiveSplitting; +} + +void ViewSplitter::removeContainer( ViewContainer* container ) +{ + Q_ASSERT( containers().contains(container) ); + + unregisterContainer(container); +} + +void ViewSplitter::addContainer( ViewContainer* container , + Qt::Orientation containerOrientation ) +{ + ViewSplitter* splitter = activeSplitter(); + + if ( splitter->count() < 2 || + containerOrientation == splitter->orientation() || + !_recursiveSplitting ) + { + splitter->registerContainer(container); + splitter->addWidget(container->containerWidget()); + + if ( splitter->orientation() != containerOrientation ) + splitter->setOrientation( containerOrientation ); + + splitter->updateSizes(); + } + else + { + ViewSplitter* newSplitter = new ViewSplitter(this); + connect( newSplitter , SIGNAL(empty(ViewSplitter*)) , splitter , SLOT(childEmpty(ViewSplitter*)) ); + + ViewContainer* oldContainer = splitter->activeContainer(); + int oldContainerIndex = splitter->indexOf(oldContainer->containerWidget()); + + splitter->unregisterContainer(oldContainer); + + newSplitter->registerContainer(oldContainer); + newSplitter->registerContainer(container); + + newSplitter->addWidget(oldContainer->containerWidget()); + newSplitter->addWidget(container->containerWidget()); + newSplitter->setOrientation(containerOrientation); + newSplitter->updateSizes(); + newSplitter->show(); + + splitter->insertWidget(oldContainerIndex,newSplitter); + } + +} + +void ViewSplitter::containerEmpty(ViewContainer* /*object*/) +{ + QListIterator<ViewContainer*> containerIter(_containers); + + int children = 0; + while (containerIter.hasNext()) + { + children += containerIter.next()->views().count(); + } + + if ( children == 0 ) + emit allContainersEmpty(); +} + +void ViewSplitter::containerDestroyed(ViewContainer* object) +{ + Q_ASSERT( _containers.contains(object) ); + + _containers.removeAll(object); + + if ( count() == 0 ) + { + emit empty(this); + } +} + +void ViewSplitter::activateNextContainer() +{ + ViewContainer* active = activeContainer(); + + int index = _containers.indexOf(active); + + if ( index == -1 ) + return; + + if ( index == _containers.count() -1 ) + index = 0; + else + index++; + + setActiveContainer( _containers.at(index) ); +} + +void ViewSplitter::activatePreviousContainer() +{ + ViewContainer* active = activeContainer(); + + int index = _containers.indexOf(active); + + if ( index == 0 ) + index = _containers.count() - 1; + else + index--; + + setActiveContainer( _containers.at(index) ); +} + + +void ViewSplitter::setActiveContainer(ViewContainer* container) +{ + QWidget* activeView = container->activeView(); + + if ( activeView ) + activeView->setFocus( Qt::OtherFocusReason ); +} + +ViewContainer* ViewSplitter::activeContainer() const +{ + if ( QWidget* focusW = focusWidget() ) + { + ViewContainer* focusContainer = 0; + + while ( focusW != 0 ) + { + QListIterator<ViewContainer*> containerIter(_containers); + while (containerIter.hasNext()) + { + ViewContainer* nextContainer = containerIter.next(); + + if (nextContainer->containerWidget() == focusW) + { + focusContainer = nextContainer; + break; + } + } + focusW = focusW->parentWidget(); + } + + if ( focusContainer ) + return focusContainer; + } + + QList<ViewSplitter*> splitters = findChildren<ViewSplitter*>(); + + if (splitters.count() > 0) + { + return splitters.last()->activeContainer(); + } + else + { + if ( _containers.count() > 0 ) + return _containers.last(); + else + return 0; + } +} + +#include "ViewSplitter.moc"
new file mode 100644 --- /dev/null +++ b/gui/konsole/ViewSplitter.h @@ -0,0 +1,188 @@ +/* + This file is part of the Konsole Terminal. + + Copyright 2006-2008 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. +*/ + +#ifndef VIEWSPLITTER_H +#define VIEWSPLITTER_H + +#include <QtCore/QList> +#include <QtGui/QSplitter> + +class QFocusEvent; + +namespace Konsole +{ + +class ViewContainer; + +/** + * A splitter which holds a number of ViewContainer objects and allows + * the user to control the size of each view container by dragging a splitter + * bar between them. + * + * Each splitter can also contain child ViewSplitter widgets, allowing + * for a hierarchy of view splitters and containers. + * + * The addContainer() method is used to split the existing view and + * insert a new view container. + * Containers can only be removed from the hierarchy by deleting them. + */ +class ViewSplitter : public QSplitter +{ +Q_OBJECT + +public: + ViewSplitter(QWidget* parent = 0); + + /** + * Locates the child ViewSplitter widget which currently has the focus + * and inserts the container into it. + * + * @param container The container to insert + * @param orientation Specifies whether the view should be split + * horizontally or vertically. If the orientation + * is the same as the ViewSplitter into which the + * container is to be inserted, or if the splitter + * has fewer than two child widgets then the container + * will be added to that splitter. If the orientation + * is different, then a new child splitter + * will be created, into which the container will + * be inserted. + */ + void addContainer( ViewContainer* container , Qt::Orientation orientation ); + + /** Removes a container from the splitter. The container is not deleted. */ + void removeContainer( ViewContainer* container ); + + /** Returns the child ViewSplitter widget which currently has the focus */ + ViewSplitter* activeSplitter() ; + + /** + * Returns the container which currently has the focus or 0 if none + * of the immediate child containers have the focus. This does not + * search through child splitters. activeSplitter() can be used + * to search recursively through child splitters for the splitter + * which currently has the focus. + * + * To find the currently active container, use + * mySplitter->activeSplitter()->activeContainer() where mySplitter + * is the ViewSplitter widget at the top of the hierarchy. + */ + ViewContainer* activeContainer() const; + + /** + * Gives the focus to the active view in the specified container + */ + void setActiveContainer(ViewContainer* container); + + /** + * Returns a list of the containers held by this splitter + */ + QList<ViewContainer*> containers() const {return _containers;} + + /** + * Gives the focus to the active view in the next container + */ + void activateNextContainer(); + + /** + * Changes the size of the specified @p container by a given @p percentage. + * @p percentage may be positive ( in which case the size of the container + * is increased ) or negative ( in which case the size of the container + * is decreased ). + * + * The sizes of the remaining containers are increased or decreased + * uniformly to maintain the width of the splitter. + */ + void adjustContainerSize(ViewContainer* container , int percentage); + + /** + * Gives the focus to the active view in the previous container + */ + void activatePreviousContainer(); + + /** + * Specifies whether the view may be split recursively. + * + * If this is false, all containers will be placed into the same + * top-level splitter. Adding a container with an orientation + * which is different to that specified when adding the previous + * containers will change the orientation for all dividers + * between containers. + * + * If this is true, adding a container to the view splitter with + * an orientation different to the orientation of the previous + * area will result in the previously active container being + * replaced with a new splitter containing the active container + * and the newly added container. + */ + void setRecursiveSplitting(bool recursive); + + /** + * Returns whether the view may be split recursively. + * See setRecursiveSplitting() + */ + bool recursiveSplitting() const; + +signals: + /** Signal emitted when the last child widget is removed from the splitter */ + void empty(ViewSplitter* splitter); + + /** + * Signal emitted when the containers held by this splitter become empty, this + * differs from the empty() signal which is only emitted when all of the containers + * are deleted. This signal is emitted even if there are still container widgets. + * + * TODO: This does not yet work recursively (ie. when splitters inside splitters have empty containers) + */ + void allContainersEmpty(); + +protected: + //virtual void focusEvent(QFocusEvent* event); + +private: + // Adds container to splitter's internal list and + // connects signals and slots + void registerContainer( ViewContainer* container ); + // Removes container from splitter's internal list and + // removes signals and slots + void unregisterContainer( ViewContainer* container ); + + void updateSizes(); + +private slots: + // Called to indicate that a child ViewContainer has been deleted + void containerDestroyed( ViewContainer* object ); + + // Called to indicate that a child ViewContainer is empty + void containerEmpty( ViewContainer* object ); + + // Called to indicate that a child ViewSplitter is empty + // (ie. all child widgets have been deleted) + void childEmpty( ViewSplitter* splitter ); + +private: + QList<ViewContainer*> _containers; + bool _recursiveSplitting; +}; + +} +#endif //VIEWSPLITTER_H +
new file mode 100644 --- /dev/null +++ b/gui/konsole/Vt102Emulation.cpp @@ -0,0 +1,1224 @@ +/* + This file is part of Konsole, an X terminal. + + Copyright 2007-2008 by Robert Knight <robert.knight@gmail.com> + Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de> + + 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 "Vt102Emulation.h" + +// XKB +//#include <config-konsole.h> + +// this allows konsole to be compiled without XKB and XTEST extensions +// even though it might be available on a particular system. +#if defined(AVOID_XKB) + #undef HAVE_XKB +#endif + +#if defined(HAVE_XKB) + void scrolllock_set_off(); + void scrolllock_set_on(); +#endif + +// Standard +#include <stdio.h> +#include <unistd.h> +#include <assert.h> + +// Qt +#include <QtCore/QEvent> +#include <QtGui/QKeyEvent> +#include <QtCore/QByteRef> + +// KDE +//#include <kdebug.h> +//#include <klocale.h> + +// Konsole +#include "KeyboardTranslator.h" +#include "Screen.h" + + +using namespace Konsole; + +Vt102Emulation::Vt102Emulation() + : Emulation(), + _titleUpdateTimer(new QTimer(this)) +{ + _titleUpdateTimer->setSingleShot(true); + QObject::connect(_titleUpdateTimer , SIGNAL(timeout()) , this , SLOT(updateTitle())); + + initTokenizer(); + reset(); +} + +Vt102Emulation::~Vt102Emulation() +{} + +void Vt102Emulation::clearEntireScreen() +{ + _currentScreen->clearEntireScreen(); + bufferedUpdate(); +} + +void Vt102Emulation::reset() +{ + resetTokenizer(); + resetModes(); + resetCharset(0); + _screen[0]->reset(); + resetCharset(1); + _screen[1]->reset(); + setCodec(LocaleCodec); + + bufferedUpdate(); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Processing the incoming byte stream */ +/* */ +/* ------------------------------------------------------------------------- */ + +/* Incoming Bytes Event pipeline + + This section deals with decoding the incoming character stream. + Decoding means here, that the stream is first separated into `tokens' + which are then mapped to a `meaning' provided as operations by the + `Screen' class or by the emulation class itself. + + The pipeline proceeds as follows: + + - Tokenizing the ESC codes (onReceiveChar) + - VT100 code page translation of plain characters (applyCharset) + - Interpretation of ESC codes (processToken) + + The escape codes and their meaning are described in the + technical reference of this program. +*/ + +// Tokens ------------------------------------------------------------------ -- + +/* + Since the tokens are the central notion if this section, we've put them + in front. They provide the syntactical elements used to represent the + terminals operations as byte sequences. + + They are encodes here into a single machine word, so that we can later + switch over them easily. Depending on the token itself, additional + argument variables are filled with parameter values. + + The tokens are defined below: + + - CHR - Printable characters (32..255 but DEL (=127)) + - CTL - Control characters (0..31 but ESC (= 27), DEL) + - ESC - Escape codes of the form <ESC><CHR but `[]()+*#'> + - ESC_DE - Escape codes of the form <ESC><any of `()+*#%'> C + - CSI_PN - Escape codes of the form <ESC>'[' {Pn} ';' {Pn} C + - CSI_PS - Escape codes of the form <ESC>'[' {Pn} ';' ... C + - CSI_PR - Escape codes of the form <ESC>'[' '?' {Pn} ';' ... C + - CSI_PE - Escape codes of the form <ESC>'[' '!' {Pn} ';' ... C + - VT52 - VT52 escape codes + - <ESC><Chr> + - <ESC>'Y'{Pc}{Pc} + - XTE_HA - Xterm window/terminal attribute commands + of the form <ESC>`]' {Pn} `;' {Text} <BEL> + (Note that these are handled differently to the other formats) + + The last two forms allow list of arguments. Since the elements of + the lists are treated individually the same way, they are passed + as individual tokens to the interpretation. Further, because the + meaning of the parameters are names (althought represented as numbers), + they are includes within the token ('N'). + +*/ + +#define TY_CONSTRUCT(T,A,N) ( ((((int)N) & 0xffff) << 16) | ((((int)A) & 0xff) << 8) | (((int)T) & 0xff) ) + +#define TY_CHR( ) TY_CONSTRUCT(0,0,0) +#define TY_CTL(A ) TY_CONSTRUCT(1,A,0) +#define TY_ESC(A ) TY_CONSTRUCT(2,A,0) +#define TY_ESC_CS(A,B) TY_CONSTRUCT(3,A,B) +#define TY_ESC_DE(A ) TY_CONSTRUCT(4,A,0) +#define TY_CSI_PS(A,N) TY_CONSTRUCT(5,A,N) +#define TY_CSI_PN(A ) TY_CONSTRUCT(6,A,0) +#define TY_CSI_PR(A,N) TY_CONSTRUCT(7,A,N) + +#define TY_VT52(A) TY_CONSTRUCT(8,A,0) +#define TY_CSI_PG(A) TY_CONSTRUCT(9,A,0) +#define TY_CSI_PE(A) TY_CONSTRUCT(10,A,0) + +#define MAX_ARGUMENT 4096 + +// Tokenizer --------------------------------------------------------------- -- + +/* The tokenizer's state + + The state is represented by the buffer (tokenBuffer, tokenBufferPos), + and accompanied by decoded arguments kept in (argv,argc). + Note that they are kept internal in the tokenizer. +*/ + +void Vt102Emulation::resetTokenizer() +{ + tokenBufferPos = 0; + argc = 0; + argv[0] = 0; + argv[1] = 0; +} + +void Vt102Emulation::addDigit(int digit) +{ + if (argv[argc] < MAX_ARGUMENT) + argv[argc] = 10*argv[argc] + digit; +} + +void Vt102Emulation::addArgument() +{ + argc = qMin(argc+1,MAXARGS-1); + argv[argc] = 0; +} + +void Vt102Emulation::addToCurrentToken(int cc) +{ + tokenBuffer[tokenBufferPos] = cc; + tokenBufferPos = qMin(tokenBufferPos+1,MAX_TOKEN_LENGTH-1); +} + +// Character Class flags used while decoding + +#define CTL 1 // Control character +#define CHR 2 // Printable character +#define CPN 4 // TODO: Document me +#define DIG 8 // Digit +#define SCS 16 // TODO: Document me +#define GRP 32 // TODO: Document me +#define CPS 64 // Character which indicates end of window resize + // escape sequence '\e[8;<row>;<col>t' + +void Vt102Emulation::initTokenizer() +{ + int i; + quint8* s; + for(i = 0;i < 256; ++i) + charClass[i] = 0; + for(i = 0;i < 32; ++i) + charClass[i] |= CTL; + for(i = 32;i < 256; ++i) + charClass[i] |= CHR; + for(s = (quint8*)"@ABCDGHILMPSTXZcdfry"; *s; ++s) + charClass[*s] |= CPN; + // resize = \e[8;<row>;<col>t + for(s = (quint8*)"t"; *s; ++s) + charClass[*s] |= CPS; + for(s = (quint8*)"0123456789"; *s; ++s) + charClass[*s] |= DIG; + for(s = (quint8*)"()+*%"; *s; ++s) + charClass[*s] |= SCS; + for(s = (quint8*)"()+*#[]%"; *s; ++s) + charClass[*s] |= GRP; + + resetTokenizer(); +} + +/* Ok, here comes the nasty part of the decoder. + + Instead of keeping an explicit state, we deduce it from the + token scanned so far. It is then immediately combined with + the current character to form a scanning decision. + + This is done by the following defines. + + - P is the length of the token scanned so far. + - L (often P-1) is the position on which contents we base a decision. + - C is a character or a group of characters (taken from 'charClass'). + + - 'cc' is the current character + - 's' is a pointer to the start of the token buffer + - 'p' is the current position within the token buffer + + Note that they need to applied in proper order. +*/ + +#define lec(P,L,C) (p == (P) && s[(L)] == (C)) +#define lun( ) (p == 1 && cc >= 32 ) +#define les(P,L,C) (p == (P) && s[L] < 256 && (charClass[s[(L)]] & (C)) == (C)) +#define eec(C) (p >= 3 && cc == (C)) +#define ees(C) (p >= 3 && cc < 256 && (charClass[cc] & (C)) == (C)) +#define eps(C) (p >= 3 && s[2] != '?' && s[2] != '!' && s[2] != '>' && cc < 256 && (charClass[cc] & (C)) == (C)) +#define epp( ) (p >= 3 && s[2] == '?') +#define epe( ) (p >= 3 && s[2] == '!') +#define egt( ) (p >= 3 && s[2] == '>') +#define Xpe (tokenBufferPos >= 2 && tokenBuffer[1] == ']') +#define Xte (Xpe && cc == 7 ) +#define ces(C) (cc < 256 && (charClass[cc] & (C)) == (C) && !Xte) + +#define ESC 27 +#define CNTL(c) ((c)-'@') + +// process an incoming unicode character +void Vt102Emulation::receiveChar(int cc) +{ + if (cc == 127) + return; //VT100: ignore. + + if (ces(CTL)) + { + // DEC HACK ALERT! Control Characters are allowed *within* esc sequences in VT100 + // This means, they do neither a resetTokenizer() nor a pushToToken(). Some of them, do + // of course. Guess this originates from a weakly layered handling of the X-on + // X-off protocol, which comes really below this level. + if (cc == CNTL('X') || cc == CNTL('Z') || cc == ESC) + resetTokenizer(); //VT100: CAN or SUB + if (cc != ESC) + { + processToken(TY_CTL(cc+'@' ),0,0); + return; + } + } + // advance the state + addToCurrentToken(cc); + + int* s = tokenBuffer; + int p = tokenBufferPos; + + if (getMode(MODE_Ansi)) + { + if (lec(1,0,ESC)) { return; } + if (lec(1,0,ESC+128)) { s[0] = ESC; receiveChar('['); return; } + if (les(2,1,GRP)) { return; } + if (Xte ) { processWindowAttributeChange(); resetTokenizer(); return; } + if (Xpe ) { return; } + if (lec(3,2,'?')) { return; } + if (lec(3,2,'>')) { return; } + if (lec(3,2,'!')) { return; } + if (lun( )) { processToken( TY_CHR(), applyCharset(cc), 0); resetTokenizer(); return; } + if (lec(2,0,ESC)) { processToken( TY_ESC(s[1]), 0, 0); resetTokenizer(); return; } + if (les(3,1,SCS)) { processToken( TY_ESC_CS(s[1],s[2]), 0, 0); resetTokenizer(); return; } + if (lec(3,1,'#')) { processToken( TY_ESC_DE(s[2]), 0, 0); resetTokenizer(); return; } + if (eps( CPN)) { processToken( TY_CSI_PN(cc), argv[0],argv[1]); resetTokenizer(); return; } + + // resize = \e[8;<row>;<col>t + if (eps(CPS)) + { + processToken( TY_CSI_PS(cc, argv[0]), argv[1], argv[2]); + resetTokenizer(); + return; + } + + if (epe( )) { processToken( TY_CSI_PE(cc), 0, 0); resetTokenizer(); return; } + if (ees(DIG)) { addDigit(cc-'0'); return; } + if (eec(';')) { addArgument(); return; } + for (int i=0;i<=argc;i++) + { + if (epp()) + processToken( TY_CSI_PR(cc,argv[i]), 0, 0); + else if (egt()) + processToken( TY_CSI_PG(cc), 0, 0); // spec. case for ESC]>0c or ESC]>c + else if (cc == 'm' && argc - i >= 4 && (argv[i] == 38 || argv[i] == 48) && argv[i+1] == 2) + { + // ESC[ ... 48;2;<red>;<green>;<blue> ... m -or- ESC[ ... 38;2;<red>;<green>;<blue> ... m + i += 2; + processToken( TY_CSI_PS(cc, argv[i-2]), COLOR_SPACE_RGB, (argv[i] << 16) | (argv[i+1] << 8) | argv[i+2]); + i += 2; + } + else if (cc == 'm' && argc - i >= 2 && (argv[i] == 38 || argv[i] == 48) && argv[i+1] == 5) + { + // ESC[ ... 48;5;<index> ... m -or- ESC[ ... 38;5;<index> ... m + i += 2; + processToken( TY_CSI_PS(cc, argv[i-2]), COLOR_SPACE_256, argv[i]); + } + else + processToken( TY_CSI_PS(cc,argv[i]), 0, 0); + } + resetTokenizer(); + } + else + { + // VT52 Mode + if (lec(1,0,ESC)) + return; + if (les(1,0,CHR)) + { + processToken( TY_CHR(), s[0], 0); + resetTokenizer(); + return; + } + if (lec(2,1,'Y')) + return; + if (lec(3,1,'Y')) + return; + if (p < 4) + { + processToken( TY_VT52(s[1] ), 0, 0); + resetTokenizer(); + return; + } + processToken( TY_VT52(s[1]), s[2], s[3]); + resetTokenizer(); + return; + } +} +void Vt102Emulation::processWindowAttributeChange() +{ + // Describes the window or terminal session attribute to change + // See Session::UserTitleChange for possible values + int attributeToChange = 0; + int i; + for (i = 2; i < tokenBufferPos && + tokenBuffer[i] >= '0' && + tokenBuffer[i] <= '9'; i++) + { + attributeToChange = 10 * attributeToChange + (tokenBuffer[i]-'0'); + } + + if (tokenBuffer[i] != ';') + { + reportDecodingError(); + return; + } + + QString newValue; + newValue.reserve(tokenBufferPos-i-2); + for (int j = 0; j < tokenBufferPos-i-2; j++) + newValue[j] = tokenBuffer[i+1+j]; + + _pendingTitleUpdates[attributeToChange] = newValue; + _titleUpdateTimer->start(20); +} + +void Vt102Emulation::updateTitle() +{ + QListIterator<int> iter( _pendingTitleUpdates.keys() ); + while (iter.hasNext()) { + int arg = iter.next(); + emit titleChanged( arg , _pendingTitleUpdates[arg] ); + } + _pendingTitleUpdates.clear(); +} + +// Interpreting Codes --------------------------------------------------------- + +/* + Now that the incoming character stream is properly tokenized, + meaning is assigned to them. These are either operations of + the current _screen, or of the emulation class itself. + + The token to be interpreteted comes in as a machine word + possibly accompanied by two parameters. + + Likewise, the operations assigned to, come with up to two + arguments. One could consider to make up a proper table + from the function below. + + The technical reference manual provides more information + about this mapping. +*/ + +void Vt102Emulation::processToken(int token, int p, int q) +{ + switch (token) + { + + case TY_CHR( ) : _currentScreen->displayCharacter (p ); break; //UTF16 + + // 127 DEL : ignored on input + + case TY_CTL('@' ) : /* NUL: ignored */ break; + case TY_CTL('A' ) : /* SOH: ignored */ break; + case TY_CTL('B' ) : /* STX: ignored */ break; + case TY_CTL('C' ) : /* ETX: ignored */ break; + case TY_CTL('D' ) : /* EOT: ignored */ break; + case TY_CTL('E' ) : reportAnswerBack ( ); break; //VT100 + case TY_CTL('F' ) : /* ACK: ignored */ break; + case TY_CTL('G' ) : emit stateSet(NOTIFYBELL); + break; //VT100 + case TY_CTL('H' ) : _currentScreen->backspace ( ); break; //VT100 + case TY_CTL('I' ) : _currentScreen->tab ( ); break; //VT100 + case TY_CTL('J' ) : _currentScreen->newLine ( ); break; //VT100 + case TY_CTL('K' ) : _currentScreen->newLine ( ); break; //VT100 + case TY_CTL('L' ) : _currentScreen->newLine ( ); break; //VT100 + case TY_CTL('M' ) : _currentScreen->toStartOfLine ( ); break; //VT100 + + case TY_CTL('N' ) : useCharset ( 1); break; //VT100 + case TY_CTL('O' ) : useCharset ( 0); break; //VT100 + + case TY_CTL('P' ) : /* DLE: ignored */ break; + case TY_CTL('Q' ) : /* DC1: XON continue */ break; //VT100 + case TY_CTL('R' ) : /* DC2: ignored */ break; + case TY_CTL('S' ) : /* DC3: XOFF halt */ break; //VT100 + case TY_CTL('T' ) : /* DC4: ignored */ break; + case TY_CTL('U' ) : /* NAK: ignored */ break; + case TY_CTL('V' ) : /* SYN: ignored */ break; + case TY_CTL('W' ) : /* ETB: ignored */ break; + case TY_CTL('X' ) : _currentScreen->displayCharacter ( 0x2592); break; //VT100 + case TY_CTL('Y' ) : /* EM : ignored */ break; + case TY_CTL('Z' ) : _currentScreen->displayCharacter ( 0x2592); break; //VT100 + case TY_CTL('[' ) : /* ESC: cannot be seen here. */ break; + case TY_CTL('\\' ) : /* FS : ignored */ break; + case TY_CTL(']' ) : /* GS : ignored */ break; + case TY_CTL('^' ) : /* RS : ignored */ break; + case TY_CTL('_' ) : /* US : ignored */ break; + + case TY_ESC('D' ) : _currentScreen->index ( ); break; //VT100 + case TY_ESC('E' ) : _currentScreen->nextLine ( ); break; //VT100 + case TY_ESC('H' ) : _currentScreen->changeTabStop (true ); break; //VT100 + case TY_ESC('M' ) : _currentScreen->reverseIndex ( ); break; //VT100 + case TY_ESC('Z' ) : reportTerminalType ( ); break; + case TY_ESC('c' ) : reset ( ); break; + + case TY_ESC('n' ) : useCharset ( 2); break; + case TY_ESC('o' ) : useCharset ( 3); break; + case TY_ESC('7' ) : saveCursor ( ); break; + case TY_ESC('8' ) : restoreCursor ( ); break; + + case TY_ESC('=' ) : setMode (MODE_AppKeyPad); break; + case TY_ESC('>' ) : resetMode (MODE_AppKeyPad); break; + case TY_ESC('<' ) : setMode (MODE_Ansi ); break; //VT100 + + case TY_ESC_CS('(', '0') : setCharset (0, '0'); break; //VT100 + case TY_ESC_CS('(', 'A') : setCharset (0, 'A'); break; //VT100 + case TY_ESC_CS('(', 'B') : setCharset (0, 'B'); break; //VT100 + + case TY_ESC_CS(')', '0') : setCharset (1, '0'); break; //VT100 + case TY_ESC_CS(')', 'A') : setCharset (1, 'A'); break; //VT100 + case TY_ESC_CS(')', 'B') : setCharset (1, 'B'); break; //VT100 + + case TY_ESC_CS('*', '0') : setCharset (2, '0'); break; //VT100 + case TY_ESC_CS('*', 'A') : setCharset (2, 'A'); break; //VT100 + case TY_ESC_CS('*', 'B') : setCharset (2, 'B'); break; //VT100 + + case TY_ESC_CS('+', '0') : setCharset (3, '0'); break; //VT100 + case TY_ESC_CS('+', 'A') : setCharset (3, 'A'); break; //VT100 + case TY_ESC_CS('+', 'B') : setCharset (3, 'B'); break; //VT100 + + case TY_ESC_CS('%', 'G') : setCodec (Utf8Codec ); break; //LINUX + case TY_ESC_CS('%', '@') : setCodec (LocaleCodec ); break; //LINUX + + case TY_ESC_DE('3' ) : /* Double height line, top half */ + _currentScreen->setLineProperty( LINE_DOUBLEWIDTH , true ); + _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT , true ); + break; + case TY_ESC_DE('4' ) : /* Double height line, bottom half */ + _currentScreen->setLineProperty( LINE_DOUBLEWIDTH , true ); + _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT , true ); + break; + case TY_ESC_DE('5' ) : /* Single width, single height line*/ + _currentScreen->setLineProperty( LINE_DOUBLEWIDTH , false); + _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT , false); + break; + case TY_ESC_DE('6' ) : /* Double width, single height line*/ + _currentScreen->setLineProperty( LINE_DOUBLEWIDTH , true); + _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT , false); + break; + case TY_ESC_DE('8' ) : _currentScreen->helpAlign ( ); break; + +// resize = \e[8;<row>;<col>t + case TY_CSI_PS('t', 8) : setImageSize( q /* columns */, p /* lines */ ); break; + +// change tab text color : \e[28;<color>t color: 0-16,777,215 + case TY_CSI_PS('t', 28) : emit changeTabTextColorRequest ( p ); break; + + case TY_CSI_PS('K', 0) : _currentScreen->clearToEndOfLine ( ); break; + case TY_CSI_PS('K', 1) : _currentScreen->clearToBeginOfLine ( ); break; + case TY_CSI_PS('K', 2) : _currentScreen->clearEntireLine ( ); break; + case TY_CSI_PS('J', 0) : _currentScreen->clearToEndOfScreen ( ); break; + case TY_CSI_PS('J', 1) : _currentScreen->clearToBeginOfScreen ( ); break; + case TY_CSI_PS('J', 2) : _currentScreen->clearEntireScreen ( ); break; + case TY_CSI_PS('J', 3) : clearHistory(); break; + case TY_CSI_PS('g', 0) : _currentScreen->changeTabStop (false ); break; //VT100 + case TY_CSI_PS('g', 3) : _currentScreen->clearTabStops ( ); break; //VT100 + case TY_CSI_PS('h', 4) : _currentScreen-> setMode (MODE_Insert ); break; + case TY_CSI_PS('h', 20) : setMode (MODE_NewLine ); break; + case TY_CSI_PS('i', 0) : /* IGNORE: attached printer */ break; //VT100 + case TY_CSI_PS('l', 4) : _currentScreen-> resetMode (MODE_Insert ); break; + case TY_CSI_PS('l', 20) : resetMode (MODE_NewLine ); break; + case TY_CSI_PS('s', 0) : saveCursor ( ); break; + case TY_CSI_PS('u', 0) : restoreCursor ( ); break; + + case TY_CSI_PS('m', 0) : _currentScreen->setDefaultRendition ( ); break; + case TY_CSI_PS('m', 1) : _currentScreen-> setRendition (RE_BOLD ); break; //VT100 + case TY_CSI_PS('m', 4) : _currentScreen-> setRendition (RE_UNDERLINE); break; //VT100 + case TY_CSI_PS('m', 5) : _currentScreen-> setRendition (RE_BLINK ); break; //VT100 + case TY_CSI_PS('m', 7) : _currentScreen-> setRendition (RE_REVERSE ); break; + case TY_CSI_PS('m', 10) : /* IGNORED: mapping related */ break; //LINUX + case TY_CSI_PS('m', 11) : /* IGNORED: mapping related */ break; //LINUX + case TY_CSI_PS('m', 12) : /* IGNORED: mapping related */ break; //LINUX + case TY_CSI_PS('m', 22) : _currentScreen->resetRendition (RE_BOLD ); break; + case TY_CSI_PS('m', 24) : _currentScreen->resetRendition (RE_UNDERLINE); break; + case TY_CSI_PS('m', 25) : _currentScreen->resetRendition (RE_BLINK ); break; + case TY_CSI_PS('m', 27) : _currentScreen->resetRendition (RE_REVERSE ); break; + + case TY_CSI_PS('m', 30) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 0); break; + case TY_CSI_PS('m', 31) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 1); break; + case TY_CSI_PS('m', 32) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 2); break; + case TY_CSI_PS('m', 33) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 3); break; + case TY_CSI_PS('m', 34) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 4); break; + case TY_CSI_PS('m', 35) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 5); break; + case TY_CSI_PS('m', 36) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 6); break; + case TY_CSI_PS('m', 37) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 7); break; + + case TY_CSI_PS('m', 38) : _currentScreen->setForeColor (p, q); break; + + case TY_CSI_PS('m', 39) : _currentScreen->setForeColor (COLOR_SPACE_DEFAULT, 0); break; + + case TY_CSI_PS('m', 40) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 0); break; + case TY_CSI_PS('m', 41) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 1); break; + case TY_CSI_PS('m', 42) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 2); break; + case TY_CSI_PS('m', 43) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 3); break; + case TY_CSI_PS('m', 44) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 4); break; + case TY_CSI_PS('m', 45) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 5); break; + case TY_CSI_PS('m', 46) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 6); break; + case TY_CSI_PS('m', 47) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 7); break; + + case TY_CSI_PS('m', 48) : _currentScreen->setBackColor (p, q); break; + + case TY_CSI_PS('m', 49) : _currentScreen->setBackColor (COLOR_SPACE_DEFAULT, 1); break; + + case TY_CSI_PS('m', 90) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 8); break; + case TY_CSI_PS('m', 91) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 9); break; + case TY_CSI_PS('m', 92) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 10); break; + case TY_CSI_PS('m', 93) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 11); break; + case TY_CSI_PS('m', 94) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 12); break; + case TY_CSI_PS('m', 95) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 13); break; + case TY_CSI_PS('m', 96) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 14); break; + case TY_CSI_PS('m', 97) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 15); break; + + case TY_CSI_PS('m', 100) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 8); break; + case TY_CSI_PS('m', 101) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 9); break; + case TY_CSI_PS('m', 102) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 10); break; + case TY_CSI_PS('m', 103) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 11); break; + case TY_CSI_PS('m', 104) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 12); break; + case TY_CSI_PS('m', 105) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 13); break; + case TY_CSI_PS('m', 106) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 14); break; + case TY_CSI_PS('m', 107) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 15); break; + + case TY_CSI_PS('n', 5) : reportStatus ( ); break; + case TY_CSI_PS('n', 6) : reportCursorPosition ( ); break; + case TY_CSI_PS('q', 0) : /* IGNORED: LEDs off */ break; //VT100 + case TY_CSI_PS('q', 1) : /* IGNORED: LED1 on */ break; //VT100 + case TY_CSI_PS('q', 2) : /* IGNORED: LED2 on */ break; //VT100 + case TY_CSI_PS('q', 3) : /* IGNORED: LED3 on */ break; //VT100 + case TY_CSI_PS('q', 4) : /* IGNORED: LED4 on */ break; //VT100 + case TY_CSI_PS('x', 0) : reportTerminalParms ( 2); break; //VT100 + case TY_CSI_PS('x', 1) : reportTerminalParms ( 3); break; //VT100 + + case TY_CSI_PN('@' ) : _currentScreen->insertChars (p ); break; + case TY_CSI_PN('A' ) : _currentScreen->cursorUp (p ); break; //VT100 + case TY_CSI_PN('B' ) : _currentScreen->cursorDown (p ); break; //VT100 + case TY_CSI_PN('C' ) : _currentScreen->cursorRight (p ); break; //VT100 + case TY_CSI_PN('D' ) : _currentScreen->cursorLeft (p ); break; //VT100 + case TY_CSI_PN('G' ) : _currentScreen->setCursorX (p ); break; //LINUX + case TY_CSI_PN('H' ) : _currentScreen->setCursorYX (p, q); break; //VT100 + case TY_CSI_PN('I' ) : _currentScreen->tab (p ); break; + case TY_CSI_PN('L' ) : _currentScreen->insertLines (p ); break; + case TY_CSI_PN('M' ) : _currentScreen->deleteLines (p ); break; + case TY_CSI_PN('P' ) : _currentScreen->deleteChars (p ); break; + case TY_CSI_PN('S' ) : _currentScreen->scrollUp (p ); break; + case TY_CSI_PN('T' ) : _currentScreen->scrollDown (p ); break; + case TY_CSI_PN('X' ) : _currentScreen->eraseChars (p ); break; + case TY_CSI_PN('Z' ) : _currentScreen->backtab (p ); break; + case TY_CSI_PN('c' ) : reportTerminalType ( ); break; //VT100 + case TY_CSI_PN('d' ) : _currentScreen->setCursorY (p ); break; //LINUX + case TY_CSI_PN('f' ) : _currentScreen->setCursorYX (p, q); break; //VT100 + case TY_CSI_PN('r' ) : setMargins (p, q); break; //VT100 + case TY_CSI_PN('y' ) : /* IGNORED: Confidence test */ break; //VT100 + + case TY_CSI_PR('h', 1) : setMode (MODE_AppCuKeys); break; //VT100 + case TY_CSI_PR('l', 1) : resetMode (MODE_AppCuKeys); break; //VT100 + case TY_CSI_PR('s', 1) : saveMode (MODE_AppCuKeys); break; //FIXME + case TY_CSI_PR('r', 1) : restoreMode (MODE_AppCuKeys); break; //FIXME + + case TY_CSI_PR('l', 2) : resetMode (MODE_Ansi ); break; //VT100 + + case TY_CSI_PR('h', 3) : setMode (MODE_132Columns);break; //VT100 + case TY_CSI_PR('l', 3) : resetMode (MODE_132Columns);break; //VT100 + + case TY_CSI_PR('h', 4) : /* IGNORED: soft scrolling */ break; //VT100 + case TY_CSI_PR('l', 4) : /* IGNORED: soft scrolling */ break; //VT100 + + case TY_CSI_PR('h', 5) : _currentScreen-> setMode (MODE_Screen ); break; //VT100 + case TY_CSI_PR('l', 5) : _currentScreen-> resetMode (MODE_Screen ); break; //VT100 + + case TY_CSI_PR('h', 6) : _currentScreen-> setMode (MODE_Origin ); break; //VT100 + case TY_CSI_PR('l', 6) : _currentScreen-> resetMode (MODE_Origin ); break; //VT100 + case TY_CSI_PR('s', 6) : _currentScreen-> saveMode (MODE_Origin ); break; //FIXME + case TY_CSI_PR('r', 6) : _currentScreen->restoreMode (MODE_Origin ); break; //FIXME + + case TY_CSI_PR('h', 7) : _currentScreen-> setMode (MODE_Wrap ); break; //VT100 + case TY_CSI_PR('l', 7) : _currentScreen-> resetMode (MODE_Wrap ); break; //VT100 + case TY_CSI_PR('s', 7) : _currentScreen-> saveMode (MODE_Wrap ); break; //FIXME + case TY_CSI_PR('r', 7) : _currentScreen->restoreMode (MODE_Wrap ); break; //FIXME + + case TY_CSI_PR('h', 8) : /* IGNORED: autorepeat on */ break; //VT100 + case TY_CSI_PR('l', 8) : /* IGNORED: autorepeat off */ break; //VT100 + case TY_CSI_PR('s', 8) : /* IGNORED: autorepeat on */ break; //VT100 + case TY_CSI_PR('r', 8) : /* IGNORED: autorepeat off */ break; //VT100 + + case TY_CSI_PR('h', 9) : /* IGNORED: interlace */ break; //VT100 + case TY_CSI_PR('l', 9) : /* IGNORED: interlace */ break; //VT100 + case TY_CSI_PR('s', 9) : /* IGNORED: interlace */ break; //VT100 + case TY_CSI_PR('r', 9) : /* IGNORED: interlace */ break; //VT100 + + case TY_CSI_PR('h', 12) : /* IGNORED: Cursor blink */ break; //att610 + case TY_CSI_PR('l', 12) : /* IGNORED: Cursor blink */ break; //att610 + case TY_CSI_PR('s', 12) : /* IGNORED: Cursor blink */ break; //att610 + case TY_CSI_PR('r', 12) : /* IGNORED: Cursor blink */ break; //att610 + + case TY_CSI_PR('h', 25) : setMode (MODE_Cursor ); break; //VT100 + case TY_CSI_PR('l', 25) : resetMode (MODE_Cursor ); break; //VT100 + case TY_CSI_PR('s', 25) : saveMode (MODE_Cursor ); break; //VT100 + case TY_CSI_PR('r', 25) : restoreMode (MODE_Cursor ); break; //VT100 + + case TY_CSI_PR('h', 40) : setMode(MODE_Allow132Columns ); break; // XTERM + case TY_CSI_PR('l', 40) : resetMode(MODE_Allow132Columns ); break; // XTERM + + case TY_CSI_PR('h', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM + case TY_CSI_PR('l', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM + case TY_CSI_PR('s', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM + case TY_CSI_PR('r', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM + + case TY_CSI_PR('h', 47) : setMode (MODE_AppScreen); break; //VT100 + case TY_CSI_PR('l', 47) : resetMode (MODE_AppScreen); break; //VT100 + case TY_CSI_PR('s', 47) : saveMode (MODE_AppScreen); break; //XTERM + case TY_CSI_PR('r', 47) : restoreMode (MODE_AppScreen); break; //XTERM + + case TY_CSI_PR('h', 67) : /* IGNORED: DECBKM */ break; //XTERM + case TY_CSI_PR('l', 67) : /* IGNORED: DECBKM */ break; //XTERM + case TY_CSI_PR('s', 67) : /* IGNORED: DECBKM */ break; //XTERM + case TY_CSI_PR('r', 67) : /* IGNORED: DECBKM */ break; //XTERM + + // XTerm defines the following modes: + // SET_VT200_MOUSE 1000 + // SET_VT200_HIGHLIGHT_MOUSE 1001 + // SET_BTN_EVENT_MOUSE 1002 + // SET_ANY_EVENT_MOUSE 1003 + // + + //Note about mouse modes: + //There are four mouse modes which xterm-compatible terminals can support - 1000,1001,1002,1003 + //Konsole currently supports mode 1000 (basic mouse press and release) and mode 1002 (dragging the mouse). + //TODO: Implementation of mouse modes 1001 (something called hilight tracking) and + //1003 (a slight variation on dragging the mouse) + // + + case TY_CSI_PR('h', 1000) : setMode (MODE_Mouse1000); break; //XTERM + case TY_CSI_PR('l', 1000) : resetMode (MODE_Mouse1000); break; //XTERM + case TY_CSI_PR('s', 1000) : saveMode (MODE_Mouse1000); break; //XTERM + case TY_CSI_PR('r', 1000) : restoreMode (MODE_Mouse1000); break; //XTERM + + case TY_CSI_PR('h', 1001) : /* IGNORED: hilite mouse tracking */ break; //XTERM + case TY_CSI_PR('l', 1001) : resetMode (MODE_Mouse1001); break; //XTERM + case TY_CSI_PR('s', 1001) : /* IGNORED: hilite mouse tracking */ break; //XTERM + case TY_CSI_PR('r', 1001) : /* IGNORED: hilite mouse tracking */ break; //XTERM + + case TY_CSI_PR('h', 1002) : setMode (MODE_Mouse1002); break; //XTERM + case TY_CSI_PR('l', 1002) : resetMode (MODE_Mouse1002); break; //XTERM + case TY_CSI_PR('s', 1002) : saveMode (MODE_Mouse1002); break; //XTERM + case TY_CSI_PR('r', 1002) : restoreMode (MODE_Mouse1002); break; //XTERM + + case TY_CSI_PR('h', 1003) : setMode (MODE_Mouse1003); break; //XTERM + case TY_CSI_PR('l', 1003) : resetMode (MODE_Mouse1003); break; //XTERM + case TY_CSI_PR('s', 1003) : saveMode (MODE_Mouse1003); break; //XTERM + case TY_CSI_PR('r', 1003) : restoreMode (MODE_Mouse1003); break; //XTERM + + case TY_CSI_PR('h', 1034) : /* IGNORED: 8bitinput activation */ break; //XTERM + + case TY_CSI_PR('h', 1047) : setMode (MODE_AppScreen); break; //XTERM + case TY_CSI_PR('l', 1047) : _screen[1]->clearEntireScreen(); resetMode(MODE_AppScreen); break; //XTERM + case TY_CSI_PR('s', 1047) : saveMode (MODE_AppScreen); break; //XTERM + case TY_CSI_PR('r', 1047) : restoreMode (MODE_AppScreen); break; //XTERM + + //FIXME: Unitoken: save translations + case TY_CSI_PR('h', 1048) : saveCursor ( ); break; //XTERM + case TY_CSI_PR('l', 1048) : restoreCursor ( ); break; //XTERM + case TY_CSI_PR('s', 1048) : saveCursor ( ); break; //XTERM + case TY_CSI_PR('r', 1048) : restoreCursor ( ); break; //XTERM + + //FIXME: every once new sequences like this pop up in xterm. + // Here's a guess of what they could mean. + case TY_CSI_PR('h', 1049) : saveCursor(); _screen[1]->clearEntireScreen(); setMode(MODE_AppScreen); break; //XTERM + case TY_CSI_PR('l', 1049) : resetMode(MODE_AppScreen); restoreCursor(); break; //XTERM + + //FIXME: weird DEC reset sequence + case TY_CSI_PE('p' ) : /* IGNORED: reset ( ) */ break; + + //FIXME: when changing between vt52 and ansi mode evtl do some resetting. + case TY_VT52('A' ) : _currentScreen->cursorUp ( 1); break; //VT52 + case TY_VT52('B' ) : _currentScreen->cursorDown ( 1); break; //VT52 + case TY_VT52('C' ) : _currentScreen->cursorRight ( 1); break; //VT52 + case TY_VT52('D' ) : _currentScreen->cursorLeft ( 1); break; //VT52 + + case TY_VT52('F' ) : setAndUseCharset (0, '0'); break; //VT52 + case TY_VT52('G' ) : setAndUseCharset (0, 'B'); break; //VT52 + + case TY_VT52('H' ) : _currentScreen->setCursorYX (1,1 ); break; //VT52 + case TY_VT52('I' ) : _currentScreen->reverseIndex ( ); break; //VT52 + case TY_VT52('J' ) : _currentScreen->clearToEndOfScreen ( ); break; //VT52 + case TY_VT52('K' ) : _currentScreen->clearToEndOfLine ( ); break; //VT52 + case TY_VT52('Y' ) : _currentScreen->setCursorYX (p-31,q-31 ); break; //VT52 + case TY_VT52('Z' ) : reportTerminalType ( ); break; //VT52 + case TY_VT52('<' ) : setMode (MODE_Ansi ); break; //VT52 + case TY_VT52('=' ) : setMode (MODE_AppKeyPad); break; //VT52 + case TY_VT52('>' ) : resetMode (MODE_AppKeyPad); break; //VT52 + + case TY_CSI_PG('c' ) : reportSecondaryAttributes( ); break; //VT100 + + default: + reportDecodingError(); + break; + }; +} + +void Vt102Emulation::clearScreenAndSetColumns(int columnCount) +{ + setImageSize(_currentScreen->getLines(),columnCount); + clearEntireScreen(); + setDefaultMargins(); + _currentScreen->setCursorYX(0,0); +} + +void Vt102Emulation::sendString(const char* s , int length) +{ + if ( length >= 0 ) + emit sendData(s,length); + else + emit sendData(s,strlen(s)); +} + +void Vt102Emulation::reportCursorPosition() +{ + char tmp[20]; + sprintf(tmp,"\033[%d;%dR",_currentScreen->getCursorY()+1,_currentScreen->getCursorX()+1); + sendString(tmp); +} + +void Vt102Emulation::reportTerminalType() +{ + // Primary device attribute response (Request was: ^[[0c or ^[[c (from TT321 Users Guide)) + // VT220: ^[[?63;1;2;3;6;7;8c (list deps on emul. capabilities) + // VT100: ^[[?1;2c + // VT101: ^[[?1;0c + // VT102: ^[[?6v + if (getMode(MODE_Ansi)) + sendString("\033[?1;2c"); // I'm a VT100 + else + sendString("\033/Z"); // I'm a VT52 +} + +void Vt102Emulation::reportSecondaryAttributes() +{ + // Seconday device attribute response (Request was: ^[[>0c or ^[[>c) + if (getMode(MODE_Ansi)) + sendString("\033[>0;115;0c"); // Why 115? ;) + else + sendString("\033/Z"); // FIXME I don't think VT52 knows about it but kept for + // konsoles backward compatibility. +} + +void Vt102Emulation::reportTerminalParms(int p) +// DECREPTPARM +{ + char tmp[100]; + sprintf(tmp,"\033[%d;1;1;112;112;1;0x",p); // not really true. + sendString(tmp); +} + +void Vt102Emulation::reportStatus() +{ + sendString("\033[0n"); //VT100. Device status report. 0 = Ready. +} + +void Vt102Emulation::reportAnswerBack() +{ + // FIXME - Test this with VTTEST + // This is really obsolete VT100 stuff. + const char* ANSWER_BACK = ""; + sendString(ANSWER_BACK); +} + +/*! + `cx',`cy' are 1-based. + `eventType' indicates the button pressed (0-2) + or a general mouse release (3). + + eventType represents the kind of mouse action that occurred: + 0 = Mouse button press or release + 1 = Mouse drag +*/ + +void Vt102Emulation::sendMouseEvent( int cb, int cx, int cy , int eventType ) +{ + if (cx < 1 || cy < 1) + return; + + // normal buttons are passed as 0x20 + button, + // mouse wheel (buttons 4,5) as 0x5c + button + if (cb >= 4) + cb += 0x3c; + + //Mouse motion handling + if ((getMode(MODE_Mouse1002) || getMode(MODE_Mouse1003)) && eventType == 1) + cb += 0x20; //add 32 to signify motion event + + char command[20]; + sprintf(command,"\033[M%c%c%c",cb+0x20,cx+0x20,cy+0x20); + sendString(command); +} + +void Vt102Emulation::sendText( const QString& text ) +{ + if (!text.isEmpty()) + { + QKeyEvent event(QEvent::KeyPress, + 0, + Qt::NoModifier, + text); + sendKeyEvent(&event); // expose as a big fat keypress event + } +} +void Vt102Emulation::sendKeyEvent( QKeyEvent* event ) +{ + Qt::KeyboardModifiers modifiers = event->modifiers(); + KeyboardTranslator::States states = KeyboardTranslator::NoState; + + // get current states + if (getMode(MODE_NewLine) ) states |= KeyboardTranslator::NewLineState; + if (getMode(MODE_Ansi) ) states |= KeyboardTranslator::AnsiState; + if (getMode(MODE_AppCuKeys)) states |= KeyboardTranslator::CursorKeysState; + if (getMode(MODE_AppScreen)) states |= KeyboardTranslator::AlternateScreenState; + if (getMode(MODE_AppKeyPad) && (modifiers & Qt::KeypadModifier)) + states |= KeyboardTranslator::ApplicationKeypadState; + + // check flow control state + if (modifiers & Qt::ControlModifier) + { + if (event->key() == Qt::Key_S) + emit flowControlKeyPressed(true); + else if (event->key() == Qt::Key_Q) + emit flowControlKeyPressed(false); + } + + // lookup key binding + if ( _keyTranslator ) + { + KeyboardTranslator::Entry entry = _keyTranslator->findEntry( + event->key() , + modifiers, + states ); + + // send result to terminal + QByteArray textToSend; + + // special handling for the Alt (aka. Meta) modifier. pressing + // Alt+[Character] results in Esc+[Character] being sent + // (unless there is an entry defined for this particular combination + // in the keyboard modifier) + bool wantsAltModifier = entry.modifiers() & entry.modifierMask() & Qt::AltModifier; + bool wantsAnyModifier = entry.state() & + entry.stateMask() & KeyboardTranslator::AnyModifierState; + + if ( modifiers & Qt::AltModifier && !(wantsAltModifier || wantsAnyModifier) + && !event->text().isEmpty() ) + { + textToSend.prepend("\033"); + } + + if ( entry.command() != KeyboardTranslator::NoCommand ) + { + if (entry.command() & KeyboardTranslator::EraseCommand) + textToSend += eraseChar(); + + // TODO command handling + } + else if ( !entry.text().isEmpty() ) + { + textToSend += _codec->fromUnicode(entry.text(true,modifiers)); + } + else + textToSend += _codec->fromUnicode(event->text()); + + sendData( textToSend.constData() , textToSend.length() ); + } + else + { + // print an error message to the terminal if no key translator has been + // set + QString translatorError = i18n("No keyboard translator available. " + "The information needed to convert key presses " + "into characters to send to the terminal " + "is missing."); + reset(); + receiveData( translatorError.toAscii().constData() , translatorError.count() ); + } +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* VT100 Charsets */ +/* */ +/* ------------------------------------------------------------------------- */ + +// Character Set Conversion ------------------------------------------------ -- + +/* + The processing contains a VT100 specific code translation layer. + It's still in use and mainly responsible for the line drawing graphics. + + These and some other glyphs are assigned to codes (0x5f-0xfe) + normally occupied by the latin letters. Since this codes also + appear within control sequences, the extra code conversion + does not permute with the tokenizer and is placed behind it + in the pipeline. It only applies to tokens, which represent + plain characters. + + This conversion it eventually continued in TerminalDisplay.C, since + it might involve VT100 enhanced fonts, which have these + particular glyphs allocated in (0x00-0x1f) in their code page. +*/ + +#define CHARSET _charset[_currentScreen==_screen[1]] + +// Apply current character map. + +unsigned short Vt102Emulation::applyCharset(unsigned short c) +{ + if (CHARSET.graphic && 0x5f <= c && c <= 0x7e) return vt100_graphics[c-0x5f]; + if (CHARSET.pound && c == '#' ) return 0xa3; //This mode is obsolete + return c; +} + +/* + "Charset" related part of the emulation state. + This configures the VT100 charset filter. + + While most operation work on the current _screen, + the following two are different. +*/ + +void Vt102Emulation::resetCharset(int scrno) +{ + _charset[scrno].cu_cs = 0; + strncpy(_charset[scrno].charset,"BBBB",4); + _charset[scrno].sa_graphic = false; + _charset[scrno].sa_pound = false; + _charset[scrno].graphic = false; + _charset[scrno].pound = false; +} + +void Vt102Emulation::setCharset(int n, int cs) // on both screens. +{ + _charset[0].charset[n&3] = cs; useCharset(_charset[0].cu_cs); + _charset[1].charset[n&3] = cs; useCharset(_charset[1].cu_cs); +} + +void Vt102Emulation::setAndUseCharset(int n, int cs) +{ + CHARSET.charset[n&3] = cs; + useCharset(n&3); +} + +void Vt102Emulation::useCharset(int n) +{ + CHARSET.cu_cs = n&3; + CHARSET.graphic = (CHARSET.charset[n&3] == '0'); + CHARSET.pound = (CHARSET.charset[n&3] == 'A'); //This mode is obsolete +} + +void Vt102Emulation::setDefaultMargins() +{ + _screen[0]->setDefaultMargins(); + _screen[1]->setDefaultMargins(); +} + +void Vt102Emulation::setMargins(int t, int b) +{ + _screen[0]->setMargins(t, b); + _screen[1]->setMargins(t, b); +} + +void Vt102Emulation::saveCursor() +{ + CHARSET.sa_graphic = CHARSET.graphic; + CHARSET.sa_pound = CHARSET.pound; //This mode is obsolete + // we are not clear about these + //sa_charset = charsets[cScreen->_charset]; + //sa_charset_num = cScreen->_charset; + _currentScreen->saveCursor(); +} + +void Vt102Emulation::restoreCursor() +{ + CHARSET.graphic = CHARSET.sa_graphic; + CHARSET.pound = CHARSET.sa_pound; //This mode is obsolete + _currentScreen->restoreCursor(); +} + +/* ------------------------------------------------------------------------- */ +/* */ +/* Mode Operations */ +/* */ +/* ------------------------------------------------------------------------- */ + +/* + Some of the emulations state is either added to the state of the screens. + + This causes some scoping problems, since different emulations choose to + located the mode either to the current _screen or to both. + + For strange reasons, the extend of the rendition attributes ranges over + all screens and not over the actual _screen. + + We decided on the precise precise extend, somehow. +*/ + +// "Mode" related part of the state. These are all booleans. + +void Vt102Emulation::resetModes() +{ + // MODE_Allow132Columns is not reset here + // to match Xterm's behaviour (see Xterm's VTReset() function) + + resetMode(MODE_132Columns); saveMode(MODE_132Columns); + resetMode(MODE_Mouse1000); saveMode(MODE_Mouse1000); + resetMode(MODE_Mouse1001); saveMode(MODE_Mouse1001); + resetMode(MODE_Mouse1002); saveMode(MODE_Mouse1002); + resetMode(MODE_Mouse1003); saveMode(MODE_Mouse1003); + + resetMode(MODE_AppScreen); saveMode(MODE_AppScreen); + resetMode(MODE_AppCuKeys); saveMode(MODE_AppCuKeys); + resetMode(MODE_AppKeyPad); saveMode(MODE_AppKeyPad); + resetMode(MODE_NewLine); + setMode(MODE_Ansi); +} + +void Vt102Emulation::setMode(int m) +{ + _currentModes.mode[m] = true; + switch (m) + { + case MODE_132Columns: + if (getMode(MODE_Allow132Columns)) + clearScreenAndSetColumns(132); + else + _currentModes.mode[m] = false; + break; + case MODE_Mouse1000: + case MODE_Mouse1001: + case MODE_Mouse1002: + case MODE_Mouse1003: + emit programUsesMouseChanged(false); + break; + + case MODE_AppScreen : _screen[1]->clearSelection(); + setScreen(1); + break; + } + if (m < MODES_SCREEN || m == MODE_NewLine) + { + _screen[0]->setMode(m); + _screen[1]->setMode(m); + } +} + +void Vt102Emulation::resetMode(int m) +{ + _currentModes.mode[m] = false; + switch (m) + { + case MODE_132Columns: + if (getMode(MODE_Allow132Columns)) + clearScreenAndSetColumns(80); + break; + case MODE_Mouse1000 : + case MODE_Mouse1001 : + case MODE_Mouse1002 : + case MODE_Mouse1003 : + emit programUsesMouseChanged(true); + break; + + case MODE_AppScreen : + _screen[0]->clearSelection(); + setScreen(0); + break; + } + if (m < MODES_SCREEN || m == MODE_NewLine) + { + _screen[0]->resetMode(m); + _screen[1]->resetMode(m); + } +} + +void Vt102Emulation::saveMode(int m) +{ + _savedModes.mode[m] = _currentModes.mode[m]; +} + +void Vt102Emulation::restoreMode(int m) +{ + if (_savedModes.mode[m]) + setMode(m); + else + resetMode(m); +} + +bool Vt102Emulation::getMode(int m) +{ + return _currentModes.mode[m]; +} + +char Vt102Emulation::eraseChar() const +{ + KeyboardTranslator::Entry entry = _keyTranslator->findEntry( + Qt::Key_Backspace, + 0, + 0); + if ( entry.text().count() > 0 ) + return entry.text()[0]; + else + return '\b'; +} + +// print contents of the scan buffer +static void hexdump(int* s, int len) +{ int i; + for (i = 0; i < len; i++) + { + if (s[i] == '\\') + printf("\\\\"); + else + if ((s[i]) > 32 && s[i] < 127) + printf("%c",s[i]); + else + printf("\\%04x(hex)",s[i]); + } +} + +void Vt102Emulation::reportDecodingError() +{ + if (tokenBufferPos == 0 || ( tokenBufferPos == 1 && (tokenBuffer[0] & 0xff) >= 32) ) + return; + printf("Undecodable sequence: "); + hexdump(tokenBuffer,tokenBufferPos); + printf("\n"); +} + +#include "Vt102Emulation.moc" +
new file mode 100644 --- /dev/null +++ b/gui/konsole/Vt102Emulation.h @@ -0,0 +1,191 @@ +/* + This file is part of Konsole, an X terminal. + + Copyright 2007-2008 by Robert Knight <robertknight@gmail.com> + Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de> + + 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 VT102EMULATION_H +#define VT102EMULATION_H + +// Standard Library +#include <stdio.h> + +// Qt +#include <QtGui/QKeyEvent> +#include <QtCore/QHash> +#include <QtCore/QTimer> + +// Konsole +#include "Emulation.h" +#include "Screen.h" + +#define MODE_AppScreen (MODES_SCREEN+0) // Mode #1 +#define MODE_AppCuKeys (MODES_SCREEN+1) // Application cursor keys (DECCKM) +#define MODE_AppKeyPad (MODES_SCREEN+2) // +#define MODE_Mouse1000 (MODES_SCREEN+3) // Send mouse X,Y position on press and release +#define MODE_Mouse1001 (MODES_SCREEN+4) // Use Hilight mouse tracking +#define MODE_Mouse1002 (MODES_SCREEN+5) // Use cell motion mouse tracking +#define MODE_Mouse1003 (MODES_SCREEN+6) // Use all motion mouse tracking +#define MODE_Ansi (MODES_SCREEN+7) // Use US Ascii for character sets G0-G3 (DECANM) +#define MODE_132Columns (MODES_SCREEN+8) // 80 <-> 132 column mode switch (DECCOLM) +#define MODE_Allow132Columns (MODES_SCREEN+9) // Allow DECCOLM mode +#define MODE_total (MODES_SCREEN+10) + +namespace Konsole +{ + +struct CharCodes +{ + // coding info + char charset[4]; // + int cu_cs; // actual charset. + bool graphic; // Some VT100 tricks + bool pound ; // Some VT100 tricks + bool sa_graphic; // saved graphic + bool sa_pound; // saved pound +}; + +/** + * Provides an xterm compatible terminal emulation based on the DEC VT102 terminal. + * A full description of this terminal can be found at http://vt100.net/docs/vt102-ug/ + * + * In addition, various additional xterm escape sequences are supported to provide + * features such as mouse input handling. + * See http://rtfm.etla.org/xterm/ctlseq.html for a description of xterm's escape + * sequences. + * + */ +class Vt102Emulation : public Emulation +{ +Q_OBJECT + +public: + /** Constructs a new emulation */ + Vt102Emulation(); + ~Vt102Emulation(); + + // reimplemented from Emulation + virtual void clearEntireScreen(); + virtual void reset(); + virtual char eraseChar() const; + +public slots: + // reimplemented from Emulation + virtual void sendString(const char*,int length = -1); + virtual void sendText(const QString& text); + virtual void sendKeyEvent(QKeyEvent*); + virtual void sendMouseEvent(int buttons, int column, int line, int eventType); + +protected: + // reimplemented from Emulation + virtual void setMode(int mode); + virtual void resetMode(int mode); + virtual void receiveChar(int cc); + +private slots: + //causes changeTitle() to be emitted for each (int,QString) pair in pendingTitleUpdates + //used to buffer multiple title updates + void updateTitle(); + +private: + unsigned short applyCharset(unsigned short c); + void setCharset(int n, int cs); + void useCharset(int n); + void setAndUseCharset(int n, int cs); + void saveCursor(); + void restoreCursor(); + void resetCharset(int scrno); + + void setMargins(int top, int bottom); + //set margins for all screens back to their defaults + void setDefaultMargins(); + + // returns true if 'mode' is set or false otherwise + bool getMode (int mode); + // saves the current boolean value of 'mode' + void saveMode (int mode); + // restores the boolean value of 'mode' + void restoreMode(int mode); + // resets all modes + // (except MODE_Allow132Columns) + void resetModes(); + + void resetTokenizer(); + #define MAX_TOKEN_LENGTH 80 + void addToCurrentToken(int cc); + int tokenBuffer[MAX_TOKEN_LENGTH]; //FIXME: overflow? + int tokenBufferPos; +#define MAXARGS 15 + void addDigit(int dig); + void addArgument(); + int argv[MAXARGS]; + int argc; + void initTokenizer(); + + // Set of flags for each of the ASCII characters which indicates + // what category they fall into (printable character, control, digit etc.) + // for the purposes of decoding terminal output + int charClass[256]; + + void reportDecodingError(); + + void processToken(int code, int p, int q); + void processWindowAttributeChange(); + + void reportTerminalType(); + void reportSecondaryAttributes(); + void reportStatus(); + void reportAnswerBack(); + void reportCursorPosition(); + void reportTerminalParms(int p); + + void onScrollLock(); + void scrollLock(const bool lock); + + // clears the screen and resizes it to the specified + // number of columns + void clearScreenAndSetColumns(int columnCount); + + CharCodes _charset[2]; + + class TerminalState + { + public: + // Initializes all modes to false + TerminalState() + { memset(&mode,false,MODE_total * sizeof(bool)); } + + bool mode[MODE_total]; + }; + + TerminalState _currentModes; + TerminalState _savedModes; + + //hash table and timer for buffering calls to the session instance + //to update the name of the session + //or window title. + //these calls occur when certain escape sequences are seen in the + //output from the terminal + QHash<int,QString> _pendingTitleUpdates; + QTimer* _titleUpdateTimer; +}; + +} + +#endif // VT102EMULATION_H
new file mode 100644 --- /dev/null +++ b/gui/konsole/WarningBox.cpp @@ -0,0 +1,74 @@ +/* + Copyright 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 "WarningBox.h" + +// Qt +#include <QLabel> +#include <QHBoxLayout> + +// KDE +#include <KIcon> +#include <KColorScheme> +#include <KDebug> + +using namespace Konsole; + +WarningBox::WarningBox(QWidget* parent) +: QFrame(parent) +{ + KColorScheme colorScheme(QPalette::Active); + QColor warningColor = colorScheme.background(KColorScheme::NeutralBackground).color(); + QColor warningColorLight = KColorScheme::shade(warningColor,KColorScheme::LightShade,0.1); + QColor borderColor = KColorScheme::shade(warningColor,KColorScheme::DarkShade,0.15); + QString gradient = "qlineargradient(x1:0, y1:0, x2:0, y2:1," + "stop: 0 %1, stop: 0.6 %1 ,stop: 1.0 %2)"; + gradient = gradient.arg(warningColor.name()).arg(warningColorLight.name()); + + QString styleSheet = "Konsole--WarningBox { background: %1;" + "border: 2px solid %2; }"; + setStyleSheet(styleSheet.arg(gradient).arg(borderColor.name())); + + _label = new QLabel(); + _label->setWordWrap(true); + _label->setAlignment(Qt::AlignLeft); + + QLabel* icon = new QLabel(); + icon->setPixmap(KIcon("dialog-warning").pixmap(QSize(48,48))); + icon->setAlignment(Qt::AlignCenter); + + QHBoxLayout* layout = new QHBoxLayout(this); + layout->addWidget(icon); + layout->addWidget(_label); + layout->setStretchFactor(icon,2); + layout->setStretchFactor(_label,5); +} +void WarningBox::setText(const QString& text) +{ + _label->setText(text); +} +QString WarningBox::text() const +{ + return _label->text(); +} + +#include "WarningBox.moc" + +
new file mode 100644 --- /dev/null +++ b/gui/konsole/WarningBox.h @@ -0,0 +1,56 @@ +/* + Copyright 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. +*/ + +#ifndef WARNINGBOX_H +#define WARNINGBOX_H + +// Qt +#include <QFrame> + +class QLabel; + +namespace Konsole +{ + +/** + * A label which displays a warning message, + * using the appropriate icon from the current icon theme + * and background color from KColorScheme + */ +class WarningBox : public QFrame +{ +Q_OBJECT + +public: + WarningBox(QWidget* parent = 0); + + /** Sets the text displayed in the warning label. */ + void setText(const QString& text); + /** Returns the text displayed in the warning label. */ + QString text() const; + +private: + QLabel* _label; +}; + +} + +#endif // WARNINGBOX_H + +
new file mode 100644 --- /dev/null +++ b/gui/konsole/XKB.cpp @@ -0,0 +1,158 @@ +/* + Originally comes from NumLockX http://dforce.sh.charactervut.characterz/~seli/en/numlockx + + NumLockX + + Copyright 2000-2001 Lubos Lunak <l.lunak@kde.org> + Copyright 2001 Oswald Buddenhagen <ossi@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +****************************************************************************/ + +#include <config-konsole.h> + +#if defined(HAVE_XKB) + #include <QtGui/QX11Info> + + + #include <X11/Xlib.h> + + #define explicit myexplicit + #include <X11/XKBlib.h> + #undef explicit + + #include <X11/keysym.h> + +/* the XKB stuff is based on code created by Oswald Buddenhagen <ossi@kde.org> */ +int xkb_init() +{ + int xkb_opcode, xkb_event, xkb_error; + int xkb_lmaj = XkbMajorVersion; + int xkb_lmin = XkbMinorVersion; + return XkbLibraryVersion( &xkb_lmaj, &xkb_lmin ) + && XkbQueryExtension( QX11Info::display(), &xkb_opcode, &xkb_event, &xkb_error, + &xkb_lmaj, &xkb_lmin ); +} + +#if 0 +// This method doesn't work in all cases. The atom "ScrollLock" doesn't seem +// to exist on all XFree versions (at least it's not here with my 3.3.6) - DF +static unsigned int xkb_mask_modifier( XkbDescPtr xkb, const char *name ) +{ + int i; + if( !xkb || !xkb->names ) + return 0; + + Atom atom = XInternAtom( xkb->dpy, name, true ); + if (atom == None) + return 0; + + for( i = 0; + i < XkbNumVirtualMods; + i++ ) + { + if (atom == xkb->names->vmods[i] ) + { + unsigned int mask; + XkbVirtualModsToReal( xkb, 1 << i, &mask ); + return mask; + } + } + return 0; +} + +static unsigned int xkb_scrolllock_mask() +{ + XkbDescPtr xkb; + if(( xkb = XkbGetKeyboard( QX11Info::display(), XkbAllComponentsMask, XkbUseCoreKbd )) != NULL ) + { + unsigned int mask = xkb_mask_modifier( xkb, "ScrollLock" ); + XkbFreeKeyboard( xkb, 0, True ); + return mask; + } + return 0; +} + +#else +unsigned int xkb_scrolllock_mask() +{ + int scrolllock_mask = 0; + XModifierKeymap* map = XGetModifierMapping( QX11Info::display() ); + KeyCode scrolllock_keycode = XKeysymToKeycode( QX11Info::display(), XK_Scroll_Lock ); + if( scrolllock_keycode == NoSymbol ) { + XFreeModifiermap(map); + return 0; + } + for( int i = 0; + i < 8; + ++i ) + { + if( map->modifiermap[ map->max_keypermod * i ] == scrolllock_keycode ) + scrolllock_mask += 1 << i; + } + + XFreeModifiermap(map); + return scrolllock_mask; +} +#endif + + +unsigned int scrolllock_mask = 0; + +int xkb_set_on() +{ + if (!scrolllock_mask) + { + if( !xkb_init()) + return 0; + scrolllock_mask = xkb_scrolllock_mask(); + if( scrolllock_mask == 0 ) + return 0; + } + XkbLockModifiers ( QX11Info::display(), XkbUseCoreKbd, scrolllock_mask, scrolllock_mask); + return 1; +} + +int xkb_set_off() +{ + if (!scrolllock_mask) + { + if( !xkb_init()) + return 0; + scrolllock_mask = xkb_scrolllock_mask(); + if( scrolllock_mask == 0 ) + return 0; + } + XkbLockModifiers ( QX11Info::display(), XkbUseCoreKbd, scrolllock_mask, 0); + return 1; +} + +void scrolllock_set_on() +{ + xkb_set_on(); +} + +void scrolllock_set_off() +{ + xkb_set_off(); +} + +#endif // defined(HAVE_XKB) +
new file mode 100644 --- /dev/null +++ b/gui/konsole/ZModemDialog.cpp @@ -0,0 +1,69 @@ +/* This file is part of the KDE libraries + * Copyright 2002 Waldo Bastian <bastian@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation; + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + **/ + +// Own +#include "ZModemDialog.h" + +// KDE +#include <KLocale> +#include <KTextEdit> + +using namespace Konsole; + +ZModemDialog::ZModemDialog(QWidget *parent, bool modal, const QString &caption) + : KDialog( parent ) +{ + setObjectName( QLatin1String( "zmodem_progress" ) ); + setModal( modal ); + setCaption( caption ); + setButtons( User1|Close ); + setButtonGuiItem( User1, KGuiItem(i18n("&Stop")) ); + + setDefaultButton( Close ); + setEscapeButton( User1 ); + + enableButton(Close, false); + _textEdit = new KTextEdit(this); + _textEdit->setMinimumSize(400, 100); + _textEdit->setReadOnly(true); + setMainWidget(_textEdit); + connect(this, SIGNAL(user1Clicked()), this, SLOT(slotClose())); + connect(this,SIGNAL(closeClicked()),this,SLOT(slotClose())); +} + +void ZModemDialog::addProgressText(const QString &txt) +{ + QTextCursor cursor = _textEdit->textCursor(); + + cursor.insertBlock(); + cursor.insertText(txt); +} + +void ZModemDialog::transferDone() +{ + enableButton(Close, true); + enableButton(User1, false); +} + +void ZModemDialog::slotClose() +{ + delayedDestruct(); + accept(); +} + +#include "ZModemDialog.moc"
new file mode 100644 --- /dev/null +++ b/gui/konsole/ZModemDialog.h @@ -0,0 +1,54 @@ +/* This file is part of the KDE libraries + * Copyright 2002 Waldo Bastian <bastian@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation; + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + **/ + +#ifndef ZMODEM_DIALOG_H +#define ZMODEM_DIALOG_H + +#include <kdialog.h> + +class KTextEdit; + +namespace Konsole +{ + +class ZModemDialog : public KDialog +{ + Q_OBJECT +public: + ZModemDialog(QWidget *parent, bool modal, const QString &caption); + + /** + * Adds a line of text to the progress window + */ + void addProgressText(const QString &); + + /** + * To indicate the process is finished. + */ + void transferDone(); + +public Q_SLOTS: + void slotClose(); + +private: + KTextEdit* _textEdit; +}; + +} + +#endif
new file mode 100644 --- /dev/null +++ b/gui/konsole/fontembedder.cpp @@ -0,0 +1,119 @@ +/* + This file is part of Konsole, an X terminal. + Copyright 2005 by Maksim Orlovich <maksim@kde.org> + + 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. +*/ + +#include <QtCore/QFile> +#include <QtCore/QTextStream> +#include <stdlib.h> +#include <iostream> +#include <iomanip> + +using namespace std; + +static quint32 charVal(QChar val) +{ + if (val == ' ') + return 0; + else + return 1; +} + +static quint32 readGlyphLine(QTextStream& input) +{ + QString line = input.readLine(); + while (line.length() < 5) + line += ' '; + + quint32 val = charVal(line[0]) | + (charVal(line[1]) << 1) | + (charVal(line[2]) << 2) | + (charVal(line[3]) << 3) | + (charVal(line[4]) << 4); + return val; +} + +static quint32 readGlyph(QTextStream& input) +{ + return readGlyphLine(input) | + (readGlyphLine(input) << 5) | + (readGlyphLine(input) << 10) | + (readGlyphLine(input) << 15) | + (readGlyphLine(input) << 20); +} + +int main(int argc, char **argv) +{ + if (argc < 1) + { + qWarning("usage: fontembedder font.src > font.h"); + exit(1); + } + QFile inFile(argv[1]); + if (!inFile.open(QIODevice::ReadOnly)) + { + qFatal("Can not open %s", argv[1]); + } + + QTextStream input(&inFile); + + quint32 glyphStates[128]; + for (int i = 0; i < 128; ++i) + glyphStates[i] = 0; //nothing.. + + while (!input.atEnd()) + { + QString line = input.readLine(); + line = line.trimmed(); + if (line.isEmpty()) + continue; //Skip empty lines + if (line[0] == '#') + continue; //Skip comments + + //Must be a glyph ID. + int glyph = line.toInt(0, 16); + if ((glyph < 0x2500) || (glyph > 0x257f)) + qFatal("Invalid glyph number"); + + glyph = glyph - 0x2500; + + glyphStates[glyph] = readGlyph(input); + } + + //Output. + cout<<"// WARNING: Autogenerated by \"fontembedder " << argv[1] << "\".\n"; + cout<<"// You probably do not want to hand-edit this!\n\n"; + cout<<"static const quint32 LineChars[] = {\n"; + + //Nicely formatted: 8 per line, 16 lines + for (int line = 0; line < 128; line += 8) + { + cout<<"\t"; + for (int col = line; col < line + 8; ++col) + { + cout<<"0x"<<hex<<setw(8)<<setfill('0')<<glyphStates[col]; + if (col != 127) + cout<<", "; + } + cout<<"\n"; + } + cout<<"};\n"; + return 0; +} + +//kate: indent-width 4; tab-width 4; space-indent on;
new file mode 100644 --- /dev/null +++ b/gui/konsole/kdecore_export.h @@ -0,0 +1,45 @@ +/* This file is part of the KDE project + Copyright (C) 2007 David Faure <faure@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KDECORE_EXPORT_H +#define KDECORE_EXPORT_H + +/* needed for KDE_EXPORT and KDE_IMPORT macros */ +//#include <kdemacros.h> +#define KDE_EXPORT +#define KDE_IMPORT + +#ifndef KDECORE_EXPORT +# if defined(KDELIBS_STATIC_LIBS) + /* No export/import for static libraries */ +# define KDECORE_EXPORT +# elif defined(MAKE_KDECORE_LIB) + /* We are building this library */ +# define KDECORE_EXPORT KDE_EXPORT +# else + /* We are using this library */ +# define KDECORE_EXPORT KDE_IMPORT +# endif +#endif + +# ifndef KDECORE_EXPORT_DEPRECATED +# define KDECORE_EXPORT_DEPRECATED KDE_DEPRECATED KDECORE_EXPORT +# endif + +#endif
new file mode 100644 --- /dev/null +++ b/gui/konsole/konsole_export.h @@ -0,0 +1,67 @@ +/* + This file is part of the KDE project + Copyright (C) 2009 Patrick Spendrin <ps_ml@gmx.de> + + This library 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 library 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 KONSOLE_EXPORT_H +#define KONSOLE_EXPORT_H + +/* needed for KDE_EXPORT macros */ +//#include <kdemacros.h> +#include <QtCore/qglobal.h> +#define KDE_EXPORT +#define KDE_IMPORT + +#ifndef KONSOLEPRIVATE_EXPORT +# if defined(MAKE_KONSOLEPRIVATE_LIB) + /* We are building this library */ +# define KONSOLEPRIVATE_EXPORT KDE_EXPORT +# else + /* We are using this library */ +# define KONSOLEPRIVATE_EXPORT KDE_IMPORT +# endif +#endif + +#include <iostream> +#define kWarning(x) std::cout + +#include <stdio.h> + +//#define i18n +inline QString i18n(char *buff,...) +{ + char msg[2048]; + va_list arglist; + + va_start(arglist,buff); + + snprintf(msg,2048,buff, arglist); + + va_end(arglist); + + return QString(msg); +} + +#define i18nc + + +//#define KDE_fseek ::fseek +//#define KDE_lseek ::lseek + + +#endif
new file mode 100644 --- /dev/null +++ b/gui/konsole/konsole_wcwidth.cpp @@ -0,0 +1,217 @@ +/* $XFree86: xc/programs/xterm/wcwidth.character,v 1.3 2001/07/29 22:08:16 tsi Exp $ */ +/* + * This is an implementation of wcwidth() and wcswidth() as defined in + * "The Single UNIX Specification, Version 2, The Open Group, 1997" + * <http://www.UNIX-systems.org/online.html> + * + * Markus Kuhn -- 2001-01-12 -- public domain + */ + +#include "konsole_wcwidth.h" + +struct interval { + unsigned short first; + unsigned short last; +}; + +/* auxiliary function for binary search in interval table */ +static int bisearch(quint16 ucs, const struct interval *table, int max) { + int min = 0; + int mid; + + if (ucs < table[0].first || ucs > table[max].last) + return 0; + while (max >= min) { + mid = (min + max) / 2; + if (ucs > table[mid].last) + min = mid + 1; + else if (ucs < table[mid].first) + max = mid - 1; + else + return 1; + } + + return 0; +} + + +/* The following functions define the column width of an ISO 10646 + * character as follows: + * + * - The null character (U+0000) has a column width of 0. + * + * - Other C0/C1 control characters and DEL will lead to a return + * value of -1. + * + * - Non-spacing and enclosing combining characters (general + * category code Mn or Me in the Unicode database) have a + * column width of 0. + * + * - Other format characters (general category code Cf in the Unicode + * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. + * + * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) + * have a column width of 0. + * + * - Spacing characters in the East Asian Wide (W) or East Asian + * FullWidth (F) category as defined in Unicode Technical + * Report #11 have a column width of 2. + * + * - All remaining characters (including all printable + * ISO 8859-1 and WGL4 characters, Unicode control characters, + * etc.) have a column width of 1. + * + * This implementation assumes that quint16 characters are encoded + * in ISO 10646. + */ + +int konsole_wcwidth(quint16 ucs) +{ + /* sorted list of non-overlapping intervals of non-spacing characters */ + static const struct interval combining[] = { + { 0x0300, 0x034E }, { 0x0360, 0x0362 }, { 0x0483, 0x0486 }, + { 0x0488, 0x0489 }, { 0x0591, 0x05A1 }, { 0x05A3, 0x05B9 }, + { 0x05BB, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, + { 0x05C4, 0x05C4 }, { 0x064B, 0x0655 }, { 0x0670, 0x0670 }, + { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, + { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, + { 0x07A6, 0x07B0 }, { 0x0901, 0x0902 }, { 0x093C, 0x093C }, + { 0x0941, 0x0948 }, { 0x094D, 0x094D }, { 0x0951, 0x0954 }, + { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, { 0x09BC, 0x09BC }, + { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, { 0x09E2, 0x09E3 }, + { 0x0A02, 0x0A02 }, { 0x0A3C, 0x0A3C }, { 0x0A41, 0x0A42 }, + { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, { 0x0A70, 0x0A71 }, + { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, { 0x0AC1, 0x0AC5 }, + { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, { 0x0B01, 0x0B01 }, + { 0x0B3C, 0x0B3C }, { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, + { 0x0B4D, 0x0B4D }, { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, + { 0x0BC0, 0x0BC0 }, { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, + { 0x0C46, 0x0C48 }, { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, + { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, + { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, { 0x0DCA, 0x0DCA }, + { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, { 0x0E31, 0x0E31 }, + { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, { 0x0EB1, 0x0EB1 }, + { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, { 0x0EC8, 0x0ECD }, + { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, { 0x0F37, 0x0F37 }, + { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, { 0x0F80, 0x0F84 }, + { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, { 0x0F99, 0x0FBC }, + { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, { 0x1032, 0x1032 }, + { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, { 0x1058, 0x1059 }, + { 0x1160, 0x11FF }, { 0x17B7, 0x17BD }, { 0x17C6, 0x17C6 }, + { 0x17C9, 0x17D3 }, { 0x180B, 0x180E }, { 0x18A9, 0x18A9 }, + { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x206A, 0x206F }, + { 0x20D0, 0x20E3 }, { 0x302A, 0x302F }, { 0x3099, 0x309A }, + { 0xFB1E, 0xFB1E }, { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, + { 0xFFF9, 0xFFFB } + }; + + /* test for 8-bit control characters */ + if (ucs == 0) + return 0; + if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) + return -1; + + /* binary search in table of non-spacing characters */ + if (bisearch(ucs, combining, + sizeof(combining) / sizeof(struct interval) - 1)) + return 0; + + /* if we arrive here, ucs is not a combining or C0/C1 control character */ + + return 1 + + (ucs >= 0x1100 && + (ucs <= 0x115f || /* Hangul Jamo init. consonants */ + (ucs >= 0x2e80 && ucs <= 0xa4cf && (ucs & ~0x0011) != 0x300a && + ucs != 0x303f) || /* CJK ... Yi */ + (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ + (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ + (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ + (ucs >= 0xff00 && ucs <= 0xff5f) || /* Fullwidth Forms */ + (ucs >= 0xffe0 && ucs <= 0xffe6) || /* do not compare UINT16 with 0x20000 */ + (ucs >= 0x300a && ucs <= 0x300b) /* Specal character 《 and 》(Unicode Standard Annex #11) || + (ucs >= 0x20000 && ucs <= 0x2ffff) */)); +} + +#if 0 +/* + * The following function is the same as konsole_wcwidth(), except that + * spacing characters in the East Asian Ambiguous (A) category as + * defined in Unicode Technical Report #11 have a column width of 2. + * This experimental variant might be useful for users of CJK legacy + * encodings who want to migrate to UCS. It is not otherwise + * recommended for general use. + */ +int konsole_wcwidth_cjk(quint16 ucs) +{ + /* sorted list of non-overlapping intervals of East Asian Ambiguous + * characters */ + static const struct interval ambiguous[] = { + { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 }, + { 0x00AA, 0x00AA }, { 0x00AD, 0x00AD }, { 0x00B0, 0x00B4 }, + { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 }, + { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 }, + { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED }, + { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA }, + { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 }, + { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B }, + { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 }, + { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 }, + { 0x0148, 0x014A }, { 0x014D, 0x014D }, { 0x0152, 0x0153 }, + { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE }, + { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 }, + { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA }, + { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 }, + { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB }, { 0x02CD, 0x02CD }, + { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB }, { 0x02DD, 0x02DD }, + { 0x0391, 0x03A1 }, { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, + { 0x03C3, 0x03C9 }, { 0x0401, 0x0401 }, { 0x0410, 0x044F }, + { 0x0451, 0x0451 }, { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, + { 0x2018, 0x2019 }, { 0x201C, 0x201D }, { 0x2020, 0x2021 }, + { 0x2025, 0x2027 }, { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, + { 0x2035, 0x2035 }, { 0x203B, 0x203B }, { 0x2074, 0x2074 }, + { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC }, + { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 }, + { 0x2113, 0x2113 }, { 0x2121, 0x2122 }, { 0x2126, 0x2126 }, + { 0x212B, 0x212B }, { 0x2154, 0x2155 }, { 0x215B, 0x215B }, + { 0x215E, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 }, + { 0x2190, 0x2199 }, { 0x21D2, 0x21D2 }, { 0x21D4, 0x21D4 }, + { 0x2200, 0x2200 }, { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, + { 0x220B, 0x220B }, { 0x220F, 0x220F }, { 0x2211, 0x2211 }, + { 0x2215, 0x2215 }, { 0x221A, 0x221A }, { 0x221D, 0x2220 }, + { 0x2223, 0x2223 }, { 0x2225, 0x2225 }, { 0x2227, 0x222C }, + { 0x222E, 0x222E }, { 0x2234, 0x2237 }, { 0x223C, 0x223D }, + { 0x2248, 0x2248 }, { 0x224C, 0x224C }, { 0x2252, 0x2252 }, + { 0x2260, 0x2261 }, { 0x2264, 0x2267 }, { 0x226A, 0x226B }, + { 0x226E, 0x226F }, { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, + { 0x2295, 0x2295 }, { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, + { 0x22BF, 0x22BF }, { 0x2312, 0x2312 }, { 0x2460, 0x24BF }, + { 0x24D0, 0x24E9 }, { 0x2500, 0x254B }, { 0x2550, 0x2574 }, + { 0x2580, 0x258F }, { 0x2592, 0x2595 }, { 0x25A0, 0x25A1 }, + { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 }, { 0x25B6, 0x25B7 }, + { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 }, { 0x25C6, 0x25C8 }, + { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 }, { 0x25E2, 0x25E5 }, + { 0x25EF, 0x25EF }, { 0x2605, 0x2606 }, { 0x2609, 0x2609 }, + { 0x260E, 0x260F }, { 0x261C, 0x261C }, { 0x261E, 0x261E }, + { 0x2640, 0x2640 }, { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, + { 0x2663, 0x2665 }, { 0x2667, 0x266A }, { 0x266C, 0x266D }, + { 0x266F, 0x266F }, { 0x300A, 0x300B }, { 0x301A, 0x301B }, + { 0xE000, 0xF8FF }, { 0xFFFD, 0xFFFD } + }; + + /* binary search in table of non-spacing characters */ + if (bisearch(ucs, ambiguous, + sizeof(ambiguous) / sizeof(struct interval) - 1)) + return 2; + + return konsole_wcwidth(ucs); +} +#endif + +// single byte char: +1, multi byte char: +2 +int string_width( const QString &txt ) +{ + int w = 0; + for ( int i = 0; i < txt.length(); ++i ) + w += konsole_wcwidth( txt[ i ].unicode() ); + return w; +}
new file mode 100644 --- /dev/null +++ b/gui/konsole/konsole_wcwidth.h @@ -0,0 +1,20 @@ +/* $XFree86: xc/programs/xterm/wcwidth.h,v 1.2 2001/06/18 19:09:27 dickey Exp $ */ + +/* Markus Kuhn -- 2001-01-12 -- public domain */ +/* Adaptions for KDE by Waldo Bastian <bastian@kde.org> */ + +#ifndef KONSOLE_WCWIDTH_H +#define KONSOLE_WCWIDTH_H + +// Qt +#include <QtCore/QBool> +#include <QtCore/QString> + +int konsole_wcwidth(quint16 ucs); +#if 0 +int konsole_wcwidth_cjk(Q_UINT16 ucs); +#endif + +int string_width( const QString &txt ); + +#endif
new file mode 100644 --- /dev/null +++ b/gui/konsole/kprocess.cpp @@ -0,0 +1,418 @@ +/* + This file is part of the KDE libraries + + Copyright (C) 2007 Oswald Buddenhagen <ossi@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kprocess_p.h" + +//#include <kstandarddirs.h> +//#include <kshell.h> +//#ifdef Q_OS_WIN +//# include <kshell_p.h> +//#endif + +#include <qfile.h> + +#ifdef Q_OS_WIN +# include <windows.h> +#else +# include <unistd.h> +# include <errno.h> +#endif + +#ifndef Q_OS_WIN +# define STD_OUTPUT_HANDLE 1 +# define STD_ERROR_HANDLE 2 +#endif + +#ifdef _WIN32_WCE +#include <stdio.h> +#endif + +void KProcessPrivate::writeAll(const QByteArray &buf, int fd) +{ +#ifdef Q_OS_WIN +#ifndef _WIN32_WCE + HANDLE h = GetStdHandle(fd); + if (h) { + DWORD wr; + WriteFile(h, buf.data(), buf.size(), &wr, 0); + } +#else + fwrite(buf.data(), 1, buf.size(), (FILE*)fd); +#endif +#else + int off = 0; + do { + int ret = ::write(fd, buf.data() + off, buf.size() - off); + if (ret < 0) { + if (errno != EINTR) + return; + } else { + off += ret; + } + } while (off < buf.size()); +#endif +} + +void KProcessPrivate::forwardStd(KProcess::ProcessChannel good, int fd) +{ + Q_Q(KProcess); + + QProcess::ProcessChannel oc = q->readChannel(); + q->setReadChannel(good); + writeAll(q->readAll(), fd); + q->setReadChannel(oc); +} + +void KProcessPrivate::_k_forwardStdout() +{ +#ifndef _WIN32_WCE + forwardStd(KProcess::StandardOutput, STD_OUTPUT_HANDLE); +#else + forwardStd(KProcess::StandardOutput, (int)stdout); +#endif +} + +void KProcessPrivate::_k_forwardStderr() +{ +#ifndef _WIN32_WCE + forwardStd(KProcess::StandardError, STD_ERROR_HANDLE); +#else + forwardStd(KProcess::StandardError, (int)stderr); +#endif +} + +///////////////////////////// +// public member functions // +///////////////////////////// + +KProcess::KProcess(QObject *parent) : + QProcess(parent), + d_ptr(new KProcessPrivate) +{ + d_ptr->q_ptr = this; + setOutputChannelMode(ForwardedChannels); +} + +KProcess::KProcess(KProcessPrivate *d, QObject *parent) : + QProcess(parent), + d_ptr(d) +{ + d_ptr->q_ptr = this; + setOutputChannelMode(ForwardedChannels); +} + +KProcess::~KProcess() +{ + delete d_ptr; +} + +void KProcess::setOutputChannelMode(OutputChannelMode mode) +{ + Q_D(KProcess); + + d->outputChannelMode = mode; + disconnect(this, SIGNAL(readyReadStandardOutput())); + disconnect(this, SIGNAL(readyReadStandardError())); + switch (mode) { + case OnlyStdoutChannel: + connect(this, SIGNAL(readyReadStandardError()), SLOT(_k_forwardStderr())); + break; + case OnlyStderrChannel: + connect(this, SIGNAL(readyReadStandardOutput()), SLOT(_k_forwardStdout())); + break; + default: + QProcess::setProcessChannelMode((ProcessChannelMode)mode); + return; + } + QProcess::setProcessChannelMode(QProcess::SeparateChannels); +} + +KProcess::OutputChannelMode KProcess::outputChannelMode() const +{ + Q_D(const KProcess); + + return d->outputChannelMode; +} + +void KProcess::setNextOpenMode(QIODevice::OpenMode mode) +{ + Q_D(KProcess); + + d->openMode = mode; +} + +#define DUMMYENV "_KPROCESS_DUMMY_=" + +void KProcess::clearEnvironment() +{ + setEnvironment(QStringList() << QString::fromLatin1(DUMMYENV)); +} + +void KProcess::setEnv(const QString &name, const QString &value, bool overwrite) +{ + QStringList env = environment(); + if (env.isEmpty()) { + env = systemEnvironment(); + env.removeAll(QString::fromLatin1(DUMMYENV)); + } + QString fname(name); + fname.append(QLatin1Char('=')); + for (QStringList::Iterator it = env.begin(); it != env.end(); ++it) + if ((*it).startsWith(fname)) { + if (overwrite) { + *it = fname.append(value); + setEnvironment(env); + } + return; + } + env.append(fname.append(value)); + setEnvironment(env); +} + +void KProcess::unsetEnv(const QString &name) +{ + QStringList env = environment(); + if (env.isEmpty()) { + env = systemEnvironment(); + env.removeAll(QString::fromLatin1(DUMMYENV)); + } + QString fname(name); + fname.append(QLatin1Char('=')); + for (QStringList::Iterator it = env.begin(); it != env.end(); ++it) + if ((*it).startsWith(fname)) { + env.erase(it); + if (env.isEmpty()) + env.append(QString::fromLatin1(DUMMYENV)); + setEnvironment(env); + return; + } +} + +void KProcess::setProgram(const QString &exe, const QStringList &args) +{ + Q_D(KProcess); + + d->prog = exe; + d->args = args; +#ifdef Q_OS_WIN + setNativeArguments(QString()); +#endif +} + +void KProcess::setProgram(const QStringList &argv) +{ + Q_D(KProcess); + + Q_ASSERT( !argv.isEmpty() ); + d->args = argv; + d->prog = d->args.takeFirst(); +#ifdef Q_OS_WIN + setNativeArguments(QString()); +#endif +} + +KProcess &KProcess::operator<<(const QString &arg) +{ + Q_D(KProcess); + + if (d->prog.isEmpty()) + d->prog = arg; + else + d->args << arg; + return *this; +} + +KProcess &KProcess::operator<<(const QStringList &args) +{ + Q_D(KProcess); + + if (d->prog.isEmpty()) + setProgram(args); + else + d->args << args; + return *this; +} + +void KProcess::clearProgram() +{ + Q_D(KProcess); + + d->prog.clear(); + d->args.clear(); +#ifdef Q_OS_WIN + setNativeArguments(QString()); +#endif +} + +void KProcess::setShellCommand(const QString &cmd) +{ + Q_D(KProcess); + + // JPS: commented out because I didn't want to pull in KShell also. It + // seems this is mostly for handling program arguments, which I won't be + // using. + /* + KShell::Errors err; + d->args = KShell::splitArgs( + cmd, KShell::AbortOnMeta | KShell::TildeExpand, &err); + if (err == KShell::NoError && !d->args.isEmpty()) { + d->prog = KStandardDirs::findExe(d->args[0]); + if (!d->prog.isEmpty()) { + d->args.removeFirst(); +#ifdef Q_OS_WIN + setNativeArguments(QString()); +#endif + return; + } + } + */ + + d->args.clear(); + +#ifdef Q_OS_UNIX +// #ifdef NON_FREE // ... as they ship non-POSIX /bin/sh +# if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__) && !defined(__GNU__) + // If /bin/sh is a symlink, we can be pretty sure that it points to a + // POSIX shell - the original bourne shell is about the only non-POSIX + // shell still in use and it is always installed natively as /bin/sh. + /* + d->prog = QFile::symLinkTarget(QString::fromLatin1("/bin/sh")); + if (d->prog.isEmpty()) { + // Try some known POSIX shells. + d->prog = KStandardDirs::findExe(QString::fromLatin1("ksh")); + if (d->prog.isEmpty()) { + d->prog = KStandardDirs::findExe(QString::fromLatin1("ash")); + if (d->prog.isEmpty()) { + d->prog = KStandardDirs::findExe(QString::fromLatin1("bash")); + if (d->prog.isEmpty()) { + d->prog = KStandardDirs::findExe(QString::fromLatin1("zsh")); + if (d->prog.isEmpty()) + // We're pretty much screwed, to be honest ... + d->prog = QString::fromLatin1("/bin/sh"); + } + } + } + } +*/ +# else + d->prog = QString::fromLatin1("/bin/sh"); +# endif + + d->args << QString::fromLatin1("-c") << cmd; +#else // Q_OS_UNIX + // KMacroExpander::expandMacrosShellQuote(), KShell::quoteArg() and + // KShell::joinArgs() may generate these for security reasons. + setEnv(PERCENT_VARIABLE, QLatin1String("%")); + +#ifndef _WIN32_WCE + WCHAR sysdir[MAX_PATH + 1]; + UINT size = GetSystemDirectoryW(sysdir, MAX_PATH + 1); + d->prog = QString::fromUtf16((const ushort *) sysdir, size); + d->prog += QLatin1String("\\cmd.exe"); + setNativeArguments(QLatin1String("/V:OFF /S /C \"") + cmd + QLatin1Char('"')); +#else + d->prog = QLatin1String("\\windows\\cmd.exe"); + setNativeArguments(QLatin1String("/S /C \"") + cmd + QLatin1Char('"')); +#endif +#endif +} + +QStringList KProcess::program() const +{ + Q_D(const KProcess); + + QStringList argv = d->args; + argv.prepend(d->prog); + return argv; +} + +void KProcess::start() +{ + Q_D(KProcess); + + QProcess::start(d->prog, d->args, d->openMode); +} + +int KProcess::execute(int msecs) +{ + start(); + if (!waitForFinished(msecs)) { + kill(); + waitForFinished(-1); + return -2; + } + return (exitStatus() == QProcess::NormalExit) ? exitCode() : -1; +} + +// static +int KProcess::execute(const QString &exe, const QStringList &args, int msecs) +{ + KProcess p; + p.setProgram(exe, args); + return p.execute(msecs); +} + +// static +int KProcess::execute(const QStringList &argv, int msecs) +{ + KProcess p; + p.setProgram(argv); + return p.execute(msecs); +} + +int KProcess::startDetached() +{ + Q_D(KProcess); + + qint64 pid; + if (!QProcess::startDetached(d->prog, d->args, workingDirectory(), &pid)) + return 0; + return (int) pid; +} + +// static +int KProcess::startDetached(const QString &exe, const QStringList &args) +{ + qint64 pid; + if (!QProcess::startDetached(exe, args, QString(), &pid)) + return 0; + return (int) pid; +} + +// static +int KProcess::startDetached(const QStringList &argv) +{ + QStringList args = argv; + QString prog = args.takeFirst(); + return startDetached(prog, args); +} + +int KProcess::pid() const +{ +#ifdef Q_OS_UNIX + return (int) QProcess::pid(); +#else + return QProcess::pid() ? QProcess::pid()->dwProcessId : 0; +#endif +} + +#include "kprocess.moc" +
new file mode 100644 --- /dev/null +++ b/gui/konsole/kprocess.h @@ -0,0 +1,341 @@ +/* + This file is part of the KDE libraries + + Copyright (C) 2007 Oswald Buddenhagen <ossi@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KPROCESS_H +#define KPROCESS_H + +#include "kdecore_export.h" + +#include <QtCore/QProcess> + +class KProcessPrivate; + +/** + * \class KProcess kprocess.h <KProcess> + * + * Child process invocation, monitoring and control. + * + * This class extends QProcess by some useful functionality, overrides + * some defaults with saner values and wraps parts of the API into a more + * accessible one. + * This is the preferred way of spawning child processes in KDE; don't + * use QProcess directly. + * + * @author Oswald Buddenhagen <ossi@kde.org> + **/ +class KDECORE_EXPORT KProcess : public QProcess +{ + Q_OBJECT + Q_DECLARE_PRIVATE(KProcess) + +public: + + /** + * Modes in which the output channels can be opened. + */ + enum OutputChannelMode { + SeparateChannels = QProcess::SeparateChannels, + /**< Standard output and standard error are handled by KProcess + as separate channels */ + MergedChannels = QProcess::MergedChannels, + /**< Standard output and standard error are handled by KProcess + as one channel */ + ForwardedChannels = QProcess::ForwardedChannels, + /**< Both standard output and standard error are forwarded + to the parent process' respective channel */ + OnlyStdoutChannel, + /**< Only standard output is handled; standard error is forwarded */ + OnlyStderrChannel /**< Only standard error is handled; standard output is forwarded */ + }; + + /** + * Constructor + */ + explicit KProcess(QObject *parent = 0); + + /** + * Destructor + */ + virtual ~KProcess(); + + /** + * Set how to handle the output channels of the child process. + * + * The default is ForwardedChannels, which is unlike in QProcess. + * Do not request more than you actually handle, as this output is + * simply lost otherwise. + * + * This function must be called before starting the process. + * + * @param mode the output channel handling mode + */ + void setOutputChannelMode(OutputChannelMode mode); + + /** + * Query how the output channels of the child process are handled. + * + * @return the output channel handling mode + */ + OutputChannelMode outputChannelMode() const; + + /** + * Set the QIODevice open mode the process will be opened in. + * + * This function must be called before starting the process, obviously. + * + * @param mode the open mode. Note that this mode is automatically + * "reduced" according to the channel modes and redirections. + * The default is QIODevice::ReadWrite. + */ + void setNextOpenMode(QIODevice::OpenMode mode); + + /** + * Adds the variable @p name to the process' environment. + * + * This function must be called before starting the process. + * + * @param name the name of the environment variable + * @param value the new value for the environment variable + * @param overwrite if @c false and the environment variable is already + * set, the old value will be preserved + */ + void setEnv(const QString &name, const QString &value, bool overwrite = true); + + /** + * Removes the variable @p name from the process' environment. + * + * This function must be called before starting the process. + * + * @param name the name of the environment variable + */ + void unsetEnv(const QString &name); + + /** + * Empties the process' environment. + * + * Note that LD_LIBRARY_PATH/DYLD_LIBRARY_PATH is automatically added + * on *NIX. + * + * This function must be called before starting the process. + */ + void clearEnvironment(); + + /** + * Set the program and the command line arguments. + * + * This function must be called before starting the process, obviously. + * + * @param exe the program to execute + * @param args the command line arguments for the program, + * one per list element + */ + void setProgram(const QString &exe, const QStringList &args = QStringList()); + + /** + * @overload + * + * @param argv the program to execute and the command line arguments + * for the program, one per list element + */ + void setProgram(const QStringList &argv); + + /** + * Append an element to the command line argument list for this process. + * + * If no executable is set yet, it will be set instead. + * + * For example, doing an "ls -l /usr/local/bin" can be achieved by: + * \code + * KProcess p; + * p << "ls" << "-l" << "/usr/local/bin"; + * ... + * \endcode + * + * This function must be called before starting the process, obviously. + * + * @param arg the argument to add + * @return a reference to this KProcess + */ + KProcess &operator<<(const QString& arg); + + /** + * @overload + * + * @param args the arguments to add + * @return a reference to this KProcess + */ + KProcess &operator<<(const QStringList& args); + + /** + * Clear the program and command line argument list. + */ + void clearProgram(); + + /** + * Set a command to execute through a shell (a POSIX sh on *NIX + * and cmd.exe on Windows). + * + * Using this for anything but user-supplied commands is usually a bad + * idea, as the command's syntax depends on the platform. + * Redirections including pipes, etc. are better handled by the + * respective functions provided by QProcess. + * + * If KProcess determines that the command does not really need a + * shell, it will trasparently execute it without one for performance + * reasons. + * + * This function must be called before starting the process, obviously. + * + * @param cmd the command to execute through a shell. + * The caller must make sure that all filenames etc. are properly + * quoted when passed as argument. Failure to do so often results in + * serious security holes. See KShell::quoteArg(). + */ + void setShellCommand(const QString &cmd); + + /** + * Obtain the currently set program and arguments. + * + * @return a list, the first element being the program, the remaining ones + * being command line arguments to the program. + */ + QStringList program() const; + + /** + * Start the process. + * + * @see QProcess::start(const QString &, const QStringList &, OpenMode) + */ + void start(); + + /** + * Start the process, wait for it to finish, and return the exit code. + * + * This method is roughly equivalent to the sequence: + * <code> + * start(); + * waitForFinished(msecs); + * return exitCode(); + * </code> + * + * Unlike the other execute() variants this method is not static, + * so the process can be parametrized properly and talked to. + * + * @param msecs time to wait for process to exit before killing it + * @return -2 if the process could not be started, -1 if it crashed, + * otherwise its exit code + */ + int execute(int msecs = -1); + + /** + * @overload + * + * @param exe the program to execute + * @param args the command line arguments for the program, + * one per list element + * @param msecs time to wait for process to exit before killing it + * @return -2 if the process could not be started, -1 if it crashed, + * otherwise its exit code + */ + static int execute(const QString &exe, const QStringList &args = QStringList(), int msecs = -1); + + /** + * @overload + * + * @param argv the program to execute and the command line arguments + * for the program, one per list element + * @param msecs time to wait for process to exit before killing it + * @return -2 if the process could not be started, -1 if it crashed, + * otherwise its exit code + */ + static int execute(const QStringList &argv, int msecs = -1); + + /** + * Start the process and detach from it. See QProcess::startDetached() + * for details. + * + * Unlike the other startDetached() variants this method is not static, + * so the process can be parametrized properly. + * @note Currently, only the setProgram()/setShellCommand() and + * setWorkingDirectory() parametrizations are supported. + * + * The KProcess object may be re-used immediately after calling this + * function. + * + * @return the PID of the started process or 0 on error + */ + int startDetached(); + + /** + * @overload + * + * @param exe the program to start + * @param args the command line arguments for the program, + * one per list element + * @return the PID of the started process or 0 on error + */ + static int startDetached(const QString &exe, const QStringList &args = QStringList()); + + /** + * @overload + * + * @param argv the program to start and the command line arguments + * for the program, one per list element + * @return the PID of the started process or 0 on error + */ + static int startDetached(const QStringList &argv); + + /** + * Obtain the process' ID as known to the system. + * + * Unlike with QProcess::pid(), this is a real PID also on Windows. + * + * This function can be called only while the process is running. + * It cannot be applied to detached processes. + * + * @return the process ID + */ + int pid() const; + +protected: + /** + * @internal + */ + KProcess(KProcessPrivate *d, QObject *parent); + + /** + * @internal + */ + KProcessPrivate * const d_ptr; + +private: + // hide those + using QProcess::setReadChannelMode; + using QProcess::readChannelMode; + using QProcess::setProcessChannelMode; + using QProcess::processChannelMode; + + Q_PRIVATE_SLOT(d_func(), void _k_forwardStdout()) + Q_PRIVATE_SLOT(d_func(), void _k_forwardStderr()) +}; + +#endif +
new file mode 100644 --- /dev/null +++ b/gui/konsole/kprocess_p.h @@ -0,0 +1,48 @@ +/* + This file is part of the KDE libraries + + Copyright (C) 2007 Oswald Buddenhagen <ossi@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KPROCESS_P_H +#define KPROCESS_P_H + +#include "kprocess.h" + +class KProcessPrivate { + Q_DECLARE_PUBLIC(KProcess) +protected: + KProcessPrivate() : + openMode(QIODevice::ReadWrite) + { + } + void writeAll(const QByteArray &buf, int fd); + void forwardStd(KProcess::ProcessChannel good, int fd); + void _k_forwardStdout(); + void _k_forwardStderr(); + + QString prog; + QStringList args; + KProcess::OutputChannelMode outputChannelMode; + QIODevice::OpenMode openMode; + + KProcess *q_ptr; +}; + + +#endif
new file mode 100644 --- /dev/null +++ b/gui/konsole/kpty.cpp @@ -0,0 +1,717 @@ +/* + + This file is part of the KDE libraries + Copyright (C) 2002 Waldo Bastian <bastian@kde.org> + Copyright (C) 2002-2003,2007-2008 Oswald Buddenhagen <ossi@kde.org> + Copyright (C) 2010 KDE e.V. <kde-ev-board@kde.org> + Author Adriaan de Groot <groot@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kpty_p.h" + +//#include <config.h> + +#ifdef __sgi +#define __svr4__ +#endif + +#ifdef __osf__ +#define _OSF_SOURCE +#include <float.h> +#endif + +#ifdef _AIX +#define _ALL_SOURCE +#endif + +// __USE_XOPEN isn't defined by default in ICC +// (needed for ptsname(), grantpt() and unlockpt()) +#ifdef __INTEL_COMPILER +# ifndef __USE_XOPEN +# define __USE_XOPEN +# endif +#endif + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/stat.h> +#include <sys/param.h> + +#include <errno.h> +#include <fcntl.h> +#include <time.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <grp.h> + +#if defined(HAVE_PTY_H) +# include <pty.h> +#endif + +#ifdef HAVE_LIBUTIL_H +# include <libutil.h> +#elif defined(HAVE_UTIL_H) +# include <util.h> +#endif + +#define HAVE_UTMPX +#define _UTMPX_COMPAT + +#ifdef HAVE_UTEMPTER +extern "C" { +# include <utempter.h> +} +#else +# include <utmp.h> +# ifdef HAVE_UTMPX +# include <utmpx.h> +# endif +# if !defined(_PATH_UTMPX) && defined(_UTMPX_FILE) +# define _PATH_UTMPX _UTMPX_FILE +# endif +# if !defined(_PATH_WTMPX) && defined(_WTMPX_FILE) +# define _PATH_WTMPX _WTMPX_FILE +# endif +#endif + +/* for HP-UX (some versions) the extern C is needed, and for other + platforms it doesn't hurt */ +extern "C" { +#include <termios.h> +#if defined(HAVE_TERMIO_H) +# include <termio.h> // struct winsize on some systems +#endif +} + +#if defined (_HPUX_SOURCE) +# define _TERMIOS_INCLUDED +# include <bsdtty.h> +#endif + +#ifdef HAVE_SYS_STROPTS_H +# include <sys/stropts.h> // Defines I_PUSH +# define _NEW_TTY_CTRL +#endif + +#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || defined (__bsdi__) || defined(__APPLE__) || defined (__DragonFly__) +# define _tcgetattr(fd, ttmode) ioctl(fd, TIOCGETA, (char *)ttmode) +#else +# if defined(_HPUX_SOURCE) || defined(__Lynx__) || defined (__CYGWIN__) || defined(__sun) +# define _tcgetattr(fd, ttmode) tcgetattr(fd, ttmode) +# else +# define _tcgetattr(fd, ttmode) ioctl(fd, TCGETS, (char *)ttmode) +# endif +#endif + +#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || defined (__bsdi__) || defined(__APPLE__) || defined (__DragonFly__) +# define _tcsetattr(fd, ttmode) ioctl(fd, TIOCSETA, (char *)ttmode) +#else +# if defined(_HPUX_SOURCE) || defined(__CYGWIN__) || defined(__sun) +# define _tcsetattr(fd, ttmode) tcsetattr(fd, TCSANOW, ttmode) +# else +# define _tcsetattr(fd, ttmode) ioctl(fd, TCSETS, (char *)ttmode) +# endif +#endif + +//#include <kdebug.h> +//#include <kstandarddirs.h> // findExe +//#include <kde_file.h> + +#include <QtCore/Q_PID> + +#define TTY_GROUP "tty" + +#ifndef PATH_MAX +# ifdef MAXPATHLEN +# define PATH_MAX MAXPATHLEN +# else +# define PATH_MAX 1024 +# endif +#endif + +// Fix: Needed to build on Ubuntu. +#undef HAVE_UTMPX + +/////////////////////// +// private functions // +/////////////////////// + +////////////////// +// private data // +////////////////// + +KPtyPrivate::KPtyPrivate(KPty* parent) : + masterFd(-1), slaveFd(-1), ownMaster(true), q_ptr(parent) +{ +} + +KPtyPrivate::~KPtyPrivate() +{ +} + +#ifndef HAVE_OPENPTY +bool KPtyPrivate::chownpty(bool grant) +{ + return !QProcess::execute(KStandardDirs::findExe("kgrantpty"), + QStringList() << (grant?"--grant":"--revoke") << QString::number(masterFd)); +} +#endif + +///////////////////////////// +// public member functions // +///////////////////////////// + +KPty::KPty() : + d_ptr(new KPtyPrivate(this)) +{ +} + +KPty::KPty(KPtyPrivate *d) : + d_ptr(d) +{ + d_ptr->q_ptr = this; +} + +KPty::~KPty() +{ + close(); + delete d_ptr; +} + +bool KPty::open() +{ + Q_D(KPty); + + if (d->masterFd >= 0) + return true; + + d->ownMaster = true; + + QByteArray ptyName; + + // Find a master pty that we can open //////////////////////////////// + + // Because not all the pty animals are created equal, they want to + // be opened by several different methods. + + // We try, as we know them, one by one. + +#ifdef HAVE_OPENPTY + + char ptsn[PATH_MAX]; + if (::openpty( &d->masterFd, &d->slaveFd, ptsn, 0, 0)) + { + d->masterFd = -1; + d->slaveFd = -1; + //kWarning(175) << "Can't open a pseudo teletype"; + return false; + } + d->ttyName = ptsn; + +#else + +#ifdef HAVE__GETPTY // irix + + char *ptsn = _getpty(&d->masterFd, O_RDWR|O_NOCTTY, S_IRUSR|S_IWUSR, 0); + if (ptsn) { + d->ttyName = ptsn; + goto grantedpt; + } + +#elif defined(HAVE_PTSNAME) || defined(TIOCGPTN) + +#ifdef HAVE_POSIX_OPENPT + d->masterFd = ::posix_openpt(O_RDWR|O_NOCTTY); +#elif defined(HAVE_GETPT) + d->masterFd = ::getpt(); +#elif defined(PTM_DEVICE) + //d->masterFd = KDE_open(PTM_DEVICE, O_RDWR|O_NOCTTY); +d->masterFd = ::open(PTM_DEVICE, O_RDWR|O_NOCTTY); +#else +# error No method to open a PTY master detected. +#endif + if (d->masterFd >= 0) + { +#ifdef HAVE_PTSNAME + char *ptsn = ptsname(d->masterFd); + if (ptsn) { + d->ttyName = ptsn; +#else + int ptyno; + if (!ioctl(d->masterFd, TIOCGPTN, &ptyno)) { + char buf[32]; + sprintf(buf, "/dev/pts/%d", ptyno); + d->ttyName = buf; +#endif +#ifdef HAVE_GRANTPT + if (!grantpt(d->masterFd)) + goto grantedpt; +#else + goto gotpty; +#endif + } + ::close(d->masterFd); + d->masterFd = -1; + } +#endif // HAVE_PTSNAME || TIOCGPTN + + // Linux device names, FIXME: Trouble on other systems? + for (const char* s3 = "pqrstuvwxyzabcde"; *s3; s3++) + { + for (const char* s4 = "0123456789abcdef"; *s4; s4++) + { + ptyName = QString().sprintf("/dev/pty%c%c", *s3, *s4).toAscii(); + d->ttyName = QString().sprintf("/dev/tty%c%c", *s3, *s4).toAscii(); + + d->masterFd = ::open(ptyName.data(), O_RDWR); + if (d->masterFd >= 0) + { +#ifdef Q_OS_SOLARIS + /* Need to check the process group of the pty. + * If it exists, then the slave pty is in use, + * and we need to get another one. + */ + int pgrp_rtn; + if (ioctl(d->masterFd, TIOCGPGRP, &pgrp_rtn) == 0 || errno != EIO) { + ::close(d->masterFd); + d->masterFd = -1; + continue; + } +#endif /* Q_OS_SOLARIS */ + if (!access(d->ttyName.data(),R_OK|W_OK)) // checks availability based on permission bits + { + if (!geteuid()) + { + struct group* p = getgrnam(TTY_GROUP); + if (!p) + p = getgrnam("wheel"); + gid_t gid = p ? p->gr_gid : getgid (); + + chown(d->ttyName.data(), getuid(), gid); + chmod(d->ttyName.data(), S_IRUSR|S_IWUSR|S_IWGRP); + } + goto gotpty; + } + ::close(d->masterFd); + d->masterFd = -1; + } + } + } + + //kWarning(175) << "Can't open a pseudo teletype"; + return false; + + gotpty: + KDE_struct_stat st; + if (KDE_stat(d->ttyName.data(), &st)) + return false; // this just cannot happen ... *cough* Yeah right, I just + // had it happen when pty #349 was allocated. I guess + // there was some sort of leak? I only had a few open. + if (((st.st_uid != getuid()) || + (st.st_mode & (S_IRGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH))) && + !d->chownpty(true)) + { + + /*kWarning(175) + << "chownpty failed for device " << ptyName << "::" << d->ttyName + << "\nThis means the communication can be eavesdropped." << endl; +*/ +} + + grantedpt: + +#ifdef HAVE_REVOKE + revoke(d->ttyName.data()); +#endif + +#ifdef HAVE_UNLOCKPT + unlockpt(d->masterFd); +#elif defined(TIOCSPTLCK) + int flag = 0; + ioctl(d->masterFd, TIOCSPTLCK, &flag); +#endif + + d->slaveFd = ::open(d->ttyName.data(), O_RDWR | O_NOCTTY); + if (d->slaveFd < 0) + { + //kWarning(175) << "Can't open slave pseudo teletype"; + ::close(d->masterFd); + d->masterFd = -1; + return false; + } + +#if (defined(__svr4__) || defined(__sgi__) || defined(Q_OS_SOLARIS)) + // Solaris uses STREAMS for terminal handling. It is possible + // for the pty handling modules to be left off the stream; in that + // case push them on. ioctl(fd, I_FIND, ...) is documented to return + // 1 if the module is on the stream already. + { + static const char *pt = "ptem"; + static const char *ld = "ldterm"; + if (ioctl(d->slaveFd, I_FIND, pt) == 0) + ioctl(d->slaveFd, I_PUSH, pt); + if (ioctl(d->slaveFd, I_FIND, ld) == 0) + ioctl(d->slaveFd, I_PUSH, ld); + } +#endif + +#endif /* HAVE_OPENPTY */ + + fcntl(d->masterFd, F_SETFD, FD_CLOEXEC); + fcntl(d->slaveFd, F_SETFD, FD_CLOEXEC); + + return true; +} + +bool KPty::open(int fd) +{ +#if !defined(HAVE_PTSNAME) && !defined(TIOCGPTN) + //kWarning(175) << "Unsupported attempt to open pty with fd" << fd; + return false; +#else + Q_D(KPty); + + if (d->masterFd >= 0) { + //kWarning(175) << "Attempting to open an already open pty"; + return false; + } + + d->ownMaster = false; + +# ifdef HAVE_PTSNAME + char *ptsn = ptsname(fd); + if (ptsn) { + d->ttyName = ptsn; +# else + int ptyno; + if (!ioctl(fd, TIOCGPTN, &ptyno)) { + char buf[32]; + sprintf(buf, "/dev/pts/%d", ptyno); + d->ttyName = buf; +# endif + } else { + //kWarning(175) << "Failed to determine pty slave device for fd" << fd; + return false; + } + + d->masterFd = fd; + if (!openSlave()) { + d->masterFd = -1; + return false; + } + + return true; +#endif +} + +void KPty::closeSlave() +{ + Q_D(KPty); + + if (d->slaveFd < 0) + return; + ::close(d->slaveFd); + d->slaveFd = -1; +} + +bool KPty::openSlave() +{ + Q_D(KPty); + + if (d->slaveFd >= 0) + return true; + if (d->masterFd < 0) { + //kWarning(175) << "Attempting to open pty slave while master is closed"; + return false; + } + d->slaveFd = ::open(d->ttyName.data(), O_RDWR | O_NOCTTY); + if (d->slaveFd < 0) { + //kWarning(175) << "Can't open slave pseudo teletype"; + return false; + } + fcntl(d->slaveFd, F_SETFD, FD_CLOEXEC); + return true; +} + +void KPty::close() +{ + Q_D(KPty); + + if (d->masterFd < 0) + return; + closeSlave(); + if (d->ownMaster) { +#ifndef HAVE_OPENPTY + // don't bother resetting unix98 pty, it will go away after closing master anyway. + if (memcmp(d->ttyName.data(), "/dev/pts/", 9)) { + if (!geteuid()) { + struct stat st; + if (!stat(d->ttyName.data(), &st)) { + chown(d->ttyName.data(), 0, st.st_gid == getgid() ? 0 : -1); + chmod(d->ttyName.data(), S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); + } + } else { + fcntl(d->masterFd, F_SETFD, 0); + d->chownpty(false); + } + } +#endif + ::close(d->masterFd); + } + d->masterFd = -1; +} + +void KPty::setCTty() +{ + Q_D(KPty); + + // Setup job control ////////////////////////////////// + + // Become session leader, process group leader, + // and get rid of the old controlling terminal. + setsid(); + + // make our slave pty the new controlling terminal. +#ifdef TIOCSCTTY + ioctl(d->slaveFd, TIOCSCTTY, 0); +#else + // __svr4__ hack: the first tty opened after setsid() becomes controlling tty + ::close(open(d->ttyName, O_WRONLY, 0)); +#endif + + // make our new process group the foreground group on the pty + int pgrp = getpid(); +#if defined(_POSIX_VERSION) || defined(__svr4__) + tcsetpgrp(d->slaveFd, pgrp); +#elif defined(TIOCSPGRP) + ioctl(d->slaveFd, TIOCSPGRP, (char *)&pgrp); +#endif +} + +void KPty::login(const char *user, const char *remotehost) +{ +#ifdef HAVE_UTEMPTER + Q_D(KPty); + + addToUtmp(d->ttyName, remotehost, d->masterFd); + Q_UNUSED(user); +#else +# ifdef HAVE_UTMPX + struct utmpx l_struct; +# else + struct utmp l_struct; +# endif + memset(&l_struct, 0, sizeof(l_struct)); + // note: strncpy without terminators _is_ correct here. man 4 utmp + + if (user) + strncpy(l_struct.ut_name, user, sizeof(l_struct.ut_name)); + + if (remotehost) { + strncpy(l_struct.ut_host, remotehost, sizeof(l_struct.ut_host)); +# ifdef HAVE_STRUCT_UTMP_UT_SYSLEN + l_struct.ut_syslen = qMin(strlen(remotehost), sizeof(l_struct.ut_host)); +# endif + } + +# ifndef __GLIBC__ + Q_D(KPty); + const char *str_ptr = d->ttyName.data(); + if (!memcmp(str_ptr, "/dev/", 5)) + str_ptr += 5; + strncpy(l_struct.ut_line, str_ptr, sizeof(l_struct.ut_line)); +# ifdef HAVE_STRUCT_UTMP_UT_ID + strncpy(l_struct.ut_id, + str_ptr + strlen(str_ptr) - sizeof(l_struct.ut_id), + sizeof(l_struct.ut_id)); +# endif +# endif + +# ifdef HAVE_UTMPX + gettimeofday(&l_struct.ut_tv, 0); +# else + l_struct.ut_time = time(0); +# endif + +# ifdef HAVE_LOGIN +# ifdef HAVE_LOGINX + ::loginx(&l_struct); +# else + ::login(&l_struct); +# endif +# else +# ifdef HAVE_STRUCT_UTMP_UT_TYPE + l_struct.ut_type = USER_PROCESS; +# endif +# ifdef HAVE_STRUCT_UTMP_UT_PID + l_struct.ut_pid = getpid(); +# ifdef HAVE_STRUCT_UTMP_UT_SESSION + l_struct.ut_session = getsid(0); +# endif +# endif +# ifdef HAVE_UTMPX + utmpxname(_PATH_UTMPX); + setutxent(); + pututxline(&l_struct); + endutxent(); + //updwtmpx(_PATH_WTMPX, &l_struct); +# else + utmpname(_PATH_UTMP); + setutent(); + pututline(&l_struct); + endutent(); + updwtmp(_PATH_WTMP, &l_struct); +# endif +# endif +#endif +} + +void KPty::logout() +{ +#ifdef HAVE_UTEMPTER + Q_D(KPty); + + removeLineFromUtmp(d->ttyName, d->masterFd); +#else + Q_D(KPty); + + const char *str_ptr = d->ttyName.data(); + if (!memcmp(str_ptr, "/dev/", 5)) + str_ptr += 5; +# ifdef __GLIBC__ + else { + const char *sl_ptr = strrchr(str_ptr, '/'); + if (sl_ptr) + str_ptr = sl_ptr + 1; + } +# endif +# ifdef HAVE_LOGIN +# ifdef HAVE_LOGINX + ::logoutx(str_ptr, 0, DEAD_PROCESS); +# else + ::logout(str_ptr); +# endif +# else +# ifdef HAVE_UTMPX + struct utmpx l_struct, *ut; +# else + struct utmp l_struct, *ut; +# endif + memset(&l_struct, 0, sizeof(l_struct)); + + strncpy(l_struct.ut_line, str_ptr, sizeof(l_struct.ut_line)); + +# ifdef HAVE_UTMPX + utmpxname(_PATH_UTMPX); + setutxent(); + if ((ut = getutxline(&l_struct))) { +# else + utmpname(_PATH_UTMP); + setutent(); + if ((ut = getutline(&l_struct))) { +# endif + memset(ut->ut_name, 0, sizeof(*ut->ut_name)); + memset(ut->ut_host, 0, sizeof(*ut->ut_host)); +# ifdef HAVE_STRUCT_UTMP_UT_SYSLEN + ut->ut_syslen = 0; +# endif +# ifdef HAVE_STRUCT_UTMP_UT_TYPE + ut->ut_type = DEAD_PROCESS; +# endif +# ifdef HAVE_UTMPX + gettimeofday(&(ut->ut_tv), 0); + pututxline(ut); + } + endutxent(); +# else + ut->ut_time = time(0); + pututline(ut); + } + endutent(); +# endif +# endif +#endif +} + +bool KPty::tcGetAttr(struct ::termios *ttmode) const +{ + Q_D(const KPty); + +#ifdef Q_OS_SOLARIS + if (_tcgetattr(d->slaveFd, ttmode) == 0) return true; +#endif + return _tcgetattr(d->masterFd, ttmode) == 0; +} + +bool KPty::tcSetAttr(struct ::termios *ttmode) +{ + Q_D(KPty); + +#ifdef Q_OS_SOLARIS + if (_tcsetattr(d->slaveFd, ttmode) == 0) return true; +#endif + return _tcsetattr(d->masterFd, ttmode) == 0; +} + +bool KPty::setWinSize(int lines, int columns) +{ + Q_D(KPty); + + struct winsize winSize; + memset(&winSize, 0, sizeof(winSize)); + winSize.ws_row = (unsigned short)lines; + winSize.ws_col = (unsigned short)columns; + return ioctl(d->masterFd, TIOCSWINSZ, (char *)&winSize) == 0; +} + +bool KPty::setEcho(bool echo) +{ + struct ::termios ttmode; + if (!tcGetAttr(&ttmode)) + return false; + if (!echo) + ttmode.c_lflag &= ~ECHO; + else + ttmode.c_lflag |= ECHO; + return tcSetAttr(&ttmode); +} + +const char *KPty::ttyName() const +{ + Q_D(const KPty); + + return d->ttyName.data(); +} + +int KPty::masterFd() const +{ + Q_D(const KPty); + + return d->masterFd; +} + +int KPty::slaveFd() const +{ + Q_D(const KPty); + + return d->slaveFd; +}
new file mode 100644 --- /dev/null +++ b/gui/konsole/kpty.h @@ -0,0 +1,205 @@ +/* This file is part of the KDE libraries + + Copyright (C) 2003,2007 Oswald Buddenhagen <ossi@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef kpty_h +#define kpty_h + +#include "kpty_export.h" + +struct KPtyPrivate; +struct termios; + +/** + * Provides primitives for opening & closing a pseudo TTY pair, assigning the + * controlling TTY, utmp registration and setting various terminal attributes. + */ +class KPTY_EXPORT KPty { + Q_DECLARE_PRIVATE(KPty) + +public: + + /** + * Constructor + */ + KPty(); + + /** + * Destructor: + * + * If the pty is still open, it will be closed. Note, however, that + * an utmp registration is @em not undone. + */ + ~KPty(); + + /** + * Create a pty master/slave pair. + * + * @return true if a pty pair was successfully opened + */ + bool open(); + + /** + * Open using an existing pty master. + * + * @param fd an open pty master file descriptor. + * The ownership of the fd remains with the caller; + * it will not be automatically closed at any point. + * @return true if a pty pair was successfully opened + */ + bool open(int fd); + + /** + * Close the pty master/slave pair. + */ + void close(); + + /** + * Close the pty slave descriptor. + * + * When creating the pty, KPty also opens the slave and keeps it open. + * Consequently the master will never receive an EOF notification. + * Usually this is the desired behavior, as a closed pty slave can be + * reopened any time - unlike a pipe or socket. However, in some cases + * pipe-alike behavior might be desired. + * + * After this function was called, slaveFd() and setCTty() cannot be + * used. + */ + void closeSlave(); + + /** + * Open the pty slave descriptor. + * + * This undoes the effect of closeSlave(). + * + * @return true if the pty slave was successfully opened + */ + bool openSlave(); + + /** + * Creates a new session and process group and makes this pty the + * controlling tty. + */ + void setCTty(); + + /** + * Creates an utmp entry for the tty. + * This function must be called after calling setCTty and + * making this pty the stdin. + * @param user the user to be logged on + * @param remotehost the host from which the login is coming. This is + * @em not the local host. For remote logins it should be the hostname + * of the client. For local logins from inside an X session it should + * be the name of the X display. Otherwise it should be empty. + */ + void login(const char *user = 0, const char *remotehost = 0); + + /** + * Removes the utmp entry for this tty. + */ + void logout(); + + /** + * Wrapper around tcgetattr(3). + * + * This function can be used only while the PTY is open. + * You will need an #include <termios.h> to do anything useful + * with it. + * + * @param ttmode a pointer to a termios structure. + * Note: when declaring ttmode, @c struct @c ::termios must be used - + * without the '::' some version of HP-UX thinks, this declares + * the struct in your class, in your method. + * @return @c true on success, false otherwise + */ + bool tcGetAttr(struct ::termios *ttmode) const; + + /** + * Wrapper around tcsetattr(3) with mode TCSANOW. + * + * This function can be used only while the PTY is open. + * + * @param ttmode a pointer to a termios structure. + * @return @c true on success, false otherwise. Note that success means + * that @em at @em least @em one attribute could be set. + */ + bool tcSetAttr(struct ::termios *ttmode); + + /** + * Change the logical (screen) size of the pty. + * The default is 24 lines by 80 columns. + * + * This function can be used only while the PTY is open. + * + * @param lines the number of rows + * @param columns the number of columns + * @return @c true on success, false otherwise + */ + bool setWinSize(int lines, int columns); + + /** + * Set whether the pty should echo input. + * + * Echo is on by default. + * If the output of automatically fed (non-interactive) PTY clients + * needs to be parsed, disabling echo often makes it much simpler. + * + * This function can be used only while the PTY is open. + * + * @param echo true if input should be echoed. + * @return @c true on success, false otherwise + */ + bool setEcho(bool echo); + + /** + * @return the name of the slave pty device. + * + * This function should be called only while the pty is open. + */ + const char *ttyName() const; + + /** + * @return the file descriptor of the master pty + * + * This function should be called only while the pty is open. + */ + int masterFd() const; + + /** + * @return the file descriptor of the slave pty + * + * This function should be called only while the pty slave is open. + */ + int slaveFd() const; + +protected: + /** + * @internal + */ + KPty(KPtyPrivate *d); + + /** + * @internal + */ + KPtyPrivate * const d_ptr; +}; + +#endif +
new file mode 100644 --- /dev/null +++ b/gui/konsole/kpty_export.h @@ -0,0 +1,46 @@ +/* This file is part of the KDE project + Copyright (C) 2007 David Faure <faure@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KPTY_EXPORT_H +#define KPTY_EXPORT_H + +/* needed for KDE_EXPORT and KDE_IMPORT macros */ +//#include <kdemacros.h> +#include <QtCore/qglobal.h> +#define KDE_EXPORT +#define KDE_IMPORT + +#ifndef KPTY_EXPORT +# if defined(KDELIBS_STATIC_LIBS) + /* No export/import for static libraries */ +# define KPTY_EXPORT +# elif defined(MAKE_KDECORE_LIB) + /* We are building this library */ +# define KPTY_EXPORT KDE_EXPORT +# else + /* We are using this library */ +# define KPTY_EXPORT KDE_IMPORT +# endif +#endif + +# ifndef KPTY_EXPORT_DEPRECATED +# define KPTY_EXPORT_DEPRECATED KDE_DEPRECATED KPTY_EXPORT +# endif + +#endif
new file mode 100644 --- /dev/null +++ b/gui/konsole/kpty_p.h @@ -0,0 +1,58 @@ +/* This file is part of the KDE libraries + + Copyright (C) 2003,2007 Oswald Buddenhagen <ossi@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef kpty_p_h +#define kpty_p_h + +#include "kpty.h" + +//#include <config-pty.h> +#if defined(Q_OS_MAC) +#define HAVE_UTIL_H +#define HAVE_UTMPX +#define _UTMPX_COMPAT +#define HAVE_SYS_TIME_H +#else +#define HAVE_PTY_H +#endif + +#define HAVE_OPENPTY + +#include <QtCore/QByteArray> + +struct KPtyPrivate { + Q_DECLARE_PUBLIC(KPty) + + KPtyPrivate(KPty* parent); + virtual ~KPtyPrivate(); +#ifndef HAVE_OPENPTY + bool chownpty(bool grant); +#endif + + int masterFd; + int slaveFd; + bool ownMaster:1; + + QByteArray ttyName; + + KPty *q_ptr; +}; + +#endif
new file mode 100644 --- /dev/null +++ b/gui/konsole/kptydevice.cpp @@ -0,0 +1,616 @@ +/* + + This file is part of the KDE libraries + Copyright (C) 2007 Oswald Buddenhagen <ossi@kde.org> + Copyright (C) 2010 KDE e.V. <kde-ev-board@kde.org> + Author Adriaan de Groot <groot@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kptydevice.h" +#include "kpty_p.h" + +//#include <config.h> +//#include <config-pty.h> + +#define i18n + +#include <QtCore/QSocketNotifier> + +//#include <klocale.h> + +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <termios.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#ifdef HAVE_SYS_FILIO_H +# include <sys/filio.h> +#endif +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#endif + +#if defined(Q_OS_FREEBSD) || defined(Q_OS_MAC) + // "the other end's output queue size" - kinda braindead, huh? +# define PTY_BYTES_AVAILABLE TIOCOUTQ +#elif defined(TIOCINQ) + // "our end's input queue size" +# define PTY_BYTES_AVAILABLE TIOCINQ +#else + // likewise. more generic ioctl (theoretically) +# define PTY_BYTES_AVAILABLE FIONREAD +#endif + +#define KMAXINT ((int)(~0U >> 1)) + +///////////////////////////////////////////////////// +// Helper. Remove when QRingBuffer becomes public. // +///////////////////////////////////////////////////// + +#include <QtCore/qbytearray.h> +#include <QtCore/qlinkedlist.h> + +#define CHUNKSIZE 4096 + +class KRingBuffer +{ +public: + KRingBuffer() + { + clear(); + } + + void clear() + { + buffers.clear(); + QByteArray tmp; + tmp.resize(CHUNKSIZE); + buffers << tmp; + head = tail = 0; + totalSize = 0; + } + + inline bool isEmpty() const + { + return buffers.count() == 1 && !tail; + } + + inline int size() const + { + return totalSize; + } + + inline int readSize() const + { + return (buffers.count() == 1 ? tail : buffers.first().size()) - head; + } + + inline const char *readPointer() const + { + Q_ASSERT(totalSize > 0); + return buffers.first().constData() + head; + } + + void free(int bytes) + { + totalSize -= bytes; + Q_ASSERT(totalSize >= 0); + + forever { + int nbs = readSize(); + + if (bytes < nbs) { + head += bytes; + if (head == tail && buffers.count() == 1) { + buffers.first().resize(CHUNKSIZE); + head = tail = 0; + } + break; + } + + bytes -= nbs; + if (buffers.count() == 1) { + buffers.first().resize(CHUNKSIZE); + head = tail = 0; + break; + } + + buffers.removeFirst(); + head = 0; + } + } + + char *reserve(int bytes) + { + totalSize += bytes; + + char *ptr; + if (tail + bytes <= buffers.last().size()) { + ptr = buffers.last().data() + tail; + tail += bytes; + } else { + buffers.last().resize(tail); + QByteArray tmp; + tmp.resize(qMax(CHUNKSIZE, bytes)); + ptr = tmp.data(); + buffers << tmp; + tail = bytes; + } + return ptr; + } + + // release a trailing part of the last reservation + inline void unreserve(int bytes) + { + totalSize -= bytes; + tail -= bytes; + } + + inline void write(const char *data, int len) + { + memcpy(reserve(len), data, len); + } + + // Find the first occurrence of c and return the index after it. + // If c is not found until maxLength, maxLength is returned, provided + // it is smaller than the buffer size. Otherwise -1 is returned. + int indexAfter(char c, int maxLength = KMAXINT) const + { + int index = 0; + int start = head; + QLinkedList<QByteArray>::ConstIterator it = buffers.begin(); + forever { + if (!maxLength) + return index; + if (index == size()) + return -1; + const QByteArray &buf = *it; + ++it; + int len = qMin((it == buffers.end() ? tail : buf.size()) - start, + maxLength); + const char *ptr = buf.data() + start; + if (const char *rptr = (const char *)memchr(ptr, c, len)) + return index + (rptr - ptr) + 1; + index += len; + maxLength -= len; + start = 0; + } + } + + inline int lineSize(int maxLength = KMAXINT) const + { + return indexAfter('\n', maxLength); + } + + inline bool canReadLine() const + { + return lineSize() != -1; + } + + int read(char *data, int maxLength) + { + int bytesToRead = qMin(size(), maxLength); + int readSoFar = 0; + while (readSoFar < bytesToRead) { + const char *ptr = readPointer(); + int bs = qMin(bytesToRead - readSoFar, readSize()); + memcpy(data + readSoFar, ptr, bs); + readSoFar += bs; + free(bs); + } + return readSoFar; + } + + int readLine(char *data, int maxLength) + { + return read(data, lineSize(qMin(maxLength, size()))); + } + +private: + QLinkedList<QByteArray> buffers; + int head, tail; + int totalSize; +}; + +////////////////// +// private data // +////////////////// + +// Lifted from Qt. I don't think they would mind. ;) +// Re-lift again from Qt whenever a proper replacement for pthread_once appears +static void qt_ignore_sigpipe() +{ + static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER(0); + if (atom.testAndSetRelaxed(0, 1)) { + struct sigaction noaction; + memset(&noaction, 0, sizeof(noaction)); + noaction.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &noaction, 0); + } +} + +#define NO_INTR(ret,func) do { ret = func; } while (ret < 0 && errno == EINTR) + +struct KPtyDevicePrivate : public KPtyPrivate { + Q_DECLARE_PUBLIC(KPtyDevice) + + KPtyDevicePrivate(KPty* parent) : + KPtyPrivate(parent), + emittedReadyRead(false), emittedBytesWritten(false), + readNotifier(0), writeNotifier(0) + { + } + + bool _k_canRead(); + bool _k_canWrite(); + + bool doWait(int msecs, bool reading); + void finishOpen(QIODevice::OpenMode mode); + + bool emittedReadyRead; + bool emittedBytesWritten; + QSocketNotifier *readNotifier; + QSocketNotifier *writeNotifier; + KRingBuffer readBuffer; + KRingBuffer writeBuffer; +}; + +bool KPtyDevicePrivate::_k_canRead() +{ + Q_Q(KPtyDevice); + qint64 readBytes = 0; + +#ifdef Q_OS_IRIX // this should use a config define, but how to check it? + size_t available; +#else + int available; +#endif + if (!::ioctl(q->masterFd(), PTY_BYTES_AVAILABLE, (char *) &available)) { +#ifdef Q_OS_SOLARIS + // A Pty is a STREAMS module, and those can be activated + // with 0 bytes available. This happens either when ^C is + // pressed, or when an application does an explicit write(a,b,0) + // which happens in experiments fairly often. When 0 bytes are + // available, you must read those 0 bytes to clear the STREAMS + // module, but we don't want to hit the !readBytes case further down. + if (!available) { + char c; + // Read the 0-byte STREAMS message + NO_INTR(readBytes, read(q->masterFd(), &c, 0)); + // Should return 0 bytes read; -1 is error + if (readBytes < 0) { + readNotifier->setEnabled(false); + emit q->readEof(); + return false; + } + return true; + } +#endif + + char *ptr = readBuffer.reserve(available); +#ifdef Q_OS_SOLARIS + // Even if available > 0, it is possible for read() + // to return 0 on Solaris, due to 0-byte writes in the stream. + // Ignore them and keep reading until we hit *some* data. + // In Solaris it is possible to have 15 bytes available + // and to (say) get 0, 0, 6, 0 and 9 bytes in subsequent reads. + // Because the stream is set to O_NONBLOCK in finishOpen(), + // an EOF read will return -1. + readBytes = 0; + while (!readBytes) +#endif + // Useless block braces except in Solaris + { + NO_INTR(readBytes, read(q->masterFd(), ptr, available)); + } + if (readBytes < 0) { + readBuffer.unreserve(available); + q->setErrorString(i18n("Error reading from PTY")); + return false; + } + readBuffer.unreserve(available - readBytes); // *should* be a no-op + } + + if (!readBytes) { + readNotifier->setEnabled(false); + emit q->readEof(); + return false; + } else { + if (!emittedReadyRead) { + emittedReadyRead = true; + emit q->readyRead(); + emittedReadyRead = false; + } + return true; + } +} + +bool KPtyDevicePrivate::_k_canWrite() +{ + Q_Q(KPtyDevice); + + writeNotifier->setEnabled(false); + if (writeBuffer.isEmpty()) + return false; + + qt_ignore_sigpipe(); + int wroteBytes; + NO_INTR(wroteBytes, + write(q->masterFd(), + writeBuffer.readPointer(), writeBuffer.readSize())); + if (wroteBytes < 0) { + q->setErrorString(i18n("Error writing to PTY")); + return false; + } + writeBuffer.free(wroteBytes); + + if (!emittedBytesWritten) { + emittedBytesWritten = true; + emit q->bytesWritten(wroteBytes); + emittedBytesWritten = false; + } + + if (!writeBuffer.isEmpty()) + writeNotifier->setEnabled(true); + return true; +} + +#ifndef timeradd +// Lifted from GLIBC +# define timeradd(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \ + if ((result)->tv_usec >= 1000000) { \ + ++(result)->tv_sec; \ + (result)->tv_usec -= 1000000; \ + } \ + } while (0) +# define timersub(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ + if ((result)->tv_usec < 0) { \ + --(result)->tv_sec; \ + (result)->tv_usec += 1000000; \ + } \ + } while (0) +#endif + +bool KPtyDevicePrivate::doWait(int msecs, bool reading) +{ + Q_Q(KPtyDevice); +#ifndef __linux__ + struct timeval etv; +#endif + struct timeval tv, *tvp; + + if (msecs < 0) + tvp = 0; + else { + tv.tv_sec = msecs / 1000; + tv.tv_usec = (msecs % 1000) * 1000; +#ifndef __linux__ + gettimeofday(&etv, 0); + timeradd(&tv, &etv, &etv); +#endif + tvp = &tv; + } + + while (reading ? readNotifier->isEnabled() : !writeBuffer.isEmpty()) { + fd_set rfds; + fd_set wfds; + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + + if (readNotifier->isEnabled()) + FD_SET(q->masterFd(), &rfds); + if (!writeBuffer.isEmpty()) + FD_SET(q->masterFd(), &wfds); + +#ifndef __linux__ + if (tvp) { + gettimeofday(&tv, 0); + timersub(&etv, &tv, &tv); + if (tv.tv_sec < 0) + tv.tv_sec = tv.tv_usec = 0; + } +#endif + + switch (select(q->masterFd() + 1, &rfds, &wfds, 0, tvp)) { + case -1: + if (errno == EINTR) + break; + return false; + case 0: + q->setErrorString(i18n("PTY operation timed out")); + return false; + default: + if (FD_ISSET(q->masterFd(), &rfds)) { + bool canRead = _k_canRead(); + if (reading && canRead) + return true; + } + if (FD_ISSET(q->masterFd(), &wfds)) { + bool canWrite = _k_canWrite(); + if (!reading) + return canWrite; + } + break; + } + } + return false; +} + +void KPtyDevicePrivate::finishOpen(QIODevice::OpenMode mode) +{ + Q_Q(KPtyDevice); + + q->QIODevice::open(mode); + fcntl(q->masterFd(), F_SETFL, O_NONBLOCK); + readBuffer.clear(); + readNotifier = new QSocketNotifier(q->masterFd(), QSocketNotifier::Read, q); + writeNotifier = new QSocketNotifier(q->masterFd(), QSocketNotifier::Write, q); + QObject::connect(readNotifier, SIGNAL(activated(int)), q, SLOT(_k_canRead())); + QObject::connect(writeNotifier, SIGNAL(activated(int)), q, SLOT(_k_canWrite())); + readNotifier->setEnabled(true); +} + +///////////////////////////// +// public member functions // +///////////////////////////// + +KPtyDevice::KPtyDevice(QObject *parent) : + QIODevice(parent), + KPty(new KPtyDevicePrivate(this)) +{ +} + +KPtyDevice::~KPtyDevice() +{ + close(); +} + +bool KPtyDevice::open(OpenMode mode) +{ + Q_D(KPtyDevice); + + if (masterFd() >= 0) + return true; + + if (!KPty::open()) { + setErrorString(i18n("Error opening PTY")); + return false; + } + + d->finishOpen(mode); + + return true; +} + +bool KPtyDevice::open(int fd, OpenMode mode) +{ + Q_D(KPtyDevice); + + if (!KPty::open(fd)) { + setErrorString(i18n("Error opening PTY")); + return false; + } + + d->finishOpen(mode); + + return true; +} + +void KPtyDevice::close() +{ + Q_D(KPtyDevice); + + if (masterFd() < 0) + return; + + delete d->readNotifier; + delete d->writeNotifier; + + QIODevice::close(); + + KPty::close(); +} + +bool KPtyDevice::isSequential() const +{ + return true; +} + +bool KPtyDevice::canReadLine() const +{ + Q_D(const KPtyDevice); + return QIODevice::canReadLine() || d->readBuffer.canReadLine(); +} + +bool KPtyDevice::atEnd() const +{ + Q_D(const KPtyDevice); + return QIODevice::atEnd() && d->readBuffer.isEmpty(); +} + +qint64 KPtyDevice::bytesAvailable() const +{ + Q_D(const KPtyDevice); + return QIODevice::bytesAvailable() + d->readBuffer.size(); +} + +qint64 KPtyDevice::bytesToWrite() const +{ + Q_D(const KPtyDevice); + return d->writeBuffer.size(); +} + +bool KPtyDevice::waitForReadyRead(int msecs) +{ + Q_D(KPtyDevice); + return d->doWait(msecs, true); +} + +bool KPtyDevice::waitForBytesWritten(int msecs) +{ + Q_D(KPtyDevice); + return d->doWait(msecs, false); +} + +void KPtyDevice::setSuspended(bool suspended) +{ + Q_D(KPtyDevice); + d->readNotifier->setEnabled(!suspended); +} + +bool KPtyDevice::isSuspended() const +{ + Q_D(const KPtyDevice); + return !d->readNotifier->isEnabled(); +} + +// protected +qint64 KPtyDevice::readData(char *data, qint64 maxlen) +{ + Q_D(KPtyDevice); + return d->readBuffer.read(data, (int)qMin<qint64>(maxlen, KMAXINT)); +} + +// protected +qint64 KPtyDevice::readLineData(char *data, qint64 maxlen) +{ + Q_D(KPtyDevice); + return d->readBuffer.readLine(data, (int)qMin<qint64>(maxlen, KMAXINT)); +} + +// protected +qint64 KPtyDevice::writeData(const char *data, qint64 len) +{ + Q_D(KPtyDevice); + Q_ASSERT(len <= KMAXINT); + + d->writeBuffer.write(data, len); + d->writeNotifier->setEnabled(true); + return len; +} + +#include "kptydevice.moc" +
new file mode 100644 --- /dev/null +++ b/gui/konsole/kptydevice.h @@ -0,0 +1,156 @@ +/* This file is part of the KDE libraries + + Copyright (C) 2007 Oswald Buddenhagen <ossi@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef kptydev_h +#define kptydev_h + +#include "kpty.h" + +#include <QtCore/QIODevice> + +struct KPtyDevicePrivate; + +#define Q_DECLARE_PRIVATE_MI(Class, SuperClass) \ + inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(SuperClass::d_ptr); } \ + inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(SuperClass::d_ptr); } \ + friend class Class##Private; + +/** + * Encapsulates KPty into a QIODevice, so it can be used with Q*Stream, etc. + */ +class KPTY_EXPORT KPtyDevice : public QIODevice, public KPty { //krazy:exclude=dpointer (via macro) + Q_OBJECT + Q_DECLARE_PRIVATE_MI(KPtyDevice, KPty) + +public: + + /** + * Constructor + */ + KPtyDevice(QObject *parent = 0); + + /** + * Destructor: + * + * If the pty is still open, it will be closed. Note, however, that + * an utmp registration is @em not undone. + */ + virtual ~KPtyDevice(); + + /** + * Create a pty master/slave pair. + * + * @return true if a pty pair was successfully opened + */ + virtual bool open(OpenMode mode = ReadWrite | Unbuffered); + + /** + * Open using an existing pty master. The ownership of the fd + * remains with the caller, i.e., close() will not close the fd. + * + * This is useful if you wish to attach a secondary "controller" to an + * existing pty device such as a terminal widget. + * Note that you will need to use setSuspended() on both devices to + * control which one gets the incoming data from the pty. + * + * @param fd an open pty master file descriptor. + * @param mode the device mode to open the pty with. + * @return true if a pty pair was successfully opened + */ + bool open(int fd, OpenMode mode = ReadWrite | Unbuffered); + + /** + * Close the pty master/slave pair. + */ + virtual void close(); + + /** + * Sets whether the KPtyDevice monitors the pty for incoming data. + * + * When the KPtyDevice is suspended, it will no longer attempt to buffer + * data that becomes available from the pty and it will not emit any + * signals. + * + * Do not use on closed ptys. + * After a call to open(), the pty is not suspended. If you need to + * ensure that no data is read, call this function before the main loop + * is entered again (i.e., immediately after opening the pty). + */ + void setSuspended(bool suspended); + + /** + * Returns true if the KPtyDevice is not monitoring the pty for incoming + * data. + * + * Do not use on closed ptys. + * + * See setSuspended() + */ + bool isSuspended() const; + + /** + * @return always true + */ + virtual bool isSequential() const; + + /** + * @reimp + */ + bool canReadLine() const; + + /** + * @reimp + */ + bool atEnd() const; + + /** + * @reimp + */ + qint64 bytesAvailable() const; + + /** + * @reimp + */ + qint64 bytesToWrite() const; + + bool waitForBytesWritten(int msecs = -1); + bool waitForReadyRead(int msecs = -1); + + +Q_SIGNALS: + /** + * Emitted when EOF is read from the PTY. + * + * Data may still remain in the buffers. + */ + void readEof(); + +protected: + virtual qint64 readData(char *data, qint64 maxSize); + virtual qint64 readLineData(char *data, qint64 maxSize); + virtual qint64 writeData(const char *data, qint64 maxSize); + +private: + Q_PRIVATE_SLOT(d_func(), bool _k_canRead()) + Q_PRIVATE_SLOT(d_func(), bool _k_canWrite()) +}; + +#endif +
new file mode 100644 --- /dev/null +++ b/gui/konsole/kptyprocess.cpp @@ -0,0 +1,141 @@ +/* + This file is part of the KDE libraries + + Copyright (C) 2007 Oswald Buddenhagen <ossi@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + +#include "kptyprocess.h" +#include "kprocess_p.h" + +//#include <kuser.h> +#include "kptydevice.h" + +#include <stdlib.h> +#include <unistd.h> + +////////////////// +// private data // +////////////////// + +struct KPtyProcessPrivate : KProcessPrivate { + KPtyProcessPrivate() : + ptyChannels(KPtyProcess::NoChannels), + addUtmp(false) + { + } + + void _k_onStateChanged(QProcess::ProcessState newState) + { + if (newState == QProcess::NotRunning && addUtmp) + pty->logout(); + } + + KPtyDevice *pty; + KPtyProcess::PtyChannels ptyChannels; + bool addUtmp : 1; +}; + +KPtyProcess::KPtyProcess(QObject *parent) : + KProcess(new KPtyProcessPrivate, parent) +{ + Q_D(KPtyProcess); + + d->pty = new KPtyDevice(this); + d->pty->open(); + connect(this, SIGNAL(stateChanged(QProcess::ProcessState)), + SLOT(_k_onStateChanged(QProcess::ProcessState))); +} + +KPtyProcess::KPtyProcess(int ptyMasterFd, QObject *parent) : + KProcess(new KPtyProcessPrivate, parent) +{ + Q_D(KPtyProcess); + + d->pty = new KPtyDevice(this); + d->pty->open(ptyMasterFd); + connect(this, SIGNAL(stateChanged(QProcess::ProcessState)), + SLOT(_k_onStateChanged(QProcess::ProcessState))); +} + +KPtyProcess::~KPtyProcess() +{ + Q_D(KPtyProcess); + + if (state() != QProcess::NotRunning && d->addUtmp) { + d->pty->logout(); + disconnect(SIGNAL(stateChanged(QProcess::ProcessState)), + this, SLOT(_k_onStateChanged(QProcess::ProcessState))); + } + delete d->pty; +} + +void KPtyProcess::setPtyChannels(PtyChannels channels) +{ + Q_D(KPtyProcess); + + d->ptyChannels = channels; +} + +KPtyProcess::PtyChannels KPtyProcess::ptyChannels() const +{ + Q_D(const KPtyProcess); + + return d->ptyChannels; +} + +void KPtyProcess::setUseUtmp(bool value) +{ + Q_D(KPtyProcess); + + d->addUtmp = value; +} + +bool KPtyProcess::isUseUtmp() const +{ + Q_D(const KPtyProcess); + + return d->addUtmp; +} + +KPtyDevice *KPtyProcess::pty() const +{ + Q_D(const KPtyProcess); + + return d->pty; +} + +void KPtyProcess::setupChildProcess() +{ + Q_D(KPtyProcess); + + d->pty->setCTty(); + if (d->addUtmp) + d->pty->login(getenv("USER"), getenv("DISPLAY")); + //d->pty->login(KUser(KUser::UseRealUserID).loginName().toLocal8Bit().data(), qgetenv("DISPLAY")); + if (d->ptyChannels & StdinChannel) + dup2(d->pty->slaveFd(), 0); + if (d->ptyChannels & StdoutChannel) + dup2(d->pty->slaveFd(), 1); + if (d->ptyChannels & StderrChannel) + dup2(d->pty->slaveFd(), 2); + + KProcess::setupChildProcess(); +} + +#include "kptyprocess.moc"
new file mode 100644 --- /dev/null +++ b/gui/konsole/kptyprocess.h @@ -0,0 +1,139 @@ +/* + This file is part of the KDE libraries + + Copyright (C) 2007 Oswald Buddenhagen <ossi@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KPTYPROCESS_H +#define KPTYPROCESS_H + +#include "kprocess.h" + +#include "kpty_export.h" + +class KPtyDevice; + +struct KPtyProcessPrivate; + +/** + * This class extends KProcess by support for PTYs (pseudo TTYs). + * + * The PTY is opened as soon as the class is instantiated. Verify that + * it was opened successfully by checking that pty()->masterFd() is not -1. + * + * The PTY is always made the process' controlling TTY. + * Utmp registration and connecting the stdio handles to the PTY are optional. + * + * No attempt to integrate with QProcess' waitFor*() functions was made, + * for it is impossible. Note that execute() does not work with the PTY, too. + * Use the PTY device's waitFor*() functions or use it asynchronously. + * + * @author Oswald Buddenhagen <ossi@kde.org> + */ +class KPTY_EXPORT KPtyProcess : public KProcess +{ + Q_OBJECT + Q_DECLARE_PRIVATE(KPtyProcess) + +public: + enum PtyChannelFlag { + NoChannels = 0, /**< The PTY is not connected to any channel. */ + StdinChannel = 1, /**< Connect PTY to stdin. */ + StdoutChannel = 2, /**< Connect PTY to stdout. */ + StderrChannel = 4, /**< Connect PTY to stderr. */ + AllOutputChannels = 6, /**< Connect PTY to all output channels. */ + AllChannels = 7 /**< Connect PTY to all channels. */ + }; + + Q_DECLARE_FLAGS(PtyChannels, PtyChannelFlag) + + /** + * Constructor + */ + explicit KPtyProcess(QObject *parent = 0); + + /** + * Construct a process using an open pty master. + * + * @param ptyMasterFd an open pty master file descriptor. + * The process does not take ownership of the descriptor; + * it will not be automatically closed at any point. + */ + KPtyProcess(int ptyMasterFd, QObject *parent = 0); + + /** + * Destructor + */ + virtual ~KPtyProcess(); + + /** + * Set to which channels the PTY should be assigned. + * + * This function must be called before starting the process. + * + * @param channels the output channel handling mode + */ + void setPtyChannels(PtyChannels channels); + + /** + * Query to which channels the PTY is assigned. + * + * @return the output channel handling mode + */ + PtyChannels ptyChannels() const; + + /** + * Set whether to register the process as a TTY login in utmp. + * + * Utmp is disabled by default. + * It should enabled for interactively fed processes, like terminal + * emulations. + * + * This function must be called before starting the process. + * + * @param value whether to register in utmp. + */ + void setUseUtmp(bool value); + + /** + * Get whether to register the process as a TTY login in utmp. + * + * @return whether to register in utmp + */ + bool isUseUtmp() const; + + /** + * Get the PTY device of this process. + * + * @return the PTY device + */ + KPtyDevice *pty() const; + +protected: + /** + * @reimp + */ + virtual void setupChildProcess(); + +private: + Q_PRIVATE_SLOT(d_func(), void _k_onStateChanged(QProcess::ProcessState)) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(KPtyProcess::PtyChannels) + +#endif
new file mode 100644 --- /dev/null +++ b/gui/konsole/main.cpp @@ -0,0 +1,188 @@ +/* + 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" +#include "MainWindow.h" +#include <KDebug> + +// Unix +#include <unistd.h> + +// KDE +#include <KAboutData> +#include <KCmdLineArgs> +#include <KLocale> + +#define KONSOLE_VERSION "2.6.999" + +using namespace Konsole; + +// fills the KAboutData structure with information about contributors to +// Konsole +void fillAboutData(KAboutData& aboutData); +void fillCommandLineOptions(KCmdLineOptions& options); +bool forceNewProcess(); // returns true if new instance should use a new + // process (instead of re-using an existing one) +void restoreSession(Application& app); + +// *** +// Entry point into the Konsole terminal application. +// *** +extern "C" int KDE_EXPORT kdemain(int argc,char** argv) +{ + KAboutData about( "konsole", 0, + ki18n("Konsole"), + KONSOLE_VERSION, + ki18n("Terminal emulator"), + KAboutData::License_GPL_V2 + ); + fillAboutData(about); + + KCmdLineArgs::init(argc,argv,&about); + KCmdLineOptions options; + fillCommandLineOptions(options); + KCmdLineArgs::addCmdLineOptions(options); + KUniqueApplication::addCmdLineOptions(); + + KUniqueApplication::StartFlags startFlags; + if (forceNewProcess()) + startFlags = KUniqueApplication::NonUniqueInstance; + + // create a new application instance if there are no running Konsole instances, + // otherwise inform the existing Konsole process and exit + if ( !KUniqueApplication::start(startFlags) ) + { + exit(0); + } + + Application app; + restoreSession(app); + return app.exec(); +} +bool forceNewProcess() +{ + // when starting Konsole from a terminal, a new process must be used + // so that the current environment is propagated into the shells of the new + // Konsole and any debug output or warnings from Konsole are written to + // the current terminal + KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); + return isatty(1) && !args->isSet("new-tab"); +} + +void fillCommandLineOptions(KCmdLineOptions& options) +{ + options.add("profile <file>", ki18n("Name of profile to use for new Konsole instance")); + options.add("list-profiles", ki18n("List the available profiles")); + // TODO - Update this when F12 is no longer hard coded + options.add("background-mode", ki18n("Start Konsole in the background" + " and bring to the front when the F12" + " key is pressed")); + options.add("new-tab",ki18n("Create a new tab in an existing window rather than creating a new window")); + options.add("tabs-from-file <file>", ki18n("Create tabs as specified in given tabs configuration file")); + options.add("workdir <dir>", ki18n("Set the initial working directory of the new tab " + "or window to 'dir'")); + options.add("notransparency",ki18n("Disable transparent backgrounds, even if the system supports them.")); + options.add("force-transparency",ki18n("Try to enable transparency, even if the system does not appear to support it.")); + options.add("hold"); + options.add("noclose",ki18n("Do not close the initial session automatically when it ends.")); + // TODO - Document this option more clearly + options.add("p <property=value>",ki18n("Change the value of a profile property.")); + options.add("!e <cmd>",ki18n("Command to execute")); + options.add("+[args]",ki18n("Arguments passed to command")); +} + +void fillAboutData(KAboutData& aboutData) +{ + aboutData.addAuthor(ki18n("Robert Knight"),ki18n("Maintainer"), "robertknight@gmail.com"); + aboutData.addAuthor(ki18n("Lars Doelle"),ki18n("Author"), "lars.doelle@on-line.de"); + aboutData.addCredit(ki18n("Kurt V. Hindenburg"), + ki18n("Bug fixes and general improvements"), + "kurt.hindenburg@gmail.com"); + aboutData.addCredit(ki18n("Waldo Bastian"), + ki18n("Bug fixes and general improvements"), + "bastian@kde.org"); + aboutData.addCredit(ki18n("Stephan Binner"), + ki18n("Bug fixes and general improvements"), + "binner@kde.org"); + aboutData.addCredit(ki18n("Thomas Dreibholz"), + ki18n("General improvements"), + "dreibh@iem.uni-due.de"); + aboutData.addCredit(ki18n("Chris Machemer"), + ki18n("Bug fixes"), + "machey@ceinetworks.com"); + aboutData.addCredit(ki18n("Stephan Kulow"), + ki18n("Solaris support and history"), + "coolo@kde.org"); + aboutData.addCredit(ki18n("Alexander Neundorf"), + ki18n("Bug fixes and improved startup performance"), + "neundorf@kde.org"); + aboutData.addCredit(ki18n("Peter Silva"), + ki18n("Marking improvements"), + "Peter.A.Silva@gmail.com"); + aboutData.addCredit(ki18n("Lotzi Boloni"), + ki18n("Embedded Konsole\n" + "Toolbar and session names"), + "boloni@cs.purdue.edu"); + aboutData.addCredit(ki18n("David Faure"), + ki18n("Embedded Konsole\n" + "General improvements"), + "faure@kde.org"); + aboutData.addCredit(ki18n("Antonio Larrosa"), + ki18n("Visual effects"), + "larrosa@kde.org"); + aboutData.addCredit(ki18n("Matthias Ettrich"), + ki18n("Code from the kvt project\n" + "General improvements"), + "ettrich@kde.org"); + aboutData.addCredit(ki18n("Warwick Allison"), + ki18n("Schema and text selection improvements"), + "warwick@troll.no"); + aboutData.addCredit(ki18n("Dan Pilone"), + ki18n("SGI port"), + "pilone@slac.com"); + aboutData.addCredit(ki18n("Kevin Street"), + ki18n("FreeBSD port"), + "street@iname.com"); + aboutData.addCredit(ki18n("Sven Fischer"), + ki18n("Bug fixes"), + "herpes@kawo2.renditionwth-aachen.de"); + aboutData.addCredit(ki18n("Dale M. Flaven"), + ki18n("Bug fixes"), + "dflaven@netport.com"); + aboutData.addCredit(ki18n("Martin Jones"), + ki18n("Bug fixes"), + "mjones@powerup.com.au"); + aboutData.addCredit(ki18n("Lars Knoll"), + ki18n("Bug fixes"), + "knoll@mpi-hd.mpg.de"); + aboutData.addCredit(ki18n("Thanks to many others.\n")); + aboutData.setProgramIconName("utilities-terminal"); +} + +void restoreSession(Application& app) +{ + if (app.isSessionRestored()) + { + int n = 1; + while (KMainWindow::canBeRestored(n)) + app.newMainWindow()->restore(n++); + } +} +
new file mode 100644 --- /dev/null +++ b/gui/konsole/qtermwidget.cpp @@ -0,0 +1,233 @@ +/* Copyright (C) 2008 e_k (e_k@users.sourceforge.net) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + +#include "qtermwidget.h" + +#include "Session.h" +#include "TerminalDisplay.h" + +using namespace Konsole; + +void *createTermWidget(int startnow, void *parent) +{ + return (void*) new QTermWidget(startnow, (QWidget*)parent); +} + +struct TermWidgetImpl +{ + TermWidgetImpl(QWidget* parent = 0); + + TerminalDisplay *m_terminalDisplay; + Session *m_session; + + Session* createSession(); + TerminalDisplay* createTerminalDisplay(Session *session, QWidget* parent); +}; + +TermWidgetImpl::TermWidgetImpl(QWidget* parent) +{ + this->m_session = createSession(); + this->m_terminalDisplay = createTerminalDisplay(this->m_session, parent); +} + + +Session *TermWidgetImpl::createSession() +{ + Session *session = new Session(); + + session->setTitle(Session::NameRole, "QTermWidget"); + session->setProgram("/bin/bash"); + QStringList args(""); + session->setArguments(args); + session->setAutoClose(true); + + session->setCodec(QTextCodec::codecForName("UTF-8")); + + session->setFlowControlEnabled(true); + session->setHistoryType(HistoryTypeBuffer(1000)); + + session->setDarkBackground(true); + + session->setKeyBindings(""); + return session; +} + +TerminalDisplay *TermWidgetImpl::createTerminalDisplay(Session *session, QWidget* parent) +{ +// TerminalDisplay* display = new TerminalDisplay(this); + TerminalDisplay* display = new TerminalDisplay(parent); + + display->setBellMode(TerminalDisplay::NotifyBell); + display->setTerminalSizeHint(true); + display->setTripleClickMode(TerminalDisplay::SelectWholeLine); + display->setTerminalSizeStartup(true); + + display->setRandomSeed(session->sessionId() * 31); + + return display; +} + + +QTermWidget::QTermWidget(int startnow, QWidget *parent) +:QWidget(parent) +{ + m_impl = new TermWidgetImpl(this); + + init(); + + if (startnow && m_impl->m_session) { + m_impl->m_session->run(); + } + + this->setFocus( Qt::OtherFocusReason ); + m_impl->m_terminalDisplay->resize(this->size()); + + this->setFocusProxy(m_impl->m_terminalDisplay); +} + +void QTermWidget::startShellProgram() +{ + if ( m_impl->m_session->isRunning() ) + return; + + m_impl->m_session->run(); +} + +void QTermWidget::openTeletype(int fd) +{ + if ( m_impl->m_session->isRunning() ) + return; + + m_impl->m_session->openTeletype(fd); +} + +void QTermWidget::init() +{ + m_impl->m_terminalDisplay->setSize(80, 40); + + QFont font = QApplication::font(); + font.setFamily("Monospace"); + font.setPointSize(10); + font.setStyleHint(QFont::TypeWriter); + setTerminalFont(font); + setScrollBarPosition(NoScrollBar); + + m_impl->m_session->addView(m_impl->m_terminalDisplay); + + connect(m_impl->m_session, SIGNAL(finished()), this, SLOT(sessionFinished())); +} + + +QTermWidget::~QTermWidget() +{ + emit destroyed(); +} + + +void QTermWidget::setTerminalFont(QFont &font) +{ + if (!m_impl->m_terminalDisplay) + return; + m_impl->m_terminalDisplay->setVTFont(font); +} + +void QTermWidget::setShellProgram(QString &progname) +{ + if (!m_impl->m_session) + return; + m_impl->m_session->setProgram(progname); +} + +void QTermWidget::setArgs(QStringList &args) +{ + if (!m_impl->m_session) + return; + m_impl->m_session->setArguments(args); +} + +void QTermWidget::setTextCodec(QTextCodec *codec) +{ + if (!m_impl->m_session) + return; + m_impl->m_session->setCodec(codec); +} + +void QTermWidget::setColorScheme(int scheme) +{ + // TODO: Fix later when color schemes are more important + /* + switch(scheme) { + case COLOR_SCHEME_WHITE_ON_BLACK: + m_impl->m_terminalDisplay->setColorTable(whiteonblack_color_table); + break; + case COLOR_SCHEME_GREEN_ON_BLACK: + m_impl->m_terminalDisplay->setColorTable(greenonblack_color_table); + break; + case COLOR_SCHEME_BLACK_ON_LIGHT_YELLOW: + m_impl->m_terminalDisplay->setColorTable(blackonlightyellow_color_table); + break; + default: //do nothing + break; + }; + */ +} + +void QTermWidget::setSize(int h, int v) +{ + if (!m_impl->m_terminalDisplay) + return; + m_impl->m_terminalDisplay->setSize(h, v); +} + +void QTermWidget::setHistorySize(int lines) +{ + if (lines < 0) + m_impl->m_session->setHistoryType(HistoryTypeFile()); + else + m_impl->m_session->setHistoryType(HistoryTypeBuffer(lines)); +} + +void QTermWidget::setScrollBarPosition(ScrollBarPosition pos) +{ + if (!m_impl->m_terminalDisplay) + return; + m_impl->m_terminalDisplay->setScrollBarPosition((TerminalDisplay::ScrollBarPosition)pos); +} + +void QTermWidget::sendText(QString &text) +{ + m_impl->m_session->sendText(text); +} + +void QTermWidget::resizeEvent(QResizeEvent*) +{ +//qDebug("global window resizing...with %d %d", this->size().width(), this->size().height()); + m_impl->m_terminalDisplay->resize(this->size()); +} + + + +void QTermWidget::sessionFinished() +{ + emit finished(); +} + +#include "qtermwidget.moc" +//#include "moc_consoleq.cpp" +
new file mode 100644 --- /dev/null +++ b/gui/konsole/qtermwidget.h @@ -0,0 +1,110 @@ +/* Copyright (C) 2008 e_k (e_k@users.sourceforge.net) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + +#ifndef _Q_TERM_WIDGET +#define _Q_TERM_WIDGET + +#include <QtGui> + +struct TermWidgetImpl; + +enum COLOR_SCHEME { COLOR_SCHEME_WHITE_ON_BLACK = 1, + COLOR_SCHEME_GREEN_ON_BLACK, + COLOR_SCHEME_BLACK_ON_LIGHT_YELLOW }; + +class QTermWidget : public QWidget +{ + Q_OBJECT +public: + + enum ScrollBarPosition + { + /** Do not show the scroll bar. */ + NoScrollBar=0, + /** Show the scroll bar on the left side of the display. */ + ScrollBarLeft=1, + /** Show the scroll bar on the right side of the display. */ + ScrollBarRight=2 + }; + + + //Creation of widget + QTermWidget(int startnow = 1, //start shell programm immediatelly + QWidget *parent = 0); + ~QTermWidget(); + + //start shell program if it was not started in constructor + void startShellProgram(); + + void openTeletype(int fd); + + //look-n-feel, if you don`t like defaults + + // Terminal font + // Default is application font with family Monospace, size 10 + void setTerminalFont(QFont &font); + + // Shell program, default is /bin/bash + void setShellProgram(QString &progname); + + // Shell program args, default is none + void setArgs(QStringList &args); + + //Text codec, default is UTF-8 + void setTextCodec(QTextCodec *codec); + + //Color scheme, default is white on black + void setColorScheme(int scheme); + + //set size + void setSize(int h, int v); + + // History size for scrolling + void setHistorySize(int lines); //infinite if lines < 0 + + // Presence of scrollbar + void setScrollBarPosition(ScrollBarPosition); + + // Send some text to terminal + void sendText(QString &text); + +signals: + void finished(); + +protected: + virtual void resizeEvent(QResizeEvent *); + +protected slots: + void sessionFinished(); + +private: + void init(); + TermWidgetImpl *m_impl; +}; + + +//Maybe useful, maybe not + +#ifdef __cplusplus +extern "C" +#endif +void *createTermWidget(int startnow, void *parent); + +#endif +
deleted file mode 100644 --- a/gui/qtermwidget/AUTHORS +++ /dev/null @@ -1,1 +0,0 @@ -e_k@users.sourceforge.net \ No newline at end of file
deleted file mode 100644 --- a/gui/qtermwidget/COPYING +++ /dev/null @@ -1,340 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - <one line to give the program's name and a brief idea of what it does.> - Copyright (C) <year> <name of author> - - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - <signature of Ty Coon>, 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License.
deleted file mode 100644 --- a/gui/qtermwidget/CVS/Entries +++ /dev/null @@ -1,9 +0,0 @@ -/AUTHORS/1.1.1.1/Sat May 10 21:27:49 2008// -/COPYING/1.1.1.1/Sat May 10 21:27:49 2008// -/INSTALL/1.1.1.1/Sat May 10 21:27:49 2008// -/TODO/1.2/Tue May 27 08:34:53 2008// -/qtermwidget.pro/1.1.1.1/Sat May 10 21:27:49 2008// -D/lib//// -D/src//// -/Changelog/1.1/Wed Jul 30 22:06:01 2008// -/README/1.3/Wed Jul 30 22:01:01 2008//
deleted file mode 100644 --- a/gui/qtermwidget/CVS/Repository +++ /dev/null @@ -1,1 +0,0 @@ -qtermwidget
deleted file mode 100644 --- a/gui/qtermwidget/CVS/Root +++ /dev/null @@ -1,1 +0,0 @@ -:ext:e_k@qtermwidget.cvs.sourceforge.net:/cvsroot/qtermwidget
deleted file mode 100644 --- a/gui/qtermwidget/Changelog +++ /dev/null @@ -1,19 +0,0 @@ -31.07.2008 -Interface class from c-style conversions rewritten with pimpl support. - - -16.07.2008 -Added optional scrollbar - - -06.06.2008 -Some artefacts were removed, some added... -Also added support for color schemes, and 3 color schemes provided (classical - white on black, green on black, black on light yellow). Is it enough or not? - - -26.05.2008 -Added file release as an archive with source code. But preferrable way is still getting code from CVS, cause file release can be outdated. - - -11.05.2008 -Initial CVS import - first version comes with number 0.0.1 \ No newline at end of file
deleted file mode 100644 --- a/gui/qtermwidget/INSTALL +++ /dev/null @@ -1,41 +0,0 @@ -Requirements -============ -Created with QT4.3.4 -Tested with QT4.3.2 -Maybe should work with other QT4 versions - -Basic Installation -================== - - These are generic installation instructions. - - There is no 'configure' shell script, `cause this package is just -a draft. And it intended for developers rather then for end-users. -There is a 'qtermwidget.pro' file provided instead. -This file is used for generating Makefiles in each appropriate -directory via 'qmake' program. - -The simplest way to compile this package is: - - 1. `cd' to the directory containing the package's source code and type - `qmake'. - - 2. Type `make' to compile the package. - - 3. You can remove the program binaries and object files from the - source code directory by typing `make clean'. - - 4. You can remove generated Makefiles form the directory - by typing `make distclean'. - -Compilers and Options -===================== - - Some systems require unusual options for compilation or linking. You can -provide additional options by modifying the appropriate *.pro files -according to qmake manual. - -Installation -================== - Installation not defined yet. -
deleted file mode 100644 --- a/gui/qtermwidget/README +++ /dev/null @@ -1,21 +0,0 @@ -QTermWidget -version 0.1.0 - -QTermWidget is an opensource project based on KDE4 Konsole application. -The main goal of this project is to provide unicode-enabled, embeddable -QT widget for using as a built-in console (or terminal emulation widget). - -Of course I`m aware about embedding abilities of original Konsole, -but once I had Qt without KDE, and it was a serious obstacle. -I decided not to rely on a chance. I cannot find any interesting related project, -so I had to write it. - -The original Konsole`s code was rewritten entirely with QT4 only; also I have to -include in the project some parts of code from kde core library. All code dealing -with user interface parts and session managers was removed (maybe later I bring it -back somehow), and the result is quite useful, I suppose. - -This library was compiled and tested on three linux systems, -based on 2.4.32, 2.6.20, 2.6.23 kernels, x86 and amd64. -Please inform about its behaviour on other systems. -
deleted file mode 100644 --- a/gui/qtermwidget/TODO +++ /dev/null @@ -1,10 +0,0 @@ -Global - - provide more compatibility for vttest - -Package - - migrate to autotools if needed - -Source - - provide more options for customization - - clean unused code - - add some QT3 support features if needed
deleted file mode 100644 --- a/gui/qtermwidget/lib/BlockArray.cpp +++ /dev/null @@ -1,337 +0,0 @@ -/* - This file is part of Konsole, an X terminal. - Copyright (C) 2000 by Stephan Kulow <coolo@kde.org> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 "BlockArray.h" - -#include <QtCore> - -// System -#include <assert.h> -#include <sys/mman.h> -#include <sys/param.h> -#include <unistd.h> -#include <stdio.h> - - -using namespace Konsole; - -static int blocksize = 0; - -BlockArray::BlockArray() - : size(0), - current(size_t(-1)), - index(size_t(-1)), - lastmap(0), - lastmap_index(size_t(-1)), - lastblock(0), ion(-1), - length(0) -{ - // lastmap_index = index = current = size_t(-1); - if (blocksize == 0) - blocksize = ((sizeof(Block) / getpagesize()) + 1) * getpagesize(); - -} - -BlockArray::~BlockArray() -{ - setHistorySize(0); - assert(!lastblock); -} - -size_t BlockArray::append(Block *block) -{ - if (!size) - return size_t(-1); - - ++current; - if (current >= size) current = 0; - - int rc; - rc = lseek(ion, current * blocksize, SEEK_SET); if (rc < 0) { perror("HistoryBuffer::add.seek"); setHistorySize(0); return size_t(-1); } - rc = write(ion, block, blocksize); if (rc < 0) { perror("HistoryBuffer::add.write"); setHistorySize(0); return size_t(-1); } - - length++; - if (length > size) length = size; - - ++index; - - delete block; - return current; -} - -size_t BlockArray::newBlock() -{ - if (!size) - return size_t(-1); - append(lastblock); - - lastblock = new Block(); - return index + 1; -} - -Block *BlockArray::lastBlock() const -{ - return lastblock; -} - -bool BlockArray::has(size_t i) const -{ - if (i == index + 1) - return true; - - if (i > index) - return false; - if (index - i >= length) - return false; - return true; -} - -const Block* BlockArray::at(size_t i) -{ - if (i == index + 1) - return lastblock; - - if (i == lastmap_index) - return lastmap; - - if (i > index) { - qDebug() << "BlockArray::at() i > index\n"; - return 0; - } - -// if (index - i >= length) { -// kDebug(1211) << "BlockArray::at() index - i >= length\n"; -// return 0; -// } - - size_t j = i; // (current - (index - i) + (index/size+1)*size) % size ; - - assert(j < size); - unmap(); - - Block *block = (Block*)mmap(0, blocksize, PROT_READ, MAP_PRIVATE, ion, j * blocksize); - - if (block == (Block*)-1) { perror("mmap"); return 0; } - - lastmap = block; - lastmap_index = i; - - return block; -} - -void BlockArray::unmap() -{ - if (lastmap) { - int res = munmap((char*)lastmap, blocksize); - if (res < 0) perror("munmap"); - } - lastmap = 0; - lastmap_index = size_t(-1); -} - -bool BlockArray::setSize(size_t newsize) -{ - return setHistorySize(newsize * 1024 / blocksize); -} - -bool BlockArray::setHistorySize(size_t newsize) -{ -// kDebug(1211) << "setHistorySize " << size << " " << newsize; - - if (size == newsize) - return false; - - unmap(); - - if (!newsize) { - delete lastblock; - lastblock = 0; - if (ion >= 0) close(ion); - ion = -1; - current = size_t(-1); - return true; - } - - if (!size) { - FILE* tmp = tmpfile(); - if (!tmp) { - perror("konsole: cannot open temp file.\n"); - } else { - ion = dup(fileno(tmp)); - if (ion<0) { - perror("konsole: cannot dup temp file.\n"); - fclose(tmp); - } - } - if (ion < 0) - return false; - - assert(!lastblock); - - lastblock = new Block(); - size = newsize; - return false; - } - - if (newsize > size) { - increaseBuffer(); - size = newsize; - return false; - } else { - decreaseBuffer(newsize); - ftruncate(ion, length*blocksize); - size = newsize; - - return true; - } -} - -void moveBlock(FILE *fion, int cursor, int newpos, char *buffer2) -{ - int res = fseek(fion, cursor * blocksize, SEEK_SET); - if (res) - perror("fseek"); - res = fread(buffer2, blocksize, 1, fion); - if (res != 1) - perror("fread"); - - res = fseek(fion, newpos * blocksize, SEEK_SET); - if (res) - perror("fseek"); - res = fwrite(buffer2, blocksize, 1, fion); - if (res != 1) - perror("fwrite"); - // printf("moving block %d to %d\n", cursor, newpos); -} - -void BlockArray::decreaseBuffer(size_t newsize) -{ - if (index < newsize) // still fits in whole - return; - - int offset = (current - (newsize - 1) + size) % size; - - if (!offset) - return; - - // The Block constructor could do somthing in future... - char *buffer1 = new char[blocksize]; - - FILE *fion = fdopen(dup(ion), "w+b"); - if (!fion) { - delete [] buffer1; - perror("fdopen/dup"); - return; - } - - int firstblock; - if (current <= newsize) { - firstblock = current + 1; - } else { - firstblock = 0; - } - - size_t oldpos; - for (size_t i = 0, cursor=firstblock; i < newsize; i++) { - oldpos = (size + cursor + offset) % size; - moveBlock(fion, oldpos, cursor, buffer1); - if (oldpos < newsize) { - cursor = oldpos; - } else - cursor++; - } - - current = newsize - 1; - length = newsize; - - delete [] buffer1; - - fclose(fion); - -} - -void BlockArray::increaseBuffer() -{ - if (index < size) // not even wrapped once - return; - - int offset = (current + size + 1) % size; - if (!offset) // no moving needed - return; - - // The Block constructor could do somthing in future... - char *buffer1 = new char[blocksize]; - char *buffer2 = new char[blocksize]; - - int runs = 1; - int bpr = size; // blocks per run - - if (size % offset == 0) { - bpr = size / offset; - runs = offset; - } - - FILE *fion = fdopen(dup(ion), "w+b"); - if (!fion) { - perror("fdopen/dup"); - delete [] buffer1; - delete [] buffer2; - return; - } - - int res; - for (int i = 0; i < runs; i++) - { - // free one block in chain - int firstblock = (offset + i) % size; - res = fseek(fion, firstblock * blocksize, SEEK_SET); - if (res) - perror("fseek"); - res = fread(buffer1, blocksize, 1, fion); - if (res != 1) - perror("fread"); - int newpos = 0; - for (int j = 1, cursor=firstblock; j < bpr; j++) - { - cursor = (cursor + offset) % size; - newpos = (cursor - offset + size) % size; - moveBlock(fion, cursor, newpos, buffer2); - } - res = fseek(fion, i * blocksize, SEEK_SET); - if (res) - perror("fseek"); - res = fwrite(buffer1, blocksize, 1, fion); - if (res != 1) - perror("fwrite"); - } - current = size - 1; - length = size; - - delete [] buffer1; - delete [] buffer2; - - fclose(fion); - -} -
deleted file mode 100644 --- a/gui/qtermwidget/lib/BlockArray.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - This file is part of Konsole, an X terminal. - Copyright (C) 2000 by Stephan Kulow <coolo@kde.org> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 BLOCKARRAY_H -#define BLOCKARRAY_H - -#include <unistd.h> - -//#error Do not use in KDE 2.1 - -#define BlockSize (1 << 12) -#define ENTRIES ((BlockSize - sizeof(size_t) ) / sizeof(unsigned char)) - -namespace Konsole -{ - -struct Block { - Block() { size = 0; } - unsigned char data[ENTRIES]; - size_t size; -}; - -// /////////////////////////////////////////////////////// - -class BlockArray { -public: - /** - * Creates a history file for holding - * maximal size blocks. If more blocks - * are requested, then it drops earlier - * added ones. - */ - BlockArray(); - - /// destructor - ~BlockArray(); - - /** - * adds the Block at the end of history. - * This may drop other blocks. - * - * The ownership on the block is transfered. - * An unique index number is returned for accessing - * it later (if not yet dropped then) - * - * Note, that the block may be dropped completely - * if history is turned off. - */ - size_t append(Block *block); - - /** - * gets the block at the index. Function may return - * 0 if the block isn't available any more. - * - * The returned block is strictly readonly as only - * maped in memory - and will be invalid on the next - * operation on this class. - */ - const Block *at(size_t index); - - /** - * reorders blocks as needed. If newsize is null, - * the history is emptied completely. The indices - * returned on append won't change their semantic, - * but they may not be valid after this call. - */ - bool setHistorySize(size_t newsize); - - size_t newBlock(); - - Block *lastBlock() const; - - /** - * Convenient function to set the size in KBytes - * instead of blocks - */ - bool setSize(size_t newsize); - - size_t len() const { return length; } - - bool has(size_t index) const; - - size_t getCurrent() const { return current; } - -private: - void unmap(); - void increaseBuffer(); - void decreaseBuffer(size_t newsize); - - size_t size; - // current always shows to the last inserted block - size_t current; - size_t index; - - Block *lastmap; - size_t lastmap_index; - Block *lastblock; - - int ion; - size_t length; - -}; - -} - -#endif
deleted file mode 100644 --- a/gui/qtermwidget/lib/CVS/Entries +++ /dev/null @@ -1,48 +0,0 @@ -/BlockArray.cpp/1.2/Wed May 14 19:36:48 2008// -/BlockArray.h/1.2/Wed May 14 19:36:48 2008// -/Character.h/1.2/Wed May 14 19:36:48 2008// -/CharacterColor.h/1.2/Wed May 14 19:36:49 2008// -/DefaultTranslatorText.h/1.1.1.1/Sat May 10 21:27:57 2008// -/Emulation.cpp/1.2/Wed May 14 19:36:49 2008// -/Emulation.h/1.2/Wed May 14 19:36:49 2008// -/ExtendedDefaultTranslator.h/1.1.1.1/Sat May 10 21:27:52 2008// -/Filter.cpp/1.2/Wed May 14 19:36:50 2008// -/Filter.h/1.2/Wed May 14 19:36:50 2008// -/History.cpp/1.2/Wed May 14 19:36:50 2008// -/History.h/1.2/Wed May 14 19:36:50 2008// -/KeyboardTranslator.cpp/1.2/Wed May 14 19:36:50 2008// -/KeyboardTranslator.h/1.2/Wed May 14 19:36:50 2008// -/LineFont.h/1.1.1.1/Sat May 10 21:27:49 2008// -/LineFont.src/1.1.1.1/Sat May 10 21:27:52 2008// -/Pty.cpp/1.2/Wed May 14 19:36:50 2008// -/Pty.h/1.2/Wed May 14 19:36:51 2008// -/README/1.1.1.1/Sat May 10 21:27:52 2008// -/Screen.cpp/1.2/Wed May 14 19:36:51 2008// -/Screen.h/1.2/Wed May 14 19:36:51 2008// -/ScreenWindow.cpp/1.2/Wed May 14 19:36:52 2008// -/ScreenWindow.h/1.2/Wed May 14 19:36:52 2008// -/Session.cpp/1.2/Wed May 14 19:36:52 2008// -/Session.h/1.2/Wed May 14 19:36:52 2008// -/ShellCommand.cpp/1.3/Fri May 23 12:30:35 2008// -/ShellCommand.h/1.2/Wed May 14 19:36:53 2008// -/TerminalCharacterDecoder.cpp/1.2/Wed May 14 19:36:53 2008// -/TerminalCharacterDecoder.h/1.2/Wed May 14 19:36:53 2008// -/Vt102Emulation.cpp/1.2/Wed May 14 19:36:53 2008// -/Vt102Emulation.h/1.2/Wed May 14 19:36:53 2008// -/default.keytab/1.1.1.1/Sat May 10 21:27:55 2008// -/k3process.cpp/1.2/Wed May 14 19:36:53 2008// -/k3process.h/1.2/Wed May 14 19:36:53 2008// -/k3processcontroller.cpp/1.2/Wed May 14 19:36:54 2008// -/k3processcontroller.h/1.2/Wed May 14 19:36:54 2008// -/konsole_wcwidth.cpp/1.1.1.1/Sat May 10 21:27:51 2008// -/konsole_wcwidth.h/1.2/Wed May 14 19:36:54 2008// -/kpty.cpp/1.2/Wed May 14 19:36:54 2008// -/kpty.h/1.2/Wed May 14 19:36:54 2008// -/kpty_p.h/1.2/Wed May 14 19:36:54 2008// -D/kb-layouts//// -/ColorTables.h/1.2/Sat Jun 7 20:13:53 2008// -/TerminalDisplay.cpp/1.4/Sat Jun 7 20:13:54 2008// -/TerminalDisplay.h/1.3/Sat Jun 7 20:13:54 2008// -/lib.pro/1.2/Wed Jul 30 22:00:49 2008// -/qtermwidget.cpp/1.7/Wed Jul 30 21:31:25 2008// -/qtermwidget.h/1.7/Wed Jul 30 21:48:51 2008//
deleted file mode 100644 --- a/gui/qtermwidget/lib/CVS/Repository +++ /dev/null @@ -1,1 +0,0 @@ -qtermwidget/lib
deleted file mode 100644 --- a/gui/qtermwidget/lib/CVS/Root +++ /dev/null @@ -1,1 +0,0 @@ -:ext:e_k@qtermwidget.cvs.sourceforge.net:/cvsroot/qtermwidget
deleted file mode 100644 --- a/gui/qtermwidget/lib/Character.h +++ /dev/null @@ -1,210 +0,0 @@ -/* - This file is part of Konsole, KDE's terminal. - - Copyright (C) 2007 by Robert Knight <robertknight@gmail.com> - Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 CHARACTER_H -#define CHARACTER_H - -// Qt -#include <QtCore/QHash> - -// Local -#include "CharacterColor.h" - -namespace Konsole -{ - -typedef unsigned char LineProperty; - -static const int LINE_DEFAULT = 0; -static const int LINE_WRAPPED = (1 << 0); -static const int LINE_DOUBLEWIDTH = (1 << 1); -static const int LINE_DOUBLEHEIGHT = (1 << 2); - -#define DEFAULT_RENDITION 0 -#define RE_BOLD (1 << 0) -#define RE_BLINK (1 << 1) -#define RE_UNDERLINE (1 << 2) -#define RE_REVERSE (1 << 3) // Screen only -#define RE_INTENSIVE (1 << 3) // Widget only -#define RE_CURSOR (1 << 4) -#define RE_EXTENDED_CHAR (1 << 5) - -/** - * A single character in the terminal which consists of a unicode character - * value, foreground and background colors and a set of rendition attributes - * which specify how it should be drawn. - */ -class Character -{ -public: - /** - * Constructs a new character. - * - * @param _c The unicode character value of this character. - * @param _f The foreground color used to draw the character. - * @param _b The color used to draw the character's background. - * @param _r A set of rendition flags which specify how this character is to be drawn. - */ - inline Character(quint16 _c = ' ', - CharacterColor _f = CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR), - CharacterColor _b = CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR), - quint8 _r = DEFAULT_RENDITION) - : character(_c), rendition(_r), foregroundColor(_f), backgroundColor(_b) {} - - union - { - /** The unicode character value for this character. */ - quint16 character; - /** - * Experimental addition which allows a single Character instance to contain more than - * one unicode character. - * - * charSequence is a hash code which can be used to look up the unicode - * character sequence in the ExtendedCharTable used to create the sequence. - */ - quint16 charSequence; - }; - - /** A combination of RENDITION flags which specify options for drawing the character. */ - quint8 rendition; - - /** The foreground color used to draw this character. */ - CharacterColor foregroundColor; - /** The color used to draw this character's background. */ - CharacterColor backgroundColor; - - /** - * Returns true if this character has a transparent background when - * it is drawn with the specified @p palette. - */ - bool isTransparent(const ColorEntry* palette) const; - /** - * Returns true if this character should always be drawn in bold when - * it is drawn with the specified @p palette, independent of whether - * or not the character has the RE_BOLD rendition flag. - */ - bool isBold(const ColorEntry* base) const; - - /** - * Compares two characters and returns true if they have the same unicode character value, - * rendition and colors. - */ - friend bool operator == (const Character& a, const Character& b); - /** - * Compares two characters and returns true if they have different unicode character values, - * renditions or colors. - */ - friend bool operator != (const Character& a, const Character& b); -}; - -inline bool operator == (const Character& a, const Character& b) -{ - return a.character == b.character && - a.rendition == b.rendition && - a.foregroundColor == b.foregroundColor && - a.backgroundColor == b.backgroundColor; -} - -inline bool operator != (const Character& a, const Character& b) -{ - return a.character != b.character || - a.rendition != b.rendition || - a.foregroundColor != b.foregroundColor || - a.backgroundColor != b.backgroundColor; -} - -inline bool Character::isTransparent(const ColorEntry* base) const -{ - return ((backgroundColor._colorSpace == COLOR_SPACE_DEFAULT) && - base[backgroundColor._u+0+(backgroundColor._v?BASE_COLORS:0)].transparent) - || ((backgroundColor._colorSpace == COLOR_SPACE_SYSTEM) && - base[backgroundColor._u+2+(backgroundColor._v?BASE_COLORS:0)].transparent); -} - -inline bool Character::isBold(const ColorEntry* base) const -{ - return ((backgroundColor._colorSpace == COLOR_SPACE_DEFAULT) && - base[backgroundColor._u+0+(backgroundColor._v?BASE_COLORS:0)].bold) - || ((backgroundColor._colorSpace == COLOR_SPACE_SYSTEM) && - base[backgroundColor._u+2+(backgroundColor._v?BASE_COLORS:0)].bold); -} - -extern unsigned short vt100_graphics[32]; - - -/** - * A table which stores sequences of unicode characters, referenced - * by hash keys. The hash key itself is the same size as a unicode - * character ( ushort ) so that it can occupy the same space in - * a structure. - */ -class ExtendedCharTable -{ -public: - /** Constructs a new character table. */ - ExtendedCharTable(); - ~ExtendedCharTable(); - - /** - * Adds a sequences of unicode characters to the table and returns - * a hash code which can be used later to look up the sequence - * using lookupExtendedChar() - * - * If the same sequence already exists in the table, the hash - * of the existing sequence will be returned. - * - * @param unicodePoints An array of unicode character points - * @param length Length of @p unicodePoints - */ - ushort createExtendedChar(ushort* unicodePoints , ushort length); - /** - * Looks up and returns a pointer to a sequence of unicode characters - * which was added to the table using createExtendedChar(). - * - * @param hash The hash key returned by createExtendedChar() - * @param length This variable is set to the length of the - * character sequence. - * - * @return A unicode character sequence of size @p length. - */ - ushort* lookupExtendedChar(ushort hash , ushort& length) const; - - /** The global ExtendedCharTable instance. */ - static ExtendedCharTable instance; -private: - // calculates the hash key of a sequence of unicode points of size 'length' - ushort extendedCharHash(ushort* unicodePoints , ushort length) const; - // tests whether the entry in the table specified by 'hash' matches the - // character sequence 'unicodePoints' of size 'length' - bool extendedCharMatch(ushort hash , ushort* unicodePoints , ushort length) const; - // internal, maps hash keys to character sequence buffers. The first ushort - // in each value is the length of the buffer, followed by the ushorts in the buffer - // themselves. - QHash<ushort,ushort*> extendedCharTable; -}; - -} - -#endif // CHARACTER_H -
deleted file mode 100644 --- a/gui/qtermwidget/lib/CharacterColor.h +++ /dev/null @@ -1,301 +0,0 @@ -/* - This file is part of Konsole, KDE's terminal. - - Copyright (C) 2007 by Robert Knight <robertknight@gmail.com> - Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 CHARACTERCOLOR_H -#define CHARACTERCOLOR_H - -// Qt -#include <QtGui/QColor> - -namespace Konsole -{ - -/** - * An entry in a terminal display's color palette. - * - * A color palette is an array of 16 ColorEntry instances which map - * system color indexes (from 0 to 15) into actual colors. - * - * Each entry can be set as bold, in which case any text - * drawn using the color should be drawn in bold. - * - * Each entry can also be transparent, in which case the terminal - * display should avoid drawing the background for any characters - * using the entry as a background. - */ -class ColorEntry -{ -public: - /** - * Constructs a new color palette entry. - * - * @param c The color value for this entry. - * @param tr Specifies that the color should be transparent when used as a background color. - * @param b Specifies that text drawn with this color should be bold. - */ - ColorEntry(QColor c, bool tr, bool b) : color(c), transparent(tr), bold(b) {} - - /** - * Constructs a new color palette entry with an undefined color, and - * with the transparent and bold flags set to false. - */ - ColorEntry() : transparent(false), bold(false) {} - - /** - * Sets the color, transparency and boldness of this color to those of @p rhs. - */ - void operator=(const ColorEntry& rhs) - { - color = rhs.color; - transparent = rhs.transparent; - bold = rhs.bold; - } - - /** The color value of this entry for display. */ - QColor color; - - /** - * If true character backgrounds using this color should be transparent. - * This is not applicable when the color is used to render text. - */ - bool transparent; - /** - * If true characters drawn using this color should be bold. - * This is not applicable when the color is used to draw a character's background. - */ - bool bold; -}; - - -// Attributed Character Representations /////////////////////////////// - -// Colors - -#define BASE_COLORS (2+8) -#define INTENSITIES 2 -#define TABLE_COLORS (INTENSITIES*BASE_COLORS) - -#define DEFAULT_FORE_COLOR 0 -#define DEFAULT_BACK_COLOR 1 - -//a standard set of colors using black text on a white background. -//defined in TerminalDisplay.cpp - -static const ColorEntry base_color_table[TABLE_COLORS] = -// The following are almost IBM standard color codes, with some slight -// gamma correction for the dim colors to compensate for bright X screens. -// It contains the 8 ansiterm/xterm colors in 2 intensities. -{ - // Fixme: could add faint colors here, also. - // normal - ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 1, 0 ), // Dfore, Dback - ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0x18), 0, 0 ), // Black, Red - ColorEntry(QColor(0x18,0xB2,0x18), 0, 0 ), ColorEntry( QColor(0xB2,0x68,0x18), 0, 0 ), // Green, Yellow - ColorEntry(QColor(0x18,0x18,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0xB2), 0, 0 ), // Blue, Magenta - ColorEntry(QColor(0x18,0xB2,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 0, 0 ), // Cyan, White - // intensiv - ColorEntry(QColor(0x00,0x00,0x00), 0, 1 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 1, 0 ), - ColorEntry(QColor(0x68,0x68,0x68), 0, 0 ), ColorEntry( QColor(0xFF,0x54,0x54), 0, 0 ), - ColorEntry(QColor(0x54,0xFF,0x54), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0x54), 0, 0 ), - ColorEntry(QColor(0x54,0x54,0xFF), 0, 0 ), ColorEntry( QColor(0xFF,0x54,0xFF), 0, 0 ), - ColorEntry(QColor(0x54,0xFF,0xFF), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 0, 0 ) -}; - -/* CharacterColor is a union of the various color spaces. - - Assignment is as follows: - - Type - Space - Values - - 0 - Undefined - u: 0, v:0 w:0 - 1 - Default - u: 0..1 v:intense w:0 - 2 - System - u: 0..7 v:intense w:0 - 3 - Index(256) - u: 16..255 v:0 w:0 - 4 - RGB - u: 0..255 v:0..256 w:0..256 - - Default colour space has two separate colours, namely - default foreground and default background colour. -*/ - -#define COLOR_SPACE_UNDEFINED 0 -#define COLOR_SPACE_DEFAULT 1 -#define COLOR_SPACE_SYSTEM 2 -#define COLOR_SPACE_256 3 -#define COLOR_SPACE_RGB 4 - -/** - * Describes the color of a single character in the terminal. - */ -class CharacterColor -{ - friend class Character; - -public: - /** Constructs a new CharacterColor whoose color and color space are undefined. */ - CharacterColor() - : _colorSpace(COLOR_SPACE_UNDEFINED), - _u(0), - _v(0), - _w(0) - {} - - /** - * Constructs a new CharacterColor using the specified @p colorSpace and with - * color value @p co - * - * The meaning of @p co depends on the @p colorSpace used. - * - * TODO : Document how @p co relates to @p colorSpace - * - * TODO : Add documentation about available color spaces. - */ - CharacterColor(quint8 colorSpace, int co) - : _colorSpace(colorSpace), - _u(0), - _v(0), - _w(0) - { - switch (colorSpace) - { - case COLOR_SPACE_DEFAULT: - _u = co & 1; - break; - case COLOR_SPACE_SYSTEM: - _u = co & 7; - _v = (co >> 3) & 1; - break; - case COLOR_SPACE_256: - _u = co & 255; - break; - case COLOR_SPACE_RGB: - _u = co >> 16; - _v = co >> 8; - _w = co; - break; - default: - _colorSpace = COLOR_SPACE_UNDEFINED; - } - } - - /** - * Returns true if this character color entry is valid. - */ - bool isValid() - { - return _colorSpace != COLOR_SPACE_UNDEFINED; - } - - /** - * Toggles the value of this color between a normal system color and the corresponding intensive - * system color. - * - * This is only applicable if the color is using the COLOR_SPACE_DEFAULT or COLOR_SPACE_SYSTEM - * color spaces. - */ - void toggleIntensive(); - - /** - * Returns the color within the specified color @palette - * - * The @p palette is only used if this color is one of the 16 system colors, otherwise - * it is ignored. - */ - QColor color(const ColorEntry* palette) const; - - /** - * Compares two colors and returns true if they represent the same color value and - * use the same color space. - */ - friend bool operator == (const CharacterColor& a, const CharacterColor& b); - /** - * Compares two colors and returns true if they represent different color values - * or use different color spaces. - */ - friend bool operator != (const CharacterColor& a, const CharacterColor& b); - -private: - quint8 _colorSpace; - - // bytes storing the character color - quint8 _u; - quint8 _v; - quint8 _w; -}; - -inline bool operator == (const CharacterColor& a, const CharacterColor& b) -{ - return *reinterpret_cast<const quint32*>(&a._colorSpace) == - *reinterpret_cast<const quint32*>(&b._colorSpace); -} - -inline bool operator != (const CharacterColor& a, const CharacterColor& b) -{ - return *reinterpret_cast<const quint32*>(&a._colorSpace) != - *reinterpret_cast<const quint32*>(&b._colorSpace); -} - -inline const QColor color256(quint8 u, const ColorEntry* base) -{ - // 0.. 16: system colors - if (u < 8) return base[u+2 ].color; u -= 8; - if (u < 8) return base[u+2+BASE_COLORS].color; u -= 8; - - // 16..231: 6x6x6 rgb color cube - if (u < 216) return QColor(255*((u/36)%6)/5, - 255*((u/ 6)%6)/5, - 255*((u/ 1)%6)/5); u -= 216; - - // 232..255: gray, leaving out black and white - int gray = u*10+8; return QColor(gray,gray,gray); -} - -inline QColor CharacterColor::color(const ColorEntry* base) const -{ - switch (_colorSpace) - { - case COLOR_SPACE_DEFAULT: return base[_u+0+(_v?BASE_COLORS:0)].color; - case COLOR_SPACE_SYSTEM: return base[_u+2+(_v?BASE_COLORS:0)].color; - case COLOR_SPACE_256: return color256(_u,base); - case COLOR_SPACE_RGB: return QColor(_u,_v,_w); - case COLOR_SPACE_UNDEFINED: return QColor(); - } - - Q_ASSERT(false); // invalid color space - - return QColor(); -} - -inline void CharacterColor::toggleIntensive() -{ - if (_colorSpace == COLOR_SPACE_SYSTEM || _colorSpace == COLOR_SPACE_DEFAULT) - { - _v = !_v; - } -} - - -} - -#endif // CHARACTERCOLOR_H -
deleted file mode 100644 --- a/gui/qtermwidget/lib/ColorTables.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef _COLOR_TABLE_H -#define _COLOR_TABLE_H - -#include "CharacterColor.h" - -using namespace Konsole; - -static const ColorEntry whiteonblack_color_table[TABLE_COLORS] = -{ - // normal - ColorEntry(QColor(0xFF,0xFF,0xFF), 0, 0 ), ColorEntry( QColor(0x00,0x00,0x00), 1, 0 ), // Dfore, Dback - ColorEntry(QColor(0x00,0x00,0x00), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0x18), 0, 0 ), // Black, Red - ColorEntry(QColor(0x18,0xB2,0x18), 0, 0 ), ColorEntry( QColor(0xB2,0x68,0x18), 0, 0 ), // Green, Yellow - ColorEntry(QColor(0x18,0x18,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0x18,0xB2), 0, 0 ), // Blue, Magenta - ColorEntry(QColor(0x18,0xB2,0xB2), 0, 0 ), ColorEntry( QColor(0xB2,0xB2,0xB2), 0, 0 ), // Cyan, White - // intensiv - ColorEntry(QColor(0x00,0x00,0x00), 0, 1 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 1, 0 ), - ColorEntry(QColor(0x68,0x68,0x68), 0, 0 ), ColorEntry( QColor(0xFF,0x54,0x54), 0, 0 ), - ColorEntry(QColor(0x54,0xFF,0x54), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0x54), 0, 0 ), - ColorEntry(QColor(0x54,0x54,0xFF), 0, 0 ), ColorEntry( QColor(0xFF,0x54,0xFF), 0, 0 ), - ColorEntry(QColor(0x54,0xFF,0xFF), 0, 0 ), ColorEntry( QColor(0xFF,0xFF,0xFF), 0, 0 ) -}; - -static const ColorEntry greenonblack_color_table[TABLE_COLORS] = -{ - ColorEntry(QColor( 24, 240, 24), 0, 0), ColorEntry(QColor( 0, 0, 0), 1, 0), - ColorEntry(QColor( 0, 0, 0), 0, 0), ColorEntry(QColor( 178, 24, 24), 0, 0), - ColorEntry(QColor( 24, 178, 24), 0, 0), ColorEntry(QColor( 178, 104, 24), 0, 0), - ColorEntry(QColor( 24, 24, 178), 0, 0), ColorEntry(QColor( 178, 24, 178), 0, 0), - ColorEntry(QColor( 24, 178, 178), 0, 0), ColorEntry(QColor( 178, 178, 178), 0, 0), - // intensive colors - ColorEntry(QColor( 24, 240, 24), 0, 1 ), ColorEntry(QColor( 0, 0, 0), 1, 0 ), - ColorEntry(QColor( 104, 104, 104), 0, 0 ), ColorEntry(QColor( 255, 84, 84), 0, 0 ), - ColorEntry(QColor( 84, 255, 84), 0, 0 ), ColorEntry(QColor( 255, 255, 84), 0, 0 ), - ColorEntry(QColor( 84, 84, 255), 0, 0 ), ColorEntry(QColor( 255, 84, 255), 0, 0 ), - ColorEntry(QColor( 84, 255, 255), 0, 0 ), ColorEntry(QColor( 255, 255, 255), 0, 0 ) -}; - -static const ColorEntry blackonlightyellow_color_table[TABLE_COLORS] = -{ - ColorEntry(QColor( 0, 0, 0), 0, 0), ColorEntry(QColor( 255, 255, 221), 1, 0), - ColorEntry(QColor( 0, 0, 0), 0, 0), ColorEntry(QColor( 178, 24, 24), 0, 0), - ColorEntry(QColor( 24, 178, 24), 0, 0), ColorEntry(QColor( 178, 104, 24), 0, 0), - ColorEntry(QColor( 24, 24, 178), 0, 0), ColorEntry(QColor( 178, 24, 178), 0, 0), - ColorEntry(QColor( 24, 178, 178), 0, 0), ColorEntry(QColor( 178, 178, 178), 0, 0), - ColorEntry(QColor( 0, 0, 0), 0, 1), ColorEntry(QColor( 255, 255, 221), 1, 0), - ColorEntry(QColor(104, 104, 104), 0, 0), ColorEntry(QColor( 255, 84, 84), 0, 0), - ColorEntry(QColor( 84, 255, 84), 0, 0), ColorEntry(QColor( 255, 255, 84), 0, 0), - ColorEntry(QColor( 84, 84, 255), 0, 0), ColorEntry(QColor( 255, 84, 255), 0, 0), - ColorEntry(QColor( 84, 255, 255), 0, 0), ColorEntry(QColor( 255, 255, 255), 0, 0) -}; - - - - - -#endif -
deleted file mode 100644 --- a/gui/qtermwidget/lib/DefaultTranslatorText.h +++ /dev/null @@ -1,2 +0,0 @@ -"keyboard \"Fallback Key Translator\"\n" -"key Tab : \"\\t\" \0"
deleted file mode 100644 --- a/gui/qtermwidget/lib/Emulation.cpp +++ /dev/null @@ -1,543 +0,0 @@ -/* - This file is part of Konsole, an X terminal. - - Copyright (C) 2007 Robert Knight <robertknight@gmail.com> - Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> - Copyright (C) 1996 by Matthias Ettrich <ettrich@kde.org> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 "Emulation.h" - -// System -#include <assert.h> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> - -// Qt -#include <QtGui/QApplication> -#include <QtGui/QClipboard> -#include <QtCore/QHash> -#include <QtGui/QKeyEvent> -#include <QtCore/QRegExp> -#include <QtCore/QTextStream> -#include <QtCore/QThread> - -#include <QtCore/QTime> - -// Konsole -#include "KeyboardTranslator.h" -#include "Screen.h" -#include "TerminalCharacterDecoder.h" -#include "ScreenWindow.h" - -using namespace Konsole; - -/* ------------------------------------------------------------------------- */ -/* */ -/* Emulation */ -/* */ -/* ------------------------------------------------------------------------- */ - -//#define CNTL(c) ((c)-'@') - -/*! -*/ - -Emulation::Emulation() : - _currentScreen(0), - _codec(0), - _decoder(0), - _keyTranslator(0), - _usesMouse(false) -{ - - // create screens with a default size - _screen[0] = new Screen(40,80); - _screen[1] = new Screen(40,80); - _currentScreen = _screen[0]; - - QObject::connect(&_bulkTimer1, SIGNAL(timeout()), this, SLOT(showBulk()) ); - QObject::connect(&_bulkTimer2, SIGNAL(timeout()), this, SLOT(showBulk()) ); - - // listen for mouse status changes - connect( this , SIGNAL(programUsesMouseChanged(bool)) , - SLOT(usesMouseChanged(bool)) ); -} - -bool Emulation::programUsesMouse() const -{ - return _usesMouse; -} - -void Emulation::usesMouseChanged(bool usesMouse) -{ - _usesMouse = usesMouse; -} - -ScreenWindow* Emulation::createWindow() -{ - ScreenWindow* window = new ScreenWindow(); - window->setScreen(_currentScreen); - _windows << window; - - connect(window , SIGNAL(selectionChanged()), - this , SLOT(bufferedUpdate())); - - connect(this , SIGNAL(outputChanged()), - window , SLOT(notifyOutputChanged()) ); - return window; -} - -/*! -*/ - -Emulation::~Emulation() -{ - QListIterator<ScreenWindow*> windowIter(_windows); - - while (windowIter.hasNext()) - { - delete windowIter.next(); - } - - delete _screen[0]; - delete _screen[1]; - delete _decoder; -} - -/*! change between primary and alternate _screen -*/ - -void Emulation::setScreen(int n) -{ - Screen *old = _currentScreen; - _currentScreen = _screen[n&1]; - if (_currentScreen != old) - { - old->setBusySelecting(false); - - // tell all windows onto this emulation to switch to the newly active _screen - QListIterator<ScreenWindow*> windowIter(_windows); - while ( windowIter.hasNext() ) - { - windowIter.next()->setScreen(_currentScreen); - } - } -} - -void Emulation::clearHistory() -{ - _screen[0]->setScroll( _screen[0]->getScroll() , false ); -} -void Emulation::setHistory(const HistoryType& t) -{ - _screen[0]->setScroll(t); - - showBulk(); -} - -const HistoryType& Emulation::history() -{ - return _screen[0]->getScroll(); -} - -void Emulation::setCodec(const QTextCodec * qtc) -{ - Q_ASSERT( qtc ); - - _codec = qtc; - delete _decoder; - _decoder = _codec->makeDecoder(); - - emit useUtf8Request(utf8()); -} - -void Emulation::setCodec(EmulationCodec codec) -{ - if ( codec == Utf8Codec ) - setCodec( QTextCodec::codecForName("utf8") ); - else if ( codec == LocaleCodec ) - setCodec( QTextCodec::codecForLocale() ); -} - -void Emulation::setKeyBindings(const QString& name) -{ - _keyTranslator = KeyboardTranslatorManager::instance()->findTranslator(name); -} - -QString Emulation::keyBindings() -{ - return _keyTranslator->name(); -} - - -// Interpreting Codes --------------------------------------------------------- - -/* - This section deals with decoding the incoming character stream. - Decoding means here, that the stream is first separated into `tokens' - which are then mapped to a `meaning' provided as operations by the - `Screen' class. -*/ - -/*! -*/ - -void Emulation::receiveChar(int c) -// process application unicode input to terminal -// this is a trivial scanner -{ - c &= 0xff; - switch (c) - { - case '\b' : _currentScreen->BackSpace(); break; - case '\t' : _currentScreen->Tabulate(); break; - case '\n' : _currentScreen->NewLine(); break; - case '\r' : _currentScreen->Return(); break; - case 0x07 : emit stateSet(NOTIFYBELL); - break; - default : _currentScreen->ShowCharacter(c); break; - }; -} - -/* ------------------------------------------------------------------------- */ -/* */ -/* Keyboard Handling */ -/* */ -/* ------------------------------------------------------------------------- */ - -/*! -*/ - -void Emulation::sendKeyEvent( QKeyEvent* ev ) -{ - emit stateSet(NOTIFYNORMAL); - - if (!ev->text().isEmpty()) - { // A block of text - // Note that the text is proper unicode. - // We should do a conversion here, but since this - // routine will never be used, we simply emit plain ascii. - //emit sendBlock(ev->text().toAscii(),ev->text().length()); - emit sendData(ev->text().toUtf8(),ev->text().length()); - } -} - -void Emulation::sendString(const char*,int) -{ - // default implementation does nothing -} - -void Emulation::sendMouseEvent(int /*buttons*/, int /*column*/, int /*row*/, int /*eventType*/) -{ - // default implementation does nothing -} - -// Unblocking, Byte to Unicode translation --------------------------------- -- - -/* - We are doing code conversion from locale to unicode first. -TODO: Character composition from the old code. See #96536 -*/ - -void Emulation::receiveData(const char* text, int length) -{ - emit stateSet(NOTIFYACTIVITY); - - bufferedUpdate(); - - QString unicodeText = _decoder->toUnicode(text,length); - - //send characters to terminal emulator - for (int i=0;i<unicodeText.length();i++) - { - receiveChar(unicodeText[i].unicode()); - } - - //look for z-modem indicator - //-- someone who understands more about z-modems that I do may be able to move - //this check into the above for loop? - for (int i=0;i<length;i++) - { - if (text[i] == '\030') - { - if ((length-i-1 > 3) && (strncmp(text+i+1, "B00", 3) == 0)) - emit zmodemDetected(); - } - } -} - -//OLDER VERSION -//This version of onRcvBlock was commented out because -// a) It decoded incoming characters one-by-one, which is slow in the current version of Qt (4.2 tech preview) -// b) It messed up decoding of non-ASCII characters, with the result that (for example) chinese characters -// were not printed properly. -// -//There is something about stopping the _decoder if "we get a control code halfway a multi-byte sequence" (see below) -//which hasn't been ported into the newer function (above). Hopefully someone who understands this better -//can find an alternative way of handling the check. - - -/*void Emulation::onRcvBlock(const char *s, int len) -{ - emit notifySessionState(NOTIFYACTIVITY); - - bufferedUpdate(); - for (int i = 0; i < len; i++) - { - - QString result = _decoder->toUnicode(&s[i],1); - int reslen = result.length(); - - // If we get a control code halfway a multi-byte sequence - // we flush the _decoder and continue with the control code. - if ((s[i] < 32) && (s[i] > 0)) - { - // Flush _decoder - while(!result.length()) - result = _decoder->toUnicode(&s[i],1); - reslen = 1; - result.resize(reslen); - result[0] = QChar(s[i]); - } - - for (int j = 0; j < reslen; j++) - { - if (result[j].characterategory() == QChar::Mark_NonSpacing) - _currentScreen->compose(result.mid(j,1)); - else - onRcvChar(result[j].unicode()); - } - if (s[i] == '\030') - { - if ((len-i-1 > 3) && (strncmp(s+i+1, "B00", 3) == 0)) - emit zmodemDetected(); - } - } -}*/ - -// Selection --------------------------------------------------------------- -- - -#if 0 -void Emulation::onSelectionBegin(const int x, const int y, const bool columnmode) { - if (!connected) return; - _currentScreen->setSelectionStart( x,y,columnmode); - showBulk(); -} - -void Emulation::onSelectionExtend(const int x, const int y) { - if (!connected) return; - _currentScreen->setSelectionEnd(x,y); - showBulk(); -} - -void Emulation::setSelection(const bool preserve_line_breaks) { - if (!connected) return; - QString t = _currentScreen->selectedText(preserve_line_breaks); - if (!t.isNull()) - { - QListIterator< TerminalDisplay* > viewIter(_views); - - while (viewIter.hasNext()) - viewIter.next()->setSelection(t); - } -} - -void Emulation::testIsSelected(const int x, const int y, bool &selected) -{ - if (!connected) return; - selected=_currentScreen->isSelected(x,y); -} - -void Emulation::clearSelection() { - if (!connected) return; - _currentScreen->clearSelection(); - showBulk(); -} - -#endif - -void Emulation::writeToStream( TerminalCharacterDecoder* _decoder , - int startLine , - int endLine) -{ - _currentScreen->writeToStream(_decoder,startLine,endLine); -} - -int Emulation::lineCount() -{ - // sum number of lines currently on _screen plus number of lines in history - return _currentScreen->getLines() + _currentScreen->getHistLines(); -} - -// Refreshing -------------------------------------------------------------- -- - -#define BULK_TIMEOUT1 10 -#define BULK_TIMEOUT2 40 - -/*! -*/ -void Emulation::showBulk() -{ - _bulkTimer1.stop(); - _bulkTimer2.stop(); - - emit outputChanged(); - - _currentScreen->resetScrolledLines(); - _currentScreen->resetDroppedLines(); -} - -void Emulation::bufferedUpdate() -{ - _bulkTimer1.setSingleShot(true); - _bulkTimer1.start(BULK_TIMEOUT1); - if (!_bulkTimer2.isActive()) - { - _bulkTimer2.setSingleShot(true); - _bulkTimer2.start(BULK_TIMEOUT2); - } -} - -char Emulation::getErase() const -{ - return '\b'; -} - -void Emulation::setImageSize(int lines, int columns) -{ - //kDebug() << "Resizing image to: " << lines << "by" << columns << QTime::currentTime().msec(); - Q_ASSERT( lines > 0 ); - Q_ASSERT( columns > 0 ); - - _screen[0]->resizeImage(lines,columns); - _screen[1]->resizeImage(lines,columns); - - emit imageSizeChanged(lines,columns); - - bufferedUpdate(); -} - -QSize Emulation::imageSize() -{ - return QSize(_currentScreen->getColumns(), _currentScreen->getLines()); -} - -ushort ExtendedCharTable::extendedCharHash(ushort* unicodePoints , ushort length) const -{ - ushort hash = 0; - for ( ushort i = 0 ; i < length ; i++ ) - { - hash = 31*hash + unicodePoints[i]; - } - return hash; -} -bool ExtendedCharTable::extendedCharMatch(ushort hash , ushort* unicodePoints , ushort length) const -{ - ushort* entry = extendedCharTable[hash]; - - // compare given length with stored sequence length ( given as the first ushort in the - // stored buffer ) - if ( entry == 0 || entry[0] != length ) - return false; - // if the lengths match, each character must be checked. the stored buffer starts at - // entry[1] - for ( int i = 0 ; i < length ; i++ ) - { - if ( entry[i+1] != unicodePoints[i] ) - return false; - } - return true; -} -ushort ExtendedCharTable::createExtendedChar(ushort* unicodePoints , ushort length) -{ - // look for this sequence of points in the table - ushort hash = extendedCharHash(unicodePoints,length); - - // check existing entry for match - while ( extendedCharTable.contains(hash) ) - { - if ( extendedCharMatch(hash,unicodePoints,length) ) - { - // this sequence already has an entry in the table, - // return its hash - return hash; - } - else - { - // if hash is already used by another, different sequence of unicode character - // points then try next hash - hash++; - } - } - - - // add the new sequence to the table and - // return that index - ushort* buffer = new ushort[length+1]; - buffer[0] = length; - for ( int i = 0 ; i < length ; i++ ) - buffer[i+1] = unicodePoints[i]; - - extendedCharTable.insert(hash,buffer); - - return hash; -} - -ushort* ExtendedCharTable::lookupExtendedChar(ushort hash , ushort& length) const -{ - // lookup index in table and if found, set the length - // argument and return a pointer to the character sequence - - ushort* buffer = extendedCharTable[hash]; - if ( buffer ) - { - length = buffer[0]; - return buffer+1; - } - else - { - length = 0; - return 0; - } -} - -ExtendedCharTable::ExtendedCharTable() -{ -} -ExtendedCharTable::~ExtendedCharTable() -{ - // free all allocated character buffers - QHashIterator<ushort,ushort*> iter(extendedCharTable); - while ( iter.hasNext() ) - { - iter.next(); - delete[] iter.value(); - } -} - -// global instance -ExtendedCharTable ExtendedCharTable::instance; - - -//#include "moc_Emulation.cpp" -
deleted file mode 100644 --- a/gui/qtermwidget/lib/Emulation.h +++ /dev/null @@ -1,465 +0,0 @@ -/* - This file is part of Konsole, an X terminal. - - Copyright (C) 2007 by Robert Knight <robertknight@gmail.com> - Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 EMULATION_H -#define EMULATION_H - -// System -#include <stdio.h> - -// Qt -#include <QtGui/QKeyEvent> -//#include <QPointer> -#include <QtCore/QTextCodec> -#include <QtCore/QTextStream> -#include <QtCore/QTimer> - - -namespace Konsole -{ - -class KeyboardTranslator; -class HistoryType; -class Screen; -class ScreenWindow; -class TerminalCharacterDecoder; - -/** - * This enum describes the available states which - * the terminal emulation may be set to. - * - * These are the values used by Emulation::stateChanged() - */ -enum -{ - /** The emulation is currently receiving user input. */ - NOTIFYNORMAL=0, - /** - * The terminal program has triggered a bell event - * to get the user's attention. - */ - NOTIFYBELL=1, - /** - * The emulation is currently receiving data from its - * terminal input. - */ - NOTIFYACTIVITY=2, - - // unused here? - NOTIFYSILENCE=3 -}; - -/** - * Base class for terminal emulation back-ends. - * - * The back-end is responsible for decoding an incoming character stream and - * producing an output image of characters. - * - * When input from the terminal is received, the receiveData() slot should be called with - * the data which has arrived. The emulation will process the data and update the - * screen image accordingly. The codec used to decode the incoming character stream - * into the unicode characters used internally can be specified using setCodec() - * - * The size of the screen image can be specified by calling setImageSize() with the - * desired number of lines and columns. When new lines are added, old content - * is moved into a history store, which can be set by calling setHistory(). - * - * The screen image can be accessed by creating a ScreenWindow onto this emulation - * by calling createWindow(). Screen windows provide access to a section of the - * output. Each screen window covers the same number of lines and columns as the - * image size returned by imageSize(). The screen window can be moved up and down - * and provides transparent access to both the current on-screen image and the - * previous output. The screen windows emit an outputChanged signal - * when the section of the image they are looking at changes. - * Graphical views can then render the contents of a screen window, listening for notifications - * of output changes from the screen window which they are associated with and updating - * accordingly. - * - * The emulation also is also responsible for converting input from the connected views such - * as keypresses and mouse activity into a character string which can be sent - * to the terminal program. Key presses can be processed by calling the sendKeyEvent() slot, - * while mouse events can be processed using the sendMouseEvent() slot. When the character - * stream has been produced, the emulation will emit a sendData() signal with a pointer - * to the character buffer. This data should be fed to the standard input of the terminal - * process. The translation of key presses into an output character stream is performed - * using a lookup in a set of key bindings which map key sequences to output - * character sequences. The name of the key bindings set used can be specified using - * setKeyBindings() - * - * The emulation maintains certain state information which changes depending on the - * input received. The emulation can be reset back to its starting state by calling - * reset(). - * - * The emulation also maintains an activity state, which specifies whether - * terminal is currently active ( when data is received ), normal - * ( when the terminal is idle or receiving user input ) or trying - * to alert the user ( also known as a "Bell" event ). The stateSet() signal - * is emitted whenever the activity state is set. This can be used to determine - * how long the emulation has been active/idle for and also respond to - * a 'bell' event in different ways. - */ -class Emulation : public QObject -{ -Q_OBJECT - -public: - - /** Constructs a new terminal emulation */ - Emulation(); - ~Emulation(); - - /** - * Creates a new window onto the output from this emulation. The contents - * of the window are then rendered by views which are set to use this window using the - * TerminalDisplay::setScreenWindow() method. - */ - ScreenWindow* createWindow(); - - /** Returns the size of the screen image which the emulation produces */ - QSize imageSize(); - - /** - * Returns the total number of lines, including those stored in the history. - */ - int lineCount(); - - - /** - * Sets the history store used by this emulation. When new lines - * are added to the output, older lines at the top of the screen are transferred to a history - * store. - * - * The number of lines which are kept and the storage location depend on the - * type of store. - */ - void setHistory(const HistoryType&); - /** Returns the history store used by this emulation. See setHistory() */ - const HistoryType& history(); - /** Clears the history scroll. */ - void clearHistory(); - - /** - * Copies the output history from @p startLine to @p endLine - * into @p stream, using @p decoder to convert the terminal - * characters into text. - * - * @param decoder A decoder which converts lines of terminal characters with - * appearance attributes into output text. PlainTextDecoder is the most commonly - * used decoder. - * @param startLine The first - */ - virtual void writeToStream(TerminalCharacterDecoder* decoder,int startLine,int endLine); - - - /** Returns the codec used to decode incoming characters. See setCodec() */ - const QTextCodec* codec() { return _codec; } - /** Sets the codec used to decode incoming characters. */ - void setCodec(const QTextCodec*); - - /** - * Convenience method. - * Returns true if the current codec used to decode incoming - * characters is UTF-8 - */ - bool utf8() { Q_ASSERT(_codec); return _codec->mibEnum() == 106; } - - - /** TODO Document me */ - virtual char getErase() const; - - /** - * Sets the key bindings used to key events - * ( received through sendKeyEvent() ) into character - * streams to send to the terminal. - */ - void setKeyBindings(const QString& name); - /** - * Returns the name of the emulation's current key bindings. - * See setKeyBindings() - */ - QString keyBindings(); - - /** - * Copies the current image into the history and clears the screen. - */ - virtual void clearEntireScreen() =0; - - /** Resets the state of the terminal. */ - virtual void reset() =0; - - /** - * Returns true if the active terminal program wants - * mouse input events. - * - * The programUsesMouseChanged() signal is emitted when this - * changes. - */ - bool programUsesMouse() const; - -public slots: - - /** Change the size of the emulation's image */ - virtual void setImageSize(int lines, int columns); - - /** - * Interprets a sequence of characters and sends the result to the terminal. - * This is equivalent to calling sendKeyEvent() for each character in @p text in succession. - */ - virtual void sendText(const QString& text) = 0; - - /** - * Interprets a key press event and emits the sendData() signal with - * the resulting character stream. - */ - virtual void sendKeyEvent(QKeyEvent*); - - /** - * Converts information about a mouse event into an xterm-compatible escape - * sequence and emits the character sequence via sendData() - */ - virtual void sendMouseEvent(int buttons, int column, int line, int eventType); - - /** - * Sends a string of characters to the foreground terminal process. - * - * @param string The characters to send. - * @param length Length of @p string or if set to a negative value, @p string will - * be treated as a null-terminated string and its length will be determined automatically. - */ - virtual void sendString(const char* string, int length = -1) = 0; - - /** - * Processes an incoming stream of characters. receiveData() decodes the incoming - * character buffer using the current codec(), and then calls receiveChar() for - * each unicode character in the resulting buffer. - * - * receiveData() also starts a timer which causes the outputChanged() signal - * to be emitted when it expires. The timer allows multiple updates in quick - * succession to be buffered into a single outputChanged() signal emission. - * - * @param buffer A string of characters received from the terminal program. - * @param len The length of @p buffer - */ - void receiveData(const char* buffer,int len); - -signals: - - /** - * Emitted when a buffer of data is ready to send to the - * standard input of the terminal. - * - * @param data The buffer of data ready to be sent - * @paran len The length of @p data in bytes - */ - void sendData(const char* data,int len); - - /** - * Requests that sending of input to the emulation - * from the terminal process be suspended or resumed. - * - * @param suspend If true, requests that sending of - * input from the terminal process' stdout be - * suspended. Otherwise requests that sending of - * input be resumed. - */ - void lockPtyRequest(bool suspend); - - /** - * Requests that the pty used by the terminal process - * be set to UTF 8 mode. - * - * TODO: More documentation - */ - void useUtf8Request(bool); - - /** - * Emitted when the activity state of the emulation is set. - * - * @param state The new activity state, one of NOTIFYNORMAL, NOTIFYACTIVITY - * or NOTIFYBELL - */ - void stateSet(int state); - - /** TODO Document me */ - void zmodemDetected(); - - - /** - * Requests that the color of the text used - * to represent the tabs associated with this - * emulation be changed. This is a Konsole-specific - * extension from pre-KDE 4 times. - * - * TODO: Document how the parameter works. - */ - void changeTabTextColorRequest(int color); - - /** - * This is emitted when the program running in the shell indicates whether or - * not it is interested in mouse events. - * - * @param usesMouse This will be true if the program wants to be informed about - * mouse events or false otherwise. - */ - void programUsesMouseChanged(bool usesMouse); - - /** - * Emitted when the contents of the screen image change. - * The emulation buffers the updates from successive image changes, - * and only emits outputChanged() at sensible intervals when - * there is a lot of terminal activity. - * - * Normally there is no need for objects other than the screen windows - * created with createWindow() to listen for this signal. - * - * ScreenWindow objects created using createWindow() will emit their - * own outputChanged() signal in response to this signal. - */ - void outputChanged(); - - /** - * Emitted when the program running in the terminal wishes to update the - * session's title. This also allows terminal programs to customize other - * aspects of the terminal emulation display. - * - * This signal is emitted when the escape sequence "\033]ARG;VALUE\007" - * is received in the input string, where ARG is a number specifying what - * should change and VALUE is a string specifying the new value. - * - * TODO: The name of this method is not very accurate since this method - * is used to perform a whole range of tasks besides just setting - * the user-title of the session. - * - * @param title Specifies what to change. - * <ul> - * <li>0 - Set window icon text and session title to @p newTitle</li> - * <li>1 - Set window icon text to @p newTitle</li> - * <li>2 - Set session title to @p newTitle</li> - * <li>11 - Set the session's default background color to @p newTitle, - * where @p newTitle can be an HTML-style string (#RRGGBB) or a named - * color (eg 'red', 'blue'). - * See http://doc.trolltech.com/4.2/qcolor.html#setNamedColor for more - * details. - * </li> - * <li>31 - Supposedly treats @p newTitle as a URL and opens it (NOT IMPLEMENTED)</li> - * <li>32 - Sets the icon associated with the session. @p newTitle is the name - * of the icon to use, which can be the name of any icon in the current KDE icon - * theme (eg: 'konsole', 'kate', 'folder_home')</li> - * </ul> - * @param newTitle Specifies the new title - */ - - void titleChanged(int title,const QString& newTitle); - - /** - * Emitted when the program running in the terminal changes the - * screen size. - */ - void imageSizeChanged(int lineCount , int columnCount); - - /** - * Emitted when the terminal program requests to change various properties - * of the terminal display. - * - * A profile change command occurs when a special escape sequence, followed - * by a string containing a series of name and value pairs is received. - * This string can be parsed using a ProfileCommandParser instance. - * - * @param text A string expected to contain a series of key and value pairs in - * the form: name=value;name2=value2 ... - */ - void profileChangeCommandReceived(const QString& text); - -protected: - virtual void setMode (int mode) = 0; - virtual void resetMode(int mode) = 0; - - /** - * Processes an incoming character. See receiveData() - * @p ch A unicode character code. - */ - virtual void receiveChar(int ch); - - /** - * Sets the active screen. The terminal has two screens, primary and alternate. - * The primary screen is used by default. When certain interactive programs such - * as Vim are run, they trigger a switch to the alternate screen. - * - * @param index 0 to switch to the primary screen, or 1 to switch to the alternate screen - */ - void setScreen(int index); - - enum EmulationCodec - { - LocaleCodec = 0, - Utf8Codec = 1 - }; - void setCodec(EmulationCodec codec); // codec number, 0 = locale, 1=utf8 - - - QList<ScreenWindow*> _windows; - - Screen* _currentScreen; // pointer to the screen which is currently active, - // this is one of the elements in the screen[] array - - Screen* _screen[2]; // 0 = primary screen ( used by most programs, including the shell - // scrollbars are enabled in this mode ) - // 1 = alternate ( used by vi , emacs etc. - // scrollbars are not enabled in this mode ) - - - //decodes an incoming C-style character stream into a unicode QString using - //the current text codec. (this allows for rendering of non-ASCII characters in text files etc.) - const QTextCodec* _codec; - QTextDecoder* _decoder; - - const KeyboardTranslator* _keyTranslator; // the keyboard layout - -protected slots: - /** - * Schedules an update of attached views. - * Repeated calls to bufferedUpdate() in close succession will result in only a single update, - * much like the Qt buffered update of widgets. - */ - void bufferedUpdate(); - -private slots: - - // triggered by timer, causes the emulation to send an updated screen image to each - // view - void showBulk(); - - void usesMouseChanged(bool usesMouse); - -private: - - bool _usesMouse; - QTimer _bulkTimer1; - QTimer _bulkTimer2; - -}; - -} - -#endif // ifndef EMULATION_H
deleted file mode 100644 --- a/gui/qtermwidget/lib/ExtendedDefaultTranslator.h +++ /dev/null @@ -1,74 +0,0 @@ -"keyboard \"Default (XFree 4)\"" -"key Escape : \"\\E\"" -"key Tab -Shift : \"\\t\"\n" -"key Tab +Shift+Ansi : \"\\E[Z\"\n" -"key Tab +Shift-Ansi : \"\\t\"\n" -"key Backtab +Ansi : \"\\E[Z\"\n" -"key Backtab -Ansi : \"\\t\"\n" -"key Return-Shift-NewLine : \"\\r\"\n" -"key Return-Shift+NewLine : \"\\r\\n\"\n" -"key Return+Shift : \"\\EOM\"\n" -"key Backspace : \"\\x7f\"\n" -"key Up -Shift-Ansi : \"\\EA\"\n" -"key Down -Shift-Ansi : \"\\EB\"\n" -"key Right-Shift-Ansi : \"\\EC\"\n" -"key Left -Shift-Ansi : \"\\ED\"\n" -"key Up -Shift-AnyMod+Ansi+AppCuKeys : \"\\EOA\"\n" -"key Down -Shift-AnyMod+Ansi+AppCuKeys : \"\\EOB\"\n" -"key Right -Shift-AnyMod+Ansi+AppCuKeys : \"\\EOC\"\n" -"key Left -Shift-AnyMod+Ansi+AppCuKeys : \"\\EOD\"\n" -"key Up -Shift-AnyMod+Ansi-AppCuKeys : \"\\E[A\"\n" -"key Down -Shift-AnyMod+Ansi-AppCuKeys : \"\\E[B\"\n" -"key Right -Shift-AnyMod+Ansi-AppCuKeys : \"\\E[C\"\n" -"key Left -Shift-AnyMod+Ansi-AppCuKeys : \"\\E[D\"\n" -"key Up -Shift+AnyMod+Ansi : \"\\E[1;*A\"\n" -"key Down -Shift+AnyMod+Ansi : \"\\E[1;*B\"\n" -"key Right -Shift+AnyMod+Ansi : \"\\E[1;*C\"\n" -"key Left -Shift+AnyMod+Ansi : \"\\E[1;*D\"\n" -"key Enter+NewLine : \"\\r\\n\"\n" -"key Enter-NewLine : \"\\r\"\n" -"key Home -AnyMod -AppCuKeys : \"\\E[H\" \n" -"key End -AnyMod -AppCuKeys : \"\\E[F\" \n" -"key Home -AnyMod +AppCuKeys : \"\\EOH\" \n" -"key End -AnyMod +AppCuKeys : \"\\EOF\" \n" -"key Home +AnyMod : \"\\E[1;*H\"\n" -"key End +AnyMod : \"\\E[1;*F\"\n" -"key Insert -AnyMod : \"\\E[2~\"\n" -"key Delete -AnyMod : \"\\E[3~\"\n" -"key Insert +AnyMod : \"\\E[2;*~\"\n" -"key Delete +AnyMod : \"\\E[3;*~\"\n" -"key Prior -Shift-AnyMod : \"\\E[5~\"\n" -"key Next -Shift-AnyMod : \"\\E[6~\"\n" -"key Prior -Shift+AnyMod : \"\\E[5;*~\"\n" -"key Next -Shift+AnyMod : \"\\E[6;*~\"\n" -"key F1 -AnyMod : \"\\EOP\"\n" -"key F2 -AnyMod : \"\\EOQ\"\n" -"key F3 -AnyMod : \"\\EOR\"\n" -"key F4 -AnyMod : \"\\EOS\"\n" -"key F5 -AnyMod : \"\\E[15~\"\n" -"key F6 -AnyMod : \"\\E[17~\"\n" -"key F7 -AnyMod : \"\\E[18~\"\n" -"key F8 -AnyMod : \"\\E[19~\"\n" -"key F9 -AnyMod : \"\\E[20~\"\n" -"key F10 -AnyMod : \"\\E[21~\"\n" -"key F11 -AnyMod : \"\\E[23~\"\n" -"key F12 -AnyMod : \"\\E[24~\"\n" -"key F1 +AnyMod : \"\\EO*P\"\n" -"key F2 +AnyMod : \"\\EO*Q\"\n" -"key F3 +AnyMod : \"\\EO*R\"\n" -"key F4 +AnyMod : \"\\EO*S\"\n" -"key F5 +AnyMod : \"\\E[15;*~\"\n" -"key F6 +AnyMod : \"\\E[17;*~\"\n" -"key F7 +AnyMod : \"\\E[18;*~\"\n" -"key F8 +AnyMod : \"\\E[19;*~\"\n" -"key F9 +AnyMod : \"\\E[20;*~\"\n" -"key F10 +AnyMod : \"\\E[21;*~\"\n" -"key F11 +AnyMod : \"\\E[23;*~\"\n" -"key F12 +AnyMod : \"\\E[24;*~\"\n" -"key Space +Control : \"\\x00\"\n" -"key Up +Shift-AppScreen : scrollLineUp\n" -"key Prior +Shift-AppScreen : scrollPageUp\n" -"key Down +Shift-AppScreen : scrollLineDown\n" -"key Next +Shift-AppScreen : scrollPageDown\n" -"key ScrollLock : scrollLock\n" -"\0"
deleted file mode 100644 --- a/gui/qtermwidget/lib/Filter.cpp +++ /dev/null @@ -1,562 +0,0 @@ -/* - Copyright (C) 2007 by Robert Knight <robertknight@gmail.com> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 "Filter.h" - -// System -#include <iostream> - -// Qt -#include <QtGui/QAction> -#include <QtGui/QApplication> -#include <QtGui/QClipboard> -#include <QtCore/QString> - -#include <QtCore/QSharedData> -#include <QtCore> - -// KDE -//#include <KLocale> -//#include <KRun> - -// Konsole -#include "TerminalCharacterDecoder.h" - -using namespace Konsole; - -FilterChain::~FilterChain() -{ - QMutableListIterator<Filter*> iter(*this); - - while ( iter.hasNext() ) - { - Filter* filter = iter.next(); - iter.remove(); - delete filter; - } -} - -void FilterChain::addFilter(Filter* filter) -{ - append(filter); -} -void FilterChain::removeFilter(Filter* filter) -{ - removeAll(filter); -} -bool FilterChain::containsFilter(Filter* filter) -{ - return contains(filter); -} -void FilterChain::reset() -{ - QListIterator<Filter*> iter(*this); - while (iter.hasNext()) - iter.next()->reset(); -} -void FilterChain::setBuffer(const QString* buffer , const QList<int>* linePositions) -{ - QListIterator<Filter*> iter(*this); - while (iter.hasNext()) - iter.next()->setBuffer(buffer,linePositions); -} -void FilterChain::process() -{ - QListIterator<Filter*> iter(*this); - while (iter.hasNext()) - iter.next()->process(); -} -void FilterChain::clear() -{ - QList<Filter*>::clear(); -} -Filter::HotSpot* FilterChain::hotSpotAt(int line , int column) const -{ - QListIterator<Filter*> iter(*this); - while (iter.hasNext()) - { - Filter* filter = iter.next(); - Filter::HotSpot* spot = filter->hotSpotAt(line,column); - if ( spot != 0 ) - { - return spot; - } - } - - return 0; -} - -QList<Filter::HotSpot*> FilterChain::hotSpots() const -{ - QList<Filter::HotSpot*> list; - QListIterator<Filter*> iter(*this); - while (iter.hasNext()) - { - Filter* filter = iter.next(); - list << filter->hotSpots(); - } - return list; -} -//QList<Filter::HotSpot*> FilterChain::hotSpotsAtLine(int line) const; - -TerminalImageFilterChain::TerminalImageFilterChain() -: _buffer(0) -, _linePositions(0) -{ -} - -TerminalImageFilterChain::~TerminalImageFilterChain() -{ - delete _buffer; - delete _linePositions; -} - -void TerminalImageFilterChain::setImage(const Character* const image , int lines , int columns, const QVector<LineProperty>& lineProperties) -{ -//qDebug("%s %d", __FILE__, __LINE__); - if (empty()) - return; -//qDebug("%s %d", __FILE__, __LINE__); - - // reset all filters and hotspots - reset(); -//qDebug("%s %d", __FILE__, __LINE__); - - PlainTextDecoder decoder; - decoder.setTrailingWhitespace(false); - -//qDebug("%s %d", __FILE__, __LINE__); - // setup new shared buffers for the filters to process on - QString* newBuffer = new QString(); - QList<int>* newLinePositions = new QList<int>(); - setBuffer( newBuffer , newLinePositions ); - - // free the old buffers - delete _buffer; - delete _linePositions; - - _buffer = newBuffer; - _linePositions = newLinePositions; - - QTextStream lineStream(_buffer); - decoder.begin(&lineStream); - - for (int i=0 ; i < lines ; i++) - { - _linePositions->append(_buffer->length()); - decoder.decodeLine(image + i*columns,columns,LINE_DEFAULT); - - // pretend that each line ends with a newline character. - // this prevents a link that occurs at the end of one line - // being treated as part of a link that occurs at the start of the next line - // - // the downside is that links which are spread over more than one line are not - // highlighted. - // - // TODO - Use the "line wrapped" attribute associated with lines in a - // terminal image to avoid adding this imaginary character for wrapped - // lines - if ( !(lineProperties.value(i,LINE_DEFAULT) & LINE_WRAPPED) ) - lineStream << QChar('\n'); - } - decoder.end(); -// qDebug("%s %d", __FILE__, __LINE__); -} - -Filter::Filter() : -_linePositions(0), -_buffer(0) -{ -} - -Filter::~Filter() -{ - QListIterator<HotSpot*> iter(_hotspotList); - while (iter.hasNext()) - { - delete iter.next(); - } -} -void Filter::reset() -{ - _hotspots.clear(); - _hotspotList.clear(); -} - -void Filter::setBuffer(const QString* buffer , const QList<int>* linePositions) -{ - _buffer = buffer; - _linePositions = linePositions; -} - -void Filter::getLineColumn(int position , int& startLine , int& startColumn) -{ - Q_ASSERT( _linePositions ); - Q_ASSERT( _buffer ); - - - for (int i = 0 ; i < _linePositions->count() ; i++) - { - //kDebug() << "line position at " << i << " = " << _linePositions[i]; - int nextLine = 0; - - if ( i == _linePositions->count()-1 ) - { - nextLine = _buffer->length() + 1; - } - else - { - nextLine = _linePositions->value(i+1); - } - - // kDebug() << "pos - " << position << " line pos(" << i<< ") " << _linePositions->value(i) << - // " next = " << nextLine << " buffer len = " << _buffer->length(); - - if ( _linePositions->value(i) <= position && position < nextLine ) - { - startLine = i; - startColumn = position - _linePositions->value(i); - return; - } - } -} - - -/*void Filter::addLine(const QString& text) -{ - _linePositions << _buffer.length(); - _buffer.append(text); -}*/ - -const QString* Filter::buffer() -{ - return _buffer; -} -Filter::HotSpot::~HotSpot() -{ -} -void Filter::addHotSpot(HotSpot* spot) -{ - _hotspotList << spot; - - for (int line = spot->startLine() ; line <= spot->endLine() ; line++) - { - _hotspots.insert(line,spot); - } -} -QList<Filter::HotSpot*> Filter::hotSpots() const -{ - return _hotspotList; -} -QList<Filter::HotSpot*> Filter::hotSpotsAtLine(int line) const -{ - return _hotspots.values(line); -} - -Filter::HotSpot* Filter::hotSpotAt(int line , int column) const -{ - QListIterator<HotSpot*> spotIter(_hotspots.values(line)); - - while (spotIter.hasNext()) - { - HotSpot* spot = spotIter.next(); - - if ( spot->startLine() == line && spot->startColumn() > column ) - continue; - if ( spot->endLine() == line && spot->endColumn() < column ) - continue; - - return spot; - } - - return 0; -} - -Filter::HotSpot::HotSpot(int startLine , int startColumn , int endLine , int endColumn) - : _startLine(startLine) - , _startColumn(startColumn) - , _endLine(endLine) - , _endColumn(endColumn) - , _type(NotSpecified) -{ -} -QString Filter::HotSpot::tooltip() const -{ - return QString(); -} -QList<QAction*> Filter::HotSpot::actions() -{ - return QList<QAction*>(); -} -int Filter::HotSpot::startLine() const -{ - return _startLine; -} -int Filter::HotSpot::endLine() const -{ - return _endLine; -} -int Filter::HotSpot::startColumn() const -{ - return _startColumn; -} -int Filter::HotSpot::endColumn() const -{ - return _endColumn; -} -Filter::HotSpot::Type Filter::HotSpot::type() const -{ - return _type; -} -void Filter::HotSpot::setType(Type type) -{ - _type = type; -} - -RegExpFilter::RegExpFilter() -{ -} - -RegExpFilter::HotSpot::HotSpot(int startLine,int startColumn,int endLine,int endColumn) - : Filter::HotSpot(startLine,startColumn,endLine,endColumn) -{ - setType(Marker); -} - -void RegExpFilter::HotSpot::activate(QObject*) -{ -} - -void RegExpFilter::HotSpot::setCapturedTexts(const QStringList& texts) -{ - _capturedTexts = texts; -} -QStringList RegExpFilter::HotSpot::capturedTexts() const -{ - return _capturedTexts; -} - -void RegExpFilter::setRegExp(const QRegExp& regExp) -{ - _searchText = regExp; -} -QRegExp RegExpFilter::regExp() const -{ - return _searchText; -} -/*void RegExpFilter::reset(int) -{ - _buffer = QString(); -}*/ -void RegExpFilter::process() -{ - int pos = 0; - const QString* text = buffer(); - - Q_ASSERT( text ); - - // ignore any regular expressions which match an empty string. - // otherwise the while loop below will run indefinitely - static const QString emptyString(""); - if ( _searchText.exactMatch(emptyString) ) - return; - - while(pos >= 0) - { - pos = _searchText.indexIn(*text,pos); - - if ( pos >= 0 ) - { - - int startLine = 0; - int endLine = 0; - int startColumn = 0; - int endColumn = 0; - - - //kDebug() << "pos from " << pos << " to " << pos + _searchText.matchedLength(); - - getLineColumn(pos,startLine,startColumn); - getLineColumn(pos + _searchText.matchedLength(),endLine,endColumn); - - //kDebug() << "start " << startLine << " / " << startColumn; - //kDebug() << "end " << endLine << " / " << endColumn; - - RegExpFilter::HotSpot* spot = newHotSpot(startLine,startColumn, - endLine,endColumn); - spot->setCapturedTexts(_searchText.capturedTexts()); - - addHotSpot( spot ); - pos += _searchText.matchedLength(); - - // if matchedLength == 0, the program will get stuck in an infinite loop - Q_ASSERT( _searchText.matchedLength() > 0 ); - } - } -} - -RegExpFilter::HotSpot* RegExpFilter::newHotSpot(int startLine,int startColumn, - int endLine,int endColumn) -{ - return new RegExpFilter::HotSpot(startLine,startColumn, - endLine,endColumn); -} -RegExpFilter::HotSpot* UrlFilter::newHotSpot(int startLine,int startColumn,int endLine, - int endColumn) -{ - return new UrlFilter::HotSpot(startLine,startColumn, - endLine,endColumn); -} -UrlFilter::HotSpot::HotSpot(int startLine,int startColumn,int endLine,int endColumn) -: RegExpFilter::HotSpot(startLine,startColumn,endLine,endColumn) -, _urlObject(new FilterObject(this)) -{ - setType(Link); -} -QString UrlFilter::HotSpot::tooltip() const -{ - QString url = capturedTexts().first(); - - const UrlType kind = urlType(); - - if ( kind == StandardUrl ) - return QString(); - else if ( kind == Email ) - return QString(); - else - return QString(); -} -UrlFilter::HotSpot::UrlType UrlFilter::HotSpot::urlType() const -{ - QString url = capturedTexts().first(); - - if ( FullUrlRegExp.exactMatch(url) ) - return StandardUrl; - else if ( EmailAddressRegExp.exactMatch(url) ) - return Email; - else - return Unknown; -} - -void UrlFilter::HotSpot::activate(QObject* object) -{ - QString url = capturedTexts().first(); - - const UrlType kind = urlType(); - - const QString& actionName = object ? object->objectName() : QString(); - - if ( actionName == "copy-action" ) - { - //kDebug() << "Copying url to clipboard:" << url; - - QApplication::clipboard()->setText(url); - return; - } - - if ( !object || actionName == "open-action" ) - { - if ( kind == StandardUrl ) - { - // if the URL path does not include the protocol ( eg. "www.kde.org" ) then - // prepend http:// ( eg. "www.kde.org" --> "http://www.kde.org" ) - if (!url.contains("://")) - { - url.prepend("http://"); - } - } - else if ( kind == Email ) - { - url.prepend("mailto:"); - } - -// new KRun(url,QApplication::activeWindow()); - } -} - -// Note: Altering these regular expressions can have a major effect on the performance of the filters -// used for finding URLs in the text, especially if they are very general and could match very long -// pieces of text. -// Please be careful when altering them. - -//regexp matches: -// full url: -// protocolname:// or www. followed by anything other than whitespaces, <, >, ' or ", and ends before whitespaces, <, >, ', ", ], !, comma and dot -const QRegExp UrlFilter::FullUrlRegExp("(www\\.(?!\\.)|[a-z][a-z0-9+.-]*://)[^\\s<>'\"]+[^!,\\.\\s<>'\"\\]]"); -// email address: -// [word chars, dots or dashes]@[word chars, dots or dashes].[word chars] -const QRegExp UrlFilter::EmailAddressRegExp("\\b(\\w|\\.|-)+@(\\w|\\.|-)+\\.\\w+\\b"); - -// matches full url or email address -const QRegExp UrlFilter::CompleteUrlRegExp('('+FullUrlRegExp.pattern()+'|'+ - EmailAddressRegExp.pattern()+')'); - -UrlFilter::UrlFilter() -{ - setRegExp( CompleteUrlRegExp ); -} -UrlFilter::HotSpot::~HotSpot() -{ - delete _urlObject; -} -void FilterObject::activated() -{ - _filter->activate(sender()); -} -QList<QAction*> UrlFilter::HotSpot::actions() -{ - QList<QAction*> list; - - const UrlType kind = urlType(); - - QAction* openAction = new QAction(_urlObject); - QAction* copyAction = new QAction(_urlObject);; - - Q_ASSERT( kind == StandardUrl || kind == Email ); - - if ( kind == StandardUrl ) - { - openAction->setText(("Open Link")); - copyAction->setText(("Copy Link Address")); - } - else if ( kind == Email ) - { - openAction->setText(("Send Email To...")); - copyAction->setText(("Copy Email Address")); - } - - // object names are set here so that the hotspot performs the - // correct action when activated() is called with the triggered - // action passed as a parameter. - openAction->setObjectName("open-action"); - copyAction->setObjectName("copy-action"); - - QObject::connect( openAction , SIGNAL(triggered()) , _urlObject , SLOT(activated()) ); - QObject::connect( copyAction , SIGNAL(triggered()) , _urlObject , SLOT(activated()) ); - - list << openAction; - list << copyAction; - - return list; -} - -//#include "moc_Filter.cpp"
deleted file mode 100644 --- a/gui/qtermwidget/lib/Filter.h +++ /dev/null @@ -1,383 +0,0 @@ -/* - Copyright (C) 2007 by Robert Knight <robertknight@gmail.com> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 FILTER_H -#define FILTER_H - -// Qt -#include <QtGui/QAction> -#include <QtCore/QList> -#include <QtCore/QObject> -#include <QtCore/QStringList> -#include <QtCore/QHash> -#include <QtCore/QRegExp> - -// Local -#include "Character.h" - -namespace Konsole -{ - -/** - * A filter processes blocks of text looking for certain patterns (such as URLs or keywords from a list) - * and marks the areas which match the filter's patterns as 'hotspots'. - * - * Each hotspot has a type identifier associated with it ( such as a link or a highlighted section ), - * and an action. When the user performs some activity such as a mouse-click in a hotspot area ( the exact - * action will depend on what is displaying the block of text which the filter is processing ), the hotspot's - * activate() method should be called. Depending on the type of hotspot this will trigger a suitable response. - * - * For example, if a hotspot represents a URL then a suitable action would be opening that URL in a web browser. - * Hotspots may have more than one action, in which case the list of actions can be obtained using the - * actions() method. - * - * Different subclasses of filter will return different types of hotspot. - * Subclasses must reimplement the process() method to examine a block of text and identify sections of interest. - * When processing the text they should create instances of Filter::HotSpot subclasses for sections of interest - * and add them to the filter's list of hotspots using addHotSpot() - */ -class Filter -{ -public: - /** - * Represents an area of text which matched the pattern a particular filter has been looking for. - * - * Each hotspot has a type identifier associated with it ( such as a link or a highlighted section ), - * and an action. When the user performs some activity such as a mouse-click in a hotspot area ( the exact - * action will depend on what is displaying the block of text which the filter is processing ), the hotspot's - * activate() method should be called. Depending on the type of hotspot this will trigger a suitable response. - * - * For example, if a hotspot represents a URL then a suitable action would be opening that URL in a web browser. - * Hotspots may have more than one action, in which case the list of actions can be obtained using the - * actions() method. These actions may then be displayed in a popup menu or toolbar for example. - */ - class HotSpot - { - public: - /** - * Constructs a new hotspot which covers the area from (@p startLine,@p startColumn) to (@p endLine,@p endColumn) - * in a block of text. - */ - HotSpot(int startLine , int startColumn , int endLine , int endColumn); - virtual ~HotSpot(); - - enum Type - { - // the type of the hotspot is not specified - NotSpecified, - // this hotspot represents a clickable link - Link, - // this hotspot represents a marker - Marker - }; - - /** Returns the line when the hotspot area starts */ - int startLine() const; - /** Returns the line where the hotspot area ends */ - int endLine() const; - /** Returns the column on startLine() where the hotspot area starts */ - int startColumn() const; - /** Returns the column on endLine() where the hotspot area ends */ - int endColumn() const; - /** - * Returns the type of the hotspot. This is usually used as a hint for views on how to represent - * the hotspot graphically. eg. Link hotspots are typically underlined when the user mouses over them - */ - Type type() const; - /** - * Causes the an action associated with a hotspot to be triggered. - * - * @param object The object which caused the hotspot to be triggered. This is - * typically null ( in which case the default action should be performed ) or - * one of the objects from the actions() list. In which case the associated - * action should be performed. - */ - virtual void activate(QObject* object = 0) = 0; - /** - * Returns a list of actions associated with the hotspot which can be used in a - * menu or toolbar - */ - virtual QList<QAction*> actions(); - - /** - * Returns the text of a tooltip to be shown when the mouse moves over the hotspot, or - * an empty string if there is no tooltip associated with this hotspot. - * - * The default implementation returns an empty string. - */ - virtual QString tooltip() const; - - protected: - /** Sets the type of a hotspot. This should only be set once */ - void setType(Type type); - - private: - int _startLine; - int _startColumn; - int _endLine; - int _endColumn; - Type _type; - - }; - - /** Constructs a new filter. */ - Filter(); - virtual ~Filter(); - - /** Causes the filter to process the block of text currently in its internal buffer */ - virtual void process() = 0; - - /** - * Empties the filters internal buffer and resets the line count back to 0. - * All hotspots are deleted. - */ - void reset(); - - /** Adds a new line of text to the filter and increments the line count */ - //void addLine(const QString& string); - - /** Returns the hotspot which covers the given @p line and @p column, or 0 if no hotspot covers that area */ - HotSpot* hotSpotAt(int line , int column) const; - - /** Returns the list of hotspots identified by the filter */ - QList<HotSpot*> hotSpots() const; - - /** Returns the list of hotspots identified by the filter which occur on a given line */ - QList<HotSpot*> hotSpotsAtLine(int line) const; - - /** - * TODO: Document me - */ - void setBuffer(const QString* buffer , const QList<int>* linePositions); - -protected: - /** Adds a new hotspot to the list */ - void addHotSpot(HotSpot*); - /** Returns the internal buffer */ - const QString* buffer(); - /** Converts a character position within buffer() to a line and column */ - void getLineColumn(int position , int& startLine , int& startColumn); - -private: - QMultiHash<int,HotSpot*> _hotspots; - QList<HotSpot*> _hotspotList; - - const QList<int>* _linePositions; - const QString* _buffer; -}; - -/** - * A filter which searches for sections of text matching a regular expression and creates a new RegExpFilter::HotSpot - * instance for them. - * - * Subclasses can reimplement newHotSpot() to return custom hotspot types when matches for the regular expression - * are found. - */ -class RegExpFilter : public Filter -{ -public: - /** - * Type of hotspot created by RegExpFilter. The capturedTexts() method can be used to find the text - * matched by the filter's regular expression. - */ - class HotSpot : public Filter::HotSpot - { - public: - HotSpot(int startLine, int startColumn, int endLine , int endColumn); - virtual void activate(QObject* object = 0); - - /** Sets the captured texts associated with this hotspot */ - void setCapturedTexts(const QStringList& texts); - /** Returns the texts found by the filter when matching the filter's regular expression */ - QStringList capturedTexts() const; - private: - QStringList _capturedTexts; - }; - - /** Constructs a new regular expression filter */ - RegExpFilter(); - - /** - * Sets the regular expression which the filter searches for in blocks of text. - * - * Regular expressions which match the empty string are treated as not matching - * anything. - */ - void setRegExp(const QRegExp& text); - /** Returns the regular expression which the filter searches for in blocks of text */ - QRegExp regExp() const; - - /** - * Reimplemented to search the filter's text buffer for text matching regExp() - * - * If regexp matches the empty string, then process() will return immediately - * without finding results. - */ - virtual void process(); - -protected: - /** - * Called when a match for the regular expression is encountered. Subclasses should reimplement this - * to return custom hotspot types - */ - virtual RegExpFilter::HotSpot* newHotSpot(int startLine,int startColumn, - int endLine,int endColumn); - -private: - QRegExp _searchText; -}; - -class FilterObject; - -/** A filter which matches URLs in blocks of text */ -class UrlFilter : public RegExpFilter -{ -public: - /** - * Hotspot type created by UrlFilter instances. The activate() method opens a web browser - * at the given URL when called. - */ - class HotSpot : public RegExpFilter::HotSpot - { - public: - HotSpot(int startLine,int startColumn,int endLine,int endColumn); - virtual ~HotSpot(); - - virtual QList<QAction*> actions(); - - /** - * Open a web browser at the current URL. The url itself can be determined using - * the capturedTexts() method. - */ - virtual void activate(QObject* object = 0); - - virtual QString tooltip() const; - private: - enum UrlType - { - StandardUrl, - Email, - Unknown - }; - UrlType urlType() const; - - FilterObject* _urlObject; - }; - - UrlFilter(); - -protected: - virtual RegExpFilter::HotSpot* newHotSpot(int,int,int,int); - -private: - - static const QRegExp FullUrlRegExp; - static const QRegExp EmailAddressRegExp; - - // combined OR of FullUrlRegExp and EmailAddressRegExp - static const QRegExp CompleteUrlRegExp; -}; - -class FilterObject : public QObject -{ -Q_OBJECT -public: - FilterObject(Filter::HotSpot* filter) : _filter(filter) {} -private slots: - void activated(); -private: - Filter::HotSpot* _filter; -}; - -/** - * A chain which allows a group of filters to be processed as one. - * The chain owns the filters added to it and deletes them when the chain itself is destroyed. - * - * Use addFilter() to add a new filter to the chain. - * When new text to be filtered arrives, use addLine() to add each additional - * line of text which needs to be processed and then after adding the last line, use - * process() to cause each filter in the chain to process the text. - * - * After processing a block of text, the reset() method can be used to set the filter chain's - * internal cursor back to the first line. - * - * The hotSpotAt() method will return the first hotspot which covers a given position. - * - * The hotSpots() and hotSpotsAtLine() method return all of the hotspots in the text and on - * a given line respectively. - */ -class FilterChain : protected QList<Filter*> -{ -public: - virtual ~FilterChain(); - - /** Adds a new filter to the chain. The chain will delete this filter when it is destroyed */ - void addFilter(Filter* filter); - /** Removes a filter from the chain. The chain will no longer delete the filter when destroyed */ - void removeFilter(Filter* filter); - /** Returns true if the chain contains @p filter */ - bool containsFilter(Filter* filter); - /** Removes all filters from the chain */ - void clear(); - - /** Resets each filter in the chain */ - void reset(); - /** - * Processes each filter in the chain - */ - void process(); - - /** Sets the buffer for each filter in the chain to process. */ - void setBuffer(const QString* buffer , const QList<int>* linePositions); - - /** Returns the first hotspot which occurs at @p line, @p column or 0 if no hotspot was found */ - Filter::HotSpot* hotSpotAt(int line , int column) const; - /** Returns a list of all the hotspots in all the chain's filters */ - QList<Filter::HotSpot*> hotSpots() const; - /** Returns a list of all hotspots at the given line in all the chain's filters */ - QList<Filter::HotSpot> hotSpotsAtLine(int line) const; - -}; - -/** A filter chain which processes character images from terminal displays */ -class TerminalImageFilterChain : public FilterChain -{ -public: - TerminalImageFilterChain(); - virtual ~TerminalImageFilterChain(); - - /** - * Set the current terminal image to @p image. - * - * @param image The terminal image - * @param lines The number of lines in the terminal image - * @param columns The number of columns in the terminal image - */ - void setImage(const Character* const image , int lines , int columns, - const QVector<LineProperty>& lineProperties); - -private: - QString* _buffer; - QList<int>* _linePositions; -}; - -} -#endif //FILTER_H
deleted file mode 100644 --- a/gui/qtermwidget/lib/History.cpp +++ /dev/null @@ -1,698 +0,0 @@ -/* - This file is part of Konsole, an X terminal. - Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 "History.h" - -// System -#include <iostream> -#include <stdlib.h> -#include <assert.h> -#include <stdio.h> -#include <sys/types.h> -#include <sys/mman.h> -#include <unistd.h> -#include <errno.h> - - -// Reasonable line size -#define LINE_SIZE 1024 - -using namespace Konsole; - -/* - An arbitrary long scroll. - - One can modify the scroll only by adding either cells - or newlines, but access it randomly. - - The model is that of an arbitrary wide typewriter scroll - in that the scroll is a serie of lines and each line is - a serie of cells with no overwriting permitted. - - The implementation provides arbitrary length and numbers - of cells and line/column indexed read access to the scroll - at constant costs. - -KDE4: Can we use QTemporaryFile here, instead of KTempFile? - -FIXME: some complain about the history buffer comsuming the - memory of their machines. This problem is critical - since the history does not behave gracefully in cases - where the memory is used up completely. - - I put in a workaround that should handle it problem - now gracefully. I'm not satisfied with the solution. - -FIXME: Terminating the history is not properly indicated - in the menu. We should throw a signal. - -FIXME: There is noticeable decrease in speed, also. Perhaps, - there whole feature needs to be revisited therefore. - Disadvantage of a more elaborated, say block-oriented - scheme with wrap around would be it's complexity. -*/ - -//FIXME: tempory replacement for tmpfile -// this is here one for debugging purpose. - -//#define tmpfile xTmpFile - -// History File /////////////////////////////////////////// - -/* - A Row(X) data type which allows adding elements to the end. -*/ - -HistoryFile::HistoryFile() - : ion(-1), - length(0), - fileMap(0) -{ - if (tmpFile.open()) - { - tmpFile.setAutoRemove(true); - ion = tmpFile.handle(); - } -} - -HistoryFile::~HistoryFile() -{ - if (fileMap) - unmap(); -} - -//TODO: Mapping the entire file in will cause problems if the history file becomes exceedingly large, -//(ie. larger than available memory). HistoryFile::map() should only map in sections of the file at a time, -//to avoid this. -void HistoryFile::map() -{ - assert( fileMap == 0 ); - - fileMap = (char*)mmap( 0 , length , PROT_READ , MAP_PRIVATE , ion , 0 ); - - //if mmap'ing fails, fall back to the read-lseek combination - if ( fileMap == MAP_FAILED ) - { - readWriteBalance = 0; - fileMap = 0; - qDebug() << ": mmap'ing history failed. errno = " << errno; - } -} - -void HistoryFile::unmap() -{ - int result = munmap( fileMap , length ); - assert( result == 0 ); - - fileMap = 0; -} - -bool HistoryFile::isMapped() -{ - return (fileMap != 0); -} - -void HistoryFile::add(const unsigned char* bytes, int len) -{ - if ( fileMap ) - unmap(); - - readWriteBalance++; - - int rc = 0; - - rc = lseek(ion,length,SEEK_SET); if (rc < 0) { perror("HistoryFile::add.seek"); return; } - rc = write(ion,bytes,len); if (rc < 0) { perror("HistoryFile::add.write"); return; } - length += rc; -} - -void HistoryFile::get(unsigned char* bytes, int len, int loc) -{ - //count number of get() calls vs. number of add() calls. - //If there are many more get() calls compared with add() - //calls (decided by using MAP_THRESHOLD) then mmap the log - //file to improve performance. - readWriteBalance--; - if ( !fileMap && readWriteBalance < MAP_THRESHOLD ) - map(); - - if ( fileMap ) - { - for (int i=0;i<len;i++) - bytes[i]=fileMap[loc+i]; - } - else - { - int rc = 0; - - if (loc < 0 || len < 0 || loc + len > length) - fprintf(stderr,"getHist(...,%d,%d): invalid args.\n",len,loc); - rc = lseek(ion,loc,SEEK_SET); if (rc < 0) { perror("HistoryFile::get.seek"); return; } - rc = read(ion,bytes,len); if (rc < 0) { perror("HistoryFile::get.read"); return; } - } -} - -int HistoryFile::len() -{ - return length; -} - - -// History Scroll abstract base class ////////////////////////////////////// - - -HistoryScroll::HistoryScroll(HistoryType* t) - : m_histType(t) -{ -} - -HistoryScroll::~HistoryScroll() -{ - delete m_histType; -} - -bool HistoryScroll::hasScroll() -{ - return true; -} - -// History Scroll File ////////////////////////////////////// - -/* - The history scroll makes a Row(Row(Cell)) from - two history buffers. The index buffer contains - start of line positions which refere to the cells - buffer. - - Note that index[0] addresses the second line - (line #1), while the first line (line #0) starts - at 0 in cells. -*/ - -HistoryScrollFile::HistoryScrollFile(const QString &logFileName) - : HistoryScroll(new HistoryTypeFile(logFileName)), - m_logFileName(logFileName) -{ -} - -HistoryScrollFile::~HistoryScrollFile() -{ -} - -int HistoryScrollFile::getLines() -{ - return index.len() / sizeof(int); -} - -int HistoryScrollFile::getLineLen(int lineno) -{ - return (startOfLine(lineno+1) - startOfLine(lineno)) / sizeof(Character); -} - -bool HistoryScrollFile::isWrappedLine(int lineno) -{ - if (lineno>=0 && lineno <= getLines()) { - unsigned char flag; - lineflags.get((unsigned char*)&flag,sizeof(unsigned char),(lineno)*sizeof(unsigned char)); - return flag; - } - return false; -} - -int HistoryScrollFile::startOfLine(int lineno) -{ - if (lineno <= 0) return 0; - if (lineno <= getLines()) - { - - if (!index.isMapped()) - index.map(); - - int res; - index.get((unsigned char*)&res,sizeof(int),(lineno-1)*sizeof(int)); - return res; - } - return cells.len(); -} - -void HistoryScrollFile::getCells(int lineno, int colno, int count, Character res[]) -{ - cells.get((unsigned char*)res,count*sizeof(Character),startOfLine(lineno)+colno*sizeof(Character)); -} - -void HistoryScrollFile::addCells(const Character text[], int count) -{ - cells.add((unsigned char*)text,count*sizeof(Character)); -} - -void HistoryScrollFile::addLine(bool previousWrapped) -{ - if (index.isMapped()) - index.unmap(); - - int locn = cells.len(); - index.add((unsigned char*)&locn,sizeof(int)); - unsigned char flags = previousWrapped ? 0x01 : 0x00; - lineflags.add((unsigned char*)&flags,sizeof(unsigned char)); -} - - -// History Scroll Buffer ////////////////////////////////////// -HistoryScrollBuffer::HistoryScrollBuffer(unsigned int maxLineCount) - : HistoryScroll(new HistoryTypeBuffer(maxLineCount)) - ,_historyBuffer() - ,_maxLineCount(0) - ,_usedLines(0) - ,_head(0) -{ - setMaxNbLines(maxLineCount); -} - -HistoryScrollBuffer::~HistoryScrollBuffer() -{ - delete[] _historyBuffer; -} - -void HistoryScrollBuffer::addCellsVector(const QVector<Character>& cells) -{ - _head++; - if ( _usedLines < _maxLineCount ) - _usedLines++; - - if ( _head >= _maxLineCount ) - { - _head = 0; - } - - _historyBuffer[bufferIndex(_usedLines-1)] = cells; - _wrappedLine[bufferIndex(_usedLines-1)] = false; -} -void HistoryScrollBuffer::addCells(const Character a[], int count) -{ - HistoryLine newLine(count); - qCopy(a,a+count,newLine.begin()); - - addCellsVector(newLine); -} - -void HistoryScrollBuffer::addLine(bool previousWrapped) -{ - _wrappedLine[bufferIndex(_usedLines-1)] = previousWrapped; -} - -int HistoryScrollBuffer::getLines() -{ - return _usedLines; -} - -int HistoryScrollBuffer::getLineLen(int lineNumber) -{ - Q_ASSERT( lineNumber >= 0 && lineNumber < _maxLineCount ); - - if ( lineNumber < _usedLines ) - { - return _historyBuffer[bufferIndex(lineNumber)].size(); - } - else - { - return 0; - } -} - -bool HistoryScrollBuffer::isWrappedLine(int lineNumber) -{ - Q_ASSERT( lineNumber >= 0 && lineNumber < _maxLineCount ); - - if (lineNumber < _usedLines) - { - //kDebug() << "Line" << lineNumber << "wrapped is" << _wrappedLine[bufferIndex(lineNumber)]; - return _wrappedLine[bufferIndex(lineNumber)]; - } - else - return false; -} - -void HistoryScrollBuffer::getCells(int lineNumber, int startColumn, int count, Character* buffer) -{ - if ( count == 0 ) return; - - Q_ASSERT( lineNumber < _maxLineCount ); - - if (lineNumber >= _usedLines) - { - memset(buffer, 0, count * sizeof(Character)); - return; - } - - const HistoryLine& line = _historyBuffer[bufferIndex(lineNumber)]; - - //kDebug() << "startCol " << startColumn; - //kDebug() << "line.size() " << line.size(); - //kDebug() << "count " << count; - - Q_ASSERT( startColumn <= line.size() - count ); - - memcpy(buffer, line.constData() + startColumn , count * sizeof(Character)); -} - -void HistoryScrollBuffer::setMaxNbLines(unsigned int lineCount) -{ - HistoryLine* oldBuffer = _historyBuffer; - HistoryLine* newBuffer = new HistoryLine[lineCount]; - - for ( int i = 0 ; i < qMin(_usedLines,(int)lineCount) ; i++ ) - { - newBuffer[i] = oldBuffer[bufferIndex(i)]; - } - - _usedLines = qMin(_usedLines,(int)lineCount); - _maxLineCount = lineCount; - _head = ( _usedLines == _maxLineCount ) ? 0 : _usedLines-1; - - _historyBuffer = newBuffer; - delete[] oldBuffer; - - _wrappedLine.resize(lineCount); -} - -int HistoryScrollBuffer::bufferIndex(int lineNumber) -{ - Q_ASSERT( lineNumber >= 0 ); - Q_ASSERT( lineNumber < _maxLineCount ); - Q_ASSERT( (_usedLines == _maxLineCount) || lineNumber <= _head ); - - if ( _usedLines == _maxLineCount ) - { - return (_head+lineNumber+1) % _maxLineCount; - } - else - { - return lineNumber; - } -} - - -// History Scroll None ////////////////////////////////////// - -HistoryScrollNone::HistoryScrollNone() - : HistoryScroll(new HistoryTypeNone()) -{ -} - -HistoryScrollNone::~HistoryScrollNone() -{ -} - -bool HistoryScrollNone::hasScroll() -{ - return false; -} - -int HistoryScrollNone::getLines() -{ - return 0; -} - -int HistoryScrollNone::getLineLen(int) -{ - return 0; -} - -bool HistoryScrollNone::isWrappedLine(int /*lineno*/) -{ - return false; -} - -void HistoryScrollNone::getCells(int, int, int, Character []) -{ -} - -void HistoryScrollNone::addCells(const Character [], int) -{ -} - -void HistoryScrollNone::addLine(bool) -{ -} - -// History Scroll BlockArray ////////////////////////////////////// - -HistoryScrollBlockArray::HistoryScrollBlockArray(size_t size) - : HistoryScroll(new HistoryTypeBlockArray(size)) -{ - m_blockArray.setHistorySize(size); // nb. of lines. -} - -HistoryScrollBlockArray::~HistoryScrollBlockArray() -{ -} - -int HistoryScrollBlockArray::getLines() -{ - return m_lineLengths.count(); -} - -int HistoryScrollBlockArray::getLineLen(int lineno) -{ - if ( m_lineLengths.contains(lineno) ) - return m_lineLengths[lineno]; - else - return 0; -} - -bool HistoryScrollBlockArray::isWrappedLine(int /*lineno*/) -{ - return false; -} - -void HistoryScrollBlockArray::getCells(int lineno, int colno, - int count, Character res[]) -{ - if (!count) return; - - const Block *b = m_blockArray.at(lineno); - - if (!b) { - memset(res, 0, count * sizeof(Character)); // still better than random data - return; - } - - assert(((colno + count) * sizeof(Character)) < ENTRIES); - memcpy(res, b->data + (colno * sizeof(Character)), count * sizeof(Character)); -} - -void HistoryScrollBlockArray::addCells(const Character a[], int count) -{ - Block *b = m_blockArray.lastBlock(); - - if (!b) return; - - // put cells in block's data - assert((count * sizeof(Character)) < ENTRIES); - - memset(b->data, 0, ENTRIES); - - memcpy(b->data, a, count * sizeof(Character)); - b->size = count * sizeof(Character); - - size_t res = m_blockArray.newBlock(); - assert (res > 0); - Q_UNUSED( res ); - - m_lineLengths.insert(m_blockArray.getCurrent(), count); -} - -void HistoryScrollBlockArray::addLine(bool) -{ -} - -////////////////////////////////////////////////////////////////////// -// History Types -////////////////////////////////////////////////////////////////////// - -HistoryType::HistoryType() -{ -} - -HistoryType::~HistoryType() -{ -} - -////////////////////////////// - -HistoryTypeNone::HistoryTypeNone() -{ -} - -bool HistoryTypeNone::isEnabled() const -{ - return false; -} - -HistoryScroll* HistoryTypeNone::scroll(HistoryScroll *old) const -{ - delete old; - return new HistoryScrollNone(); -} - -int HistoryTypeNone::maximumLineCount() const -{ - return 0; -} - -////////////////////////////// - -HistoryTypeBlockArray::HistoryTypeBlockArray(size_t size) - : m_size(size) -{ -} - -bool HistoryTypeBlockArray::isEnabled() const -{ - return true; -} - -int HistoryTypeBlockArray::maximumLineCount() const -{ - return m_size; -} - -HistoryScroll* HistoryTypeBlockArray::scroll(HistoryScroll *old) const -{ - delete old; - return new HistoryScrollBlockArray(m_size); -} - - -////////////////////////////// - -HistoryTypeBuffer::HistoryTypeBuffer(unsigned int nbLines) - : m_nbLines(nbLines) -{ -} - -bool HistoryTypeBuffer::isEnabled() const -{ - return true; -} - -int HistoryTypeBuffer::maximumLineCount() const -{ - return m_nbLines; -} - -HistoryScroll* HistoryTypeBuffer::scroll(HistoryScroll *old) const -{ - if (old) - { - HistoryScrollBuffer *oldBuffer = dynamic_cast<HistoryScrollBuffer*>(old); - if (oldBuffer) - { - oldBuffer->setMaxNbLines(m_nbLines); - return oldBuffer; - } - - HistoryScroll *newScroll = new HistoryScrollBuffer(m_nbLines); - int lines = old->getLines(); - int startLine = 0; - if (lines > (int) m_nbLines) - startLine = lines - m_nbLines; - - Character line[LINE_SIZE]; - for(int i = startLine; i < lines; i++) - { - int size = old->getLineLen(i); - if (size > LINE_SIZE) - { - Character *tmp_line = new Character[size]; - old->getCells(i, 0, size, tmp_line); - newScroll->addCells(tmp_line, size); - newScroll->addLine(old->isWrappedLine(i)); - delete [] tmp_line; - } - else - { - old->getCells(i, 0, size, line); - newScroll->addCells(line, size); - newScroll->addLine(old->isWrappedLine(i)); - } - } - delete old; - return newScroll; - } - return new HistoryScrollBuffer(m_nbLines); -} - -////////////////////////////// - -HistoryTypeFile::HistoryTypeFile(const QString& fileName) - : m_fileName(fileName) -{ -} - -bool HistoryTypeFile::isEnabled() const -{ - return true; -} - -const QString& HistoryTypeFile::getFileName() const -{ - return m_fileName; -} - -HistoryScroll* HistoryTypeFile::scroll(HistoryScroll *old) const -{ - if (dynamic_cast<HistoryFile *>(old)) - return old; // Unchanged. - - HistoryScroll *newScroll = new HistoryScrollFile(m_fileName); - - Character line[LINE_SIZE]; - int lines = (old != 0) ? old->getLines() : 0; - for(int i = 0; i < lines; i++) - { - int size = old->getLineLen(i); - if (size > LINE_SIZE) - { - Character *tmp_line = new Character[size]; - old->getCells(i, 0, size, tmp_line); - newScroll->addCells(tmp_line, size); - newScroll->addLine(old->isWrappedLine(i)); - delete [] tmp_line; - } - else - { - old->getCells(i, 0, size, line); - newScroll->addCells(line, size); - newScroll->addLine(old->isWrappedLine(i)); - } - } - - delete old; - return newScroll; -} - -int HistoryTypeFile::maximumLineCount() const -{ - return 0; -}
deleted file mode 100644 --- a/gui/qtermwidget/lib/History.h +++ /dev/null @@ -1,344 +0,0 @@ -/* - This file is part of Konsole, an X terminal. - Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 TEHISTORY_H -#define TEHISTORY_H - -// Qt -#include <QtCore/QBitRef> -#include <QtCore/QHash> -#include <QtCore> - -// Konsole -#include "BlockArray.h" -#include "Character.h" - -namespace Konsole -{ - -#if 1 -/* - An extendable tmpfile(1) based buffer. -*/ - -class HistoryFile -{ -public: - HistoryFile(); - virtual ~HistoryFile(); - - virtual void add(const unsigned char* bytes, int len); - virtual void get(unsigned char* bytes, int len, int loc); - virtual int len(); - - //mmaps the file in read-only mode - void map(); - //un-mmaps the file - void unmap(); - //returns true if the file is mmap'ed - bool isMapped(); - - -private: - int ion; - int length; - QTemporaryFile tmpFile; - - //pointer to start of mmap'ed file data, or 0 if the file is not mmap'ed - char* fileMap; - - //incremented whenver 'add' is called and decremented whenever - //'get' is called. - //this is used to detect when a large number of lines are being read and processed from the history - //and automatically mmap the file for better performance (saves the overhead of many lseek-read calls). - int readWriteBalance; - - //when readWriteBalance goes below this threshold, the file will be mmap'ed automatically - static const int MAP_THRESHOLD = -1000; -}; -#endif - -////////////////////////////////////////////////////////////////////// - -////////////////////////////////////////////////////////////////////// -// Abstract base class for file and buffer versions -////////////////////////////////////////////////////////////////////// -class HistoryType; - -class HistoryScroll -{ -public: - HistoryScroll(HistoryType*); - virtual ~HistoryScroll(); - - virtual bool hasScroll(); - - // access to history - virtual int getLines() = 0; - virtual int getLineLen(int lineno) = 0; - virtual void getCells(int lineno, int colno, int count, Character res[]) = 0; - virtual bool isWrappedLine(int lineno) = 0; - - // backward compatibility (obsolete) - Character getCell(int lineno, int colno) { Character res; getCells(lineno,colno,1,&res); return res; } - - // adding lines. - virtual void addCells(const Character a[], int count) = 0; - // convenience method - this is virtual so that subclasses can take advantage - // of QVector's implicit copying - virtual void addCellsVector(const QVector<Character>& cells) - { - addCells(cells.data(),cells.size()); - } - - virtual void addLine(bool previousWrapped=false) = 0; - - // - // FIXME: Passing around constant references to HistoryType instances - // is very unsafe, because those references will no longer - // be valid if the history scroll is deleted. - // - const HistoryType& getType() { return *m_histType; } - -protected: - HistoryType* m_histType; - -}; - -#if 1 - -////////////////////////////////////////////////////////////////////// -// File-based history (e.g. file log, no limitation in length) -////////////////////////////////////////////////////////////////////// - -class HistoryScrollFile : public HistoryScroll -{ -public: - HistoryScrollFile(const QString &logFileName); - virtual ~HistoryScrollFile(); - - virtual int getLines(); - virtual int getLineLen(int lineno); - virtual void getCells(int lineno, int colno, int count, Character res[]); - virtual bool isWrappedLine(int lineno); - - virtual void addCells(const Character a[], int count); - virtual void addLine(bool previousWrapped=false); - -private: - int startOfLine(int lineno); - - QString m_logFileName; - HistoryFile index; // lines Row(int) - HistoryFile cells; // text Row(Character) - HistoryFile lineflags; // flags Row(unsigned char) -}; - - -////////////////////////////////////////////////////////////////////// -// Buffer-based history (limited to a fixed nb of lines) -////////////////////////////////////////////////////////////////////// -class HistoryScrollBuffer : public HistoryScroll -{ -public: - typedef QVector<Character> HistoryLine; - - HistoryScrollBuffer(unsigned int maxNbLines = 1000); - virtual ~HistoryScrollBuffer(); - - virtual int getLines(); - virtual int getLineLen(int lineno); - virtual void getCells(int lineno, int colno, int count, Character res[]); - virtual bool isWrappedLine(int lineno); - - virtual void addCells(const Character a[], int count); - virtual void addCellsVector(const QVector<Character>& cells); - virtual void addLine(bool previousWrapped=false); - - void setMaxNbLines(unsigned int nbLines); - unsigned int maxNbLines() { return _maxLineCount; } - - -private: - int bufferIndex(int lineNumber); - - HistoryLine* _historyBuffer; - QBitArray _wrappedLine; - int _maxLineCount; - int _usedLines; - int _head; - - //QVector<histline*> m_histBuffer; - //QBitArray m_wrappedLine; - //unsigned int m_maxNbLines; - //unsigned int m_nbLines; - //unsigned int m_arrayIndex; - //bool m_buffFilled; -}; - -/*class HistoryScrollBufferV2 : public HistoryScroll -{ -public: - virtual int getLines(); - virtual int getLineLen(int lineno); - virtual void getCells(int lineno, int colno, int count, Character res[]); - virtual bool isWrappedLine(int lineno); - - virtual void addCells(const Character a[], int count); - virtual void addCells(const QVector<Character>& cells); - virtual void addLine(bool previousWrapped=false); - -};*/ - -#endif - -////////////////////////////////////////////////////////////////////// -// Nothing-based history (no history :-) -////////////////////////////////////////////////////////////////////// -class HistoryScrollNone : public HistoryScroll -{ -public: - HistoryScrollNone(); - virtual ~HistoryScrollNone(); - - virtual bool hasScroll(); - - virtual int getLines(); - virtual int getLineLen(int lineno); - virtual void getCells(int lineno, int colno, int count, Character res[]); - virtual bool isWrappedLine(int lineno); - - virtual void addCells(const Character a[], int count); - virtual void addLine(bool previousWrapped=false); -}; - -////////////////////////////////////////////////////////////////////// -// BlockArray-based history -////////////////////////////////////////////////////////////////////// -class HistoryScrollBlockArray : public HistoryScroll -{ -public: - HistoryScrollBlockArray(size_t size); - virtual ~HistoryScrollBlockArray(); - - virtual int getLines(); - virtual int getLineLen(int lineno); - virtual void getCells(int lineno, int colno, int count, Character res[]); - virtual bool isWrappedLine(int lineno); - - virtual void addCells(const Character a[], int count); - virtual void addLine(bool previousWrapped=false); - -protected: - BlockArray m_blockArray; - QHash<int,size_t> m_lineLengths; -}; - -////////////////////////////////////////////////////////////////////// -// History type -////////////////////////////////////////////////////////////////////// - -class HistoryType -{ -public: - HistoryType(); - virtual ~HistoryType(); - - /** - * Returns true if the history is enabled ( can store lines of output ) - * or false otherwise. - */ - virtual bool isEnabled() const = 0; - /** - * Returns true if the history size is unlimited. - */ - bool isUnlimited() const { return maximumLineCount() == 0; } - /** - * Returns the maximum number of lines which this history type - * can store or 0 if the history can store an unlimited number of lines. - */ - virtual int maximumLineCount() const = 0; - - virtual HistoryScroll* scroll(HistoryScroll *) const = 0; -}; - -class HistoryTypeNone : public HistoryType -{ -public: - HistoryTypeNone(); - - virtual bool isEnabled() const; - virtual int maximumLineCount() const; - - virtual HistoryScroll* scroll(HistoryScroll *) const; -}; - -class HistoryTypeBlockArray : public HistoryType -{ -public: - HistoryTypeBlockArray(size_t size); - - virtual bool isEnabled() const; - virtual int maximumLineCount() const; - - virtual HistoryScroll* scroll(HistoryScroll *) const; - -protected: - size_t m_size; -}; - -#if 1 -class HistoryTypeFile : public HistoryType -{ -public: - HistoryTypeFile(const QString& fileName=QString()); - - virtual bool isEnabled() const; - virtual const QString& getFileName() const; - virtual int maximumLineCount() const; - - virtual HistoryScroll* scroll(HistoryScroll *) const; - -protected: - QString m_fileName; -}; - - -class HistoryTypeBuffer : public HistoryType -{ -public: - HistoryTypeBuffer(unsigned int nbLines); - - virtual bool isEnabled() const; - virtual int maximumLineCount() const; - - virtual HistoryScroll* scroll(HistoryScroll *) const; - -protected: - unsigned int m_nbLines; -}; - -#endif - -} - -#endif // TEHISTORY_H
deleted file mode 100644 --- a/gui/qtermwidget/lib/KeyboardTranslator.cpp +++ /dev/null @@ -1,903 +0,0 @@ -/* - This source file was part of Konsole, a terminal emulator. - - Copyright (C) 2007 by Robert Knight <robertknight@gmail.com> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 "KeyboardTranslator.h" - -// System -#include <ctype.h> -#include <stdio.h> - -// Qt -#include <QtCore/QBuffer> -//#include <KDebug> -#include <QtCore/QFile> -#include <QtCore/QFileInfo> -#include <QtCore> -#include <QtGui> - -// KDE -//#include <KDebug> -//#include <KLocale> -//#include <KStandardDirs> - -using namespace Konsole; - -//this is for default REALLY fallback translator. - -//const char* KeyboardTranslatorManager::defaultTranslatorText = -//#include "DefaultTranslatorText.h" -//; - -//and this is default now translator - default.keytab from original Konsole -const char* KeyboardTranslatorManager::defaultTranslatorText = -#include "ExtendedDefaultTranslator.h" -; - -KeyboardTranslatorManager::KeyboardTranslatorManager() - : _haveLoadedAll(false) -{ -} -KeyboardTranslatorManager::~KeyboardTranslatorManager() -{ - qDeleteAll(_translators.values()); -} -QString KeyboardTranslatorManager::findTranslatorPath(const QString& name) -{ - return QString("kb-layouts/" + name + ".keytab"); -} -void KeyboardTranslatorManager::findTranslators() -{ - QDir dir("kb-layouts/"); - QStringList filters; - filters << "*.keytab"; - dir.setNameFilters(filters); - QStringList list = dir.entryList(filters); //(".keytab"); // = KGlobal::dirs()->findAllResources("data", - // "konsole/*.keytab", - // KStandardDirs::NoDuplicates); - list = dir.entryList(filters); - // add the name of each translator to the list and associated - // the name with a null pointer to indicate that the translator - // has not yet been loaded from disk - QStringListIterator listIter(list); - while (listIter.hasNext()) - { - QString translatorPath = listIter.next(); - - QString name = QFileInfo(translatorPath).baseName(); - - if ( !_translators.contains(name) ) { - _translators.insert(name,0); - } - } - _haveLoadedAll = true; -} - -const KeyboardTranslator* KeyboardTranslatorManager::findTranslator(const QString& name) -{ - if ( name.isEmpty() ) - return defaultTranslator(); - -//here was smth wrong in original Konsole source - findTranslators(); - - if ( _translators.contains(name) && _translators[name] != 0 ) { - return _translators[name]; - } - - KeyboardTranslator* translator = loadTranslator(name); - - if ( translator != 0 ) - _translators[name] = translator; - else if ( !name.isEmpty() ) - qWarning() << "Unable to load translator" << name; - - return translator; -} - -bool KeyboardTranslatorManager::saveTranslator(const KeyboardTranslator* translator) -{ - const QString path = ".keytab";// = KGlobal::dirs()->saveLocation("data","konsole/")+translator->name() -// +".keytab"; - - qDebug() << "Saving translator to" << path; - - QFile destination(path); - - if (!destination.open(QIODevice::WriteOnly | QIODevice::Text)) - { - qWarning() << "Unable to save keyboard translation:" - << destination.errorString(); - - return false; - } - - { - KeyboardTranslatorWriter writer(&destination); - writer.writeHeader(translator->description()); - - QListIterator<KeyboardTranslator::Entry> iter(translator->entries()); - while ( iter.hasNext() ) - writer.writeEntry(iter.next()); - } - - destination.close(); - - return true; -} - -KeyboardTranslator* KeyboardTranslatorManager::loadTranslator(const QString& name) -{ - const QString& path = findTranslatorPath(name); - - QFile source(path); - - if (name.isEmpty() || !source.open(QIODevice::ReadOnly | QIODevice::Text)) - return 0; - - return loadTranslator(&source,name); -} - -const KeyboardTranslator* KeyboardTranslatorManager::defaultTranslator() -{ - qDebug() << "Loading default translator from text"; - QBuffer textBuffer; - textBuffer.setData(defaultTranslatorText,strlen(defaultTranslatorText)); - - if (!textBuffer.open(QIODevice::ReadOnly)) - return 0; - - return loadTranslator(&textBuffer,"fallback"); -} - -KeyboardTranslator* KeyboardTranslatorManager::loadTranslator(QIODevice* source,const QString& name) -{ - KeyboardTranslator* translator = new KeyboardTranslator(name); - KeyboardTranslatorReader reader(source); - translator->setDescription( reader.description() ); - - while ( reader.hasNextEntry() ) { - translator->addEntry(reader.nextEntry()); - } - - source->close(); - - if ( !reader.parseError() ) - { - return translator; - } - else - { - delete translator; - return 0; - } -} - -KeyboardTranslatorWriter::KeyboardTranslatorWriter(QIODevice* destination) -: _destination(destination) -{ - Q_ASSERT( destination && destination->isWritable() ); - - _writer = new QTextStream(_destination); -} -KeyboardTranslatorWriter::~KeyboardTranslatorWriter() -{ - delete _writer; -} -void KeyboardTranslatorWriter::writeHeader( const QString& description ) -{ - *_writer << "keyboard \"" << description << '\"' << '\n'; -} -void KeyboardTranslatorWriter::writeEntry( const KeyboardTranslator::Entry& entry ) -{ - QString result; - - if ( entry.command() != KeyboardTranslator::NoCommand ) - result = entry.resultToString(); - else - result = '\"' + entry.resultToString() + '\"'; - - *_writer << "key " << entry.conditionToString() << " : " << result << '\n'; -} - - -// each line of the keyboard translation file is one of: -// -// - keyboard "name" -// - key KeySequence : "characters" -// - key KeySequence : CommandName -// -// KeySequence begins with the name of the key ( taken from the Qt::Key enum ) -// and is followed by the keyboard modifiers and state flags ( with + or - in front -// of each modifier or flag to indicate whether it is required ). All keyboard modifiers -// and flags are optional, if a particular modifier or state is not specified it is -// assumed not to be a part of the sequence. The key sequence may contain whitespace -// -// eg: "key Up+Shift : scrollLineUp" -// "key Next-Shift : "\E[6~" -// -// (lines containing only whitespace are ignored, parseLine assumes that comments have -// already been removed) -// - -KeyboardTranslatorReader::KeyboardTranslatorReader( QIODevice* source ) - : _source(source) - , _hasNext(false) -{ - // read input until we find the description - while ( _description.isEmpty() && !source->atEnd() ) - { - const QList<Token>& tokens = tokenize( QString(source->readLine()) ); - - if ( !tokens.isEmpty() && tokens.first().type == Token::TitleKeyword ) - { - _description = (tokens[1].text.toUtf8()); - } - } - - readNext(); -} -void KeyboardTranslatorReader::readNext() -{ - // find next entry - while ( !_source->atEnd() ) - { - const QList<Token>& tokens = tokenize( QString(_source->readLine()) ); - if ( !tokens.isEmpty() && tokens.first().type == Token::KeyKeyword ) - { - KeyboardTranslator::States flags = KeyboardTranslator::NoState; - KeyboardTranslator::States flagMask = KeyboardTranslator::NoState; - Qt::KeyboardModifiers modifiers = Qt::NoModifier; - Qt::KeyboardModifiers modifierMask = Qt::NoModifier; - - int keyCode = Qt::Key_unknown; - - decodeSequence(tokens[1].text.toLower(), - keyCode, - modifiers, - modifierMask, - flags, - flagMask); - - KeyboardTranslator::Command command = KeyboardTranslator::NoCommand; - QByteArray text; - - // get text or command - if ( tokens[2].type == Token::OutputText ) - { - text = tokens[2].text.toLocal8Bit(); - } - else if ( tokens[2].type == Token::Command ) - { - // identify command - if (!parseAsCommand(tokens[2].text,command)) - qWarning() << "Command" << tokens[2].text << "not understood."; - } - - KeyboardTranslator::Entry newEntry; - newEntry.setKeyCode( keyCode ); - newEntry.setState( flags ); - newEntry.setStateMask( flagMask ); - newEntry.setModifiers( modifiers ); - newEntry.setModifierMask( modifierMask ); - newEntry.setText( text ); - newEntry.setCommand( command ); - - _nextEntry = newEntry; - - _hasNext = true; - - return; - } - } - - _hasNext = false; -} - -bool KeyboardTranslatorReader::parseAsCommand(const QString& text,KeyboardTranslator::Command& command) -{ - if ( text.compare("erase",Qt::CaseInsensitive) == 0 ) - command = KeyboardTranslator::EraseCommand; - else if ( text.compare("scrollpageup",Qt::CaseInsensitive) == 0 ) - command = KeyboardTranslator::ScrollPageUpCommand; - else if ( text.compare("scrollpagedown",Qt::CaseInsensitive) == 0 ) - command = KeyboardTranslator::ScrollPageDownCommand; - else if ( text.compare("scrolllineup",Qt::CaseInsensitive) == 0 ) - command = KeyboardTranslator::ScrollLineUpCommand; - else if ( text.compare("scrolllinedown",Qt::CaseInsensitive) == 0 ) - command = KeyboardTranslator::ScrollLineDownCommand; - else if ( text.compare("scrolllock",Qt::CaseInsensitive) == 0 ) - command = KeyboardTranslator::ScrollLockCommand; - else - return false; - - return true; -} - -bool KeyboardTranslatorReader::decodeSequence(const QString& text, - int& keyCode, - Qt::KeyboardModifiers& modifiers, - Qt::KeyboardModifiers& modifierMask, - KeyboardTranslator::States& flags, - KeyboardTranslator::States& flagMask) -{ - bool isWanted = true; - bool endOfItem = false; - QString buffer; - - Qt::KeyboardModifiers tempModifiers = modifiers; - Qt::KeyboardModifiers tempModifierMask = modifierMask; - KeyboardTranslator::States tempFlags = flags; - KeyboardTranslator::States tempFlagMask = flagMask; - - for ( int i = 0 ; i < text.count() ; i++ ) - { - const QChar& ch = text[i]; - bool isLastLetter = ( i == text.count()-1 ); - - endOfItem = true; - if ( ch.isLetterOrNumber() ) - { - endOfItem = false; - buffer.append(ch); - } - - if ( (endOfItem || isLastLetter) && !buffer.isEmpty() ) - { - Qt::KeyboardModifier itemModifier = Qt::NoModifier; - int itemKeyCode = 0; - KeyboardTranslator::State itemFlag = KeyboardTranslator::NoState; - - if ( parseAsModifier(buffer,itemModifier) ) - { - tempModifierMask |= itemModifier; - - if ( isWanted ) - tempModifiers |= itemModifier; - } - else if ( parseAsStateFlag(buffer,itemFlag) ) - { - tempFlagMask |= itemFlag; - - if ( isWanted ) - tempFlags |= itemFlag; - } - else if ( parseAsKeyCode(buffer,itemKeyCode) ) - keyCode = itemKeyCode; - else - qDebug() << "Unable to parse key binding item:" << buffer; - - buffer.clear(); - } - - // check if this is a wanted / not-wanted flag and update the - // state ready for the next item - if ( ch == '+' ) - isWanted = true; - else if ( ch == '-' ) - isWanted = false; - } - - modifiers = tempModifiers; - modifierMask = tempModifierMask; - flags = tempFlags; - flagMask = tempFlagMask; - - return true; -} - -bool KeyboardTranslatorReader::parseAsModifier(const QString& item , Qt::KeyboardModifier& modifier) -{ - if ( item == "shift" ) - modifier = Qt::ShiftModifier; - else if ( item == "ctrl" || item == "control" ) - modifier = Qt::ControlModifier; - else if ( item == "alt" ) - modifier = Qt::AltModifier; - else if ( item == "meta" ) - modifier = Qt::MetaModifier; - else if ( item == "keypad" ) - modifier = Qt::KeypadModifier; - else - return false; - - return true; -} -bool KeyboardTranslatorReader::parseAsStateFlag(const QString& item , KeyboardTranslator::State& flag) -{ - if ( item == "appcukeys" ) - flag = KeyboardTranslator::CursorKeysState; - else if ( item == "ansi" ) - flag = KeyboardTranslator::AnsiState; - else if ( item == "newline" ) - flag = KeyboardTranslator::NewLineState; - else if ( item == "appscreen" ) - flag = KeyboardTranslator::AlternateScreenState; - else if ( item == "anymod" ) - flag = KeyboardTranslator::AnyModifierState; - else - return false; - - return true; -} -bool KeyboardTranslatorReader::parseAsKeyCode(const QString& item , int& keyCode) -{ - QKeySequence sequence = QKeySequence::fromString(item); - if ( !sequence.isEmpty() ) - { - keyCode = sequence[0]; - - if ( sequence.count() > 1 ) - { - qDebug() << "Unhandled key codes in sequence: " << item; - } - } - // additional cases implemented for backwards compatibility with KDE 3 - else if ( item == "prior" ) - keyCode = Qt::Key_PageUp; - else if ( item == "next" ) - keyCode = Qt::Key_PageDown; - else - return false; - - return true; -} - -QString KeyboardTranslatorReader::description() const -{ - return _description; -} -bool KeyboardTranslatorReader::hasNextEntry() -{ - return _hasNext; -} -KeyboardTranslator::Entry KeyboardTranslatorReader::createEntry( const QString& condition , - const QString& result ) -{ - QString entryString("keyboard \"temporary\"\nkey "); - entryString.append(condition); - entryString.append(" : "); - - // if 'result' is the name of a command then the entry result will be that command, - // otherwise the result will be treated as a string to echo when the key sequence - // specified by 'condition' is pressed - KeyboardTranslator::Command command; - if (parseAsCommand(result,command)) - entryString.append(result); - else - entryString.append('\"' + result + '\"'); - - QByteArray array = entryString.toUtf8(); - - KeyboardTranslator::Entry entry; - - QBuffer buffer(&array); - buffer.open(QIODevice::ReadOnly); - KeyboardTranslatorReader reader(&buffer); - - if ( reader.hasNextEntry() ) - entry = reader.nextEntry(); - - return entry; -} - -KeyboardTranslator::Entry KeyboardTranslatorReader::nextEntry() -{ - Q_ASSERT( _hasNext ); - - - KeyboardTranslator::Entry entry = _nextEntry; - - readNext(); - - return entry; -} -bool KeyboardTranslatorReader::parseError() -{ - return false; -} -QList<KeyboardTranslatorReader::Token> KeyboardTranslatorReader::tokenize(const QString& line) -{ - QString text = line.simplified(); - - // comment line: # comment - static QRegExp comment("\\#.*"); - // title line: keyboard "title" - static QRegExp title("keyboard\\s+\"(.*)\""); - // key line: key KeySequence : "output" - // key line: key KeySequence : command - static QRegExp key("key\\s+([\\w\\+\\s\\-]+)\\s*:\\s*(\"(.*)\"|\\w+)"); - - QList<Token> list; - - if ( text.isEmpty() || comment.exactMatch(text) ) - { - return list; - } - - if ( title.exactMatch(text) ) - { - Token titleToken = { Token::TitleKeyword , QString() }; - Token textToken = { Token::TitleText , title.capturedTexts()[1] }; - - list << titleToken << textToken; - } - else if ( key.exactMatch(text) ) - { - Token keyToken = { Token::KeyKeyword , QString() }; - Token sequenceToken = { Token::KeySequence , key.capturedTexts()[1].remove(' ') }; - - list << keyToken << sequenceToken; - - if ( key.capturedTexts()[3].isEmpty() ) - { - // capturedTexts()[2] is a command - Token commandToken = { Token::Command , key.capturedTexts()[2] }; - list << commandToken; - } - else - { - // capturedTexts()[3] is the output string - Token outputToken = { Token::OutputText , key.capturedTexts()[3] }; - list << outputToken; - } - } - else - { - qWarning() << "Line in keyboard translator file could not be understood:" << text; - } - - return list; -} - -QList<QString> KeyboardTranslatorManager::allTranslators() -{ - if ( !_haveLoadedAll ) - { - findTranslators(); - } - - return _translators.keys(); -} - -KeyboardTranslator::Entry::Entry() -: _keyCode(0) -, _modifiers(Qt::NoModifier) -, _modifierMask(Qt::NoModifier) -, _state(NoState) -, _stateMask(NoState) -, _command(NoCommand) -{ -} - -bool KeyboardTranslator::Entry::operator==(const Entry& rhs) const -{ - return _keyCode == rhs._keyCode && - _modifiers == rhs._modifiers && - _modifierMask == rhs._modifierMask && - _state == rhs._state && - _stateMask == rhs._stateMask && - _command == rhs._command && - _text == rhs._text; -} - -bool KeyboardTranslator::Entry::matches(int keyCode , - Qt::KeyboardModifiers modifiers, - States state) const -{ - if ( _keyCode != keyCode ) - return false; - - if ( (modifiers & _modifierMask) != (_modifiers & _modifierMask) ) - return false; - - // if modifiers is non-zero, the 'any modifier' state is implicit - if ( modifiers != 0 ) - state |= AnyModifierState; - - if ( (state & _stateMask) != (_state & _stateMask) ) - return false; - - // special handling for the 'Any Modifier' state, which checks for the presence of - // any or no modifiers. In this context, the 'keypad' modifier does not count. - bool anyModifiersSet = modifiers != 0 && modifiers != Qt::KeypadModifier; - if ( _stateMask & KeyboardTranslator::AnyModifierState ) - { - // test fails if any modifier is required but none are set - if ( (_state & KeyboardTranslator::AnyModifierState) && !anyModifiersSet ) - return false; - - // test fails if no modifier is allowed but one or more are set - if ( !(_state & KeyboardTranslator::AnyModifierState) && anyModifiersSet ) - return false; - } - - return true; -} -QByteArray KeyboardTranslator::Entry::escapedText(bool expandWildCards,Qt::KeyboardModifiers modifiers) const -{ - QByteArray result(text(expandWildCards,modifiers)); - - for ( int i = 0 ; i < result.count() ; i++ ) - { - char ch = result[i]; - char replacement = 0; - - switch ( ch ) - { - case 27 : replacement = 'E'; break; - case 8 : replacement = 'b'; break; - case 12 : replacement = 'f'; break; - case 9 : replacement = 't'; break; - case 13 : replacement = 'r'; break; - case 10 : replacement = 'n'; break; - default: - // any character which is not printable is replaced by an equivalent - // \xhh escape sequence (where 'hh' are the corresponding hex digits) - if ( !QChar(ch).isPrint() ) - replacement = 'x'; - } - - if ( replacement == 'x' ) - { - result.replace(i,1,"\\x"+QByteArray(1,ch).toInt(0, 16)); - } else if ( replacement != 0 ) - { - result.remove(i,1); - result.insert(i,'\\'); - result.insert(i+1,replacement); - } - } - - return result; -} -QByteArray KeyboardTranslator::Entry::unescape(const QByteArray& input) const -{ - QByteArray result(input); - - for ( int i = 0 ; i < result.count()-1 ; i++ ) - { - - QByteRef ch = result[i]; - if ( ch == '\\' ) - { - char replacement[2] = {0,0}; - int charsToRemove = 2; - bool escapedChar = true; - - switch ( result[i+1] ) - { - case 'E' : replacement[0] = 27; break; - case 'b' : replacement[0] = 8 ; break; - case 'f' : replacement[0] = 12; break; - case 't' : replacement[0] = 9 ; break; - case 'r' : replacement[0] = 13; break; - case 'n' : replacement[0] = 10; break; - case 'x' : - { - // format is \xh or \xhh where 'h' is a hexadecimal - // digit from 0-9 or A-F which should be replaced - // with the corresponding character value - char hexDigits[3] = {0}; - - if ( (i < result.count()-2) && isxdigit(result[i+2]) ) - hexDigits[0] = result[i+2]; - if ( (i < result.count()-3) && isxdigit(result[i+3]) ) - hexDigits[1] = result[i+3]; - - int charValue = 0; - sscanf(hexDigits,"%x",&charValue); - - replacement[0] = (char)charValue; - - charsToRemove = 2 + strlen(hexDigits); - } - break; - default: - escapedChar = false; - } - - if ( escapedChar ) - result.replace(i,charsToRemove,replacement); - } - } - - return result; -} - -void KeyboardTranslator::Entry::insertModifier( QString& item , int modifier ) const -{ - if ( !(modifier & _modifierMask) ) - return; - - if ( modifier & _modifiers ) - item += '+'; - else - item += '-'; - - if ( modifier == Qt::ShiftModifier ) - item += "Shift"; - else if ( modifier == Qt::ControlModifier ) - item += "Ctrl"; - else if ( modifier == Qt::AltModifier ) - item += "Alt"; - else if ( modifier == Qt::MetaModifier ) - item += "Meta"; - else if ( modifier == Qt::KeypadModifier ) - item += "KeyPad"; -} -void KeyboardTranslator::Entry::insertState( QString& item , int state ) const -{ - if ( !(state & _stateMask) ) - return; - - if ( state & _state ) - item += '+' ; - else - item += '-' ; - - if ( state == KeyboardTranslator::AlternateScreenState ) - item += "AppScreen"; - else if ( state == KeyboardTranslator::NewLineState ) - item += "NewLine"; - else if ( state == KeyboardTranslator::AnsiState ) - item += "Ansi"; - else if ( state == KeyboardTranslator::CursorKeysState ) - item += "AppCuKeys"; - else if ( state == KeyboardTranslator::AnyModifierState ) - item += "AnyMod"; -} -QString KeyboardTranslator::Entry::resultToString(bool expandWildCards,Qt::KeyboardModifiers modifiers) const -{ - if ( !_text.isEmpty() ) - return escapedText(expandWildCards,modifiers); - else if ( _command == EraseCommand ) - return "Erase"; - else if ( _command == ScrollPageUpCommand ) - return "ScrollPageUp"; - else if ( _command == ScrollPageDownCommand ) - return "ScrollPageDown"; - else if ( _command == ScrollLineUpCommand ) - return "ScrollLineUp"; - else if ( _command == ScrollLineDownCommand ) - return "ScrollLineDown"; - else if ( _command == ScrollLockCommand ) - return "ScrollLock"; - - return QString(); -} -QString KeyboardTranslator::Entry::conditionToString() const -{ - QString result = QKeySequence(_keyCode).toString(); - - // add modifiers - insertModifier( result , Qt::ShiftModifier ); - insertModifier( result , Qt::ControlModifier ); - insertModifier( result , Qt::AltModifier ); - insertModifier( result , Qt::MetaModifier ); - - // add states - insertState( result , KeyboardTranslator::AlternateScreenState ); - insertState( result , KeyboardTranslator::NewLineState ); - insertState( result , KeyboardTranslator::AnsiState ); - insertState( result , KeyboardTranslator::CursorKeysState ); - insertState( result , KeyboardTranslator::AnyModifierState ); - - return result; -} - -KeyboardTranslator::KeyboardTranslator(const QString& name) -: _name(name) -{ -} - -void KeyboardTranslator::setDescription(const QString& description) -{ - _description = description; -} -QString KeyboardTranslator::description() const -{ - return _description; -} -void KeyboardTranslator::setName(const QString& name) -{ - _name = name; -} -QString KeyboardTranslator::name() const -{ - return _name; -} - -QList<KeyboardTranslator::Entry> KeyboardTranslator::entries() const -{ - return _entries.values(); -} - -void KeyboardTranslator::addEntry(const Entry& entry) -{ - const int keyCode = entry.keyCode(); - _entries.insertMulti(keyCode,entry); -} -void KeyboardTranslator::replaceEntry(const Entry& existing , const Entry& replacement) -{ - if ( !existing.isNull() ) - _entries.remove(existing.keyCode()); - _entries.insertMulti(replacement.keyCode(),replacement); -} -void KeyboardTranslator::removeEntry(const Entry& entry) -{ - _entries.remove(entry.keyCode()); -} -KeyboardTranslator::Entry KeyboardTranslator::findEntry(int keyCode, Qt::KeyboardModifiers modifiers, States state) const -{ - if ( _entries.contains(keyCode) ) - { - QList<Entry> entriesForKey = _entries.values(keyCode); - - QListIterator<Entry> iter(entriesForKey); - - while (iter.hasNext()) - { - const Entry& next = iter.next(); - if ( next.matches(keyCode,modifiers,state) ) - return next; - } - - return Entry(); // entry not found - } - else - { - return Entry(); - } - -} -void KeyboardTranslatorManager::addTranslator(KeyboardTranslator* translator) -{ - _translators.insert(translator->name(),translator); - - if ( !saveTranslator(translator) ) - qWarning() << "Unable to save translator" << translator->name() - << "to disk."; -} -bool KeyboardTranslatorManager::deleteTranslator(const QString& name) -{ - Q_ASSERT( _translators.contains(name) ); - - // locate and delete - QString path = findTranslatorPath(name); - if ( QFile::remove(path) ) - { - _translators.remove(name); - return true; - } - else - { - qWarning() << "Failed to remove translator - " << path; - return false; - } -} -K_GLOBAL_STATIC( KeyboardTranslatorManager , theKeyboardTranslatorManager ) -KeyboardTranslatorManager* KeyboardTranslatorManager::instance() -{ - return theKeyboardTranslatorManager; -}
deleted file mode 100644 --- a/gui/qtermwidget/lib/KeyboardTranslator.h +++ /dev/null @@ -1,657 +0,0 @@ -/* - This source file is part of Konsole, a terminal emulator. - - Copyright (C) 2007 by Robert Knight <robertknight@gmail.com> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 KEYBOARDTRANSLATOR_H -#define KEYBOARDTRANSLATOR_H - -// Qt -#include <QtCore/QHash> -#include <QtCore/QList> -#include <QtGui/QKeySequence> -#include <QtCore/QMetaType> -#include <QtCore/QVarLengthArray> -#include <QtCore> - -typedef void (*CleanUpFunction)(); - -/** - * @internal - * - * Helper class for K_GLOBAL_STATIC to clean up the object on library unload or application - * shutdown. - */ -class CleanUpGlobalStatic -{ - public: - CleanUpFunction func; - - inline ~CleanUpGlobalStatic() { func(); } -}; - - -//these directives are taken from the heart of kdecore - -# define K_GLOBAL_STATIC_STRUCT_NAME(NAME) - -#if QT_VERSION < 0x040400 -# define Q_BASIC_ATOMIC_INITIALIZER Q_ATOMIC_INIT -# define testAndSetOrdered testAndSet -#endif - -#define K_GLOBAL_STATIC(TYPE, NAME) K_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ()) - -#define K_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \ -static QBasicAtomicPointer<TYPE > _k_static_##NAME = Q_BASIC_ATOMIC_INITIALIZER(0); \ -static bool _k_static_##NAME##_destroyed; \ -static struct K_GLOBAL_STATIC_STRUCT_NAME(NAME) \ -{ \ - bool isDestroyed() \ - { \ - return _k_static_##NAME##_destroyed; \ - } \ - inline operator TYPE*() \ - { \ - return operator->(); \ - } \ - inline TYPE *operator->() \ - { \ - if (!_k_static_##NAME) { \ - if (isDestroyed()) { \ - qFatal("Fatal Error: Accessed global static '%s *%s()' after destruction. " \ - "Defined at %s:%d", #TYPE, #NAME, __FILE__, __LINE__); \ - } \ - TYPE *x = new TYPE ARGS; \ - if (!_k_static_##NAME.testAndSetOrdered(0, x) \ - && _k_static_##NAME != x ) { \ - delete x; \ - } else { \ - static CleanUpGlobalStatic cleanUpObject = { destroy }; \ - } \ - } \ - return _k_static_##NAME; \ - } \ - inline TYPE &operator*() \ - { \ - return *operator->(); \ - } \ - static void destroy() \ - { \ - _k_static_##NAME##_destroyed = true; \ - TYPE *x = _k_static_##NAME; \ - _k_static_##NAME = 0; \ - delete x; \ - } \ -} NAME; - - - - - -class QIODevice; -class QTextStream; - -namespace Konsole -{ - -/** - * A convertor which maps between key sequences pressed by the user and the - * character strings which should be sent to the terminal and commands - * which should be invoked when those character sequences are pressed. - * - * Konsole supports multiple keyboard translators, allowing the user to - * specify the character sequences which are sent to the terminal - * when particular key sequences are pressed. - * - * A key sequence is defined as a key code, associated keyboard modifiers - * (Shift,Ctrl,Alt,Meta etc.) and state flags which indicate the state - * which the terminal must be in for the key sequence to apply. - */ -class KeyboardTranslator -{ -public: - /** - * The meaning of a particular key sequence may depend upon the state which - * the terminal emulation is in. Therefore findEntry() may return a different - * Entry depending upon the state flags supplied. - * - * This enum describes the states which may be associated with with a particular - * entry in the keyboard translation entry. - */ - enum State - { - /** Indicates that no special state is active */ - NoState = 0, - /** - * TODO More documentation - */ - NewLineState = 1, - /** - * Indicates that the terminal is in 'Ansi' mode. - * TODO: More documentation - */ - AnsiState = 2, - /** - * TODO More documentation - */ - CursorKeysState = 4, - /** - * Indicates that the alternate screen ( typically used by interactive programs - * such as screen or vim ) is active - */ - AlternateScreenState = 8, - /** Indicates that any of the modifier keys is active. */ - AnyModifierState = 16 - }; - Q_DECLARE_FLAGS(States,State) - - /** - * This enum describes commands which are associated with particular key sequences. - */ - enum Command - { - /** Indicates that no command is associated with this command sequence */ - NoCommand = 0, - /** TODO Document me */ - SendCommand = 1, - /** Scroll the terminal display up one page */ - ScrollPageUpCommand = 2, - /** Scroll the terminal display down one page */ - ScrollPageDownCommand = 4, - /** Scroll the terminal display up one line */ - ScrollLineUpCommand = 8, - /** Scroll the terminal display down one line */ - ScrollLineDownCommand = 16, - /** Toggles scroll lock mode */ - ScrollLockCommand = 32, - /** Echos the operating system specific erase character. */ - EraseCommand = 64 - }; - Q_DECLARE_FLAGS(Commands,Command) - - /** - * Represents an association between a key sequence pressed by the user - * and the character sequence and commands associated with it for a particular - * KeyboardTranslator. - */ - class Entry - { - public: - /** - * Constructs a new entry for a keyboard translator. - */ - Entry(); - - /** - * Returns true if this entry is null. - * This is true for newly constructed entries which have no properties set. - */ - bool isNull() const; - - /** Returns the commands associated with this entry */ - Command command() const; - /** Sets the command associated with this entry. */ - void setCommand(Command command); - - /** - * Returns the character sequence associated with this entry, optionally replacing - * wildcard '*' characters with numbers to indicate the keyboard modifiers being pressed. - * - * TODO: The numbers used to replace '*' characters are taken from the Konsole/KDE 3 code. - * Document them. - * - * @param expandWildCards Specifies whether wild cards (occurrences of the '*' character) in - * the entry should be replaced with a number to indicate the modifier keys being pressed. - * - * @param modifiers The keyboard modifiers being pressed. - */ - QByteArray text(bool expandWildCards = false, - Qt::KeyboardModifiers modifiers = Qt::NoModifier) const; - - /** Sets the character sequence associated with this entry */ - void setText(const QByteArray& text); - - /** - * Returns the character sequence associated with this entry, - * with any non-printable characters replaced with escape sequences. - * - * eg. \\E for Escape, \\t for tab, \\n for new line. - * - * @param expandWildCards See text() - * @param modifiers See text() - */ - QByteArray escapedText(bool expandWildCards = false, - Qt::KeyboardModifiers modifiers = Qt::NoModifier) const; - - /** Returns the character code ( from the Qt::Key enum ) associated with this entry */ - int keyCode() const; - /** Sets the character code associated with this entry */ - void setKeyCode(int keyCode); - - /** - * Returns a bitwise-OR of the enabled keyboard modifiers associated with this entry. - * If a modifier is set in modifierMask() but not in modifiers(), this means that the entry - * only matches when that modifier is NOT pressed. - * - * If a modifier is not set in modifierMask() then the entry matches whether the modifier - * is pressed or not. - */ - Qt::KeyboardModifiers modifiers() const; - - /** Returns the keyboard modifiers which are valid in this entry. See modifiers() */ - Qt::KeyboardModifiers modifierMask() const; - - /** See modifiers() */ - void setModifiers( Qt::KeyboardModifiers modifiers ); - /** See modifierMask() and modifiers() */ - void setModifierMask( Qt::KeyboardModifiers modifiers ); - - /** - * Returns a bitwise-OR of the enabled state flags associated with this entry. - * If flag is set in stateMask() but not in state(), this means that the entry only - * matches when the terminal is NOT in that state. - * - * If a state is not set in stateMask() then the entry matches whether the terminal - * is in that state or not. - */ - States state() const; - - /** Returns the state flags which are valid in this entry. See state() */ - States stateMask() const; - - /** See state() */ - void setState( States state ); - /** See stateMask() */ - void setStateMask( States mask ); - - /** - * Returns the key code and modifiers associated with this entry - * as a QKeySequence - */ - //QKeySequence keySequence() const; - - /** - * Returns this entry's conditions ( ie. its key code, modifier and state criteria ) - * as a string. - */ - QString conditionToString() const; - - /** - * Returns this entry's result ( ie. its command or character sequence ) - * as a string. - * - * @param expandWildCards See text() - * @param modifiers See text() - */ - QString resultToString(bool expandWildCards = false, - Qt::KeyboardModifiers modifiers = Qt::NoModifier) const; - - /** - * Returns true if this entry matches the given key sequence, specified - * as a combination of @p keyCode , @p modifiers and @p state. - */ - bool matches( int keyCode , - Qt::KeyboardModifiers modifiers , - States flags ) const; - - bool operator==(const Entry& rhs) const; - - private: - void insertModifier( QString& item , int modifier ) const; - void insertState( QString& item , int state ) const; - QByteArray unescape(const QByteArray& text) const; - - int _keyCode; - Qt::KeyboardModifiers _modifiers; - Qt::KeyboardModifiers _modifierMask; - States _state; - States _stateMask; - - Command _command; - QByteArray _text; - }; - - /** Constructs a new keyboard translator with the given @p name */ - KeyboardTranslator(const QString& name); - - //KeyboardTranslator(const KeyboardTranslator& other); - - /** Returns the name of this keyboard translator */ - QString name() const; - - /** Sets the name of this keyboard translator */ - void setName(const QString& name); - - /** Returns the descriptive name of this keyboard translator */ - QString description() const; - - /** Sets the descriptive name of this keyboard translator */ - void setDescription(const QString& description); - - /** - * Looks for an entry in this keyboard translator which matches the given - * key code, keyboard modifiers and state flags. - * - * Returns the matching entry if found or a null Entry otherwise ( ie. - * entry.isNull() will return true ) - * - * @param keyCode A key code from the Qt::Key enum - * @param modifiers A combination of modifiers - * @param state Optional flags which specify the current state of the terminal - */ - Entry findEntry(int keyCode , - Qt::KeyboardModifiers modifiers , - States state = NoState) const; - - /** - * Adds an entry to this keyboard translator's table. Entries can be looked up according - * to their key sequence using findEntry() - */ - void addEntry(const Entry& entry); - - /** - * Replaces an entry in the translator. If the @p existing entry is null, - * then this is equivalent to calling addEntry(@p replacement) - */ - void replaceEntry(const Entry& existing , const Entry& replacement); - - /** - * Removes an entry from the table. - */ - void removeEntry(const Entry& entry); - - /** Returns a list of all entries in the translator. */ - QList<Entry> entries() const; - -private: - - QHash<int,Entry> _entries; // entries in this keyboard translation, - // entries are indexed according to - // their keycode - QString _name; - QString _description; -}; -Q_DECLARE_OPERATORS_FOR_FLAGS(KeyboardTranslator::States) -Q_DECLARE_OPERATORS_FOR_FLAGS(KeyboardTranslator::Commands) - -/** - * Parses the contents of a Keyboard Translator (.keytab) file and - * returns the entries found in it. - * - * Usage example: - * - * @code - * QFile source( "/path/to/keytab" ); - * source.open( QIODevice::ReadOnly ); - * - * KeyboardTranslator* translator = new KeyboardTranslator( "name-of-translator" ); - * - * KeyboardTranslatorReader reader(source); - * while ( reader.hasNextEntry() ) - * translator->addEntry(reader.nextEntry()); - * - * source.close(); - * - * if ( !reader.parseError() ) - * { - * // parsing succeeded, do something with the translator - * } - * else - * { - * // parsing failed - * } - * @endcode - */ -class KeyboardTranslatorReader -{ -public: - /** Constructs a new reader which parses the given @p source */ - KeyboardTranslatorReader( QIODevice* source ); - - /** - * Returns the description text. - * TODO: More documentation - */ - QString description() const; - - /** Returns true if there is another entry in the source stream */ - bool hasNextEntry(); - /** Returns the next entry found in the source stream */ - KeyboardTranslator::Entry nextEntry(); - - /** - * Returns true if an error occurred whilst parsing the input or - * false if no error occurred. - */ - bool parseError(); - - /** - * Parses a condition and result string for a translator entry - * and produces a keyboard translator entry. - * - * The condition and result strings are in the same format as in - */ - static KeyboardTranslator::Entry createEntry( const QString& condition , - const QString& result ); -private: - struct Token - { - enum Type - { - TitleKeyword, - TitleText, - KeyKeyword, - KeySequence, - Command, - OutputText - }; - Type type; - QString text; - }; - QList<Token> tokenize(const QString&); - void readNext(); - bool decodeSequence(const QString& , - int& keyCode, - Qt::KeyboardModifiers& modifiers, - Qt::KeyboardModifiers& modifierMask, - KeyboardTranslator::States& state, - KeyboardTranslator::States& stateFlags); - - static bool parseAsModifier(const QString& item , Qt::KeyboardModifier& modifier); - static bool parseAsStateFlag(const QString& item , KeyboardTranslator::State& state); - static bool parseAsKeyCode(const QString& item , int& keyCode); - static bool parseAsCommand(const QString& text , KeyboardTranslator::Command& command); - - QIODevice* _source; - QString _description; - KeyboardTranslator::Entry _nextEntry; - bool _hasNext; -}; - -/** Writes a keyboard translation to disk. */ -class KeyboardTranslatorWriter -{ -public: - /** - * Constructs a new writer which saves data into @p destination. - * The caller is responsible for closing the device when writing is complete. - */ - KeyboardTranslatorWriter(QIODevice* destination); - ~KeyboardTranslatorWriter(); - - /** - * Writes the header for the keyboard translator. - * @param description Description of the keyboard translator. - */ - void writeHeader( const QString& description ); - /** Writes a translator entry. */ - void writeEntry( const KeyboardTranslator::Entry& entry ); - -private: - QIODevice* _destination; - QTextStream* _writer; -}; - -/** - * Manages the keyboard translations available for use by terminal sessions, - * see KeyboardTranslator. - */ -class KeyboardTranslatorManager -{ -public: - /** - * Constructs a new KeyboardTranslatorManager and loads the list of - * available keyboard translations. - * - * The keyboard translations themselves are not loaded until they are - * first requested via a call to findTranslator() - */ - KeyboardTranslatorManager(); - ~KeyboardTranslatorManager(); - - /** - * Adds a new translator. If a translator with the same name - * already exists, it will be replaced by the new translator. - * - * TODO: More documentation. - */ - void addTranslator(KeyboardTranslator* translator); - - /** - * Deletes a translator. Returns true on successful deletion or false otherwise. - * - * TODO: More documentation - */ - bool deleteTranslator(const QString& name); - - /** Returns the default translator for Konsole. */ - const KeyboardTranslator* defaultTranslator(); - - /** - * Returns the keyboard translator with the given name or 0 if no translator - * with that name exists. - * - * The first time that a translator with a particular name is requested, - * the on-disk .keyboard file is loaded and parsed. - */ - const KeyboardTranslator* findTranslator(const QString& name); - /** - * Returns a list of the names of available keyboard translators. - * - * The first time this is called, a search for available - * translators is started. - */ - QList<QString> allTranslators(); - - /** Returns the global KeyboardTranslatorManager instance. */ - static KeyboardTranslatorManager* instance(); - -private: - static const char* defaultTranslatorText; - - void findTranslators(); // locate the available translators - KeyboardTranslator* loadTranslator(const QString& name); // loads the translator - // with the given name - KeyboardTranslator* loadTranslator(QIODevice* device,const QString& name); - - bool saveTranslator(const KeyboardTranslator* translator); - QString findTranslatorPath(const QString& name); - - QHash<QString,KeyboardTranslator*> _translators; // maps translator-name -> KeyboardTranslator - // instance - bool _haveLoadedAll; -}; - -inline int KeyboardTranslator::Entry::keyCode() const { return _keyCode; } -inline void KeyboardTranslator::Entry::setKeyCode(int keyCode) { _keyCode = keyCode; } - -inline void KeyboardTranslator::Entry::setModifiers( Qt::KeyboardModifiers modifier ) -{ - _modifiers = modifier; -} -inline Qt::KeyboardModifiers KeyboardTranslator::Entry::modifiers() const { return _modifiers; } - -inline void KeyboardTranslator::Entry::setModifierMask( Qt::KeyboardModifiers mask ) -{ - _modifierMask = mask; -} -inline Qt::KeyboardModifiers KeyboardTranslator::Entry::modifierMask() const { return _modifierMask; } - -inline bool KeyboardTranslator::Entry::isNull() const -{ - return ( *this == Entry() ); -} - -inline void KeyboardTranslator::Entry::setCommand( Command command ) -{ - _command = command; -} -inline KeyboardTranslator::Command KeyboardTranslator::Entry::command() const { return _command; } - -inline void KeyboardTranslator::Entry::setText( const QByteArray& text ) -{ - _text = unescape(text); -} -inline int oneOrZero(int value) -{ - return value ? 1 : 0; -} -inline QByteArray KeyboardTranslator::Entry::text(bool expandWildCards,Qt::KeyboardModifiers modifiers) const -{ - QByteArray expandedText = _text; - - if (expandWildCards) - { - int modifierValue = 1; - modifierValue += oneOrZero(modifiers & Qt::ShiftModifier); - modifierValue += oneOrZero(modifiers & Qt::AltModifier) << 1; - modifierValue += oneOrZero(modifiers & Qt::ControlModifier) << 2; - - for (int i=0;i<_text.length();i++) - { - if (expandedText[i] == '*') - expandedText[i] = '0' + modifierValue; - } - } - - return expandedText; -} - -inline void KeyboardTranslator::Entry::setState( States state ) -{ - _state = state; -} -inline KeyboardTranslator::States KeyboardTranslator::Entry::state() const { return _state; } - -inline void KeyboardTranslator::Entry::setStateMask( States stateMask ) -{ - _stateMask = stateMask; -} -inline KeyboardTranslator::States KeyboardTranslator::Entry::stateMask() const { return _stateMask; } - -} - -Q_DECLARE_METATYPE(Konsole::KeyboardTranslator::Entry) -Q_DECLARE_METATYPE(const Konsole::KeyboardTranslator*) - -#endif // KEYBOARDTRANSLATOR_H -
deleted file mode 100644 --- a/gui/qtermwidget/lib/LineFont.h +++ /dev/null @@ -1,21 +0,0 @@ -// WARNING: Autogenerated by "fontembedder ./linefont.src". -// You probably do not want to hand-edit this! - -static const quint32 LineChars[] = { - 0x00007c00, 0x000fffe0, 0x00421084, 0x00e739ce, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00427000, 0x004e7380, 0x00e77800, 0x00ef7bc0, - 0x00421c00, 0x00439ce0, 0x00e73c00, 0x00e7bde0, 0x00007084, 0x000e7384, 0x000079ce, 0x000f7bce, - 0x00001c84, 0x00039ce4, 0x00003dce, 0x0007bdee, 0x00427084, 0x004e7384, 0x004279ce, 0x00e77884, - 0x00e779ce, 0x004f7bce, 0x00ef7bc4, 0x00ef7bce, 0x00421c84, 0x00439ce4, 0x00423dce, 0x00e73c84, - 0x00e73dce, 0x0047bdee, 0x00e7bde4, 0x00e7bdee, 0x00427c00, 0x0043fce0, 0x004e7f80, 0x004fffe0, - 0x004fffe0, 0x00e7fde0, 0x006f7fc0, 0x00efffe0, 0x00007c84, 0x0003fce4, 0x000e7f84, 0x000fffe4, - 0x00007dce, 0x0007fdee, 0x000f7fce, 0x000fffee, 0x00427c84, 0x0043fce4, 0x004e7f84, 0x004fffe4, - 0x00427dce, 0x00e77c84, 0x00e77dce, 0x0047fdee, 0x004e7fce, 0x00e7fde4, 0x00ef7f84, 0x004fffee, - 0x00efffe4, 0x00e7fdee, 0x00ef7fce, 0x00efffee, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x000f83e0, 0x00a5294a, 0x004e1380, 0x00a57800, 0x00ad0bc0, 0x004390e0, 0x00a53c00, 0x00a5a1e0, - 0x000e1384, 0x0000794a, 0x000f0b4a, 0x000390e4, 0x00003d4a, 0x0007a16a, 0x004e1384, 0x00a5694a, - 0x00ad2b4a, 0x004390e4, 0x00a52d4a, 0x00a5a16a, 0x004f83e0, 0x00a57c00, 0x00ad83e0, 0x000f83e4, - 0x00007d4a, 0x000f836a, 0x004f93e4, 0x00a57d4a, 0x00ad836a, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00001c00, 0x00001084, 0x00007000, 0x00421000, - 0x00039ce0, 0x000039ce, 0x000e7380, 0x00e73800, 0x000e7f80, 0x00e73884, 0x0003fce0, 0x004239ce -};
deleted file mode 100644 --- a/gui/qtermwidget/lib/LineFont.src +++ /dev/null @@ -1,786 +0,0 @@ -#2500: single horizontal line -2500 - - ------ - - - -#2501: triple horizontal line -2501 - ------ ------ ------ - - -#2502: single vertical line -2502 - | - | - | - | - | - -#2503: triple vertical line -2503 - ||| - ||| - ||| - ||| - ||| - -#2504-250B are dashed - not handled - -#250C: top-left corner (lines on bottom + right) -250C - - - .-- - | - | - -#250D: as above, but top line triple-width -250D - - .-- - .-- - |-- - | - -#250E: now the vert line triple-width -250E - - - ..-- - ||| - ||| - -#250F: and now both lines triple-width -250F - - .___ - |.-- - ||._ - ||| - -#2510: top-right corner -2510 - - ---. - | - | - -2511 - -==. -==. -==| - | - -2512 - - -==.. - ||| - ||| - -2513 - -===. -==.| -=.|| - ||| - -#2514: bottom-left corner -2514 - | - | - .== - - - -2515 - | - |== - |== - === - - - -2516 - ||| - ||| - |.== - - - -2517 - ||| - ||.= - |.== - .=== - - -#2518: bottm-right corner -2518 - | - | -==. - - - -2519 - | -==| -==| -=== - - - -251A - ||| - ||| -==== - - - -251B - ||| -=.|| -==.| -===. - - -#251C: Join of vertical line and one from the right -251C - | - | - |== - | - | - -251D - | - |== - |== - |== - | - -251E - ||| - ||| - ||== - | - | - -251F - | - | - ||== - ||| - ||| - - -2520 - ||| - ||| - ||== - ||| - ||| - -2521 - ||| - |||= - ||== - .|== - | - -2522 - | - .|== - ||== - |||= - ||| - -2523 - ||| - ||.= - ||== - ||.= - ||| - -#2524: Join of vertical line and one from the left -2524 - | - | -==| - | - | - -2525 - | -==| -==| -==| - | - -2526 - ||| - ||| -==+| - | - | - -2527 - | - | -==+| - ||| - ||| - -2528 - ||| - ||| -==+| - ||| - ||| - -2529 - ||| -=+|| -==+| -===+ - | - -252A - | -=+|| -==+| -===+ - ||| - -252B - ||| -=+|| -==+| -=+|| - ||| - -#252C: horizontal line joined to from below -252C - - -===== - | - | - -252D - -=== -==|== -==| - | - -252E - - === -==|== - |== - | - -252F - -==+== -==|== -==|== - | - -2530 - -===== -===== -==|== - | - -2531 - -===| -==||= -=||| - ||| - -2532 - - |=== -=||== - ||== - || - -2533 - -===== -==|== -=+|+= - ||| - -#2534: bottom line, connected to from top -2534 - | - | -===== - - - -2535 - | -==| -===== -=== - - -2536 - | - |== -===== - === - - -2537 - | -==|== -===== -===== - - -2538 - ||| - ||| -===== - - - -2539 - ||| -==|| -===== -===| - - - -253A - ||| - ||== -=|=== - |=== - - -253B - ||| -==|== -===== -===== - - -#253C: vertical + horizontal lines intersecting -253C - | - | -===== - | - | - -253D - | -==| -===== -==| - | - -253E - | - |== -===== - |== - | - -253F - | -==|== -===== -==|== - | - -2540 - ||| - ||| -===== - | - | - -2541 - | - | -===== - ||| - ||| - -2542 - ||| - ||| -===== - ||| - ||| - -2543 - ||| -=||| -===== -==|+ - | - -2544 - ||| - ||== -===== - |== - | - -2545 - | -==|+ -===== -=||| - ||| - -2546 - | - |== -===== - ||== - ||| - -2547 - ||| -=|||= -===== -=|||= - | - -2548 - | -=|||= -===== -=|||= - ||| - -2549 - ||| -=||| -===== -=||| - ||| - -254A - ||| - |||= -===== - |||= - ||| - -254B - ||| -=|||= -===== -=|||= - ||| - -#254C-254F are dashed -2550 - -_____ - -_____ - - -2551 - | | - | | - | | - | | - | | - -2552 - - |-- - | - |-- - | - -2553 - - - ---- - | | - | | - -2554 - - +--- - | - + +- - | | - -2555 - ---+ - | ---+ - | - -2556 - - --+-+ - | | - | | - -2557 - ----+ - | --+ | - | | - -2558 - | - +-- - | - +-- - -2559 - | | - | | - +-+- - - - -255A - | | - | +- - | - +--- - - -255B - | ---+ - | ---+ - - -255C - | | - | | --+-+ - - -255D - | | --+ | - | ----+ - - -255E - | - +-- - | - +-- - | - -255F - | | - | | - | +- - | | - | | - -2560 - | | - | +- - | | - | +- - | | - -2561 - | ---+ - | ---+ - | - -2562 - | | - | | --+ + - | | - | | - -2563 - | | --+ | - | --+ | - | | - -2564 - ------ - ---+-- - | - -2565 - - --+-+- - | | - | | - -2566 - ------ - --+ +- - | | - -2567 - | ---+-- - ------ - - -2568 - | | - | | --+-+- - - - -2569 - | | --+ +- - ------ - - -256A - | ---+-- - | ---+-- - | - -256B - | | - | | --+-+- - | | - | | - -256C - | | --+ +- - --+ +- - | | - -#256F-2570 are curly, -#2571-2573 are slashes and X - -2574 - - -___ - - - -2575 - | - | - | - - - -2576 - - - ___ - - - -2577 - - - | - | - | - -2578 - -___ -___ -___ - - -2579 - ||| - ||| - ||| - - - -257A - - ___ - ___ - ___ - - -257B - - - ||| - ||| - ||| - -257C - - ___ -_____ - ___ - - -257D - | - | - ||| - ||| - ||| - -257E - -___ -_____ -___ - - -257F - ||| - ||| - ||| - | - |
deleted file mode 100644 --- a/gui/qtermwidget/lib/Pty.cpp +++ /dev/null @@ -1,320 +0,0 @@ -/* - This file is part of Konsole, an X terminal. - Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 "Pty.h" - -// System -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include <errno.h> -#include <termios.h> - -// Qt -#include <QtCore> - -// KDE -//#include <KStandardDirs> -//#include <KLocale> -//#include <KDebug> -#include "kpty.h" - -using namespace Konsole; - -void Pty::donePty() -{ - emit done(exitStatus()); -} - -void Pty::setWindowSize(int lines, int cols) -{ - _windowColumns = cols; - _windowLines = lines; - - if (pty()->masterFd() >= 0) - pty()->setWinSize(lines, cols); -} -QSize Pty::windowSize() const -{ - return QSize(_windowColumns,_windowLines); -} - -void Pty::setXonXoff(bool enable) -{ - _xonXoff = enable; - - if (pty()->masterFd() >= 0) - { - struct ::termios ttmode; - pty()->tcGetAttr(&ttmode); - if (!enable) - ttmode.c_iflag &= ~(IXOFF | IXON); - else - ttmode.c_iflag |= (IXOFF | IXON); - if (!pty()->tcSetAttr(&ttmode)) - qWarning("Unable to set terminal attributes."); - } -} - -void Pty::setUtf8Mode(bool enable) -{ -#ifdef IUTF8 // XXX not a reasonable place to check it. - _utf8 = enable; - - if (pty()->masterFd() >= 0) - { - struct ::termios ttmode; - pty()->tcGetAttr(&ttmode); - if (!enable) - ttmode.c_iflag &= ~IUTF8; - else - ttmode.c_iflag |= IUTF8; - if (!pty()->tcSetAttr(&ttmode)) - qWarning("Unable to set terminal attributes."); - } -#endif -} - -void Pty::setErase(char erase) -{ - _eraseChar = erase; - - if (pty()->masterFd() >= 0) - { - struct ::termios ttmode; - - pty()->tcGetAttr(&ttmode); - - ttmode.c_cc[VERASE] = erase; - - if (!pty()->tcSetAttr(&ttmode)) - qWarning("Unable to set terminal attributes."); - } -} - -char Pty::erase() const -{ - if (pty()->masterFd() >= 0) - { - qDebug() << "Getting erase char"; - struct ::termios ttyAttributes; - pty()->tcGetAttr(&ttyAttributes); - return ttyAttributes.c_cc[VERASE]; - } - - return _eraseChar; -} - -void Pty::addEnvironmentVariables(const QStringList& environment) -{ - QListIterator<QString> iter(environment); - while (iter.hasNext()) - { - QString pair = iter.next(); - - // split on the first '=' character - int pos = pair.indexOf('='); - - if ( pos >= 0 ) - { - QString variable = pair.left(pos); - QString value = pair.mid(pos+1); - - //kDebug() << "Setting environment pair" << variable << - // " set to " << value; - - setEnvironment(variable,value); - } - } -} - -int Pty::start(const QString& program, - const QStringList& programArguments, - const QStringList& environment, - ulong winid, - bool addToUtmp -// const QString& dbusService, -// const QString& dbusSession) - ) -{ - clearArguments(); - - setBinaryExecutable(program.toLatin1()); - - addEnvironmentVariables(environment); - - QStringListIterator it( programArguments ); - while (it.hasNext()) - arguments.append( it.next().toUtf8() ); - -// if ( !dbusService.isEmpty() ) -// setEnvironment("KONSOLE_DBUS_SERVICE",dbusService); -// if ( !dbusSession.isEmpty() ) -// setEnvironment("KONSOLE_DBUS_SESSION", dbusSession); - - setEnvironment("WINDOWID", QString::number(winid)); - - // unless the LANGUAGE environment variable has been set explicitly - // set it to a null string - // this fixes the problem where KCatalog sets the LANGUAGE environment - // variable during the application's startup to something which - // differs from LANG,LC_* etc. and causes programs run from - // the terminal to display mesages in the wrong language - // - // this can happen if LANG contains a language which KDE - // does not have a translation for - // - // BR:149300 - if (!environment.contains("LANGUAGE")) - setEnvironment("LANGUAGE",QString()); - - setUsePty(All, addToUtmp); - - pty()->open(); - - struct ::termios ttmode; - pty()->tcGetAttr(&ttmode); - if (!_xonXoff) - ttmode.c_iflag &= ~(IXOFF | IXON); - else - ttmode.c_iflag |= (IXOFF | IXON); -#ifdef IUTF8 // XXX not a reasonable place to check it. - if (!_utf8) - ttmode.c_iflag &= ~IUTF8; - else - ttmode.c_iflag |= IUTF8; -#endif - - if (_eraseChar != 0) - ttmode.c_cc[VERASE] = _eraseChar; - - if (!pty()->tcSetAttr(&ttmode)) - qWarning("Unable to set terminal attributes."); - - pty()->setWinSize(_windowLines, _windowColumns); - - if ( K3Process::start(NotifyOnExit, (Communication) (Stdin | Stdout)) == false ) - return -1; - - resume(); // Start... - return 0; - -} - -void Pty::setWriteable(bool writeable) -{ - struct stat sbuf; - stat(pty()->ttyName(), &sbuf); - if (writeable) - chmod(pty()->ttyName(), sbuf.st_mode | S_IWGRP); - else - chmod(pty()->ttyName(), sbuf.st_mode & ~(S_IWGRP|S_IWOTH)); -} - -Pty::Pty() - : _bufferFull(false), - _windowColumns(0), - _windowLines(0), - _eraseChar(0), - _xonXoff(true), - _utf8(true) -{ - connect(this, SIGNAL(receivedStdout(K3Process *, char *, int )), - this, SLOT(dataReceived(K3Process *,char *, int))); - connect(this, SIGNAL(processExited(K3Process *)), - this, SLOT(donePty())); - connect(this, SIGNAL(wroteStdin(K3Process *)), - this, SLOT(writeReady())); - _pty = new KPty; - - setUsePty(All, false); // utmp will be overridden later -} - -Pty::~Pty() -{ - delete _pty; -} - -void Pty::writeReady() -{ - _pendingSendJobs.erase(_pendingSendJobs.begin()); - _bufferFull = false; - doSendJobs(); -} - -void Pty::doSendJobs() { - if(_pendingSendJobs.isEmpty()) - { - emit bufferEmpty(); - return; - } - - SendJob& job = _pendingSendJobs.first(); - - - if (!writeStdin( job.data(), job.length() )) - { - qWarning("Pty::doSendJobs - Could not send input data to terminal process."); - return; - } - _bufferFull = true; -} - -void Pty::appendSendJob(const char* s, int len) -{ - _pendingSendJobs.append(SendJob(s,len)); -} - -void Pty::sendData(const char* s, int len) -{ - appendSendJob(s,len); - if (!_bufferFull) - doSendJobs(); -} - -void Pty::dataReceived(K3Process *,char *buf, int len) -{ - emit receivedData(buf,len); -} - -void Pty::lockPty(bool lock) -{ - if (lock) - suspend(); - else - resume(); -} - -int Pty::foregroundProcessGroup() const -{ - int pid = tcgetpgrp(pty()->masterFd()); - - if ( pid != -1 ) - { - return pid; - } - - return 0; -} - -//#include "moc_Pty.cpp"
deleted file mode 100644 --- a/gui/qtermwidget/lib/Pty.h +++ /dev/null @@ -1,243 +0,0 @@ -/* - This file is part of Konsole, KDE's terminal emulator. - - Copyright (C) 2007 by Robert Knight <robertknight@gmail.com> - Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 PTY_H -#define PTY_H - -// Qt -#include <QtCore/QStringList> -#include <QtCore/QVector> -#include <QtCore/QList> -#include <QtCore> - -#include "k3process.h" - - -namespace Konsole -{ - -/** - * The Pty class is used to start the terminal process, - * send data to it, receive data from it and manipulate - * various properties of the pseudo-teletype interface - * used to communicate with the process. - * - * To use this class, construct an instance and connect - * to the sendData slot and receivedData signal to - * send data to or receive data from the process. - * - * To start the terminal process, call the start() method - * with the program name and appropriate arguments. - */ -class Pty: public K3Process -{ -Q_OBJECT - - public: - - /** - * Constructs a new Pty. - * - * Connect to the sendData() slot and receivedData() signal to prepare - * for sending and receiving data from the terminal process. - * - * To start the terminal process, call the run() method with the - * name of the program to start and appropriate arguments. - */ - Pty(); - ~Pty(); - - /** - * Starts the terminal process. - * - * Returns 0 if the process was started successfully or non-zero - * otherwise. - * - * @param program Path to the program to start - * @param arguments Arguments to pass to the program being started - * @param environment A list of key=value pairs which will be added - * to the environment for the new process. At the very least this - * should include an assignment for the TERM environment variable. - * @param winid Specifies the value of the WINDOWID environment variable - * in the process's environment. - * @param addToUtmp Specifies whether a utmp entry should be created for - * the pty used. See K3Process::setUsePty() - * @param dbusService Specifies the value of the KONSOLE_DBUS_SERVICE - * environment variable in the process's environment. - * @param dbusSession Specifies the value of the KONSOLE_DBUS_SESSION - * environment variable in the process's environment. - */ - int start( const QString& program, - const QStringList& arguments, - const QStringList& environment, - ulong winid, - bool addToUtmp -// const QString& dbusService, -// const QString& dbusSession - ); - - /** TODO: Document me */ - void setWriteable(bool writeable); - - /** - * Enables or disables Xon/Xoff flow control. - */ - void setXonXoff(bool on); - - /** - * Sets the size of the window (in lines and columns of characters) - * used by this teletype. - */ - void setWindowSize(int lines, int cols); - - /** Returns the size of the window used by this teletype. See setWindowSize() */ - QSize windowSize() const; - - /** TODO Document me */ - void setErase(char erase); - - /** */ - char erase() const; - - /** - * Returns the process id of the teletype's current foreground - * process. This is the process which is currently reading - * input sent to the terminal via. sendData() - * - * If there is a problem reading the foreground process group, - * 0 will be returned. - */ - int foregroundProcessGroup() const; - - /** - * Returns whether the buffer used to send data to the - * terminal process is full. - */ - bool bufferFull() const { return _bufferFull; } - - - public slots: - - /** - * Put the pty into UTF-8 mode on systems which support it. - */ - void setUtf8Mode(bool on); - - /** - * Suspend or resume processing of data from the standard - * output of the terminal process. - * - * See K3Process::suspend() and K3Process::resume() - * - * @param lock If true, processing of output is suspended, - * otherwise processing is resumed. - */ - void lockPty(bool lock); - - /** - * Sends data to the process currently controlling the - * teletype ( whose id is returned by foregroundProcessGroup() ) - * - * @param buffer Pointer to the data to send. - * @param length Length of @p buffer. - */ - void sendData(const char* buffer, int length); - - signals: - - /** - * Emitted when the terminal process terminates. - * - * @param exitCode The status code which the process exited with. - */ - void done(int exitCode); - - /** - * Emitted when a new block of data is received from - * the teletype. - * - * @param buffer Pointer to the data received. - * @param length Length of @p buffer - */ - void receivedData(const char* buffer, int length); - - /** - * Emitted when the buffer used to send data to the terminal - * process becomes empty, i.e. all data has been sent. - */ - void bufferEmpty(); - - - private slots: - - // called when terminal process exits - void donePty(); - // called when data is received from the terminal process - void dataReceived(K3Process*, char* buffer, int length); - // sends the first enqueued buffer of data to the - // terminal process - void doSendJobs(); - // called when the terminal process is ready to - // receive more data - void writeReady(); - - private: - // takes a list of key=value pairs and adds them - // to the environment for the process - void addEnvironmentVariables(const QStringList& environment); - - // enqueues a buffer of data to be sent to the - // terminal process - void appendSendJob(const char* buffer, int length); - - // a buffer of data in the queue to be sent to the - // terminal process - class SendJob { - public: - SendJob() {} - SendJob(const char* b, int len) : buffer(len) - { - memcpy( buffer.data() , b , len ); - } - - const char* data() const { return buffer.constData(); } - int length() const { return buffer.size(); } - private: - QVector<char> buffer; - }; - - QList<SendJob> _pendingSendJobs; - bool _bufferFull; - - int _windowColumns; - int _windowLines; - char _eraseChar; - bool _xonXoff; - bool _utf8; - KPty *_pty; -}; - -} - -#endif // PTY_H
deleted file mode 100644 --- a/gui/qtermwidget/lib/README +++ /dev/null @@ -1,7 +0,0 @@ -lib.pro is a *.pro-file for qmake - -It produces static lib (libqtermwidget.a) only. -For creating shared lib (*.so) uncomment "dll" in "CONFIG" line in *.pro-file - -Library was tested both with HAVE_POSIX_OPENPT and HAVE_GETPT precompiler directives, -defined in "DEFINES" line. You should select variant which would be correct for your system. \ No newline at end of file
deleted file mode 100644 --- a/gui/qtermwidget/lib/Screen.cpp +++ /dev/null @@ -1,1567 +0,0 @@ -/* - This file is part of Konsole, an X terminal. - Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 "Screen.h" - -// Standard -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <assert.h> -#include <string.h> -#include <ctype.h> - -// Qt -#include <QtCore/QTextStream> -#include <QtCore/QDate> - -// Konsole -#include "konsole_wcwidth.h" -#include "TerminalCharacterDecoder.h" - -using namespace Konsole; - -//FIXME: this is emulation specific. Use false for xterm, true for ANSI. -//FIXME: see if we can get this from terminfo. -#define BS_CLEARS false - -//Macro to convert x,y position on screen to position within an image. -// -//Originally the image was stored as one large contiguous block of -//memory, so a position within the image could be represented as an -//offset from the beginning of the block. For efficiency reasons this -//is no longer the case. -//Many internal parts of this class still use this representation for parameters and so on, -//notably moveImage() and clearImage(). -//This macro converts from an X,Y position into an image offset. -#ifndef loc -#define loc(X,Y) ((Y)*columns+(X)) -#endif - - -Character Screen::defaultChar = Character(' ', - CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR), - CharacterColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR), - DEFAULT_RENDITION); - -//#define REVERSE_WRAPPED_LINES // for wrapped line debug - -Screen::Screen(int l, int c) - : lines(l), - columns(c), - screenLines(new ImageLine[lines+1] ), - _scrolledLines(0), - _droppedLines(0), - hist(new HistoryScrollNone()), - cuX(0), cuY(0), - cu_re(0), - tmargin(0), bmargin(0), - tabstops(0), - sel_begin(0), sel_TL(0), sel_BR(0), - sel_busy(false), - columnmode(false), - ef_fg(CharacterColor()), ef_bg(CharacterColor()), ef_re(0), - sa_cuX(0), sa_cuY(0), - sa_cu_re(0), - lastPos(-1) -{ - lineProperties.resize(lines+1); - for (int i=0;i<lines+1;i++) - lineProperties[i]=LINE_DEFAULT; - - initTabStops(); - clearSelection(); - reset(); -} - -/*! Destructor -*/ - -Screen::~Screen() -{ - delete[] screenLines; - delete[] tabstops; - delete hist; -} - -/* ------------------------------------------------------------------------- */ -/* */ -/* Normalized Screen Operations */ -/* */ -/* ------------------------------------------------------------------------- */ - -// Cursor Setting -------------------------------------------------------------- - -/*! \section Cursor - - The `cursor' is a location within the screen that is implicitely used in - many operations. The operations within this section allow to manipulate - the cursor explicitly and to obtain it's value. - - The position of the cursor is guarantied to be between (including) 0 and - `columns-1' and `lines-1'. -*/ - -/*! - Move the cursor up. - - The cursor will not be moved beyond the top margin. -*/ - -void Screen::cursorUp(int n) -//=CUU -{ - if (n == 0) n = 1; // Default - int stop = cuY < tmargin ? 0 : tmargin; - cuX = qMin(columns-1,cuX); // nowrap! - cuY = qMax(stop,cuY-n); -} - -/*! - Move the cursor down. - - The cursor will not be moved beyond the bottom margin. -*/ - -void Screen::cursorDown(int n) -//=CUD -{ - if (n == 0) n = 1; // Default - int stop = cuY > bmargin ? lines-1 : bmargin; - cuX = qMin(columns-1,cuX); // nowrap! - cuY = qMin(stop,cuY+n); -} - -/*! - Move the cursor left. - - The cursor will not move beyond the first column. -*/ - -void Screen::cursorLeft(int n) -//=CUB -{ - if (n == 0) n = 1; // Default - cuX = qMin(columns-1,cuX); // nowrap! - cuX = qMax(0,cuX-n); -} - -/*! - Move the cursor left. - - The cursor will not move beyond the rightmost column. -*/ - -void Screen::cursorRight(int n) -//=CUF -{ - if (n == 0) n = 1; // Default - cuX = qMin(columns-1,cuX+n); -} - -void Screen::setMargins(int top, int bot) -//=STBM -{ - if (top == 0) top = 1; // Default - if (bot == 0) bot = lines; // Default - top = top - 1; // Adjust to internal lineno - bot = bot - 1; // Adjust to internal lineno - if ( !( 0 <= top && top < bot && bot < lines ) ) - { qDebug()<<" setRegion("<<top<<","<<bot<<") : bad range."; - return; // Default error action: ignore - } - tmargin = top; - bmargin = bot; - cuX = 0; - cuY = getMode(MODE_Origin) ? top : 0; - -} - -int Screen::topMargin() const -{ - return tmargin; -} -int Screen::bottomMargin() const -{ - return bmargin; -} - -void Screen::index() -//=IND -{ - if (cuY == bmargin) - { - scrollUp(1); - } - else if (cuY < lines-1) - cuY += 1; -} - -void Screen::reverseIndex() -//=RI -{ - if (cuY == tmargin) - scrollDown(tmargin,1); - else if (cuY > 0) - cuY -= 1; -} - -/*! - Move the cursor to the begin of the next line. - - If cursor is on bottom margin, the region between the - actual top and bottom margin is scrolled up. -*/ - -void Screen::NextLine() -//=NEL -{ - Return(); index(); -} - -void Screen::eraseChars(int n) -{ - if (n == 0) n = 1; // Default - int p = qMax(0,qMin(cuX+n-1,columns-1)); - clearImage(loc(cuX,cuY),loc(p,cuY),' '); -} - -void Screen::deleteChars(int n) -{ - Q_ASSERT( n >= 0 ); - - // always delete at least one char - if (n == 0) - n = 1; - - // if cursor is beyond the end of the line there is nothing to do - if ( cuX >= screenLines[cuY].count() ) - return; - - if ( cuX+n >= screenLines[cuY].count() ) - n = screenLines[cuY].count() - 1 - cuX; - - Q_ASSERT( n >= 0 ); - Q_ASSERT( cuX+n < screenLines[cuY].count() ); - - screenLines[cuY].remove(cuX,n); -} - -void Screen::insertChars(int n) -{ - if (n == 0) n = 1; // Default - - if ( screenLines[cuY].size() < cuX ) - screenLines[cuY].resize(cuX); - - screenLines[cuY].insert(cuX,n,' '); - - if ( screenLines[cuY].count() > columns ) - screenLines[cuY].resize(columns); -} - -void Screen::deleteLines(int n) -{ - if (n == 0) n = 1; // Default - scrollUp(cuY,n); -} - -/*! insert `n' lines at the cursor position. - - The cursor is not moved by the operation. -*/ - -void Screen::insertLines(int n) -{ - if (n == 0) n = 1; // Default - scrollDown(cuY,n); -} - -// Mode Operations ----------------------------------------------------------- - -/*! Set a specific mode. */ - -void Screen::setMode(int m) -{ - currParm.mode[m] = true; - switch(m) - { - case MODE_Origin : cuX = 0; cuY = tmargin; break; //FIXME: home - } -} - -/*! Reset a specific mode. */ - -void Screen::resetMode(int m) -{ - currParm.mode[m] = false; - switch(m) - { - case MODE_Origin : cuX = 0; cuY = 0; break; //FIXME: home - } -} - -/*! Save a specific mode. */ - -void Screen::saveMode(int m) -{ - saveParm.mode[m] = currParm.mode[m]; -} - -/*! Restore a specific mode. */ - -void Screen::restoreMode(int m) -{ - currParm.mode[m] = saveParm.mode[m]; -} - -bool Screen::getMode(int m) const -{ - return currParm.mode[m]; -} - -void Screen::saveCursor() -{ - sa_cuX = cuX; - sa_cuY = cuY; - sa_cu_re = cu_re; - sa_cu_fg = cu_fg; - sa_cu_bg = cu_bg; -} - -void Screen::restoreCursor() -{ - cuX = qMin(sa_cuX,columns-1); - cuY = qMin(sa_cuY,lines-1); - cu_re = sa_cu_re; - cu_fg = sa_cu_fg; - cu_bg = sa_cu_bg; - effectiveRendition(); -} - -/* ------------------------------------------------------------------------- */ -/* */ -/* Screen Operations */ -/* */ -/* ------------------------------------------------------------------------- */ - -/*! Resize the screen image - - The topmost left position is maintained, while lower lines - or right hand side columns might be removed or filled with - spaces to fit the new size. - - The region setting is reset to the whole screen and the - tab positions reinitialized. - - If the new image is narrower than the old image then text on lines - which extends past the end of the new image is preserved so that it becomes - visible again if the screen is later resized to make it larger. -*/ - -void Screen::resizeImage(int new_lines, int new_columns) -{ - if ((new_lines==lines) && (new_columns==columns)) return; - - if (cuY > new_lines-1) - { // attempt to preserve focus and lines - bmargin = lines-1; //FIXME: margin lost - for (int i = 0; i < cuY-(new_lines-1); i++) - { - addHistLine(); scrollUp(0,1); - } - } - - // create new screen lines and copy from old to new - - ImageLine* newScreenLines = new ImageLine[new_lines+1]; - for (int i=0; i < qMin(lines-1,new_lines+1) ;i++) - newScreenLines[i]=screenLines[i]; - for (int i=lines;(i > 0) && (i<new_lines+1);i++) - newScreenLines[i].resize( new_columns ); - - lineProperties.resize(new_lines+1); - for (int i=lines;(i > 0) && (i<new_lines+1);i++) - lineProperties[i] = LINE_DEFAULT; - - clearSelection(); - - delete[] screenLines; - screenLines = newScreenLines; - - lines = new_lines; - columns = new_columns; - cuX = qMin(cuX,columns-1); - cuY = qMin(cuY,lines-1); - - // FIXME: try to keep values, evtl. - tmargin=0; - bmargin=lines-1; - initTabStops(); - clearSelection(); -} - -void Screen::setDefaultMargins() -{ - tmargin = 0; - bmargin = lines-1; -} - - -/* - Clarifying rendition here and in the display. - - currently, the display's color table is - 0 1 2 .. 9 10 .. 17 - dft_fg, dft_bg, dim 0..7, intensive 0..7 - - cu_fg, cu_bg contain values 0..8; - - 0 = default color - - 1..8 = ansi specified color - - re_fg, re_bg contain values 0..17 - due to the TerminalDisplay's color table - - rendition attributes are - - attr widget screen - -------------- ------ ------ - RE_UNDERLINE XX XX affects foreground only - RE_BLINK XX XX affects foreground only - RE_BOLD XX XX affects foreground only - RE_REVERSE -- XX - RE_TRANSPARENT XX -- affects background only - RE_INTENSIVE XX -- affects foreground only - - Note that RE_BOLD is used in both widget - and screen rendition. Since xterm/vt102 - is to poor to distinguish between bold - (which is a font attribute) and intensive - (which is a color attribute), we translate - this and RE_BOLD in falls eventually appart - into RE_BOLD and RE_INTENSIVE. -*/ - -void Screen::reverseRendition(Character& p) const -{ - CharacterColor f = p.foregroundColor; - CharacterColor b = p.backgroundColor; - - p.foregroundColor = b; - p.backgroundColor = f; //p->r &= ~RE_TRANSPARENT; -} - -void Screen::effectiveRendition() -// calculate rendition -{ - //copy "current rendition" straight into "effective rendition", which is then later copied directly - //into the image[] array which holds the characters and their appearance properties. - //- The old version below filtered out all attributes other than underline and blink at this stage, - //so that they would not be copied into the image[] array and hence would not be visible by TerminalDisplay - //which actually paints the screen using the information from the image[] array. - //I don't know why it did this, but I'm fairly sure it was the wrong thing to do. The net result - //was that bold text wasn't printed in bold by Konsole. - ef_re = cu_re; - - //OLD VERSION: - //ef_re = cu_re & (RE_UNDERLINE | RE_BLINK); - - if (cu_re & RE_REVERSE) - { - ef_fg = cu_bg; - ef_bg = cu_fg; - } - else - { - ef_fg = cu_fg; - ef_bg = cu_bg; - } - - if (cu_re & RE_BOLD) - ef_fg.toggleIntensive(); -} - -/*! - returns the image. - - Get the size of the image by \sa getLines and \sa getColumns. - - NOTE that the image returned by this function must later be - freed. - -*/ - -void Screen::copyFromHistory(Character* dest, int startLine, int count) const -{ - Q_ASSERT( startLine >= 0 && count > 0 && startLine + count <= hist->getLines() ); - - for (int line = startLine; line < startLine + count; line++) - { - const int length = qMin(columns,hist->getLineLen(line)); - const int destLineOffset = (line-startLine)*columns; - - hist->getCells(line,0,length,dest + destLineOffset); - - for (int column = length; column < columns; column++) - dest[destLineOffset+column] = defaultChar; - - // invert selected text - if (sel_begin !=-1) - { - for (int column = 0; column < columns; column++) - { - if (isSelected(column,line)) - { - reverseRendition(dest[destLineOffset + column]); - } - } - } - } -} - -void Screen::copyFromScreen(Character* dest , int startLine , int count) const -{ - Q_ASSERT( startLine >= 0 && count > 0 && startLine + count <= lines ); - - for (int line = startLine; line < (startLine+count) ; line++) - { - int srcLineStartIndex = line*columns; - int destLineStartIndex = (line-startLine)*columns; - - for (int column = 0; column < columns; column++) - { - int srcIndex = srcLineStartIndex + column; - int destIndex = destLineStartIndex + column; - - dest[destIndex] = screenLines[srcIndex/columns].value(srcIndex%columns,defaultChar); - - // invert selected text - if (sel_begin != -1 && isSelected(column,line + hist->getLines())) - reverseRendition(dest[destIndex]); - } - - } -} - -void Screen::getImage( Character* dest, int size, int startLine, int endLine ) const -{ - Q_ASSERT( startLine >= 0 ); - Q_ASSERT( endLine >= startLine && endLine < hist->getLines() + lines ); - - const int mergedLines = endLine - startLine + 1; - - Q_ASSERT( size >= mergedLines * columns ); - - const int linesInHistoryBuffer = qBound(0,hist->getLines()-startLine,mergedLines); - const int linesInScreenBuffer = mergedLines - linesInHistoryBuffer; - - // copy lines from history buffer - if (linesInHistoryBuffer > 0) { - copyFromHistory(dest,startLine,linesInHistoryBuffer); - } - - // copy lines from screen buffer - if (linesInScreenBuffer > 0) { - copyFromScreen(dest + linesInHistoryBuffer*columns, - startLine + linesInHistoryBuffer - hist->getLines(), - linesInScreenBuffer); - } - - // invert display when in screen mode - if (getMode(MODE_Screen)) - { - for (int i = 0; i < mergedLines*columns; i++) - reverseRendition(dest[i]); // for reverse display - } - - // mark the character at the current cursor position - int cursorIndex = loc(cuX, cuY + linesInHistoryBuffer); - if(getMode(MODE_Cursor) && cursorIndex < columns*mergedLines) - dest[cursorIndex].rendition |= RE_CURSOR; -} - -QVector<LineProperty> Screen::getLineProperties( int startLine , int endLine ) const -{ - Q_ASSERT( startLine >= 0 ); - Q_ASSERT( endLine >= startLine && endLine < hist->getLines() + lines ); - - const int mergedLines = endLine-startLine+1; - const int linesInHistory = qBound(0,hist->getLines()-startLine,mergedLines); - const int linesInScreen = mergedLines - linesInHistory; - - QVector<LineProperty> result(mergedLines); - int index = 0; - - // copy properties for lines in history - for (int line = startLine; line < startLine + linesInHistory; line++) - { - //TODO Support for line properties other than wrapped lines - if (hist->isWrappedLine(line)) - { - result[index] = (LineProperty)(result[index] | LINE_WRAPPED); - } - index++; - } - - // copy properties for lines in screen buffer - const int firstScreenLine = startLine + linesInHistory - hist->getLines(); - for (int line = firstScreenLine; line < firstScreenLine+linesInScreen; line++) - { - result[index]=lineProperties[line]; - index++; - } - - return result; -} - -/*! -*/ - -void Screen::reset(bool clearScreen) -{ - setMode(MODE_Wrap ); saveMode(MODE_Wrap ); // wrap at end of margin - resetMode(MODE_Origin); saveMode(MODE_Origin); // position refere to [1,1] - resetMode(MODE_Insert); saveMode(MODE_Insert); // overstroke - setMode(MODE_Cursor); // cursor visible - resetMode(MODE_Screen); // screen not inverse - resetMode(MODE_NewLine); - - tmargin=0; - bmargin=lines-1; - - setDefaultRendition(); - saveCursor(); - - if ( clearScreen ) - clear(); -} - -/*! Clear the entire screen and home the cursor. -*/ - -void Screen::clear() -{ - clearEntireScreen(); - home(); -} - -void Screen::BackSpace() -{ - cuX = qMin(columns-1,cuX); // nowrap! - cuX = qMax(0,cuX-1); - // if (BS_CLEARS) image[loc(cuX,cuY)].character = ' '; - - if (screenLines[cuY].size() < cuX+1) - screenLines[cuY].resize(cuX+1); - - if (BS_CLEARS) screenLines[cuY][cuX].character = ' '; -} - -void Screen::Tabulate(int n) -{ - // note that TAB is a format effector (does not write ' '); - if (n == 0) n = 1; - while((n > 0) && (cuX < columns-1)) - { - cursorRight(1); while((cuX < columns-1) && !tabstops[cuX]) cursorRight(1); - n--; - } -} - -void Screen::backTabulate(int n) -{ - // note that TAB is a format effector (does not write ' '); - if (n == 0) n = 1; - while((n > 0) && (cuX > 0)) - { - cursorLeft(1); while((cuX > 0) && !tabstops[cuX]) cursorLeft(1); - n--; - } -} - -void Screen::clearTabStops() -{ - for (int i = 0; i < columns; i++) tabstops[i] = false; -} - -void Screen::changeTabStop(bool set) -{ - if (cuX >= columns) return; - tabstops[cuX] = set; -} - -void Screen::initTabStops() -{ - delete[] tabstops; - tabstops = new bool[columns]; - - // Arrg! The 1st tabstop has to be one longer than the other. - // i.e. the kids start counting from 0 instead of 1. - // Other programs might behave correctly. Be aware. - for (int i = 0; i < columns; i++) tabstops[i] = (i%8 == 0 && i != 0); -} - -/*! - This behaves either as IND (Screen::Index) or as NEL (Screen::NextLine) - depending on the NewLine Mode (LNM). This mode also - affects the key sequence returned for newline ([CR]LF). -*/ - -void Screen::NewLine() -{ - if (getMode(MODE_NewLine)) Return(); - index(); -} - -/*! put `c' literally onto the screen at the current cursor position. - - VT100 uses the convention to produce an automatic newline (am) - with the *first* character that would fall onto the next line (xenl). -*/ - -void Screen::checkSelection(int from, int to) -{ - if (sel_begin == -1) return; - int scr_TL = loc(0, hist->getLines()); - //Clear entire selection if it overlaps region [from, to] - if ( (sel_BR > (from+scr_TL) )&&(sel_TL < (to+scr_TL)) ) - { - clearSelection(); - } -} - -void Screen::ShowCharacter(unsigned short c) -{ - // Note that VT100 does wrapping BEFORE putting the character. - // This has impact on the assumption of valid cursor positions. - // We indicate the fact that a newline has to be triggered by - // putting the cursor one right to the last column of the screen. - - int w = konsole_wcwidth(c); - - if (w <= 0) - return; - - if (cuX+w > columns) { - if (getMode(MODE_Wrap)) { - lineProperties[cuY] = (LineProperty)(lineProperties[cuY] | LINE_WRAPPED); - NextLine(); - } - else - cuX = columns-w; - } - - // ensure current line vector has enough elements - int size = screenLines[cuY].size(); - if (size == 0 && cuY > 0) - { - screenLines[cuY].resize( qMax(screenLines[cuY-1].size() , cuX+w) ); - } - else - { - if (size < cuX+w) - { - screenLines[cuY].resize(cuX+w); - } - } - - if (getMode(MODE_Insert)) insertChars(w); - - lastPos = loc(cuX,cuY); - - // check if selection is still valid. - checkSelection(cuX,cuY); - - Character& currentChar = screenLines[cuY][cuX]; - - currentChar.character = c; - currentChar.foregroundColor = ef_fg; - currentChar.backgroundColor = ef_bg; - currentChar.rendition = ef_re; - - int i = 0; - int newCursorX = cuX + w--; - while(w) - { - i++; - - if ( screenLines[cuY].size() < cuX + i + 1 ) - screenLines[cuY].resize(cuX+i+1); - - Character& ch = screenLines[cuY][cuX + i]; - ch.character = 0; - ch.foregroundColor = ef_fg; - ch.backgroundColor = ef_bg; - ch.rendition = ef_re; - - w--; - } - cuX = newCursorX; -} - -void Screen::compose(const QString& /*compose*/) -{ - Q_ASSERT( 0 /*Not implemented yet*/ ); - -/* if (lastPos == -1) - return; - - QChar c(image[lastPos].character); - compose.prepend(c); - //compose.compose(); ### FIXME! - image[lastPos].character = compose[0].unicode();*/ -} - -int Screen::scrolledLines() const -{ - return _scrolledLines; -} -int Screen::droppedLines() const -{ - return _droppedLines; -} -void Screen::resetDroppedLines() -{ - _droppedLines = 0; -} -void Screen::resetScrolledLines() -{ - //kDebug() << "scrolled lines reset"; - - _scrolledLines = 0; -} - -// Region commands ------------------------------------------------------------- - -void Screen::scrollUp(int n) -{ - if (n == 0) n = 1; // Default - if (tmargin == 0) addHistLine(); // hist.history - scrollUp(tmargin, n); -} - -/*! scroll up `n' lines within current region. - The `n' new lines are cleared. - \sa setRegion \sa scrollDown -*/ - -QRect Screen::lastScrolledRegion() const -{ - return _lastScrolledRegion; -} - -void Screen::scrollUp(int from, int n) -{ - if (n <= 0 || from + n > bmargin) return; - - _scrolledLines -= n; - _lastScrolledRegion = QRect(0,tmargin,columns-1,(bmargin-tmargin)); - - //FIXME: make sure `tmargin', `bmargin', `from', `n' is in bounds. - moveImage(loc(0,from),loc(0,from+n),loc(columns-1,bmargin)); - clearImage(loc(0,bmargin-n+1),loc(columns-1,bmargin),' '); -} - -void Screen::scrollDown(int n) -{ - if (n == 0) n = 1; // Default - scrollDown(tmargin, n); -} - -/*! scroll down `n' lines within current region. - The `n' new lines are cleared. - \sa setRegion \sa scrollUp -*/ - -void Screen::scrollDown(int from, int n) -{ - - //kDebug() << "Screen::scrollDown( from: " << from << " , n: " << n << ")"; - - _scrolledLines += n; - -//FIXME: make sure `tmargin', `bmargin', `from', `n' is in bounds. - if (n <= 0) return; - if (from > bmargin) return; - if (from + n > bmargin) n = bmargin - from; - moveImage(loc(0,from+n),loc(0,from),loc(columns-1,bmargin-n)); - clearImage(loc(0,from),loc(columns-1,from+n-1),' '); -} - -void Screen::setCursorYX(int y, int x) -{ - setCursorY(y); setCursorX(x); -} - -void Screen::setCursorX(int x) -{ - if (x == 0) x = 1; // Default - x -= 1; // Adjust - cuX = qMax(0,qMin(columns-1, x)); -} - -void Screen::setCursorY(int y) -{ - if (y == 0) y = 1; // Default - y -= 1; // Adjust - cuY = qMax(0,qMin(lines -1, y + (getMode(MODE_Origin) ? tmargin : 0) )); -} - -void Screen::home() -{ - cuX = 0; - cuY = 0; -} - -void Screen::Return() -{ - cuX = 0; -} - -int Screen::getCursorX() const -{ - return cuX; -} - -int Screen::getCursorY() const -{ - return cuY; -} - -// Erasing --------------------------------------------------------------------- - -/*! \section Erasing - - This group of operations erase parts of the screen contents by filling - it with spaces colored due to the current rendition settings. - - Althought the cursor position is involved in most of these operations, - it is never modified by them. -*/ - -/*! fill screen between (including) `loca' (start) and `loce' (end) with spaces. - - This is an internal helper functions. The parameter types are internal - addresses of within the screen image and make use of the way how the - screen matrix is mapped to the image vector. -*/ - -void Screen::clearImage(int loca, int loce, char c) -{ - int scr_TL=loc(0,hist->getLines()); - //FIXME: check positions - - //Clear entire selection if it overlaps region to be moved... - if ( (sel_BR > (loca+scr_TL) )&&(sel_TL < (loce+scr_TL)) ) - { - clearSelection(); - } - - int topLine = loca/columns; - int bottomLine = loce/columns; - - Character clearCh(c,cu_fg,cu_bg,DEFAULT_RENDITION); - - //if the character being used to clear the area is the same as the - //default character, the affected lines can simply be shrunk. - bool isDefaultCh = (clearCh == Character()); - - for (int y=topLine;y<=bottomLine;y++) - { - lineProperties[y] = 0; - - int endCol = ( y == bottomLine) ? loce%columns : columns-1; - int startCol = ( y == topLine ) ? loca%columns : 0; - - QVector<Character>& line = screenLines[y]; - - if ( isDefaultCh && endCol == columns-1 ) - { - line.resize(startCol); - } - else - { - if (line.size() < endCol + 1) - line.resize(endCol+1); - - Character* data = line.data(); - for (int i=startCol;i<=endCol;i++) - data[i]=clearCh; - } - } -} - -/*! move image between (including) `sourceBegin' and `sourceEnd' to 'dest'. - - The 'dest', 'sourceBegin' and 'sourceEnd' parameters can be generated using - the loc(column,line) macro. - -NOTE: moveImage() can only move whole lines. - - This is an internal helper functions. The parameter types are internal - addresses of within the screen image and make use of the way how the - screen matrix is mapped to the image vector. -*/ - -void Screen::moveImage(int dest, int sourceBegin, int sourceEnd) -{ - //kDebug() << "moving image from (" << (sourceBegin/columns) - // << "," << (sourceEnd/columns) << ") to " << - // (dest/columns); - - Q_ASSERT( sourceBegin <= sourceEnd ); - - int lines=(sourceEnd-sourceBegin)/columns; - - //move screen image and line properties: - //the source and destination areas of the image may overlap, - //so it matters that we do the copy in the right order - - //forwards if dest < sourceBegin or backwards otherwise. - //(search the web for 'memmove implementation' for details) - if (dest < sourceBegin) - { - for (int i=0;i<=lines;i++) - { - screenLines[ (dest/columns)+i ] = screenLines[ (sourceBegin/columns)+i ]; - lineProperties[(dest/columns)+i]=lineProperties[(sourceBegin/columns)+i]; - } - } - else - { - for (int i=lines;i>=0;i--) - { - screenLines[ (dest/columns)+i ] = screenLines[ (sourceBegin/columns)+i ]; - lineProperties[(dest/columns)+i]=lineProperties[(sourceBegin/columns)+i]; - } - } - - if (lastPos != -1) - { - int diff = dest - sourceBegin; // Scroll by this amount - lastPos += diff; - if ((lastPos < 0) || (lastPos >= (lines*columns))) - lastPos = -1; - } - - // Adjust selection to follow scroll. - if (sel_begin != -1) - { - bool beginIsTL = (sel_begin == sel_TL); - int diff = dest - sourceBegin; // Scroll by this amount - int scr_TL=loc(0,hist->getLines()); - int srca = sourceBegin+scr_TL; // Translate index from screen to global - int srce = sourceEnd+scr_TL; // Translate index from screen to global - int desta = srca+diff; - int deste = srce+diff; - - if ((sel_TL >= srca) && (sel_TL <= srce)) - sel_TL += diff; - else if ((sel_TL >= desta) && (sel_TL <= deste)) - sel_BR = -1; // Clear selection (see below) - - if ((sel_BR >= srca) && (sel_BR <= srce)) - sel_BR += diff; - else if ((sel_BR >= desta) && (sel_BR <= deste)) - sel_BR = -1; // Clear selection (see below) - - if (sel_BR < 0) - { - clearSelection(); - } - else - { - if (sel_TL < 0) - sel_TL = 0; - } - - if (beginIsTL) - sel_begin = sel_TL; - else - sel_begin = sel_BR; - } -} - -void Screen::clearToEndOfScreen() -{ - clearImage(loc(cuX,cuY),loc(columns-1,lines-1),' '); -} - -void Screen::clearToBeginOfScreen() -{ - clearImage(loc(0,0),loc(cuX,cuY),' '); -} - -void Screen::clearEntireScreen() -{ - // Add entire screen to history - for (int i = 0; i < (lines-1); i++) - { - addHistLine(); scrollUp(0,1); - } - - clearImage(loc(0,0),loc(columns-1,lines-1),' '); -} - -/*! fill screen with 'E' - This is to aid screen alignment -*/ - -void Screen::helpAlign() -{ - clearImage(loc(0,0),loc(columns-1,lines-1),'E'); -} - -void Screen::clearToEndOfLine() -{ - clearImage(loc(cuX,cuY),loc(columns-1,cuY),' '); -} - -void Screen::clearToBeginOfLine() -{ - clearImage(loc(0,cuY),loc(cuX,cuY),' '); -} - -void Screen::clearEntireLine() -{ - clearImage(loc(0,cuY),loc(columns-1,cuY),' '); -} - -void Screen::setRendition(int re) -{ - cu_re |= re; - effectiveRendition(); -} - -void Screen::resetRendition(int re) -{ - cu_re &= ~re; - effectiveRendition(); -} - -void Screen::setDefaultRendition() -{ - setForeColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR); - setBackColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR); - cu_re = DEFAULT_RENDITION; - effectiveRendition(); -} - -void Screen::setForeColor(int space, int color) -{ - cu_fg = CharacterColor(space, color); - - if ( cu_fg.isValid() ) - effectiveRendition(); - else - setForeColor(COLOR_SPACE_DEFAULT,DEFAULT_FORE_COLOR); -} - -void Screen::setBackColor(int space, int color) -{ - cu_bg = CharacterColor(space, color); - - if ( cu_bg.isValid() ) - effectiveRendition(); - else - setBackColor(COLOR_SPACE_DEFAULT,DEFAULT_BACK_COLOR); -} - -/* ------------------------------------------------------------------------- */ -/* */ -/* Marking & Selection */ -/* */ -/* ------------------------------------------------------------------------- */ - -void Screen::clearSelection() -{ - sel_BR = -1; - sel_TL = -1; - sel_begin = -1; -} - -void Screen::getSelectionStart(int& column , int& line) -{ - if ( sel_TL != -1 ) - { - column = sel_TL % columns; - line = sel_TL / columns; - } - else - { - column = cuX + getHistLines(); - line = cuY + getHistLines(); - } -} -void Screen::getSelectionEnd(int& column , int& line) -{ - if ( sel_BR != -1 ) - { - column = sel_BR % columns; - line = sel_BR / columns; - } - else - { - column = cuX + getHistLines(); - line = cuY + getHistLines(); - } -} -void Screen::setSelectionStart(/*const ScreenCursor& viewCursor ,*/ const int x, const int y, const bool mode) -{ -// kDebug(1211) << "setSelBeginXY(" << x << "," << y << ")"; - sel_begin = loc(x,y); //+histCursor) ; - - /* FIXME, HACK to correct for x too far to the right... */ - if (x == columns) sel_begin--; - - sel_BR = sel_begin; - sel_TL = sel_begin; - columnmode = mode; -} - -void Screen::setSelectionEnd( const int x, const int y) -{ -// kDebug(1211) << "setSelExtentXY(" << x << "," << y << ")"; - if (sel_begin == -1) return; - int l = loc(x,y); // + histCursor); - - if (l < sel_begin) - { - sel_TL = l; - sel_BR = sel_begin; - } - else - { - /* FIXME, HACK to correct for x too far to the right... */ - if (x == columns) l--; - - sel_TL = sel_begin; - sel_BR = l; - } -} - -bool Screen::isSelected( const int x,const int y) const -{ - if (columnmode) { - int sel_Left,sel_Right; - if ( sel_TL % columns < sel_BR % columns ) { - sel_Left = sel_TL; sel_Right = sel_BR; - } else { - sel_Left = sel_BR; sel_Right = sel_TL; - } - return ( x >= sel_Left % columns ) && ( x <= sel_Right % columns ) && - ( y >= sel_TL / columns ) && ( y <= sel_BR / columns ); - //( y+histCursor >= sel_TL / columns ) && ( y+histCursor <= sel_BR / columns ); - } - else { - //int pos = loc(x,y+histCursor); - int pos = loc(x,y); - return ( pos >= sel_TL && pos <= sel_BR ); - } -} - -QString Screen::selectedText(bool preserveLineBreaks) -{ - QString result; - QTextStream stream(&result, QIODevice::ReadWrite); - - PlainTextDecoder decoder; - decoder.begin(&stream); - writeSelectionToStream(&decoder , preserveLineBreaks); - decoder.end(); - - return result; -} - -bool Screen::isSelectionValid() const -{ - return ( sel_TL >= 0 && sel_BR >= 0 ); -} - -void Screen::writeSelectionToStream(TerminalCharacterDecoder* decoder , - bool preserveLineBreaks) -{ - // do nothing if selection is invalid - if ( !isSelectionValid() ) - return; - - int top = sel_TL / columns; - int left = sel_TL % columns; - - int bottom = sel_BR / columns; - int right = sel_BR % columns; - - Q_ASSERT( top >= 0 && left >= 0 && bottom >= 0 && right >= 0 ); - - //kDebug() << "sel_TL = " << sel_TL; - //kDebug() << "columns = " << columns; - - for (int y=top;y<=bottom;y++) - { - int start = 0; - if ( y == top || columnmode ) start = left; - - int count = -1; - if ( y == bottom || columnmode ) count = right - start + 1; - - const bool appendNewLine = ( y != bottom ); - copyLineToStream( y, - start, - count, - decoder, - appendNewLine, - preserveLineBreaks ); - } -} - - -void Screen::copyLineToStream(int line , - int start, - int count, - TerminalCharacterDecoder* decoder, - bool appendNewLine, - bool preserveLineBreaks) -{ - //buffer to hold characters for decoding - //the buffer is static to avoid initialising every - //element on each call to copyLineToStream - //(which is unnecessary since all elements will be overwritten anyway) - static const int MAX_CHARS = 1024; - static Character characterBuffer[MAX_CHARS]; - - assert( count < MAX_CHARS ); - - LineProperty currentLineProperties = 0; - - //determine if the line is in the history buffer or the screen image - if (line < hist->getLines()) - { - const int lineLength = hist->getLineLen(line); - - // ensure that start position is before end of line - start = qMin(start,qMax(0,lineLength-1)); - - //retrieve line from history buffer - if (count == -1) - { - count = lineLength-start; - } - else - { - count = qMin(start+count,lineLength)-start; - } - - // safety checks - assert( start >= 0 ); - assert( count >= 0 ); - assert( (start+count) <= hist->getLineLen(line) ); - - hist->getCells(line,start,count,characterBuffer); - - if ( hist->isWrappedLine(line) ) - currentLineProperties |= LINE_WRAPPED; - } - else - { - if ( count == -1 ) - count = columns - start; - - assert( count >= 0 ); - - const int screenLine = line-hist->getLines(); - - Character* data = screenLines[screenLine].data(); - int length = screenLines[screenLine].count(); - - //retrieve line from screen image - for (int i=start;i < qMin(start+count,length);i++) - { - characterBuffer[i-start] = data[i]; - } - - // count cannot be any greater than length - count = qBound(0,count,length-start); - - Q_ASSERT( screenLine < lineProperties.count() ); - currentLineProperties |= lineProperties[screenLine]; - } - - //do not decode trailing whitespace characters - for (int i=count-1 ; i >= 0; i--) - if (QChar(characterBuffer[i].character).isSpace()) - count--; - else - break; - - // add new line character at end - const bool omitLineBreak = (currentLineProperties & LINE_WRAPPED) || - !preserveLineBreaks; - - if ( !omitLineBreak && appendNewLine && (count+1 < MAX_CHARS) ) - { - characterBuffer[count] = '\n'; - count++; - } - - //decode line and write to text stream - decoder->decodeLine( (Character*) characterBuffer , - count, currentLineProperties ); -} - -// Method below has been removed because of its reliance on 'histCursor' -// and I want to restrict the methods which have knowledge of the scroll position -// to just those which deal with selection and supplying final screen images. -// -/*void Screen::writeToStream(QTextStream* stream , TerminalCharacterDecoder* decoder) { - sel_begin = 0; - sel_BR = sel_begin; - sel_TL = sel_begin; - setSelectionEnd(columns-1,lines-1+hist->getLines()-histCursor); - - writeSelectionToStream(stream,decoder); - - clearSelection(); -}*/ - -void Screen::writeToStream(TerminalCharacterDecoder* decoder, int from, int to) -{ - sel_begin = loc(0,from); - sel_TL = sel_begin; - sel_BR = loc(columns-1,to); - writeSelectionToStream(decoder); - clearSelection(); -} - -QString Screen::getHistoryLine(int no) -{ - sel_begin = loc(0,no); - sel_TL = sel_begin; - sel_BR = loc(columns-1,no); - return selectedText(false); -} - -void Screen::addHistLine() -{ - // add line to history buffer - // we have to take care about scrolling, too... - - if (hasScroll()) - { - int oldHistLines = hist->getLines(); - - hist->addCellsVector(screenLines[0]); - hist->addLine( lineProperties[0] & LINE_WRAPPED ); - - int newHistLines = hist->getLines(); - - bool beginIsTL = (sel_begin == sel_TL); - - // If the history is full, increment the count - // of dropped lines - if ( newHistLines == oldHistLines ) - _droppedLines++; - - // Adjust selection for the new point of reference - if (newHistLines > oldHistLines) - { - if (sel_begin != -1) - { - sel_TL += columns; - sel_BR += columns; - } - } - - if (sel_begin != -1) - { - // Scroll selection in history up - int top_BR = loc(0, 1+newHistLines); - - if (sel_TL < top_BR) - sel_TL -= columns; - - if (sel_BR < top_BR) - sel_BR -= columns; - - if (sel_BR < 0) - { - clearSelection(); - } - else - { - if (sel_TL < 0) - sel_TL = 0; - } - - if (beginIsTL) - sel_begin = sel_TL; - else - sel_begin = sel_BR; - } - } - -} - -int Screen::getHistLines() -{ - return hist->getLines(); -} - -void Screen::setScroll(const HistoryType& t , bool copyPreviousScroll) -{ - clearSelection(); - - if ( copyPreviousScroll ) - hist = t.scroll(hist); - else - { - HistoryScroll* oldScroll = hist; - hist = t.scroll(0); - delete oldScroll; - } -} - -bool Screen::hasScroll() -{ - return hist->hasScroll(); -} - -const HistoryType& Screen::getScroll() -{ - return hist->getType(); -} - -void Screen::setLineProperty(LineProperty property , bool enable) -{ - if ( enable ) - { - lineProperties[cuY] = (LineProperty)(lineProperties[cuY] | property); - } - else - { - lineProperties[cuY] = (LineProperty)(lineProperties[cuY] & ~property); - } -} -void Screen::fillWithDefaultChar(Character* dest, int count) -{ - for (int i=0;i<count;i++) - dest[i] = defaultChar; -}
deleted file mode 100644 --- a/gui/qtermwidget/lib/Screen.h +++ /dev/null @@ -1,662 +0,0 @@ -/* - This file is part of Konsole, KDE's terminal. - - Copyright (C) 2007 by Robert Knight <robertknight@gmail.com> - Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 SCREEN_H -#define SCREEN_H - -// Qt -#include <QtCore/QRect> -#include <QtCore/QTextStream> -#include <QtCore/QVarLengthArray> - -// Konsole -#include "Character.h" -#include "History.h" - -#define MODE_Origin 0 -#define MODE_Wrap 1 -#define MODE_Insert 2 -#define MODE_Screen 3 -#define MODE_Cursor 4 -#define MODE_NewLine 5 -#define MODES_SCREEN 6 - -namespace Konsole -{ - -/*! -*/ -struct ScreenParm -{ - int mode[MODES_SCREEN]; -}; - -class TerminalCharacterDecoder; - -/** - \brief An image of characters with associated attributes. - - The terminal emulation ( Emulation ) receives a serial stream of - characters from the program currently running in the terminal. - From this stream it creates an image of characters which is ultimately - rendered by the display widget ( TerminalDisplay ). Some types of emulation - may have more than one screen image. - - getImage() is used to retrieve the currently visible image - which is then used by the display widget to draw the output from the - terminal. - - The number of lines of output history which are kept in addition to the current - screen image depends on the history scroll being used to store the output. - The scroll is specified using setScroll() - The output history can be retrieved using writeToStream() - - The screen image has a selection associated with it, specified using - setSelectionStart() and setSelectionEnd(). The selected text can be retrieved - using selectedText(). When getImage() is used to retrieve the the visible image, - characters which are part of the selection have their colours inverted. -*/ -class Screen -{ -public: - /** Construct a new screen image of size @p lines by @p columns. */ - Screen(int lines, int columns); - ~Screen(); - - // VT100/2 Operations - // Cursor Movement - - /** Move the cursor up by @p n lines. */ - void cursorUp (int n); - /** Move the cursor down by @p n lines. */ - void cursorDown (int n); - /** Move the cursor to the left by @p n columns. */ - void cursorLeft (int n); - /** Move the cursor to the right by @p n columns. */ - void cursorRight (int n); - /** Position the cursor on line @p y. */ - void setCursorY (int y); - /** Position the cursor at column @p x. */ - void setCursorX (int x); - /** Position the cursor at line @p y, column @p x. */ - void setCursorYX (int y, int x); - /** - * Sets the margins for scrolling the screen. - * - * @param topLine The top line of the new scrolling margin. - * @param bottomLine The bottom line of the new scrolling margin. - */ - void setMargins (int topLine , int bottomLine); - /** Returns the top line of the scrolling region. */ - int topMargin() const; - /** Returns the bottom line of the scrolling region. */ - int bottomMargin() const; - - /** - * Resets the scrolling margins back to the top and bottom lines - * of the screen. - */ - void setDefaultMargins(); - - /** - * Moves the cursor down one line, if the MODE_NewLine mode - * flag is enabled then the cursor is returned to the leftmost - * column first. - * - * Equivalent to NextLine() if the MODE_NewLine flag is set - * or index() otherwise. - */ - void NewLine (); - /** - * Moves the cursor down one line and positions it at the beginning - * of the line. - */ - void NextLine (); - - /** - * Move the cursor down one line. If the cursor is on the bottom - * line of the scrolling region (as returned by bottomMargin()) the - * scrolling region is scrolled up by one line instead. - */ - void index (); - /** - * Move the cursor up one line. If the cursor is on the top line - * of the scrolling region (as returned by topMargin()) the scrolling - * region is scrolled down by one line instead. - */ - void reverseIndex(); - - /** - * Scroll the scrolling region of the screen up by @p n lines. - * The scrolling region is initially the whole screen, but can be changed - * using setMargins() - */ - void scrollUp(int n); - /** - * Scroll the scrolling region of the screen down by @p n lines. - * The scrolling region is initially the whole screen, but can be changed - * using setMargins() - */ - void scrollDown(int n); - - /** - * Moves the cursor to the beginning of the current line. - * Equivalent to setCursorX(0) - */ - void Return (); - /** - * Moves the cursor one column to the left and erases the character - * at the new cursor position. - */ - void BackSpace (); - /** - * Moves the cursor @p n tab-stops to the right. - */ - void Tabulate (int n = 1); - /** - * Moves the cursor @p n tab-stops to the left. - */ - void backTabulate(int n); - - // Editing - - /** - * Erase @p n characters beginning from the current cursor position. - * This is equivalent to over-writing @p n characters starting with the current - * cursor position with spaces. - * If @p n is 0 then one character is erased. - */ - void eraseChars (int n); - /** - * Delete @p n characters beginning from the current cursor position. - * If @p n is 0 then one character is deleted. - */ - void deleteChars (int n); - /** - * Insert @p n blank characters beginning from the current cursor position. - * The position of the cursor is not altered. - * If @p n is 0 then one character is inserted. - */ - void insertChars (int n); - /** - * Removes @p n lines beginning from the current cursor position. - * The position of the cursor is not altered. - * If @p n is 0 then one line is removed. - */ - void deleteLines (int n); - /** - * Inserts @p lines beginning from the current cursor position. - * The position of the cursor is not altered. - * If @p n is 0 then one line is inserted. - */ - void insertLines (int n); - /** Clears all the tab stops. */ - void clearTabStops(); - /** Sets or removes a tab stop at the cursor's current column. */ - void changeTabStop(bool set); - - /** Resets (clears) the specified screen @p mode. */ - void resetMode (int mode); - /** Sets (enables) the specified screen @p mode. */ - void setMode (int mode); - /** - * Saves the state of the specified screen @p mode. It can be restored - * using restoreMode() - */ - void saveMode (int mode); - /** Restores the state of a screen @p mode saved by calling saveMode() */ - void restoreMode (int mode); - /** Returns whether the specified screen @p mode is enabled or not .*/ - bool getMode (int mode) const; - - /** - * Saves the current position and appearence (text color and style) of the cursor. - * It can be restored by calling restoreCursor() - */ - void saveCursor (); - /** Restores the position and appearence of the cursor. See saveCursor() */ - void restoreCursor(); - - /** Clear the whole screen, moving the current screen contents into the history first. */ - void clearEntireScreen(); - /** - * Clear the area of the screen from the current cursor position to the end of - * the screen. - */ - void clearToEndOfScreen(); - /** - * Clear the area of the screen from the current cursor position to the start - * of the screen. - */ - void clearToBeginOfScreen(); - /** Clears the whole of the line on which the cursor is currently positioned. */ - void clearEntireLine(); - /** Clears from the current cursor position to the end of the line. */ - void clearToEndOfLine(); - /** Clears from the current cursor position to the beginning of the line. */ - void clearToBeginOfLine(); - - /** Fills the entire screen with the letter 'E' */ - void helpAlign (); - - /** - * Enables the given @p rendition flag. Rendition flags control the appearence - * of characters on the screen. - * - * @see Character::rendition - */ - void setRendition (int rendition); - /** - * Disables the given @p rendition flag. Rendition flags control the appearence - * of characters on the screen. - * - * @see Character::rendition - */ - void resetRendition(int rendition); - - /** - * Sets the cursor's foreground color. - * @param space The color space used by the @p color argument - * @param color The new foreground color. The meaning of this depends on - * the color @p space used. - * - * @see CharacterColor - */ - void setForeColor (int space, int color); - /** - * Sets the cursor's background color. - * @param space The color space used by the @p color argumnet. - * @param color The new background color. The meaning of this depends on - * the color @p space used. - * - * @see CharacterColor - */ - void setBackColor (int space, int color); - /** - * Resets the cursor's color back to the default and sets the - * character's rendition flags back to the default settings. - */ - void setDefaultRendition(); - - /** Returns the column which the cursor is positioned at. */ - int getCursorX() const; - /** Returns the line which the cursor is positioned on. */ - int getCursorY() const; - - /** TODO Document me */ - void clear(); - /** - * Sets the position of the cursor to the 'home' position at the top-left - * corner of the screen (0,0) - */ - void home(); - /** - * Resets the state of the screen. This resets the various screen modes - * back to their default states. The cursor style and colors are reset - * (as if setDefaultRendition() had been called) - * - * <ul> - * <li>Line wrapping is enabled.</li> - * <li>Origin mode is disabled.</li> - * <li>Insert mode is disabled.</li> - * <li>Cursor mode is enabled. TODO Document me</li> - * <li>Screen mode is disabled. TODO Document me</li> - * <li>New line mode is disabled. TODO Document me</li> - * </ul> - * - * If @p clearScreen is true then the screen contents are erased entirely, - * otherwise they are unaltered. - */ - void reset(bool clearScreen = true); - - /** - * Displays a new character at the current cursor position. - * - * If the cursor is currently positioned at the right-edge of the screen and - * line wrapping is enabled then the character is added at the start of a new - * line below the current one. - * - * If the MODE_Insert screen mode is currently enabled then the character - * is inserted at the current cursor position, otherwise it will replace the - * character already at the current cursor position. - */ - void ShowCharacter(unsigned short c); - - // Do composition with last shown character FIXME: Not implemented yet for KDE 4 - void compose(const QString& compose); - - /** - * Resizes the image to a new fixed size of @p new_lines by @p new_columns. - * In the case that @p new_columns is smaller than the current number of columns, - * existing lines are not truncated. This prevents characters from being lost - * if the terminal display is resized smaller and then larger again. - * - * (note that in versions of Konsole prior to KDE 4, existing lines were - * truncated when making the screen image smaller) - */ - void resizeImage(int new_lines, int new_columns); - - /** - * Returns the current screen image. - * The result is an array of Characters of size [getLines()][getColumns()] which - * must be freed by the caller after use. - * - * @param dest Buffer to copy the characters into - * @param size Size of @p dest in Characters - * @param startLine Index of first line to copy - * @param endLine Index of last line to copy - */ - void getImage( Character* dest , int size , int startLine , int endLine ) const; - - /** - * Returns the additional attributes associated with lines in the image. - * The most important attribute is LINE_WRAPPED which specifies that the - * line is wrapped, - * other attributes control the size of characters in the line. - */ - QVector<LineProperty> getLineProperties( int startLine , int endLine ) const; - - - /** Return the number of lines. */ - int getLines() { return lines; } - /** Return the number of columns. */ - int getColumns() { return columns; } - /** Return the number of lines in the history buffer. */ - int getHistLines (); - /** - * Sets the type of storage used to keep lines in the history. - * If @p copyPreviousScroll is true then the contents of the previous - * history buffer are copied into the new scroll. - */ - void setScroll(const HistoryType& , bool copyPreviousScroll = true); - /** Returns the type of storage used to keep lines in the history. */ - const HistoryType& getScroll(); - /** - * Returns true if this screen keeps lines that are scrolled off the screen - * in a history buffer. - */ - bool hasScroll(); - - /** - * Sets the start of the selection. - * - * @param column The column index of the first character in the selection. - * @param line The line index of the first character in the selection. - * @param columnmode True if the selection is in column mode. - */ - void setSelectionStart(const int column, const int line, const bool columnmode); - - /** - * Sets the end of the current selection. - * - * @param column The column index of the last character in the selection. - * @param line The line index of the last character in the selection. - */ - void setSelectionEnd(const int column, const int line); - - /** - * Retrieves the start of the selection or the cursor position if there - * is no selection. - */ - void getSelectionStart(int& column , int& line); - - /** - * Retrieves the end of the selection or the cursor position if there - * is no selection. - */ - void getSelectionEnd(int& column , int& line); - - /** Clears the current selection */ - void clearSelection(); - - void setBusySelecting(bool busy) { sel_busy = busy; } - - /** - * Returns true if the character at (@p column, @p line) is part of the - * current selection. - */ - bool isSelected(const int column,const int line) const; - - /** - * Convenience method. Returns the currently selected text. - * @param preserveLineBreaks Specifies whether new line characters should - * be inserted into the returned text at the end of each terminal line. - */ - QString selectedText(bool preserveLineBreaks); - - /** - * Copies part of the output to a stream. - * - * @param decoder A decoder which coverts terminal characters into text - * @param from The first line in the history to retrieve - * @param to The last line in the history to retrieve - */ - void writeToStream(TerminalCharacterDecoder* decoder, int from, int to); - - /** - * Sets the selection to line @p no in the history and returns - * the text of that line from the history buffer. - */ - QString getHistoryLine(int no); - - /** - * Copies the selected characters, set using @see setSelBeginXY and @see setSelExtentXY - * into a stream. - * - * @param decoder A decoder which converts terminal characters into text. - * PlainTextDecoder is the most commonly used decoder which coverts characters - * into plain text with no formatting. - * @param preserveLineBreaks Specifies whether new line characters should - * be inserted into the returned text at the end of each terminal line. - */ - void writeSelectionToStream(TerminalCharacterDecoder* decoder , bool - preserveLineBreaks = true); - - /** TODO Document me */ - void checkSelection(int from, int to); - - /** - * Sets or clears an attribute of the current line. - * - * @param property The attribute to set or clear - * Possible properties are: - * LINE_WRAPPED: Specifies that the line is wrapped. - * LINE_DOUBLEWIDTH: Specifies that the characters in the current line should be double the normal width. - * LINE_DOUBLEHEIGHT:Specifies that the characters in the current line should be double the normal height. - * Double-height lines are formed of two lines containing the same characters, - * with both having the LINE_DOUBLEHEIGHT attribute. This allows other parts of the - * code to work on the assumption that all lines are the same height. - * - * @param enable true to apply the attribute to the current line or false to remove it - */ - void setLineProperty(LineProperty property , bool enable); - - - /** - * Returns the number of lines that the image has been scrolled up or down by, - * since the last call to resetScrolledLines(). - * - * a positive return value indicates that the image has been scrolled up, - * a negative return value indicates that the image has been scrolled down. - */ - int scrolledLines() const; - - /** - * Returns the region of the image which was last scrolled. - * - * This is the area of the image from the top margin to the - * bottom margin when the last scroll occurred. - */ - QRect lastScrolledRegion() const; - - /** - * Resets the count of the number of lines that the image has been scrolled up or down by, - * see scrolledLines() - */ - void resetScrolledLines(); - - /** - * Returns the number of lines of output which have been - * dropped from the history since the last call - * to resetDroppedLines() - * - * If the history is not unlimited then it will drop - * the oldest lines of output if new lines are added when - * it is full. - */ - int droppedLines() const; - - /** - * Resets the count of the number of lines dropped from - * the history. - */ - void resetDroppedLines(); - - /** - * Fills the buffer @p dest with @p count instances of the default (ie. blank) - * Character style. - */ - static void fillWithDefaultChar(Character* dest, int count); - -private: - - //copies a line of text from the screen or history into a stream using a - //specified character decoder - //line - the line number to copy, from 0 (the earliest line in the history) up to - // hist->getLines() + lines - 1 - //start - the first column on the line to copy - //count - the number of characters on the line to copy - //decoder - a decoder which coverts terminal characters (an Character array) into text - //appendNewLine - if true a new line character (\n) is appended to the end of the line - void copyLineToStream(int line, - int start, - int count, - TerminalCharacterDecoder* decoder, - bool appendNewLine, - bool preserveLineBreaks); - - //fills a section of the screen image with the character 'c' - //the parameters are specified as offsets from the start of the screen image. - //the loc(x,y) macro can be used to generate these values from a column,line pair. - void clearImage(int loca, int loce, char c); - - //move screen image between 'sourceBegin' and 'sourceEnd' to 'dest'. - //the parameters are specified as offsets from the start of the screen image. - //the loc(x,y) macro can be used to generate these values from a column,line pair. - void moveImage(int dest, int sourceBegin, int sourceEnd); - - void scrollUp(int from, int i); - void scrollDown(int from, int i); - - void addHistLine(); - - void initTabStops(); - - void effectiveRendition(); - void reverseRendition(Character& p) const; - - bool isSelectionValid() const; - - // copies 'count' lines from the screen buffer into 'dest', - // starting from 'startLine', where 0 is the first line in the screen buffer - void copyFromScreen(Character* dest, int startLine, int count) const; - // copies 'count' lines from the history buffer into 'dest', - // starting from 'startLine', where 0 is the first line in the history - void copyFromHistory(Character* dest, int startLine, int count) const; - - - // screen image ---------------- - int lines; - int columns; - - typedef QVector<Character> ImageLine; // [0..columns] - ImageLine* screenLines; // [lines] - - int _scrolledLines; - QRect _lastScrolledRegion; - - int _droppedLines; - - QVarLengthArray<LineProperty,64> lineProperties; - - // history buffer --------------- - HistoryScroll *hist; - - // cursor location - int cuX; - int cuY; - - // cursor color and rendition info - CharacterColor cu_fg; // foreground - CharacterColor cu_bg; // background - quint8 cu_re; // rendition - - // margins ---------------- - int tmargin; // top margin - int bmargin; // bottom margin - - // states ---------------- - ScreenParm currParm; - - // ---------------------------- - - bool* tabstops; - - // selection ------------------- - int sel_begin; // The first location selected. - int sel_TL; // TopLeft Location. - int sel_BR; // Bottom Right Location. - bool sel_busy; // Busy making a selection. - bool columnmode; // Column selection mode - - // effective colors and rendition ------------ - CharacterColor ef_fg; // These are derived from - CharacterColor ef_bg; // the cu_* variables above - quint8 ef_re; // to speed up operation - - // - // save cursor, rendition & states ------------ - // - - // cursor location - int sa_cuX; - int sa_cuY; - - // rendition info - quint8 sa_cu_re; - CharacterColor sa_cu_fg; - CharacterColor sa_cu_bg; - - // last position where we added a character - int lastPos; - - // modes - ScreenParm saveParm; - - static Character defaultChar; -}; - -} - -#endif // SCREEN_H
deleted file mode 100644 --- a/gui/qtermwidget/lib/ScreenWindow.cpp +++ /dev/null @@ -1,296 +0,0 @@ -/* - Copyright (C) 2007 by Robert Knight <robertknight@gmail.com> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 "ScreenWindow.h" - -// Qt -#include <QtCore> - -// Konsole -#include "Screen.h" - -using namespace Konsole; - -ScreenWindow::ScreenWindow(QObject* parent) - : QObject(parent) - , _windowBuffer(0) - , _windowBufferSize(0) - , _bufferNeedsUpdate(true) - , _windowLines(1) - , _currentLine(0) - , _trackOutput(true) - , _scrollCount(0) -{ -} -ScreenWindow::~ScreenWindow() -{ - delete[] _windowBuffer; -} -void ScreenWindow::setScreen(Screen* screen) -{ - Q_ASSERT( screen ); - - _screen = screen; -} - -Screen* ScreenWindow::screen() const -{ - return _screen; -} - -Character* ScreenWindow::getImage() -{ - // reallocate internal buffer if the window size has changed - int size = windowLines() * windowColumns(); - if (_windowBuffer == 0 || _windowBufferSize != size) - { - delete[] _windowBuffer; - _windowBufferSize = size; - _windowBuffer = new Character[size]; - _bufferNeedsUpdate = true; - } - - if (!_bufferNeedsUpdate) - return _windowBuffer; - - _screen->getImage(_windowBuffer,size, - currentLine(),endWindowLine()); - - // this window may look beyond the end of the screen, in which - // case there will be an unused area which needs to be filled - // with blank characters - fillUnusedArea(); - - _bufferNeedsUpdate = false; - return _windowBuffer; -} - -void ScreenWindow::fillUnusedArea() -{ - int screenEndLine = _screen->getHistLines() + _screen->getLines() - 1; - int windowEndLine = currentLine() + windowLines() - 1; - - int unusedLines = windowEndLine - screenEndLine; - int charsToFill = unusedLines * windowColumns(); - - Screen::fillWithDefaultChar(_windowBuffer + _windowBufferSize - charsToFill,charsToFill); -} - -// return the index of the line at the end of this window, or if this window -// goes beyond the end of the screen, the index of the line at the end -// of the screen. -// -// when passing a line number to a Screen method, the line number should -// never be more than endWindowLine() -// -int ScreenWindow::endWindowLine() const -{ - return qMin(currentLine() + windowLines() - 1, - lineCount() - 1); -} -QVector<LineProperty> ScreenWindow::getLineProperties() -{ - QVector<LineProperty> result = _screen->getLineProperties(currentLine(),endWindowLine()); - - if (result.count() != windowLines()) - result.resize(windowLines()); - - return result; -} - -QString ScreenWindow::selectedText( bool preserveLineBreaks ) const -{ - return _screen->selectedText( preserveLineBreaks ); -} - -void ScreenWindow::getSelectionStart( int& column , int& line ) -{ - _screen->getSelectionStart(column,line); - line -= currentLine(); -} -void ScreenWindow::getSelectionEnd( int& column , int& line ) -{ - _screen->getSelectionEnd(column,line); - line -= currentLine(); -} -void ScreenWindow::setSelectionStart( int column , int line , bool columnMode ) -{ - _screen->setSelectionStart( column , qMin(line + currentLine(),endWindowLine()) , columnMode); - - _bufferNeedsUpdate = true; - emit selectionChanged(); -} - -void ScreenWindow::setSelectionEnd( int column , int line ) -{ - _screen->setSelectionEnd( column , qMin(line + currentLine(),endWindowLine()) ); - - _bufferNeedsUpdate = true; - emit selectionChanged(); -} - -bool ScreenWindow::isSelected( int column , int line ) -{ - return _screen->isSelected( column , qMin(line + currentLine(),endWindowLine()) ); -} - -void ScreenWindow::clearSelection() -{ - _screen->clearSelection(); - - emit selectionChanged(); -} - -void ScreenWindow::setWindowLines(int lines) -{ - Q_ASSERT(lines > 0); - _windowLines = lines; -} -int ScreenWindow::windowLines() const -{ - return _windowLines; -} - -int ScreenWindow::windowColumns() const -{ - return _screen->getColumns(); -} - -int ScreenWindow::lineCount() const -{ - return _screen->getHistLines() + _screen->getLines(); -} - -int ScreenWindow::columnCount() const -{ - return _screen->getColumns(); -} - -QPoint ScreenWindow::cursorPosition() const -{ - QPoint position; - - position.setX( _screen->getCursorX() ); - position.setY( _screen->getCursorY() ); - - return position; -} - -int ScreenWindow::currentLine() const -{ - return qBound(0,_currentLine,lineCount()-windowLines()); -} - -void ScreenWindow::scrollBy( RelativeScrollMode mode , int amount ) -{ - if ( mode == ScrollLines ) - { - scrollTo( currentLine() + amount ); - } - else if ( mode == ScrollPages ) - { - scrollTo( currentLine() + amount * ( windowLines() / 2 ) ); - } -} - -bool ScreenWindow::atEndOfOutput() const -{ - return currentLine() == (lineCount()-windowLines()); -} - -void ScreenWindow::scrollTo( int line ) -{ - int maxCurrentLineNumber = lineCount() - windowLines(); - line = qBound(0,line,maxCurrentLineNumber); - - const int delta = line - _currentLine; - _currentLine = line; - - // keep track of number of lines scrolled by, - // this can be reset by calling resetScrollCount() - _scrollCount += delta; - - _bufferNeedsUpdate = true; - - emit scrolled(_currentLine); -} - -void ScreenWindow::setTrackOutput(bool trackOutput) -{ - _trackOutput = trackOutput; -} - -bool ScreenWindow::trackOutput() const -{ - return _trackOutput; -} - -int ScreenWindow::scrollCount() const -{ - return _scrollCount; -} - -void ScreenWindow::resetScrollCount() -{ - _scrollCount = 0; -} - -QRect ScreenWindow::scrollRegion() const -{ - bool equalToScreenSize = windowLines() == _screen->getLines(); - - if ( atEndOfOutput() && equalToScreenSize ) - return _screen->lastScrolledRegion(); - else - return QRect(0,0,windowColumns(),windowLines()); -} - -void ScreenWindow::notifyOutputChanged() -{ - // move window to the bottom of the screen and update scroll count - // if this window is currently tracking the bottom of the screen - if ( _trackOutput ) - { - _scrollCount -= _screen->scrolledLines(); - _currentLine = qMax(0,_screen->getHistLines() - (windowLines()-_screen->getLines())); - } - else - { - // if the history is not unlimited then it may - // have run out of space and dropped the oldest - // lines of output - in this case the screen - // window's current line number will need to - // be adjusted - otherwise the output will scroll - _currentLine = qMax(0,_currentLine - - _screen->droppedLines()); - - // ensure that the screen window's current position does - // not go beyond the bottom of the screen - _currentLine = qMin( _currentLine , _screen->getHistLines() ); - } - - _bufferNeedsUpdate = true; - - emit outputChanged(); -} - -//#include "moc_ScreenWindow.cpp"
deleted file mode 100644 --- a/gui/qtermwidget/lib/ScreenWindow.h +++ /dev/null @@ -1,256 +0,0 @@ -/* - Copyright (C) 2007 by Robert Knight <robertknight@gmail.com> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 SCREENWINDOW_H -#define SCREENWINDOW_H - -// Qt -#include <QtCore/QObject> -#include <QtCore/QPoint> -#include <QtCore/QRect> - -// Konsole -#include "Character.h" - -namespace Konsole -{ - -class Screen; - -/** - * Provides a window onto a section of a terminal screen. - * This window can then be rendered by a terminal display widget ( TerminalDisplay ). - * - * To use the screen window, create a new ScreenWindow() instance and associated it with - * a terminal screen using setScreen(). - * Use the scrollTo() method to scroll the window up and down on the screen. - * Call the getImage() method to retrieve the character image which is currently visible in the window. - * - * setTrackOutput() controls whether the window moves to the bottom of the associated screen when new - * lines are added to it. - * - * Whenever the output from the underlying screen is changed, the notifyOutputChanged() slot should - * be called. This in turn will update the window's position and emit the outputChanged() signal - * if necessary. - */ -class ScreenWindow : public QObject -{ -Q_OBJECT - -public: - /** - * Constructs a new screen window with the given parent. - * A screen must be specified by calling setScreen() before calling getImage() or getLineProperties(). - * - * You should not call this constructor directly, instead use the Emulation::createWindow() method - * to create a window on the emulation which you wish to view. This allows the emulation - * to notify the window when the associated screen has changed and synchronize selection updates - * between all views on a session. - */ - ScreenWindow(QObject* parent = 0); - virtual ~ScreenWindow(); - - /** Sets the screen which this window looks onto */ - void setScreen(Screen* screen); - /** Returns the screen which this window looks onto */ - Screen* screen() const; - - /** - * Returns the image of characters which are currently visible through this window - * onto the screen. - * - * The buffer is managed by the ScreenWindow instance and does not need to be - * deleted by the caller. - */ - Character* getImage(); - - /** - * Returns the line attributes associated with the lines of characters which - * are currently visible through this window - */ - QVector<LineProperty> getLineProperties(); - - /** - * Returns the number of lines which the region of the window - * specified by scrollRegion() has been scrolled by since the last call - * to resetScrollCount(). scrollRegion() is in most cases the - * whole window, but will be a smaller area in, for example, applications - * which provide split-screen facilities. - * - * This is not guaranteed to be accurate, but allows views to optimise - * rendering by reducing the amount of costly text rendering that - * needs to be done when the output is scrolled. - */ - int scrollCount() const; - - /** - * Resets the count of scrolled lines returned by scrollCount() - */ - void resetScrollCount(); - - /** - * Returns the area of the window which was last scrolled, this is - * usually the whole window area. - * - * Like scrollCount(), this is not guaranteed to be accurate, - * but allows views to optimise rendering. - */ - QRect scrollRegion() const; - - /** - * Sets the start of the selection to the given @p line and @p column within - * the window. - */ - void setSelectionStart( int column , int line , bool columnMode ); - /** - * Sets the end of the selection to the given @p line and @p column within - * the window. - */ - void setSelectionEnd( int column , int line ); - /** - * Retrieves the start of the selection within the window. - */ - void getSelectionStart( int& column , int& line ); - /** - * Retrieves the end of the selection within the window. - */ - void getSelectionEnd( int& column , int& line ); - /** - * Returns true if the character at @p line , @p column is part of the selection. - */ - bool isSelected( int column , int line ); - /** - * Clears the current selection - */ - void clearSelection(); - - /** Sets the number of lines in the window */ - void setWindowLines(int lines); - /** Returns the number of lines in the window */ - int windowLines() const; - /** Returns the number of columns in the window */ - int windowColumns() const; - - /** Returns the total number of lines in the screen */ - int lineCount() const; - /** Returns the total number of columns in the screen */ - int columnCount() const; - - /** Returns the index of the line which is currently at the top of this window */ - int currentLine() const; - - /** - * Returns the position of the cursor - * within the window. - */ - QPoint cursorPosition() const; - - /** - * Convenience method. Returns true if the window is currently at the bottom - * of the screen. - */ - bool atEndOfOutput() const; - - /** Scrolls the window so that @p line is at the top of the window */ - void scrollTo( int line ); - - enum RelativeScrollMode - { - ScrollLines, - ScrollPages - }; - - /** - * Scrolls the window relative to its current position on the screen. - * - * @param mode Specifies whether @p amount refers to the number of lines or the number - * of pages to scroll. - * @param amount The number of lines or pages ( depending on @p mode ) to scroll by. If - * this number is positive, the view is scrolled down. If this number is negative, the view - * is scrolled up. - */ - void scrollBy( RelativeScrollMode mode , int amount ); - - /** - * Specifies whether the window should automatically move to the bottom - * of the screen when new output is added. - * - * If this is set to true, the window will be moved to the bottom of the associated screen ( see - * screen() ) when the notifyOutputChanged() method is called. - */ - void setTrackOutput(bool trackOutput); - /** - * Returns whether the window automatically moves to the bottom of the screen as - * new output is added. See setTrackOutput() - */ - bool trackOutput() const; - - /** - * Returns the text which is currently selected. - * - * @param preserveLineBreaks See Screen::selectedText() - */ - QString selectedText( bool preserveLineBreaks ) const; - -public slots: - /** - * Notifies the window that the contents of the associated terminal screen have changed. - * This moves the window to the bottom of the screen if trackOutput() is true and causes - * the outputChanged() signal to be emitted. - */ - void notifyOutputChanged(); - -signals: - /** - * Emitted when the contents of the associated terminal screen ( see screen() ) changes. - */ - void outputChanged(); - - /** - * Emitted when the screen window is scrolled to a different position. - * - * @param line The line which is now at the top of the window. - */ - void scrolled(int line); - - /** - * Emitted when the selection is changed. - */ - void selectionChanged(); - -private: - int endWindowLine() const; - void fillUnusedArea(); - - Screen* _screen; // see setScreen() , screen() - Character* _windowBuffer; - int _windowBufferSize; - bool _bufferNeedsUpdate; - - int _windowLines; - int _currentLine; // see scrollTo() , currentLine() - bool _trackOutput; // see setTrackOutput() , trackOutput() - int _scrollCount; // count of lines which the window has been scrolled by since - // the last call to resetScrollCount() -}; - -} -#endif // SCREENWINDOW_H
deleted file mode 100644 --- a/gui/qtermwidget/lib/Session.cpp +++ /dev/null @@ -1,1021 +0,0 @@ -/* - This file is part of Konsole - - Copyright (C) 2006-2007 by Robert Knight <robertknight@gmail.com> - Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 "Session.h" - -// Standard -#include <assert.h> -#include <stdlib.h> - -// Qt -#include <QtGui/QApplication> -#include <QtCore/QByteRef> -#include <QtCore/QDir> -#include <QtCore/QFile> -#include <QtCore/QRegExp> -#include <QtCore/QStringList> -#include <QtCore> - -#include "Pty.h" -#include "TerminalDisplay.h" -#include "ShellCommand.h" -#include "Vt102Emulation.h" - -using namespace Konsole; - -int Session::lastSessionId = 0; - -Session::Session() : - _shellProcess(0) - , _emulation(0) - , _monitorActivity(false) - , _monitorSilence(false) - , _notifiedActivity(false) - , _autoClose(true) - , _wantedClose(false) - , _silenceSeconds(10) - , _addToUtmp(false) // disabled by default because of a bug encountered on certain systems - // which caused Konsole to hang when closing a tab and then opening a new - // one. A 'QProcess destroyed while still running' warning was being - // printed to the terminal. Likely a problem in KPty::logout() - // or KPty::login() which uses a QProcess to start /usr/bin/utempter - , _flowControl(true) - , _fullScripting(false) - , _sessionId(0) -// , _zmodemBusy(false) -// , _zmodemProc(0) -// , _zmodemProgress(0) - , _hasDarkBackground(false) -{ - //prepare DBus communication -// new SessionAdaptor(this); - _sessionId = ++lastSessionId; -// QDBusConnection::sessionBus().registerObject(QLatin1String("/Sessions/")+QString::number(_sessionId), this); - - //create teletype for I/O with shell process - _shellProcess = new Pty(); - - //create emulation backend - _emulation = new Vt102Emulation(); - - connect( _emulation, SIGNAL( titleChanged( int, const QString & ) ), - this, SLOT( setUserTitle( int, const QString & ) ) ); - connect( _emulation, SIGNAL( stateSet(int) ), - this, SLOT( activityStateSet(int) ) ); -// connect( _emulation, SIGNAL( zmodemDetected() ), this , -// SLOT( fireZModemDetected() ) ); - connect( _emulation, SIGNAL( changeTabTextColorRequest( int ) ), - this, SIGNAL( changeTabTextColorRequest( int ) ) ); - connect( _emulation, SIGNAL(profileChangeCommandReceived(const QString&)), - this, SIGNAL( profileChangeCommandReceived(const QString&)) ); - // TODO - // connect( _emulation,SIGNAL(imageSizeChanged(int,int)) , this , - // SLOT(onEmulationSizeChange(int,int)) ); - - //connect teletype to emulation backend - _shellProcess->setUtf8Mode(_emulation->utf8()); - - connect( _shellProcess,SIGNAL(receivedData(const char*,int)),this, - SLOT(onReceiveBlock(const char*,int)) ); - connect( _emulation,SIGNAL(sendData(const char*,int)),_shellProcess, - SLOT(sendData(const char*,int)) ); - connect( _emulation,SIGNAL(lockPtyRequest(bool)),_shellProcess,SLOT(lockPty(bool)) ); - connect( _emulation,SIGNAL(useUtf8Request(bool)),_shellProcess,SLOT(setUtf8Mode(bool)) ); - - - connect( _shellProcess,SIGNAL(done(int)), this, SLOT(done(int)) ); - - //setup timer for monitoring session activity - _monitorTimer = new QTimer(this); - _monitorTimer->setSingleShot(true); - connect(_monitorTimer, SIGNAL(timeout()), this, SLOT(monitorTimerDone())); -} - -WId Session::windowId() const -{ - // Returns a window ID for this session which is used - // to set the WINDOWID environment variable in the shell - // process. - // - // Sessions can have multiple views or no views, which means - // that a single ID is not always going to be accurate. - // - // If there are no views, the window ID is just 0. If - // there are multiple views, then the window ID for the - // top-level window which contains the first view is - // returned - - if ( _views.count() == 0 ) - return 0; - else - { - QWidget* window = _views.first(); - - Q_ASSERT( window ); - - while ( window->parentWidget() != 0 ) - window = window->parentWidget(); - - return window->winId(); - } -} - -void Session::setDarkBackground(bool darkBackground) -{ - _hasDarkBackground = darkBackground; -} -bool Session::hasDarkBackground() const -{ - return _hasDarkBackground; -} -bool Session::isRunning() const -{ - return _shellProcess->isRunning(); -} - -void Session::setCodec(QTextCodec* codec) -{ - emulation()->setCodec(codec); -} - -void Session::setProgram(const QString& program) -{ - _program = ShellCommand::expand(program); -} -void Session::setInitialWorkingDirectory(const QString& dir) -{ - _initialWorkingDir = ShellCommand::expand(dir); -} -void Session::setArguments(const QStringList& arguments) -{ - _arguments = ShellCommand::expand(arguments); -} - -QList<TerminalDisplay*> Session::views() const -{ - return _views; -} - -void Session::addView(TerminalDisplay* widget) -{ - Q_ASSERT( !_views.contains(widget) ); - - _views.append(widget); - - if ( _emulation != 0 ) - { - // connect emulation - view signals and slots - connect( widget , SIGNAL(keyPressedSignal(QKeyEvent*)) , _emulation , - SLOT(sendKeyEvent(QKeyEvent*)) ); - connect( widget , SIGNAL(mouseSignal(int,int,int,int)) , _emulation , - SLOT(sendMouseEvent(int,int,int,int)) ); - connect( widget , SIGNAL(sendStringToEmu(const char*)) , _emulation , - SLOT(sendString(const char*)) ); - - // allow emulation to notify view when the foreground process - // indicates whether or not it is interested in mouse signals - connect( _emulation , SIGNAL(programUsesMouseChanged(bool)) , widget , - SLOT(setUsesMouse(bool)) ); - - widget->setUsesMouse( _emulation->programUsesMouse() ); - - widget->setScreenWindow(_emulation->createWindow()); - } - - //connect view signals and slots - QObject::connect( widget ,SIGNAL(changedContentSizeSignal(int,int)),this, - SLOT(onViewSizeChange(int,int))); - - QObject::connect( widget ,SIGNAL(destroyed(QObject*)) , this , - SLOT(viewDestroyed(QObject*)) ); -//slot for close - QObject::connect(this, SIGNAL(finished()), widget, SLOT(close())); - -} - -void Session::viewDestroyed(QObject* view) -{ - TerminalDisplay* display = (TerminalDisplay*)view; - - Q_ASSERT( _views.contains(display) ); - - removeView(display); -} - -void Session::removeView(TerminalDisplay* widget) -{ - _views.removeAll(widget); - - disconnect(widget,0,this,0); - - if ( _emulation != 0 ) - { - // disconnect - // - key presses signals from widget - // - mouse activity signals from widget - // - string sending signals from widget - // - // ... and any other signals connected in addView() - disconnect( widget, 0, _emulation, 0); - - // disconnect state change signals emitted by emulation - disconnect( _emulation , 0 , widget , 0); - } - - // close the session automatically when the last view is removed - if ( _views.count() == 0 ) - { - close(); - } -} - -void Session::run() -{ - //check that everything is in place to run the session - if (_program.isEmpty()) - qDebug() << "Session::run() - program to run not set."; - if (_arguments.isEmpty()) - qDebug() << "Session::run() - no command line arguments specified."; - - // Upon a KPty error, there is no description on what that error was... - // Check to see if the given program is executable. - QString exec = QFile::encodeName(_program); - - // if 'exec' is not specified, fall back to default shell. if that - // is not set then fall back to /bin/sh - if ( exec.isEmpty() ) - exec = getenv("SHELL"); - if ( exec.isEmpty() ) - exec = "/bin/sh"; - - // if no arguments are specified, fall back to shell - QStringList arguments = _arguments.join(QChar(' ')).isEmpty() ? - QStringList() << exec : _arguments; - QString pexec = exec; - - if ( pexec.isEmpty() ) { - qDebug()<<"can not execute "<<exec<<endl; - QTimer::singleShot(1, this, SIGNAL(finished())); - return; - } - - QString cwd_save = QDir::currentPath(); - if (!_initialWorkingDir.isEmpty()) - _shellProcess->setWorkingDirectory(_initialWorkingDir); - else - _shellProcess->setWorkingDirectory(QDir::homePath()); - - _shellProcess->setXonXoff(_flowControl); - _shellProcess->setErase(_emulation->getErase()); - - // this is not strictly accurate use of the COLORFGBG variable. This does not - // tell the terminal exactly which colors are being used, but instead approximates - // the color scheme as "black on white" or "white on black" depending on whether - // the background color is deemed dark or not - QString backgroundColorHint = _hasDarkBackground ? "COLORFGBG=15;0" : "COLORFGBG=0;15"; - - int result = _shellProcess->start(QFile::encodeName(_program), - arguments, - _environment << backgroundColorHint, - windowId(), - _addToUtmp); - - if (result < 0) - { - return; - } - - _shellProcess->setWriteable(false); // We are reachable via kwrited. - - emit started(); -} - -void Session::setUserTitle( int what, const QString &caption ) -{ - //set to true if anything is actually changed (eg. old _nameTitle != new _nameTitle ) - bool modified = false; - - // (btw: what=0 changes _userTitle and icon, what=1 only icon, what=2 only _nameTitle - if ((what == 0) || (what == 2)) - { - if ( _userTitle != caption ) { - _userTitle = caption; - modified = true; - } - } - - if ((what == 0) || (what == 1)) - { - if ( _iconText != caption ) { - _iconText = caption; - modified = true; - } - } - - if (what == 11) - { - QString colorString = caption.section(';',0,0); - qDebug() << __FILE__ << __LINE__ << ": setting background colour to " << colorString; - QColor backColor = QColor(colorString); - if (backColor.isValid()){// change color via \033]11;Color\007 - if (backColor != _modifiedBackground) - { - _modifiedBackground = backColor; - - // bail out here until the code to connect the terminal display - // to the changeBackgroundColor() signal has been written - // and tested - just so we don't forget to do this. - Q_ASSERT( 0 ); - - emit changeBackgroundColorRequest(backColor); - } - } - } - - if (what == 30) - { - if ( _nameTitle != caption ) { - setTitle(Session::NameRole,caption); - return; - } - } - - if (what == 31) - { - QString cwd=caption; - cwd=cwd.replace( QRegExp("^~"), QDir::homePath() ); - emit openUrlRequest(cwd); - } - - // change icon via \033]32;Icon\007 - if (what == 32) - { - if ( _iconName != caption ) { - _iconName = caption; - - modified = true; - } - } - - if (what == 50) - { - emit profileChangeCommandReceived(caption); - return; - } - - if ( modified ) - emit titleChanged(); -} - -QString Session::userTitle() const -{ - return _userTitle; -} -void Session::setTabTitleFormat(TabTitleContext context , const QString& format) -{ - if ( context == LocalTabTitle ) - _localTabTitleFormat = format; - else if ( context == RemoteTabTitle ) - _remoteTabTitleFormat = format; -} -QString Session::tabTitleFormat(TabTitleContext context) const -{ - if ( context == LocalTabTitle ) - return _localTabTitleFormat; - else if ( context == RemoteTabTitle ) - return _remoteTabTitleFormat; - - return QString(); -} - -void Session::monitorTimerDone() -{ - //FIXME: The idea here is that the notification popup will appear to tell the user than output from - //the terminal has stopped and the popup will disappear when the user activates the session. - // - //This breaks with the addition of multiple views of a session. The popup should disappear - //when any of the views of the session becomes active - - - //FIXME: Make message text for this notification and the activity notification more descriptive. - if (_monitorSilence) { -// KNotification::event("Silence", ("Silence in session '%1'", _nameTitle), QPixmap(), -// QApplication::activeWindow(), -// KNotification::CloseWhenWidgetActivated); - emit stateChanged(NOTIFYSILENCE); - } - else - { - emit stateChanged(NOTIFYNORMAL); - } - - _notifiedActivity=false; -} - -void Session::activityStateSet(int state) -{ - if (state==NOTIFYBELL) - { - QString s; s.sprintf("Bell in session '%s'",_nameTitle.toAscii().data()); - - emit bellRequest( s ); - } - else if (state==NOTIFYACTIVITY) - { - if (_monitorSilence) { - _monitorTimer->start(_silenceSeconds*1000); - } - - if ( _monitorActivity ) { - //FIXME: See comments in Session::monitorTimerDone() - if (!_notifiedActivity) { -// KNotification::event("Activity", ("Activity in session '%1'", _nameTitle), QPixmap(), -// QApplication::activeWindow(), -// KNotification::CloseWhenWidgetActivated); - _notifiedActivity=true; - } - } - } - - if ( state==NOTIFYACTIVITY && !_monitorActivity ) - state = NOTIFYNORMAL; - if ( state==NOTIFYSILENCE && !_monitorSilence ) - state = NOTIFYNORMAL; - - emit stateChanged(state); -} - -void Session::onViewSizeChange(int /*height*/, int /*width*/) -{ - updateTerminalSize(); -} -void Session::onEmulationSizeChange(int lines , int columns) -{ - setSize( QSize(lines,columns) ); -} - -void Session::updateTerminalSize() -{ - QListIterator<TerminalDisplay*> viewIter(_views); - - int minLines = -1; - int minColumns = -1; - - // minimum number of lines and columns that views require for - // their size to be taken into consideration ( to avoid problems - // with new view widgets which haven't yet been set to their correct size ) - const int VIEW_LINES_THRESHOLD = 2; - const int VIEW_COLUMNS_THRESHOLD = 2; - - //select largest number of lines and columns that will fit in all visible views - while ( viewIter.hasNext() ) - { - TerminalDisplay* view = viewIter.next(); - if ( view->isHidden() == false && - view->lines() >= VIEW_LINES_THRESHOLD && - view->columns() >= VIEW_COLUMNS_THRESHOLD ) - { - minLines = (minLines == -1) ? view->lines() : qMin( minLines , view->lines() ); - minColumns = (minColumns == -1) ? view->columns() : qMin( minColumns , view->columns() ); - } - } - - // backend emulation must have a _terminal of at least 1 column x 1 line in size - if ( minLines > 0 && minColumns > 0 ) - { - _emulation->setImageSize( minLines , minColumns ); - _shellProcess->setWindowSize( minLines , minColumns ); - } -} - -void Session::refresh() -{ - // attempt to get the shell process to redraw the display - // - // this requires the program running in the shell - // to cooperate by sending an update in response to - // a window size change - // - // the window size is changed twice, first made slightly larger and then - // resized back to its normal size so that there is actually a change - // in the window size (some shells do nothing if the - // new and old sizes are the same) - // - // if there is a more 'correct' way to do this, please - // send an email with method or patches to konsole-devel@kde.org - - const QSize existingSize = _shellProcess->windowSize(); - _shellProcess->setWindowSize(existingSize.height(),existingSize.width()+1); - _shellProcess->setWindowSize(existingSize.height(),existingSize.width()); -} - -bool Session::sendSignal(int signal) -{ - return _shellProcess->kill(signal); -} - -void Session::close() -{ - _autoClose = true; - _wantedClose = true; - if (!_shellProcess->isRunning() || !sendSignal(SIGHUP)) - { - // Forced close. - QTimer::singleShot(1, this, SIGNAL(finished())); - } -} - -void Session::sendText(const QString &text) const -{ - _emulation->sendText(text); -} - -Session::~Session() -{ - delete _emulation; - delete _shellProcess; -// delete _zmodemProc; -} - -void Session::setProfileKey(const QString& key) -{ - _profileKey = key; - emit profileChanged(key); -} -QString Session::profileKey() const { return _profileKey; } - -void Session::done(int exitStatus) -{ - if (!_autoClose) - { - _userTitle = ("<Finished>"); - emit titleChanged(); - return; - } - if (!_wantedClose && (exitStatus || _shellProcess->signalled())) - { - QString message; - - if (_shellProcess->normalExit()) - message.sprintf ("Session '%s' exited with status %d.", _nameTitle.toAscii().data(), exitStatus); - else if (_shellProcess->signalled()) - { - if (_shellProcess->coreDumped()) - { - - message.sprintf("Session '%s' exited with signal %d and dumped core.", _nameTitle.toAscii().data(), _shellProcess->exitSignal()); - } - else { - message.sprintf("Session '%s' exited with signal %d.", _nameTitle.toAscii().data(), _shellProcess->exitSignal()); - } - } - else - message.sprintf ("Session '%s' exited unexpectedly.", _nameTitle.toAscii().data()); - - //FIXME: See comments in Session::monitorTimerDone() -// KNotification::event("Finished", message , QPixmap(), -// QApplication::activeWindow(), -// KNotification::CloseWhenWidgetActivated); - } - emit finished(); -} - -Emulation* Session::emulation() const -{ - return _emulation; -} - -QString Session::keyBindings() const -{ - return _emulation->keyBindings(); -} - -QStringList Session::environment() const -{ - return _environment; -} - -void Session::setEnvironment(const QStringList& environment) -{ - _environment = environment; -} - -int Session::sessionId() const -{ - return _sessionId; -} - -void Session::setKeyBindings(const QString &id) -{ - _emulation->setKeyBindings(id); -} - -void Session::setTitle(TitleRole role , const QString& newTitle) -{ - if ( title(role) != newTitle ) - { - if ( role == NameRole ) - _nameTitle = newTitle; - else if ( role == DisplayedTitleRole ) - _displayTitle = newTitle; - - emit titleChanged(); - } -} - -QString Session::title(TitleRole role) const -{ - if ( role == NameRole ) - return _nameTitle; - else if ( role == DisplayedTitleRole ) - return _displayTitle; - else - return QString(); -} - -void Session::setIconName(const QString& iconName) -{ - if ( iconName != _iconName ) - { - _iconName = iconName; - emit titleChanged(); - } -} - -void Session::setIconText(const QString& iconText) -{ - _iconText = iconText; - //kDebug(1211)<<"Session setIconText " << _iconText; -} - -QString Session::iconName() const -{ - return _iconName; -} - -QString Session::iconText() const -{ - return _iconText; -} - -void Session::setHistoryType(const HistoryType &hType) -{ - _emulation->setHistory(hType); -} - -const HistoryType& Session::historyType() const -{ - return _emulation->history(); -} - -void Session::clearHistory() -{ - _emulation->clearHistory(); -} - -QStringList Session::arguments() const -{ - return _arguments; -} - -QString Session::program() const -{ - return _program; -} - -// unused currently -bool Session::isMonitorActivity() const { return _monitorActivity; } -// unused currently -bool Session::isMonitorSilence() const { return _monitorSilence; } - -void Session::setMonitorActivity(bool _monitor) -{ - _monitorActivity=_monitor; - _notifiedActivity=false; - - activityStateSet(NOTIFYNORMAL); -} - -void Session::setMonitorSilence(bool _monitor) -{ - if (_monitorSilence==_monitor) - return; - - _monitorSilence=_monitor; - if (_monitorSilence) - { - _monitorTimer->start(_silenceSeconds*1000); - } - else - _monitorTimer->stop(); - - activityStateSet(NOTIFYNORMAL); -} - -void Session::setMonitorSilenceSeconds(int seconds) -{ - _silenceSeconds=seconds; - if (_monitorSilence) { - _monitorTimer->start(_silenceSeconds*1000); - } -} - -void Session::setAddToUtmp(bool set) -{ - _addToUtmp = set; -} - -void Session::setFlowControlEnabled(bool enabled) -{ - if (_flowControl == enabled) - return; - - _flowControl = enabled; - - if (_shellProcess) - _shellProcess->setXonXoff(_flowControl); - - emit flowControlEnabledChanged(enabled); -} -bool Session::flowControlEnabled() const -{ - return _flowControl; -} -//void Session::fireZModemDetected() -//{ -// if (!_zmodemBusy) -// { -// QTimer::singleShot(10, this, SIGNAL(zmodemDetected())); -// _zmodemBusy = true; -// } -//} - -//void Session::cancelZModem() -//{ -// _shellProcess->sendData("\030\030\030\030", 4); // Abort -// _zmodemBusy = false; -//} - -//void Session::startZModem(const QString &zmodem, const QString &dir, const QStringList &list) -//{ -// _zmodemBusy = true; -// _zmodemProc = new KProcess(); -// _zmodemProc->setOutputChannelMode( KProcess::SeparateChannels ); -// -// *_zmodemProc << zmodem << "-v" << list; -// -// if (!dir.isEmpty()) -// _zmodemProc->setWorkingDirectory(dir); -// -// _zmodemProc->start(); -// -// connect(_zmodemProc,SIGNAL (readyReadStandardOutput()), -// this, SLOT(zmodemReadAndSendBlock())); -// connect(_zmodemProc,SIGNAL (readyReadStandardError()), -// this, SLOT(zmodemReadStatus())); -// connect(_zmodemProc,SIGNAL (finished(int,QProcess::ExitStatus)), -// this, SLOT(zmodemFinished())); -// -// disconnect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(onReceiveBlock(const char*,int)) ); -// connect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(zmodemRcvBlock(const char*,int)) ); -// -// _zmodemProgress = new ZModemDialog(QApplication::activeWindow(), false, -// i18n("ZModem Progress")); -// -// connect(_zmodemProgress, SIGNAL(user1Clicked()), -// this, SLOT(zmodemDone())); -// -// _zmodemProgress->show(); -//} - -/*void Session::zmodemReadAndSendBlock() -{ - _zmodemProc->setReadChannel( QProcess::StandardOutput ); - QByteArray data = _zmodemProc->readAll(); - - if ( data.count() == 0 ) - return; - - _shellProcess->sendData(data.constData(),data.count()); -} -*/ -/* -void Session::zmodemReadStatus() -{ - _zmodemProc->setReadChannel( QProcess::StandardError ); - QByteArray msg = _zmodemProc->readAll(); - while(!msg.isEmpty()) - { - int i = msg.indexOf('\015'); - int j = msg.indexOf('\012'); - QByteArray txt; - if ((i != -1) && ((j == -1) || (i < j))) - { - msg = msg.mid(i+1); - } - else if (j != -1) - { - txt = msg.left(j); - msg = msg.mid(j+1); - } - else - { - txt = msg; - msg.truncate(0); - } - if (!txt.isEmpty()) - _zmodemProgress->addProgressText(QString::fromLocal8Bit(txt)); - } -} -*/ -/* -void Session::zmodemRcvBlock(const char *data, int len) -{ - QByteArray ba( data, len ); - - _zmodemProc->write( ba ); -} -*/ -/* -void Session::zmodemFinished() -{ - if (_zmodemProc) - { - delete _zmodemProc; - _zmodemProc = 0; - _zmodemBusy = false; - - disconnect( _shellProcess,SIGNAL(block_in(const char*,int)), this ,SLOT(zmodemRcvBlock(const char*,int)) ); - connect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(onReceiveBlock(const char*,int)) ); - - _shellProcess->sendData("\030\030\030\030", 4); // Abort - _shellProcess->sendData("\001\013\n", 3); // Try to get prompt back - _zmodemProgress->transferDone(); - } -} -*/ -void Session::onReceiveBlock( const char* buf, int len ) -{ - _emulation->receiveData( buf, len ); - emit receivedData( QString::fromLatin1( buf, len ) ); -} - -QSize Session::size() -{ - return _emulation->imageSize(); -} - -void Session::setSize(const QSize& size) -{ - if ((size.width() <= 1) || (size.height() <= 1)) - return; - - emit resizeRequest(size); -} -int Session::foregroundProcessId() const -{ - return _shellProcess->foregroundProcessGroup(); -} -int Session::processId() const -{ - return _shellProcess->pid(); -} - -SessionGroup::SessionGroup() - : _masterMode(0) -{ -} -SessionGroup::~SessionGroup() -{ - // disconnect all - connectAll(false); -} -int SessionGroup::masterMode() const { return _masterMode; } -QList<Session*> SessionGroup::sessions() const { return _sessions.keys(); } -bool SessionGroup::masterStatus(Session* session) const { return _sessions[session]; } - -void SessionGroup::addSession(Session* session) -{ - _sessions.insert(session,false); - - QListIterator<Session*> masterIter(masters()); - - while ( masterIter.hasNext() ) - connectPair(masterIter.next(),session); -} -void SessionGroup::removeSession(Session* session) -{ - setMasterStatus(session,false); - - QListIterator<Session*> masterIter(masters()); - - while ( masterIter.hasNext() ) - disconnectPair(masterIter.next(),session); - - _sessions.remove(session); -} -void SessionGroup::setMasterMode(int mode) -{ - _masterMode = mode; - - connectAll(false); - connectAll(true); -} -QList<Session*> SessionGroup::masters() const -{ - return _sessions.keys(true); -} -void SessionGroup::connectAll(bool connect) -{ - QListIterator<Session*> masterIter(masters()); - - while ( masterIter.hasNext() ) - { - Session* master = masterIter.next(); - - QListIterator<Session*> otherIter(_sessions.keys()); - while ( otherIter.hasNext() ) - { - Session* other = otherIter.next(); - - if ( other != master ) - { - if ( connect ) - connectPair(master,other); - else - disconnectPair(master,other); - } - } - } -} -void SessionGroup::setMasterStatus(Session* session , bool master) -{ - bool wasMaster = _sessions[session]; - _sessions[session] = master; - - if ( !wasMaster && !master - || wasMaster && master ) - return; - - QListIterator<Session*> iter(_sessions.keys()); - while ( iter.hasNext() ) - { - Session* other = iter.next(); - - if ( other != session ) - { - if ( master ) - connectPair(session,other); - else - disconnectPair(session,other); - } - } -} -void SessionGroup::connectPair(Session* master , Session* other) -{ -// qDebug() << k_funcinfo; - - if ( _masterMode & CopyInputToAll ) - { - qDebug() << "Connection session " << master->nameTitle() << "to" << other->nameTitle(); - - connect( master->emulation() , SIGNAL(sendData(const char*,int)) , other->emulation() , - SLOT(sendString(const char*,int)) ); - } -} -void SessionGroup::disconnectPair(Session* master , Session* other) -{ -// qDebug() << k_funcinfo; - - if ( _masterMode & CopyInputToAll ) - { - qDebug() << "Disconnecting session " << master->nameTitle() << "from" << other->nameTitle(); - - disconnect( master->emulation() , SIGNAL(sendData(const char*,int)) , other->emulation() , - SLOT(sendString(const char*,int)) ); - } -} - -//#include "moc_Session.cpp"
deleted file mode 100644 --- a/gui/qtermwidget/lib/Session.h +++ /dev/null @@ -1,621 +0,0 @@ -/* - This file is part of Konsole, an X terminal. - - Copyright (C) 2007 by Robert Knight <robertknight@gmail.com> - Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 SESSION_H -#define SESSION_H - -// Qt -#include <QtCore/QStringList> -#include <QtCore> -#include <QWidget> - -// Konsole -#include "History.h" - -class KProcess; - -namespace Konsole -{ - -class Emulation; -class Pty; -class TerminalDisplay; -//class ZModemDialog; - -/** - * Represents a terminal session consisting of a pseudo-teletype and a terminal emulation. - * The pseudo-teletype (or PTY) handles I/O between the terminal process and Konsole. - * The terminal emulation ( Emulation and subclasses ) processes the output stream from the - * PTY and produces a character image which is then shown on views connected to the session. - * - * Each Session can be connected to one or more views by using the addView() method. - * The attached views can then display output from the program running in the terminal - * or send input to the program in the terminal in the form of keypresses and mouse - * activity. - */ -class Session : public QObject -{ -Q_OBJECT - -public: - Q_PROPERTY(QString name READ nameTitle) - Q_PROPERTY(int processId READ processId) - Q_PROPERTY(QString keyBindings READ keyBindings WRITE setKeyBindings) - Q_PROPERTY(QSize size READ size WRITE setSize) - - /** - * Constructs a new session. - * - * To start the terminal process, call the run() method, - * after specifying the program and arguments - * using setProgram() and setArguments() - * - * If no program or arguments are specified explicitly, the Session - * falls back to using the program specified in the SHELL environment - * variable. - */ - Session(); - ~Session(); - - /** - * Returns true if the session is currently running. This will be true - * after run() has been called successfully. - */ - bool isRunning() const; - - /** - * Sets the profile associated with this session. - * - * @param profileKey A key which can be used to obtain the current - * profile settings from the SessionManager - */ - void setProfileKey(const QString& profileKey); - /** - * Returns the profile key associated with this session. - * This can be passed to the SessionManager to obtain the current - * profile settings. - */ - QString profileKey() const; - - /** - * Adds a new view for this session. - * - * The viewing widget will display the output from the terminal and - * input from the viewing widget (key presses, mouse activity etc.) - * will be sent to the terminal. - * - * Views can be removed using removeView(). The session is automatically - * closed when the last view is removed. - */ - void addView(TerminalDisplay* widget); - /** - * Removes a view from this session. When the last view is removed, - * the session will be closed automatically. - * - * @p widget will no longer display output from or send input - * to the terminal - */ - void removeView(TerminalDisplay* widget); - - /** - * Returns the views connected to this session - */ - QList<TerminalDisplay*> views() const; - - /** - * Returns the terminal emulation instance being used to encode / decode - * characters to / from the process. - */ - Emulation* emulation() const; - - /** - * Returns the environment of this session as a list of strings like - * VARIABLE=VALUE - */ - QStringList environment() const; - /** - * Sets the environment for this session. - * @p environment should be a list of strings like - * VARIABLE=VALUE - */ - void setEnvironment(const QStringList& environment); - - /** Returns the unique ID for this session. */ - int sessionId() const; - - /** - * Return the session title set by the user (ie. the program running - * in the terminal), or an empty string if the user has not set a custom title - */ - QString userTitle() const; - - /** - * This enum describes the contexts for which separate - * tab title formats may be specified. - */ - enum TabTitleContext - { - /** Default tab title format */ - LocalTabTitle, - /** - * Tab title format used session currently contains - * a connection to a remote computer (via SSH) - */ - RemoteTabTitle - }; - /** - * Sets the format used by this session for tab titles. - * - * @param context The context whoose format should be set. - * @param format The tab title format. This may be a mixture - * of plain text and dynamic elements denoted by a '%' character - * followed by a letter. (eg. %d for directory). The dynamic - * elements available depend on the @p context - */ - void setTabTitleFormat(TabTitleContext context , const QString& format); - /** Returns the format used by this session for tab titles. */ - QString tabTitleFormat(TabTitleContext context) const; - - - /** Returns the arguments passed to the shell process when run() is called. */ - QStringList arguments() const; - /** Returns the program name of the shell process started when run() is called. */ - QString program() const; - - /** - * Sets the command line arguments which the session's program will be passed when - * run() is called. - */ - void setArguments(const QStringList& arguments); - /** Sets the program to be executed when run() is called. */ - void setProgram(const QString& program); - - /** Returns the session's current working directory. */ - QString initialWorkingDirectory() { return _initialWorkingDir; } - - /** - * Sets the initial working directory for the session when it is run - * This has no effect once the session has been started. - */ - void setInitialWorkingDirectory( const QString& dir ); - - /** - * Sets the type of history store used by this session. - * Lines of output produced by the terminal are added - * to the history store. The type of history store - * used affects the number of lines which can be - * remembered before they are lost and the storage - * (in memory, on-disk etc.) used. - */ - void setHistoryType(const HistoryType& type); - /** - * Returns the type of history store used by this session. - */ - const HistoryType& historyType() const; - /** - * Clears the history store used by this session. - */ - void clearHistory(); - - /** - * Enables monitoring for activity in the session. - * This will cause notifySessionState() to be emitted - * with the NOTIFYACTIVITY state flag when output is - * received from the terminal. - */ - void setMonitorActivity(bool); - /** Returns true if monitoring for activity is enabled. */ - bool isMonitorActivity() const; - - /** - * Enables monitoring for silence in the session. - * This will cause notifySessionState() to be emitted - * with the NOTIFYSILENCE state flag when output is not - * received from the terminal for a certain period of - * time, specified with setMonitorSilenceSeconds() - */ - void setMonitorSilence(bool); - /** - * Returns true if monitoring for inactivity (silence) - * in the session is enabled. - */ - bool isMonitorSilence() const; - /** See setMonitorSilence() */ - void setMonitorSilenceSeconds(int seconds); - - /** - * Sets the key bindings used by this session. The bindings - * specify how input key sequences are translated into - * the character stream which is sent to the terminal. - * - * @param id The name of the key bindings to use. The - * names of available key bindings can be determined using the - * KeyboardTranslatorManager class. - */ - void setKeyBindings(const QString& id); - /** Returns the name of the key bindings used by this session. */ - QString keyBindings() const; - - /** - * This enum describes the available title roles. - */ - enum TitleRole - { - /** The name of the session. */ - NameRole, - /** The title of the session which is displayed in tabs etc. */ - DisplayedTitleRole - }; - - /** Sets the session's title for the specified @p role to @p title. */ - void setTitle(TitleRole role , const QString& title); - /** Returns the session's title for the specified @p role. */ - QString title(TitleRole role) const; - /** Convenience method used to read the name property. Returns title(Session::NameRole). */ - QString nameTitle() const { return title(Session::NameRole); } - - /** Sets the name of the icon associated with this session. */ - void setIconName(const QString& iconName); - /** Returns the name of the icon associated with this session. */ - QString iconName() const; - - /** Sets the text of the icon associated with this session. */ - void setIconText(const QString& iconText); - /** Returns the text of the icon associated with this session. */ - QString iconText() const; - - /** Specifies whether a utmp entry should be created for the pty used by this session. */ - void setAddToUtmp(bool); - - /** Sends the specified @p signal to the terminal process. */ - bool sendSignal(int signal); - - /** - * Specifies whether to close the session automatically when the terminal - * process terminates. - */ - void setAutoClose(bool b) { _autoClose = b; } - - /** - * Sets whether flow control is enabled for this terminal - * session. - */ - void setFlowControlEnabled(bool enabled); - - /** Returns whether flow control is enabled for this terminal session. */ - bool flowControlEnabled() const; - - /** - * Sends @p text to the current foreground terminal program. - */ - void sendText(const QString& text) const; - - /** - * Returns the process id of the terminal process. - * This is the id used by the system API to refer to the process. - */ - int processId() const; - - /** - * Returns the process id of the terminal's foreground process. - * This is initially the same as processId() but can change - * as the user starts other programs inside the terminal. - */ - int foregroundProcessId() const; - - /** Returns the terminal session's window size in lines and columns. */ - QSize size(); - /** - * Emits a request to resize the session to accommodate - * the specified window size. - * - * @param size The size in lines and columns to request. - */ - void setSize(const QSize& size); - - /** Sets the text codec used by this session's terminal emulation. */ - void setCodec(QTextCodec* codec); - - /** - * Sets whether the session has a dark background or not. The session - * uses this information to set the COLORFGBG variable in the process's - * environment, which allows the programs running in the terminal to determine - * whether the background is light or dark and use appropriate colors by default. - * - * This has no effect once the session is running. - */ - void setDarkBackground(bool darkBackground); - /** - * Returns true if the session has a dark background. - * See setDarkBackground() - */ - bool hasDarkBackground() const; - - /** - * Attempts to get the shell program to redraw the current display area. - * This can be used after clearing the screen, for example, to get the - * shell to redraw the prompt line. - */ - void refresh(); - -// void startZModem(const QString &rz, const QString &dir, const QStringList &list); -// void cancelZModem(); -// bool isZModemBusy() { return _zmodemBusy; } - -public slots: - - /** - * Starts the terminal session. - * - * This creates the terminal process and connects the teletype to it. - */ - void run(); - - /** - * Closes the terminal session. This sends a hangup signal - * (SIGHUP) to the terminal process and causes the done(Session*) - * signal to be emitted. - */ - void close(); - - /** - * Changes the session title or other customizable aspects of the terminal - * emulation display. For a list of what may be changed see the - * Emulation::titleChanged() signal. - */ - void setUserTitle( int, const QString &caption ); - -signals: - - /** Emitted when the terminal process starts. */ - void started(); - - /** - * Emitted when the terminal process exits. - */ - void finished(); - - /** - * Emitted when output is received from the terminal process. - */ - void receivedData( const QString& text ); - - /** Emitted when the session's title has changed. */ - void titleChanged(); - - /** Emitted when the session's profile has changed. */ - void profileChanged(const QString& profile); - - /** - * Emitted when the activity state of this session changes. - * - * @param state The new state of the session. This may be one - * of NOTIFYNORMAL, NOTIFYSILENCE or NOTIFYACTIVITY - */ - void stateChanged(int state); - - /** Emitted when a bell event occurs in the session. */ - void bellRequest( const QString& message ); - - /** - * Requests that the color the text for any tabs associated with - * this session should be changed; - * - * TODO: Document what the parameter does - */ - void changeTabTextColorRequest(int); - - /** - * Requests that the background color of views on this session - * should be changed. - */ - void changeBackgroundColorRequest(const QColor&); - - /** TODO: Document me. */ - void openUrlRequest(const QString& url); - - /** TODO: Document me. */ -// void zmodemDetected(); - - /** - * Emitted when the terminal process requests a change - * in the size of the terminal window. - * - * @param size The requested window size in terms of lines and columns. - */ - void resizeRequest(const QSize& size); - - /** - * Emitted when a profile change command is received from the terminal. - * - * @param text The text of the command. This is a string of the form - * "PropertyName=Value;PropertyName=Value ..." - */ - void profileChangeCommandReceived(const QString& text); - - /** - * Emitted when the flow control state changes. - * - * @param enabled True if flow control is enabled or false otherwise. - */ - void flowControlEnabledChanged(bool enabled); - -private slots: - void done(int); - -// void fireZModemDetected(); - - void onReceiveBlock( const char* buffer, int len ); - void monitorTimerDone(); - - void onViewSizeChange(int height, int width); - void onEmulationSizeChange(int lines , int columns); - - void activityStateSet(int); - - //automatically detach views from sessions when view is destroyed - void viewDestroyed(QObject* view); - -// void zmodemReadStatus(); -// void zmodemReadAndSendBlock(); -// void zmodemRcvBlock(const char *data, int len); -// void zmodemFinished(); - -private: - - void updateTerminalSize(); - WId windowId() const; - - int _uniqueIdentifier; - - Pty* _shellProcess; - Emulation* _emulation; - - QList<TerminalDisplay*> _views; - - bool _monitorActivity; - bool _monitorSilence; - bool _notifiedActivity; - bool _masterMode; - bool _autoClose; - bool _wantedClose; - QTimer* _monitorTimer; - - int _silenceSeconds; - - QString _nameTitle; - QString _displayTitle; - QString _userTitle; - - QString _localTabTitleFormat; - QString _remoteTabTitleFormat; - - QString _iconName; - QString _iconText; // as set by: echo -en '\033]1;IconText\007 - bool _addToUtmp; - bool _flowControl; - bool _fullScripting; - - QString _program; - QStringList _arguments; - - QStringList _environment; - int _sessionId; - - QString _initialWorkingDir; - - // ZModem -// bool _zmodemBusy; -// KProcess* _zmodemProc; -// ZModemDialog* _zmodemProgress; - - // Color/Font Changes by ESC Sequences - - QColor _modifiedBackground; // as set by: echo -en '\033]11;Color\007 - - QString _profileKey; - - bool _hasDarkBackground; - - static int lastSessionId; - -}; - -/** - * Provides a group of sessions which is divided into master and slave sessions. - * Activity in master sessions can be propagated to all sessions within the group. - * The type of activity which is propagated and method of propagation is controlled - * by the masterMode() flags. - */ -class SessionGroup : public QObject -{ -Q_OBJECT - -public: - /** Constructs an empty session group. */ - SessionGroup(); - /** Destroys the session group and removes all connections between master and slave sessions. */ - ~SessionGroup(); - - /** Adds a session to the group. */ - void addSession( Session* session ); - /** Removes a session from the group. */ - void removeSession( Session* session ); - - /** Returns the list of sessions currently in the group. */ - QList<Session*> sessions() const; - - /** - * Sets whether a particular session is a master within the group. - * Changes or activity in the group's master sessions may be propagated - * to all the sessions in the group, depending on the current masterMode() - * - * @param session The session whoose master status should be changed. - * @param master True to make this session a master or false otherwise - */ - void setMasterStatus( Session* session , bool master ); - /** Returns the master status of a session. See setMasterStatus() */ - bool masterStatus( Session* session ) const; - - /** - * This enum describes the options for propagating certain activity or - * changes in the group's master sessions to all sessions in the group. - */ - enum MasterMode - { - /** - * Any input key presses in the master sessions are sent to all - * sessions in the group. - */ - CopyInputToAll = 1 - }; - - /** - * Specifies which activity in the group's master sessions is propagated - * to all sessions in the group. - * - * @param mode A bitwise OR of MasterMode flags. - */ - void setMasterMode( int mode ); - /** - * Returns a bitwise OR of the active MasterMode flags for this group. - * See setMasterMode() - */ - int masterMode() const; - -private: - void connectPair(Session* master , Session* other); - void disconnectPair(Session* master , Session* other); - void connectAll(bool connect); - QList<Session*> masters() const; - - // maps sessions to their master status - QHash<Session*,bool> _sessions; - - int _masterMode; -}; - -} - -#endif
deleted file mode 100644 --- a/gui/qtermwidget/lib/ShellCommand.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* - Copyright (C) 2007 by Robert Knight <robertknight@gmail.com> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 "ShellCommand.h" - -//some versions of gcc(4.3) require explicit include -#include <cstdlib> - - -using namespace Konsole; - -// expands environment variables in 'text' -// function copied from kdelibs/kio/kio/kurlcompletion.cpp -static bool expandEnv(QString& text); - -ShellCommand::ShellCommand(const QString& fullCommand) -{ - bool inQuotes = false; - - QString builder; - - for ( int i = 0 ; i < fullCommand.count() ; i++ ) - { - QChar ch = fullCommand[i]; - - const bool isLastChar = ( i == fullCommand.count() - 1 ); - const bool isQuote = ( ch == '\'' || ch == '\"' ); - - if ( !isLastChar && isQuote ) - inQuotes = !inQuotes; - else - { - if ( (!ch.isSpace() || inQuotes) && !isQuote ) - builder.append(ch); - - if ( (ch.isSpace() && !inQuotes) || ( i == fullCommand.count()-1 ) ) - { - _arguments << builder; - builder.clear(); - } - } - } -} -ShellCommand::ShellCommand(const QString& command , const QStringList& arguments) -{ - _arguments = arguments; - - if ( !_arguments.isEmpty() ) - _arguments[0] == command; -} -QString ShellCommand::fullCommand() const -{ - return _arguments.join(QChar(' ')); -} -QString ShellCommand::command() const -{ - if ( !_arguments.isEmpty() ) - return _arguments[0]; - else - return QString(); -} -QStringList ShellCommand::arguments() const -{ - return _arguments; -} -bool ShellCommand::isRootCommand() const -{ - Q_ASSERT(0); // not implemented yet - return false; -} -bool ShellCommand::isAvailable() const -{ - Q_ASSERT(0); // not implemented yet - return false; -} -QStringList ShellCommand::expand(const QStringList& items) -{ - QStringList result; - - foreach( QString item , items ) - result << expand(item); - - return result; -} -QString ShellCommand::expand(const QString& text) -{ - QString result = text; - expandEnv(result); - return result; -} - -/* - * expandEnv - * - * Expand environment variables in text. Escaped '$' characters are ignored. - * Return true if any variables were expanded - */ -static bool expandEnv( QString &text ) -{ - // Find all environment variables beginning with '$' - // - int pos = 0; - - bool expanded = false; - - while ( (pos = text.indexOf(QLatin1Char('$'), pos)) != -1 ) { - - // Skip escaped '$' - // - if ( pos > 0 && text.at(pos-1) == QLatin1Char('\\') ) { - pos++; - } - // Variable found => expand - // - else { - // Find the end of the variable = next '/' or ' ' - // - int pos2 = text.indexOf( QLatin1Char(' '), pos+1 ); - int pos_tmp = text.indexOf( QLatin1Char('/'), pos+1 ); - - if ( pos2 == -1 || (pos_tmp != -1 && pos_tmp < pos2) ) - pos2 = pos_tmp; - - if ( pos2 == -1 ) - pos2 = text.length(); - - // Replace if the variable is terminated by '/' or ' ' - // and defined - // - if ( pos2 >= 0 ) { - int len = pos2 - pos; - QString key = text.mid( pos+1, len-1); - QString value = - QString::fromLocal8Bit( ::getenv(key.toLocal8Bit()) ); - - if ( !value.isEmpty() ) { - expanded = true; - text.replace( pos, len, value ); - pos = pos + value.length(); - } - else { - pos = pos2; - } - } - } - } - - return expanded; -}
deleted file mode 100644 --- a/gui/qtermwidget/lib/ShellCommand.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - Copyright (C) 2007 by Robert Knight <robertknight@gmail.com> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 <QtCore/QStringList> - -namespace Konsole -{ - -/** - * A class to parse and extract information about shell commands. - * - * ShellCommand can be used to: - * - * <ul> - * <li>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") - * </li> - * <li>Take a command and a list of arguments and combine them to - * form a complete command line. - * </li> - * <li>Determine whether the binary specified by a command exists in the - * user's PATH. - * </li> - * <li>Determine whether a command-line specifies the execution of - * another command as the root user using su/sudo etc. - * </li> - * </ul> - */ -class ShellCommand -{ -public: - /** - * Constructs a ShellCommand from a command line. - * - * @param fullCommand The command line to parse. - */ - ShellCommand(const QString& fullCommand); - /** - * Constructs a ShellCommand with the specified @p command and @p arguments. - */ - ShellCommand(const QString& command , const QStringList& arguments); - - /** 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 -
deleted file mode 100644 --- a/gui/qtermwidget/lib/TerminalCharacterDecoder.cpp +++ /dev/null @@ -1,227 +0,0 @@ -/* - This file is part of Konsole, an X terminal. - - Copyright (C) 2006 by Robert Knight <robertknight@gmail.com> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser 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 Lesser 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 "TerminalCharacterDecoder.h" - -// Qt -#include <QtCore/QTextStream> - - -using namespace Konsole; - -PlainTextDecoder::PlainTextDecoder() - : _output(0) - , _includeTrailingWhitespace(true) -{ - -} -void PlainTextDecoder::setTrailingWhitespace(bool enable) -{ - _includeTrailingWhitespace = enable; -} -bool PlainTextDecoder::trailingWhitespace() const -{ - return _includeTrailingWhitespace; -} -void PlainTextDecoder::begin(QTextStream* output) -{ - _output = output; -} -void PlainTextDecoder::end() -{ - _output = 0; -} -void PlainTextDecoder::decodeLine(const Character* const characters, int count, LineProperty /*properties*/ - ) -{ - Q_ASSERT( _output ); - - //TODO should we ignore or respect the LINE_WRAPPED line property? - - //note: we build up a QString and send it to the text stream rather writing into the text - //stream a character at a time because it is more efficient. - //(since QTextStream always deals with QStrings internally anyway) - QString plainText; - plainText.reserve(count); - - int outputCount = count; - - // if inclusion of trailing whitespace is disabled then find the end of the - // line - if ( !_includeTrailingWhitespace ) - { - for (int i = count-1 ; i >= 0 ; i--) - { - if ( characters[i].character != ' ' ) - break; - else - outputCount--; - } - } - - for (int i=0;i<outputCount;i++) - { - plainText.append( QChar(characters[i].character) ); - } - - *_output << plainText; -} - -HTMLDecoder::HTMLDecoder() : - _output(0) - ,_colorTable(base_color_table) - ,_innerSpanOpen(false) - ,_lastRendition(DEFAULT_RENDITION) -{ - -} - -void HTMLDecoder::begin(QTextStream* output) -{ - _output = output; - - QString text; - - //open monospace span - openSpan(text,"font-family:monospace"); - - *output << text; -} - -void HTMLDecoder::end() -{ - Q_ASSERT( _output ); - - QString text; - - closeSpan(text); - - *_output << text; - - _output = 0; - -} - -//TODO: Support for LineProperty (mainly double width , double height) -void HTMLDecoder::decodeLine(const Character* const characters, int count, LineProperty /*properties*/ - ) -{ - Q_ASSERT( _output ); - - QString text; - - int spaceCount = 0; - - for (int i=0;i<count;i++) - { - QChar ch(characters[i].character); - - //check if appearance of character is different from previous char - if ( characters[i].rendition != _lastRendition || - characters[i].foregroundColor != _lastForeColor || - characters[i].backgroundColor != _lastBackColor ) - { - if ( _innerSpanOpen ) - closeSpan(text); - - _lastRendition = characters[i].rendition; - _lastForeColor = characters[i].foregroundColor; - _lastBackColor = characters[i].backgroundColor; - - //build up style string - QString style; - - if ( _lastRendition & RE_BOLD || - (_colorTable && characters[i].isBold(_colorTable)) ) - style.append("font-weight:bold;"); - - - if ( _lastRendition & RE_UNDERLINE ) - style.append("font-decoration:underline;"); - - //colours - a colour table must have been defined first - if ( _colorTable ) - { - style.append( QString("color:%1;").arg(_lastForeColor.color(_colorTable).name() ) ); - - if (!characters[i].isTransparent(_colorTable)) - { - style.append( QString("background-color:%1;").arg(_lastBackColor.color(_colorTable).name() ) ); - } - } - - //open the span with the current style - openSpan(text,style); - _innerSpanOpen = true; - } - - //handle whitespace - if (ch.isSpace()) - spaceCount++; - else - spaceCount = 0; - - - //output current character - if (spaceCount < 2) - { - //escape HTML tag characters and just display others as they are - if ( ch == '<' ) - text.append("<"); - else if (ch == '>') - text.append(">"); - else - text.append(ch); - } - else - { - text.append(" "); //HTML truncates multiple spaces, so use a space marker instead - } - - } - - //close any remaining open inner spans - if ( _innerSpanOpen ) - closeSpan(text); - - //start new line - text.append("<br>"); - - *_output << text; -} - -void HTMLDecoder::openSpan(QString& text , const QString& style) -{ - text.append( QString("<span style=\"%1\">").arg(style) ); -} - -void HTMLDecoder::closeSpan(QString& text) -{ - text.append("</span>"); -} - -void HTMLDecoder::setColorTable(const ColorEntry* table) -{ - _colorTable = table; -}
deleted file mode 100644 --- a/gui/qtermwidget/lib/TerminalCharacterDecoder.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - This file is part of Konsole, an X terminal. - - Copyright (C) 2006-7 by Robert Knight <robertknight@gmail.com> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser 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 Lesser 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 TERMINAL_CHARACTER_DECODER_H -#define TERMINAL_CHARACTER_DECODER_H - -#include "Character.h" - -class QTextStream; - -namespace Konsole -{ - -/** - * Base class for terminal character decoders - * - * The decoder converts lines of terminal characters which consist of a unicode character, foreground - * and background colours and other appearance-related properties into text strings. - * - * Derived classes may produce either plain text with no other colour or appearance information, or - * they may produce text which incorporates these additional properties. - */ -class TerminalCharacterDecoder -{ -public: - virtual ~TerminalCharacterDecoder() {} - - /** Begin decoding characters. The resulting text is appended to @p output. */ - virtual void begin(QTextStream* output) = 0; - /** End decoding. */ - virtual void end() = 0; - - /** - * Converts a line of terminal characters with associated properties into a text string - * and writes the string into an output QTextStream. - * - * @param characters An array of characters of length @p count. - * @param properties Additional properties which affect all characters in the line - * @param output The output stream which receives the decoded text - */ - virtual void decodeLine(const Character* const characters, - int count, - LineProperty properties) = 0; -}; - -/** - * A terminal character decoder which produces plain text, ignoring colours and other appearance-related - * properties of the original characters. - */ -class PlainTextDecoder : public TerminalCharacterDecoder -{ -public: - PlainTextDecoder(); - - /** - * Set whether trailing whitespace at the end of lines should be included - * in the output. - * Defaults to true. - */ - void setTrailingWhitespace(bool enable); - /** - * Returns whether trailing whitespace at the end of lines is included - * in the output. - */ - bool trailingWhitespace() const; - - virtual void begin(QTextStream* output); - virtual void end(); - - virtual void decodeLine(const Character* const characters, - int count, - LineProperty properties); - - -private: - QTextStream* _output; - bool _includeTrailingWhitespace; -}; - -/** - * A terminal character decoder which produces pretty HTML markup - */ -class HTMLDecoder : public TerminalCharacterDecoder -{ -public: - /** - * Constructs an HTML decoder using a default black-on-white color scheme. - */ - HTMLDecoder(); - - /** - * Sets the colour table which the decoder uses to produce the HTML colour codes in its - * output - */ - void setColorTable( const ColorEntry* table ); - - virtual void decodeLine(const Character* const characters, - int count, - LineProperty properties); - - virtual void begin(QTextStream* output); - virtual void end(); - -private: - void openSpan(QString& text , const QString& style); - void closeSpan(QString& text); - - QTextStream* _output; - const ColorEntry* _colorTable; - bool _innerSpanOpen; - quint8 _lastRendition; - CharacterColor _lastForeColor; - CharacterColor _lastBackColor; - -}; - -} - -#endif
deleted file mode 100644 --- a/gui/qtermwidget/lib/TerminalDisplay.cpp +++ /dev/null @@ -1,2724 +0,0 @@ -/* - This file is part of Konsole, a terminal emulator for KDE. - - Copyright (C) 2006-7 by Robert Knight <robertknight@gmail.com> - Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 "TerminalDisplay.h" - -// Qt -#include <QtGui/QApplication> -#include <QtGui/QBoxLayout> -#include <QtGui/QClipboard> -#include <QtGui/QKeyEvent> -#include <QtCore/QEvent> -#include <QtCore/QTime> -#include <QtCore/QFile> -#include <QtGui/QGridLayout> -#include <QtGui/QLabel> -#include <QtGui/QLayout> -#include <QtGui/QPainter> -#include <QtGui/QPixmap> -#include <QtGui/QScrollBar> -#include <QtGui/QStyle> -#include <QtCore> -#include <QtGui> - -#include "Filter.h" -#include "konsole_wcwidth.h" -#include "ScreenWindow.h" -#include "TerminalCharacterDecoder.h" -#include "ColorTables.h" - -using namespace Konsole; - -#ifndef loc -#define loc(X,Y) ((Y)*_columns+(X)) -#endif - -#define yMouseScroll 1 - -#define REPCHAR "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ - "abcdefgjijklmnopqrstuvwxyz" \ - "0123456789./+@" - -// scroll increment used when dragging selection at top/bottom of window. - -// static -bool TerminalDisplay::_antialiasText = true; -bool TerminalDisplay::HAVE_TRANSPARENCY = false; - -/* ------------------------------------------------------------------------- */ -/* */ -/* Colors */ -/* */ -/* ------------------------------------------------------------------------- */ - -/* Note that we use ANSI color order (bgr), while IBMPC color order is (rgb) - - Code 0 1 2 3 4 5 6 7 - ----------- ------- ------- ------- ------- ------- ------- ------- ------- - ANSI (bgr) Black Red Green Yellow Blue Magenta Cyan White - IBMPC (rgb) Black Blue Green Cyan Red Magenta Yellow White -*/ - -ScreenWindow* TerminalDisplay::screenWindow() const -{ - return _screenWindow; -} -void TerminalDisplay::setScreenWindow(ScreenWindow* window) -{ - // disconnect existing screen window if any - if ( _screenWindow ) - { - disconnect( _screenWindow , 0 , this , 0 ); - } - - _screenWindow = window; - - if ( window ) - { -//#warning "The order here is not specified - does it matter whether updateImage or updateLineProperties comes first?" - connect( _screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateLineProperties()) ); - connect( _screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateImage()) ); - window->setWindowLines(_lines); - } -} - -const ColorEntry* TerminalDisplay::colorTable() const -{ - return _colorTable; -} - -void TerminalDisplay::setColorTable(const ColorEntry table[]) -{ - for (int i = 0; i < TABLE_COLORS; i++) - _colorTable[i] = table[i]; - - QPalette p = palette(); - p.setColor( backgroundRole(), _colorTable[DEFAULT_BACK_COLOR].color ); - setPalette( p ); - - // Avoid propagating the palette change to the scroll bar - _scrollBar->setPalette( QApplication::palette() ); - - update(); -} - -/* ------------------------------------------------------------------------- */ -/* */ -/* Font */ -/* */ -/* ------------------------------------------------------------------------- */ - -/* - The VT100 has 32 special graphical characters. The usual vt100 extended - xterm fonts have these at 0x00..0x1f. - - QT's iso mapping leaves 0x00..0x7f without any changes. But the graphicals - come in here as proper unicode characters. - - We treat non-iso10646 fonts as VT100 extended and do the requiered mapping - from unicode to 0x00..0x1f. The remaining translation is then left to the - QCodec. -*/ - -static inline bool isLineChar(quint16 c) { return ((c & 0xFF80) == 0x2500);} -static inline bool isLineCharString(const QString& string) -{ - return (string.length() > 0) && (isLineChar(string.at(0).unicode())); -} - - -// assert for i in [0..31] : vt100extended(vt100_graphics[i]) == i. - -unsigned short Konsole::vt100_graphics[32] = -{ // 0/8 1/9 2/10 3/11 4/12 5/13 6/14 7/15 - 0x0020, 0x25C6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, - 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, - 0xF800, 0xF801, 0x2500, 0xF803, 0xF804, 0x251c, 0x2524, 0x2534, - 0x252c, 0x2502, 0x2264, 0x2265, 0x03C0, 0x2260, 0x00A3, 0x00b7 -}; - -void TerminalDisplay::fontChange(const QFont&) -{ - QFontMetrics fm(font()); - _fontHeight = fm.height() + _lineSpacing; - - - // waba TerminalDisplay 1.123: - // "Base character width on widest ASCII character. This prevents too wide - // characters in the presence of double wide (e.g. Japanese) characters." - // Get the width from representative normal width characters - _fontWidth = qRound((double)fm.width(REPCHAR)/(double)strlen(REPCHAR)); - - _fixedFont = true; - - int fw = fm.width(REPCHAR[0]); - for(unsigned int i=1; i< strlen(REPCHAR); i++) - { - if (fw != fm.width(REPCHAR[i])) - { - _fixedFont = false; - break; - } - } - - if (_fontWidth < 1) - _fontWidth=1; - - _fontAscent = fm.ascent(); - - emit changedFontMetricSignal( _fontHeight, _fontWidth ); - propagateSize(); - update(); -} - -void TerminalDisplay::setVTFont(const QFont& f) -{ - QFont font = f; - - QFontMetrics metrics(font); - - if ( metrics.height() < height() && metrics.maxWidth() < width() ) - { - // hint that text should be drawn without anti-aliasing. - // depending on the user's font configuration, this may not be respected - if (!_antialiasText) - font.setStyleStrategy( QFont::NoAntialias ); - - // experimental optimization. Konsole assumes that the terminal is using a - // mono-spaced font, in which case kerning information should have an effect. - // Disabling kerning saves some computation when rendering text. - font.setKerning(false); - - QWidget::setFont(font); - fontChange(font); - } -} - -void TerminalDisplay::setFont(const QFont &) -{ - // ignore font change request if not coming from konsole itself -} - -/* ------------------------------------------------------------------------- */ -/* */ -/* Constructor / Destructor */ -/* */ -/* ------------------------------------------------------------------------- */ - -TerminalDisplay::TerminalDisplay(QWidget *parent) -:QWidget(parent) -,_screenWindow(0) -,_allowBell(true) -,_gridLayout(0) -,_fontHeight(1) -,_fontWidth(1) -,_fontAscent(1) -,_lines(1) -,_columns(1) -,_usedLines(1) -,_usedColumns(1) -,_contentHeight(1) -,_contentWidth(1) -,_image(0) -,_randomSeed(0) -,_resizing(false) -,_terminalSizeHint(false) -,_terminalSizeStartup(true) -,_bidiEnabled(false) -,_actSel(0) -,_wordSelectionMode(false) -,_lineSelectionMode(false) -,_preserveLineBreaks(false) -,_columnSelectionMode(false) -,_scrollbarLocation(NoScrollBar) -,_wordCharacters(":@-./_~") -,_bellMode(SystemBeepBell) -,_blinking(false) -,_cursorBlinking(false) -,_hasBlinkingCursor(false) -,_ctrlDrag(false) -,_tripleClickMode(SelectWholeLine) -,_isFixedSize(false) -,_possibleTripleClick(false) -,_resizeWidget(0) -,_resizeTimer(0) -,_flowControlWarningEnabled(false) -,_outputSuspendedLabel(0) -,_lineSpacing(0) -,_colorsInverted(false) -,_blendColor(qRgba(0,0,0,0xff)) -,_filterChain(new TerminalImageFilterChain()) -,_cursorShape(BlockCursor) -{ - // terminal applications are not designed with Right-To-Left in mind, - // so the layout is forced to Left-To-Right - setLayoutDirection(Qt::LeftToRight); - - // The offsets are not yet calculated. - // Do not calculate these too often to be more smoothly when resizing - // konsole in opaque mode. - _topMargin = DEFAULT_TOP_MARGIN; - _leftMargin = DEFAULT_LEFT_MARGIN; - - // create scroll bar for scrolling output up and down - // set the scroll bar's slider to occupy the whole area of the scroll bar initially - _scrollBar = new QScrollBar(this); - setScroll(0,0); - _scrollBar->setCursor( Qt::ArrowCursor ); - connect(_scrollBar, SIGNAL(valueChanged(int)), this, - SLOT(scrollBarPositionChanged(int))); - - // setup timers for blinking cursor and text - _blinkTimer = new QTimer(this); - connect(_blinkTimer, SIGNAL(timeout()), this, SLOT(blinkEvent())); - _blinkCursorTimer = new QTimer(this); - connect(_blinkCursorTimer, SIGNAL(timeout()), this, SLOT(blinkCursorEvent())); - -// QCursor::setAutoHideCursor( this, true ); - - setUsesMouse(true); - setColorTable(whiteonblack_color_table); -// setColorTable(blackonlightyellow_color_table); - setMouseTracking(true); - - // Enable drag and drop - setAcceptDrops(true); // attempt - dragInfo.state = diNone; - - setFocusPolicy( Qt::WheelFocus ); - - // enable input method support - setAttribute(Qt::WA_InputMethodEnabled, true); - - // this is an important optimization, it tells Qt - // that TerminalDisplay will handle repainting its entire area. - setAttribute(Qt::WA_OpaquePaintEvent); - - _gridLayout = new QGridLayout(this); - _gridLayout->setMargin(0); - - setLayout( _gridLayout ); - - //set up a warning message when the user presses Ctrl+S to avoid confusion - connect( this,SIGNAL(flowControlKeyPressed(bool)),this,SLOT(outputSuspended(bool)) ); -} - -TerminalDisplay::~TerminalDisplay() -{ - qApp->removeEventFilter( this ); - - delete[] _image; - - delete _gridLayout; - delete _outputSuspendedLabel; - delete _filterChain; -} - -/* ------------------------------------------------------------------------- */ -/* */ -/* Display Operations */ -/* */ -/* ------------------------------------------------------------------------- */ - -/** - A table for emulating the simple (single width) unicode drawing chars. - It represents the 250x - 257x glyphs. If it's zero, we can't use it. - if it's not, it's encoded as follows: imagine a 5x5 grid where the points are numbered - 0 to 24 left to top, top to bottom. Each point is represented by the corresponding bit. - - Then, the pixels basically have the following interpretation: - _|||_ - -...- - -...- - -...- - _|||_ - -where _ = none - | = vertical line. - - = horizontal line. - */ - - -enum LineEncode -{ - TopL = (1<<1), - TopC = (1<<2), - TopR = (1<<3), - - LeftT = (1<<5), - Int11 = (1<<6), - Int12 = (1<<7), - Int13 = (1<<8), - RightT = (1<<9), - - LeftC = (1<<10), - Int21 = (1<<11), - Int22 = (1<<12), - Int23 = (1<<13), - RightC = (1<<14), - - LeftB = (1<<15), - Int31 = (1<<16), - Int32 = (1<<17), - Int33 = (1<<18), - RightB = (1<<19), - - BotL = (1<<21), - BotC = (1<<22), - BotR = (1<<23) -}; - -#include "LineFont.h" - -static void drawLineChar(QPainter& paint, int x, int y, int w, int h, uchar code) -{ - //Calculate cell midpoints, end points. - int cx = x + w/2; - int cy = y + h/2; - int ex = x + w - 1; - int ey = y + h - 1; - - quint32 toDraw = LineChars[code]; - - //Top _lines: - if (toDraw & TopL) - paint.drawLine(cx-1, y, cx-1, cy-2); - if (toDraw & TopC) - paint.drawLine(cx, y, cx, cy-2); - if (toDraw & TopR) - paint.drawLine(cx+1, y, cx+1, cy-2); - - //Bot _lines: - if (toDraw & BotL) - paint.drawLine(cx-1, cy+2, cx-1, ey); - if (toDraw & BotC) - paint.drawLine(cx, cy+2, cx, ey); - if (toDraw & BotR) - paint.drawLine(cx+1, cy+2, cx+1, ey); - - //Left _lines: - if (toDraw & LeftT) - paint.drawLine(x, cy-1, cx-2, cy-1); - if (toDraw & LeftC) - paint.drawLine(x, cy, cx-2, cy); - if (toDraw & LeftB) - paint.drawLine(x, cy+1, cx-2, cy+1); - - //Right _lines: - if (toDraw & RightT) - paint.drawLine(cx+2, cy-1, ex, cy-1); - if (toDraw & RightC) - paint.drawLine(cx+2, cy, ex, cy); - if (toDraw & RightB) - paint.drawLine(cx+2, cy+1, ex, cy+1); - - //Intersection points. - if (toDraw & Int11) - paint.drawPoint(cx-1, cy-1); - if (toDraw & Int12) - paint.drawPoint(cx, cy-1); - if (toDraw & Int13) - paint.drawPoint(cx+1, cy-1); - - if (toDraw & Int21) - paint.drawPoint(cx-1, cy); - if (toDraw & Int22) - paint.drawPoint(cx, cy); - if (toDraw & Int23) - paint.drawPoint(cx+1, cy); - - if (toDraw & Int31) - paint.drawPoint(cx-1, cy+1); - if (toDraw & Int32) - paint.drawPoint(cx, cy+1); - if (toDraw & Int33) - paint.drawPoint(cx+1, cy+1); - -} - -void TerminalDisplay::drawLineCharString( QPainter& painter, int x, int y, const QString& str, - const Character* attributes) -{ - const QPen& currentPen = painter.pen(); - - if ( attributes->rendition & RE_BOLD ) - { - QPen boldPen(currentPen); - boldPen.setWidth(3); - painter.setPen( boldPen ); - } - - for (int i=0 ; i < str.length(); i++) - { - uchar code = str[i].cell(); - if (LineChars[code]) - drawLineChar(painter, x + (_fontWidth*i), y, _fontWidth, _fontHeight, code); - } - - painter.setPen( currentPen ); -} - -void TerminalDisplay::setKeyboardCursorShape(KeyboardCursorShape shape) -{ - _cursorShape = shape; -} -TerminalDisplay::KeyboardCursorShape TerminalDisplay::keyboardCursorShape() const -{ - return _cursorShape; -} -void TerminalDisplay::setKeyboardCursorColor(bool useForegroundColor, const QColor& color) -{ - if (useForegroundColor) - _cursorColor = QColor(); // an invalid color means that - // the foreground color of the - // current character should - // be used - - else - _cursorColor = color; -} -QColor TerminalDisplay::keyboardCursorColor() const -{ - return _cursorColor; -} - -void TerminalDisplay::setOpacity(qreal opacity) -{ - QColor color(_blendColor); - color.setAlphaF(opacity); - - // enable automatic background filling to prevent the display - // flickering if there is no transparency - if ( color.alpha() == 255 ) - { - setAutoFillBackground(true); - } - else - { - setAutoFillBackground(false); - } - - _blendColor = color.rgba(); -} - -void TerminalDisplay::drawBackground(QPainter& painter, const QRect& rect, const QColor& backgroundColor, bool useOpacitySetting ) -{ - // the area of the widget showing the contents of the terminal display is drawn - // using the background color from the color scheme set with setColorTable() - // - // the area of the widget behind the scroll-bar is drawn using the background - // brush from the scroll-bar's palette, to give the effect of the scroll-bar - // being outside of the terminal display and visual consistency with other KDE - // applications. - // - QRect scrollBarArea = _scrollBar->isVisible() ? - rect.intersected(_scrollBar->geometry()) : - QRect(); - QRegion contentsRegion = QRegion(rect).subtracted(scrollBarArea); - QRect contentsRect = contentsRegion.boundingRect(); - - if ( HAVE_TRANSPARENCY && qAlpha(_blendColor) < 0xff && useOpacitySetting ) - { - QColor color(backgroundColor); - color.setAlpha(qAlpha(_blendColor)); - - painter.save(); - painter.setCompositionMode(QPainter::CompositionMode_Source); - painter.fillRect(contentsRect, color); - painter.restore(); - } - else { - painter.fillRect(contentsRect, backgroundColor); - } - - painter.fillRect(scrollBarArea,_scrollBar->palette().background()); -} - -void TerminalDisplay::drawCursor(QPainter& painter, - const QRect& rect, - const QColor& foregroundColor, - const QColor& /*backgroundColor*/, - bool& invertCharacterColor) -{ - QRect cursorRect = rect; - cursorRect.setHeight(_fontHeight - _lineSpacing - 1); - - if (!_cursorBlinking) - { - if ( _cursorColor.isValid() ) - painter.setPen(_cursorColor); - else { - painter.setPen(foregroundColor); - } - - if ( _cursorShape == BlockCursor ) - { - // draw the cursor outline, adjusting the area so that - // it is draw entirely inside 'rect' - int penWidth = qMax(1,painter.pen().width()); - - painter.drawRect(cursorRect.adjusted(penWidth/2, - penWidth/2, - - penWidth/2 - penWidth%2, - - penWidth/2 - penWidth%2)); - if ( hasFocus() ) - { - painter.fillRect(cursorRect, _cursorColor.isValid() ? _cursorColor : foregroundColor); - - if ( !_cursorColor.isValid() ) - { - // invert the colour used to draw the text to ensure that the character at - // the cursor position is readable - invertCharacterColor = true; - } - } - } - else if ( _cursorShape == UnderlineCursor ) - painter.drawLine(cursorRect.left(), - cursorRect.bottom(), - cursorRect.right(), - cursorRect.bottom()); - else if ( _cursorShape == IBeamCursor ) - painter.drawLine(cursorRect.left(), - cursorRect.top(), - cursorRect.left(), - cursorRect.bottom()); - - } -} - -void TerminalDisplay::drawCharacters(QPainter& painter, - const QRect& rect, - const QString& text, - const Character* style, - bool invertCharacterColor) -{ - // don't draw text which is currently blinking - if ( _blinking && (style->rendition & RE_BLINK) ) - return; - - // setup bold and underline - bool useBold = style->rendition & RE_BOLD || style->isBold(_colorTable) || font().bold(); - bool useUnderline = style->rendition & RE_UNDERLINE || font().underline(); - - QFont font = painter.font(); - if ( font.bold() != useBold - || font.underline() != useUnderline ) - { - font.setBold(useBold); - font.setUnderline(useUnderline); - painter.setFont(font); - } - - const CharacterColor& textColor = ( invertCharacterColor ? style->backgroundColor : style->foregroundColor ); - const QColor color = textColor.color(_colorTable); - - QPen pen = painter.pen(); - if ( pen.color() != color ) - { - pen.setColor(color); - painter.setPen(color); - } - // draw text - if ( isLineCharString(text) ) { - drawLineCharString(painter,rect.x(),rect.y(),text,style); - } - else - { - // the drawText(rect,flags,string) overload is used here with null flags - // instead of drawText(rect,string) because the (rect,string) overload causes - // the application's default layout direction to be used instead of - // the widget-specific layout direction, which should always be - // Qt::LeftToRight for this widget - painter.drawText(rect,0,text); - } -} - -void TerminalDisplay::drawTextFragment(QPainter& painter , - const QRect& rect, - const QString& text, - const Character* style) -{ - painter.save(); - - // setup painter - const QColor foregroundColor = style->foregroundColor.color(_colorTable); - const QColor backgroundColor = style->backgroundColor.color(_colorTable); - - // draw background if different from the display's background color - if ( backgroundColor != palette().background().color() ) - drawBackground(painter,rect,backgroundColor, false /* do not use transparency */); - - // draw cursor shape if the current character is the cursor - // this may alter the foreground and background colors - bool invertCharacterColor = false; - - if ( style->rendition & RE_CURSOR ) - drawCursor(painter,rect,foregroundColor,backgroundColor,invertCharacterColor); - // draw text - drawCharacters(painter,rect,text,style,invertCharacterColor); - - painter.restore(); -} - -void TerminalDisplay::setRandomSeed(uint randomSeed) { _randomSeed = randomSeed; } -uint TerminalDisplay::randomSeed() const { return _randomSeed; } - -#if 0 -/*! - Set XIM Position -*/ -void TerminalDisplay::setCursorPos(const int curx, const int cury) -{ - QPoint tL = contentsRect().topLeft(); - int tLx = tL.x(); - int tLy = tL.y(); - - int xpos, ypos; - ypos = _topMargin + tLy + _fontHeight*(cury-1) + _fontAscent; - xpos = _leftMargin + tLx + _fontWidth*curx; - //setMicroFocusHint(xpos, ypos, 0, _fontHeight); //### ??? - // fprintf(stderr, "x/y = %d/%d\txpos/ypos = %d/%d\n", curx, cury, xpos, ypos); - _cursorLine = cury; - _cursorCol = curx; -} -#endif - -// scrolls the image by 'lines', down if lines > 0 or up otherwise. -// -// the terminal emulation keeps track of the scrolling of the character -// image as it receives input, and when the view is updated, it calls scrollImage() -// with the final scroll amount. this improves performance because scrolling the -// display is much cheaper than re-rendering all the text for the -// part of the image which has moved up or down. -// Instead only new lines have to be drawn -// -// note: it is important that the area of the display which is -// scrolled aligns properly with the character grid - -// which has a top left point at (_leftMargin,_topMargin) , -// a cell width of _fontWidth and a cell height of _fontHeight). -void TerminalDisplay::scrollImage(int lines , const QRect& screenWindowRegion) -{ - // if the flow control warning is enabled this will interfere with the - // scrolling optimisations and cause artifacts. the simple solution here - // is to just disable the optimisation whilst it is visible - if ( _outputSuspendedLabel && _outputSuspendedLabel->isVisible() ) { - return; - } - - // constrain the region to the display - // the bottom of the region is capped to the number of lines in the display's - // internal image - 2, so that the height of 'region' is strictly less - // than the height of the internal image. - QRect region = screenWindowRegion; - region.setBottom( qMin(region.bottom(),this->_lines-2) ); - - if ( lines == 0 - || _image == 0 - || !region.isValid() - || (region.top() + abs(lines)) >= region.bottom() - || this->_lines <= region.height() ) return; - - QRect scrollRect; - - void* firstCharPos = &_image[ region.top() * this->_columns ]; - void* lastCharPos = &_image[ (region.top() + abs(lines)) * this->_columns ]; - - int top = _topMargin + (region.top() * _fontHeight); - int linesToMove = region.height() - abs(lines); - int bytesToMove = linesToMove * - this->_columns * - sizeof(Character); - - Q_ASSERT( linesToMove > 0 ); - Q_ASSERT( bytesToMove > 0 ); - - //scroll internal image - if ( lines > 0 ) - { - // check that the memory areas that we are going to move are valid - Q_ASSERT( (char*)lastCharPos + bytesToMove < - (char*)(_image + (this->_lines * this->_columns)) ); - - Q_ASSERT( (lines*this->_columns) < _imageSize ); - - //scroll internal image down - memmove( firstCharPos , lastCharPos , bytesToMove ); - - //set region of display to scroll, making sure that - //the region aligns correctly to the character grid - scrollRect = QRect( _leftMargin , top, - this->_usedColumns * _fontWidth , - linesToMove * _fontHeight ); - } - else - { - // check that the memory areas that we are going to move are valid - Q_ASSERT( (char*)firstCharPos + bytesToMove < - (char*)(_image + (this->_lines * this->_columns)) ); - - //scroll internal image up - memmove( lastCharPos , firstCharPos , bytesToMove ); - - //set region of the display to scroll, making sure that - //the region aligns correctly to the character grid - QPoint topPoint( _leftMargin , top + abs(lines)*_fontHeight ); - - scrollRect = QRect( topPoint , - QSize( this->_usedColumns*_fontWidth , - linesToMove * _fontHeight )); - } - - //scroll the display vertically to match internal _image - scroll( 0 , _fontHeight * (-lines) , scrollRect ); -} - -QRegion TerminalDisplay::hotSpotRegion() const -{ - QRegion region; - foreach( Filter::HotSpot* hotSpot , _filterChain->hotSpots() ) - { - QRect rect; - rect.setLeft(hotSpot->startColumn()); - rect.setTop(hotSpot->startLine()); - rect.setRight(hotSpot->endColumn()); - rect.setBottom(hotSpot->endLine()); - - region |= imageToWidget(rect); - } - return region; -} - -void TerminalDisplay::processFilters() -{ - if (!_screenWindow) - return; - - QRegion preUpdateHotSpots = hotSpotRegion(); - - // use _screenWindow->getImage() here rather than _image because - // other classes may call processFilters() when this display's - // ScreenWindow emits a scrolled() signal - which will happen before - // updateImage() is called on the display and therefore _image is - // out of date at this point - _filterChain->setImage( _screenWindow->getImage(), - _screenWindow->windowLines(), - _screenWindow->windowColumns(), - _screenWindow->getLineProperties() ); - _filterChain->process(); - - QRegion postUpdateHotSpots = hotSpotRegion(); - - update( preUpdateHotSpots | postUpdateHotSpots ); -} - -void TerminalDisplay::updateImage() -{ - if ( !_screenWindow ) - return; - - // optimization - scroll the existing image where possible and - // avoid expensive text drawing for parts of the image that - // can simply be moved up or down - scrollImage( _screenWindow->scrollCount() , - _screenWindow->scrollRegion() ); - _screenWindow->resetScrollCount(); - - Character* const newimg = _screenWindow->getImage(); - int lines = _screenWindow->windowLines(); - int columns = _screenWindow->windowColumns(); - - setScroll( _screenWindow->currentLine() , _screenWindow->lineCount() ); - - if (!_image) - updateImageSize(); // Create _image - - Q_ASSERT( this->_usedLines <= this->_lines ); - Q_ASSERT( this->_usedColumns <= this->_columns ); - - int y,x,len; - - QPoint tL = contentsRect().topLeft(); - - int tLx = tL.x(); - int tLy = tL.y(); - _hasBlinker = false; - - CharacterColor cf; // undefined - CharacterColor _clipboard; // undefined - int cr = -1; // undefined - - const int linesToUpdate = qMin(this->_lines, qMax(0,lines )); - const int columnsToUpdate = qMin(this->_columns,qMax(0,columns)); - - QChar *disstrU = new QChar[columnsToUpdate]; - char *dirtyMask = new char[columnsToUpdate+2]; - QRegion dirtyRegion; - - // debugging variable, this records the number of lines that are found to - // be 'dirty' ( ie. have changed from the old _image to the new _image ) and - // which therefore need to be repainted - int dirtyLineCount = 0; - - for (y = 0; y < linesToUpdate; y++) - { - const Character* currentLine = &_image[y*this->_columns]; - const Character* const newLine = &newimg[y*columns]; - - bool updateLine = false; - - // The dirty mask indicates which characters need repainting. We also - // mark surrounding neighbours dirty, in case the character exceeds - // its cell boundaries - memset(dirtyMask, 0, columnsToUpdate+2); - - for( x = 0 ; x < columnsToUpdate ; x++) - { - if ( newLine[x] != currentLine[x] ) - { - dirtyMask[x] = true; - } - } - - if (!_resizing) // not while _resizing, we're expecting a paintEvent - for (x = 0; x < columnsToUpdate; x++) - { - _hasBlinker |= (newLine[x].rendition & RE_BLINK); - - // Start drawing if this character or the next one differs. - // We also take the next one into account to handle the situation - // where characters exceed their cell width. - if (dirtyMask[x]) - { - quint16 c = newLine[x+0].character; - if ( !c ) - continue; - int p = 0; - disstrU[p++] = c; //fontMap(c); - bool lineDraw = isLineChar(c); - bool doubleWidth = (x+1 == columnsToUpdate) ? false : (newLine[x+1].character == 0); - cr = newLine[x].rendition; - _clipboard = newLine[x].backgroundColor; - if (newLine[x].foregroundColor != cf) cf = newLine[x].foregroundColor; - int lln = columnsToUpdate - x; - for (len = 1; len < lln; len++) - { - const Character& ch = newLine[x+len]; - - if (!ch.character) - continue; // Skip trailing part of multi-col chars. - - bool nextIsDoubleWidth = (x+len+1 == columnsToUpdate) ? false : (newLine[x+len+1].character == 0); - - if ( ch.foregroundColor != cf || - ch.backgroundColor != _clipboard || - ch.rendition != cr || - !dirtyMask[x+len] || - isLineChar(c) != lineDraw || - nextIsDoubleWidth != doubleWidth ) - break; - - disstrU[p++] = c; //fontMap(c); - } - - QString unistr(disstrU, p); - - bool saveFixedFont = _fixedFont; - if (lineDraw) - _fixedFont = false; - if (doubleWidth) - _fixedFont = false; - - updateLine = true; - - _fixedFont = saveFixedFont; - x += len - 1; - } - - } - - //both the top and bottom halves of double height _lines must always be redrawn - //although both top and bottom halves contain the same characters, only - //the top one is actually - //drawn. - if (_lineProperties.count() > y) - updateLine |= (_lineProperties[y] & LINE_DOUBLEHEIGHT); - - // if the characters on the line are different in the old and the new _image - // then this line must be repainted. - if (updateLine) - { - dirtyLineCount++; - - // add the area occupied by this line to the region which needs to be - // repainted - QRect dirtyRect = QRect( _leftMargin+tLx , - _topMargin+tLy+_fontHeight*y , - _fontWidth * columnsToUpdate , - _fontHeight ); - - dirtyRegion |= dirtyRect; - } - - // replace the line of characters in the old _image with the - // current line of the new _image - memcpy((void*)currentLine,(const void*)newLine,columnsToUpdate*sizeof(Character)); - } - - // if the new _image is smaller than the previous _image, then ensure that the area - // outside the new _image is cleared - if ( linesToUpdate < _usedLines ) - { - dirtyRegion |= QRect( _leftMargin+tLx , - _topMargin+tLy+_fontHeight*linesToUpdate , - _fontWidth * this->_columns , - _fontHeight * (_usedLines-linesToUpdate) ); - } - _usedLines = linesToUpdate; - - if ( columnsToUpdate < _usedColumns ) - { - dirtyRegion |= QRect( _leftMargin+tLx+columnsToUpdate*_fontWidth , - _topMargin+tLy , - _fontWidth * (_usedColumns-columnsToUpdate) , - _fontHeight * this->_lines ); - } - _usedColumns = columnsToUpdate; - - dirtyRegion |= _inputMethodData.previousPreeditRect; - - // update the parts of the display which have changed - update(dirtyRegion); - - if ( _hasBlinker && !_blinkTimer->isActive()) _blinkTimer->start( BLINK_DELAY ); - if (!_hasBlinker && _blinkTimer->isActive()) { _blinkTimer->stop(); _blinking = false; } - delete[] dirtyMask; - delete[] disstrU; - -} - -void TerminalDisplay::showResizeNotification() -{ - if (_terminalSizeHint && isVisible()) - { - if (_terminalSizeStartup) { - _terminalSizeStartup=false; - return; - } - if (!_resizeWidget) - { - _resizeWidget = new QLabel(("Size: XXX x XXX"), this); - _resizeWidget->setMinimumWidth(_resizeWidget->fontMetrics().width(("Size: XXX x XXX"))); - _resizeWidget->setMinimumHeight(_resizeWidget->sizeHint().height()); - _resizeWidget->setAlignment(Qt::AlignCenter); - - _resizeWidget->setStyleSheet("background-color:palette(window);border-style:solid;border-width:1px;border-color:palette(dark)"); - - _resizeTimer = new QTimer(this); - _resizeTimer->setSingleShot(true); - connect(_resizeTimer, SIGNAL(timeout()), _resizeWidget, SLOT(hide())); - - } - QString sizeStr; - sizeStr.sprintf("Size: %d x %d", _columns, _lines); - _resizeWidget->setText(sizeStr); - _resizeWidget->move((width()-_resizeWidget->width())/2, - (height()-_resizeWidget->height())/2+20); - _resizeWidget->show(); - _resizeTimer->start(1000); - } -} - -void TerminalDisplay::setBlinkingCursor(bool blink) -{ - _hasBlinkingCursor=blink; - - if (blink && !_blinkCursorTimer->isActive()) - _blinkCursorTimer->start(BLINK_DELAY); - - if (!blink && _blinkCursorTimer->isActive()) - { - _blinkCursorTimer->stop(); - if (_cursorBlinking) - blinkCursorEvent(); - else - _cursorBlinking = false; - } -} - -void TerminalDisplay::paintEvent( QPaintEvent* pe ) -{ -//qDebug("%s %d paintEvent", __FILE__, __LINE__); - QPainter paint(this); - - foreach (QRect rect, (pe->region() & contentsRect()).rects()) - { - drawBackground(paint,rect,palette().background().color(), true /* use opacity setting */); - drawContents(paint, rect); - } -// drawBackground(paint,contentsRect(),palette().background().color(), true /* use opacity setting */); -// drawContents(paint, contentsRect()); - drawInputMethodPreeditString(paint,preeditRect()); - paintFilters(paint); - - paint.end(); -} - -QPoint TerminalDisplay::cursorPosition() const -{ - if (_screenWindow) - return _screenWindow->cursorPosition(); - else - return QPoint(0,0); -} - -QRect TerminalDisplay::preeditRect() const -{ - const int preeditLength = string_width(_inputMethodData.preeditString); - - if ( preeditLength == 0 ) - return QRect(); - - return QRect(_leftMargin + _fontWidth*cursorPosition().x(), - _topMargin + _fontHeight*cursorPosition().y(), - _fontWidth*preeditLength, - _fontHeight); -} - -void TerminalDisplay::drawInputMethodPreeditString(QPainter& painter , const QRect& rect) -{ - if ( _inputMethodData.preeditString.isEmpty() ) { - return; - } - const QPoint cursorPos = cursorPosition(); - - bool invertColors = false; - const QColor background = _colorTable[DEFAULT_BACK_COLOR].color; - const QColor foreground = _colorTable[DEFAULT_FORE_COLOR].color; - const Character* style = &_image[loc(cursorPos.x(),cursorPos.y())]; - - drawBackground(painter,rect,background,true); - drawCursor(painter,rect,foreground,background,invertColors); - drawCharacters(painter,rect,_inputMethodData.preeditString,style,invertColors); - - _inputMethodData.previousPreeditRect = rect; -} - -FilterChain* TerminalDisplay::filterChain() const -{ - return _filterChain; -} - -void TerminalDisplay::paintFilters(QPainter& painter) -{ -//qDebug("%s %d paintFilters", __FILE__, __LINE__); - - // get color of character under mouse and use it to draw - // lines for filters - QPoint cursorPos = mapFromGlobal(QCursor::pos()); - int cursorLine; - int cursorColumn; - getCharacterPosition( cursorPos , cursorLine , cursorColumn ); - Character cursorCharacter = _image[loc(cursorColumn,cursorLine)]; - - painter.setPen( QPen(cursorCharacter.foregroundColor.color(colorTable())) ); - - // iterate over hotspots identified by the display's currently active filters - // and draw appropriate visuals to indicate the presence of the hotspot - - QList<Filter::HotSpot*> spots = _filterChain->hotSpots(); - QListIterator<Filter::HotSpot*> iter(spots); - while (iter.hasNext()) - { - Filter::HotSpot* spot = iter.next(); - - for ( int line = spot->startLine() ; line <= spot->endLine() ; line++ ) - { - int startColumn = 0; - int endColumn = _columns-1; // TODO use number of _columns which are actually - // occupied on this line rather than the width of the - // display in _columns - - // ignore whitespace at the end of the lines - while ( QChar(_image[loc(endColumn,line)].character).isSpace() && endColumn > 0 ) - endColumn--; - - // increment here because the column which we want to set 'endColumn' to - // is the first whitespace character at the end of the line - endColumn++; - - if ( line == spot->startLine() ) - startColumn = spot->startColumn(); - if ( line == spot->endLine() ) - endColumn = spot->endColumn(); - - // subtract one pixel from - // the right and bottom so that - // we do not overdraw adjacent - // hotspots - // - // subtracting one pixel from all sides also prevents an edge case where - // moving the mouse outside a link could still leave it underlined - // because the check below for the position of the cursor - // finds it on the border of the target area - QRect r; - r.setCoords( startColumn*_fontWidth + 1, line*_fontHeight + 1, - endColumn*_fontWidth - 1, (line+1)*_fontHeight - 1 ); - - // Underline link hotspots - if ( spot->type() == Filter::HotSpot::Link ) - { - QFontMetrics metrics(font()); - - // find the baseline (which is the invisible line that the characters in the font sit on, - // with some having tails dangling below) - int baseline = r.bottom() - metrics.descent(); - // find the position of the underline below that - int underlinePos = baseline + metrics.underlinePos(); - - if ( r.contains( mapFromGlobal(QCursor::pos()) ) ) - painter.drawLine( r.left() , underlinePos , - r.right() , underlinePos ); - } - // Marker hotspots simply have a transparent rectanglular shape - // drawn on top of them - else if ( spot->type() == Filter::HotSpot::Marker ) - { - //TODO - Do not use a hardcoded colour for this - painter.fillRect(r,QBrush(QColor(255,0,0,120))); - } - } - } -} -void TerminalDisplay::drawContents(QPainter &paint, const QRect &rect) -{ -//qDebug("%s %d drawContents and rect x=%d y=%d w=%d h=%d", __FILE__, __LINE__, rect.x(), rect.y(),rect.width(),rect.height()); - - QPoint tL = contentsRect().topLeft(); -// int tLx = tL.x(); - int tLy = tL.y(); - - int tLx = (_contentWidth - _usedColumns * _fontWidth)/2; -// int tLy = (_contentHeight - _usedLines * _fontHeight)/2; -//qDebug("%d %d %d %d", tLx, tLy, _contentWidth, _usedColumns * _fontWidth); - - int lux = qMin(_usedColumns-1, qMax(0,(rect.left() - tLx - _leftMargin ) / _fontWidth)); - int luy = qMin(_usedLines-1, qMax(0, (rect.top() - tLy - _topMargin ) / _fontHeight)); - int rlx = qMin(_usedColumns-1, qMax(0, (rect.right() - tLx - _leftMargin ) / _fontWidth)); - int rly = qMin(_usedLines-1, qMax(0, (rect.bottom() - tLy - _topMargin ) / _fontHeight)); - - const int bufferSize = _usedColumns; - QChar *disstrU = new QChar[bufferSize]; - for (int y = luy; y <= rly; y++) - { - quint16 c = _image[loc(lux,y)].character; - int x = lux; - if(!c && x) - x--; // Search for start of multi-column character - for (; x <= rlx; x++) - { - int len = 1; - int p = 0; - - // is this a single character or a sequence of characters ? - if ( _image[loc(x,y)].rendition & RE_EXTENDED_CHAR ) - { - // sequence of characters - ushort extendedCharLength = 0; - ushort* chars = ExtendedCharTable::instance - .lookupExtendedChar(_image[loc(x,y)].charSequence,extendedCharLength); - for ( int index = 0 ; index < extendedCharLength ; index++ ) - { - Q_ASSERT( p < bufferSize ); - disstrU[p++] = chars[index]; - } - } - else - { - // single character - c = _image[loc(x,y)].character; - if (c) - { - Q_ASSERT( p < bufferSize ); - disstrU[p++] = c; //fontMap(c); - } - } - - bool lineDraw = isLineChar(c); - bool doubleWidth = (_image[ qMin(loc(x,y)+1,_imageSize) ].character == 0); - CharacterColor currentForeground = _image[loc(x,y)].foregroundColor; - CharacterColor currentBackground = _image[loc(x,y)].backgroundColor; - quint8 currentRendition = _image[loc(x,y)].rendition; - - while (x+len <= rlx && - _image[loc(x+len,y)].foregroundColor == currentForeground && - _image[loc(x+len,y)].backgroundColor == currentBackground && - _image[loc(x+len,y)].rendition == currentRendition && - (_image[ qMin(loc(x+len,y)+1,_imageSize) ].character == 0) == doubleWidth && - isLineChar( c = _image[loc(x+len,y)].character) == lineDraw) // Assignment! - { - if (c) - disstrU[p++] = c; //fontMap(c); - if (doubleWidth) // assert((_image[loc(x+len,y)+1].character == 0)), see above if condition - len++; // Skip trailing part of multi-column character - len++; - } - if ((x+len < _usedColumns) && (!_image[loc(x+len,y)].character)) - len++; // Adjust for trailing part of multi-column character - - bool save__fixedFont = _fixedFont; - if (lineDraw) - _fixedFont = false; - if (doubleWidth) - _fixedFont = false; - QString unistr(disstrU,p); - - if (y < _lineProperties.size()) - { - if (_lineProperties[y] & LINE_DOUBLEWIDTH) { - paint.scale(2,1); - } - - if (_lineProperties[y] & LINE_DOUBLEHEIGHT) { - paint.scale(1,2); - } - } - - //calculate the area in which the text will be drawn - QRect textArea = QRect( _leftMargin+tLx+_fontWidth*x , - _topMargin+tLy+_fontHeight*y , - _fontWidth*len, - _fontHeight); - - //move the calculated area to take account of scaling applied to the painter. - //the position of the area from the origin (0,0) is scaled - //by the opposite of whatever - //transformation has been applied to the painter. this ensures that - //painting does actually start from textArea.topLeft() - //(instead of textArea.topLeft() * painter-scale) - QMatrix inverted = paint.matrix().inverted(); -// textArea.moveTopLeft( inverted.map(textArea.topLeft()) ); - textArea.moveCenter( inverted.map(textArea.center()) ); - - - //paint text fragment - drawTextFragment( paint, - textArea, - unistr, - &_image[loc(x,y)] ); //, - //0, - //!_isPrinting ); - - _fixedFont = save__fixedFont; - - //reset back to single-width, single-height _lines - paint.resetMatrix(); - - if (y < _lineProperties.size()-1) - { - //double-height _lines are represented by two adjacent _lines - //containing the same characters - //both _lines will have the LINE_DOUBLEHEIGHT attribute. - //If the current line has the LINE_DOUBLEHEIGHT attribute, - //we can therefore skip the next line - if (_lineProperties[y] & LINE_DOUBLEHEIGHT) - y++; - } - - x += len - 1; - } - } - delete [] disstrU; -} - -void TerminalDisplay::blinkEvent() -{ - _blinking = !_blinking; - - //TODO: Optimise to only repaint the areas of the widget - // where there is blinking text - // rather than repainting the whole widget. - update(); -} - -QRect TerminalDisplay::imageToWidget(const QRect& imageArea) const -{ -//qDebug("%s %d imageToWidget", __FILE__, __LINE__); - QRect result; - result.setLeft( _leftMargin + _fontWidth * imageArea.left() ); - result.setTop( _topMargin + _fontHeight * imageArea.top() ); - result.setWidth( _fontWidth * imageArea.width() ); - result.setHeight( _fontHeight * imageArea.height() ); - - return result; -} - -void TerminalDisplay::blinkCursorEvent() -{ - _cursorBlinking = !_cursorBlinking; - - QRect cursorRect = imageToWidget( QRect(cursorPosition(),QSize(1,1)) ); - - update(cursorRect); -} - -/* ------------------------------------------------------------------------- */ -/* */ -/* Resizing */ -/* */ -/* ------------------------------------------------------------------------- */ - -void TerminalDisplay::resizeEvent(QResizeEvent*) -{ - updateImageSize(); -} - -void TerminalDisplay::propagateSize() -{ - if (_isFixedSize) - { - setSize(_columns, _lines); - QWidget::setFixedSize(sizeHint()); - parentWidget()->adjustSize(); - parentWidget()->setFixedSize(parentWidget()->sizeHint()); - return; - } - if (_image) - updateImageSize(); -} - -void TerminalDisplay::updateImageSize() -{ -//qDebug("%s %d updateImageSize", __FILE__, __LINE__); - Character* oldimg = _image; - int oldlin = _lines; - int oldcol = _columns; - - makeImage(); - - - // copy the old image to reduce flicker - int lines = qMin(oldlin,_lines); - int columns = qMin(oldcol,_columns); - - if (oldimg) - { - for (int line = 0; line < lines; line++) - { - memcpy((void*)&_image[_columns*line], - (void*)&oldimg[oldcol*line],columns*sizeof(Character)); - } - delete[] oldimg; - } - - if (_screenWindow) - _screenWindow->setWindowLines(_lines); - - _resizing = (oldlin!=_lines) || (oldcol!=_columns); - - if ( _resizing ) - { - showResizeNotification(); - emit changedContentSizeSignal(_contentHeight, _contentWidth); // expose resizeEvent - } - - _resizing = false; -} - -//showEvent and hideEvent are reimplemented here so that it appears to other classes that the -//display has been resized when the display is hidden or shown. -// -//this allows -//TODO: Perhaps it would be better to have separate signals for show and hide instead of using -//the same signal as the one for a content size change -void TerminalDisplay::showEvent(QShowEvent*) -{ - emit changedContentSizeSignal(_contentHeight,_contentWidth); -} -void TerminalDisplay::hideEvent(QHideEvent*) -{ - emit changedContentSizeSignal(_contentHeight,_contentWidth); -} - -/* ------------------------------------------------------------------------- */ -/* */ -/* Scrollbar */ -/* */ -/* ------------------------------------------------------------------------- */ - -void TerminalDisplay::scrollBarPositionChanged(int) -{ - if ( !_screenWindow ) - return; - - _screenWindow->scrollTo( _scrollBar->value() ); - - // if the thumb has been moved to the bottom of the _scrollBar then set - // the display to automatically track new output, - // that is, scroll down automatically - // to how new _lines as they are added - const bool atEndOfOutput = (_scrollBar->value() == _scrollBar->maximum()); - _screenWindow->setTrackOutput( atEndOfOutput ); - - updateImage(); -} - -void TerminalDisplay::setScroll(int cursor, int slines) -{ -//qDebug("%s %d setScroll", __FILE__, __LINE__); - // update _scrollBar if the range or value has changed, - // otherwise return - // - // setting the range or value of a _scrollBar will always trigger - // a repaint, so it should be avoided if it is not necessary - if ( _scrollBar->minimum() == 0 && - _scrollBar->maximum() == (slines - _lines) && - _scrollBar->value() == cursor ) - { - return; - } - - disconnect(_scrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollBarPositionChanged(int))); - _scrollBar->setRange(0,slines - _lines); - _scrollBar->setSingleStep(1); - _scrollBar->setPageStep(_lines); - _scrollBar->setValue(cursor); - connect(_scrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollBarPositionChanged(int))); -} - -void TerminalDisplay::setScrollBarPosition(ScrollBarPosition position) -{ - if (_scrollbarLocation == position) { -// return; - } - - if ( position == NoScrollBar ) - _scrollBar->hide(); - else - _scrollBar->show(); - - _topMargin = _leftMargin = 1; - _scrollbarLocation = position; - - propagateSize(); - update(); -} - -void TerminalDisplay::mousePressEvent(QMouseEvent* ev) -{ - if ( _possibleTripleClick && (ev->button()==Qt::LeftButton) ) { - mouseTripleClickEvent(ev); - return; - } - - if ( !contentsRect().contains(ev->pos()) ) return; - - if ( !_screenWindow ) return; - - int charLine; - int charColumn; - getCharacterPosition(ev->pos(),charLine,charColumn); - QPoint pos = QPoint(charColumn,charLine); - - if ( ev->button() == Qt::LeftButton) - { - _lineSelectionMode = false; - _wordSelectionMode = false; - - emit isBusySelecting(true); // Keep it steady... - // Drag only when the Control key is hold - bool selected = false; - - // The receiver of the testIsSelected() signal will adjust - // 'selected' accordingly. - //emit testIsSelected(pos.x(), pos.y(), selected); - - selected = _screenWindow->isSelected(pos.x(),pos.y()); - - if ((!_ctrlDrag || ev->modifiers() & Qt::ControlModifier) && selected ) { - // The user clicked inside selected text - dragInfo.state = diPending; - dragInfo.start = ev->pos(); - } - else { - // No reason to ever start a drag event - dragInfo.state = diNone; - - _preserveLineBreaks = !( ( ev->modifiers() & Qt::ControlModifier ) && !(ev->modifiers() & Qt::AltModifier) ); - _columnSelectionMode = (ev->modifiers() & Qt::AltModifier) && (ev->modifiers() & Qt::ControlModifier); - - if (_mouseMarks || (ev->modifiers() & Qt::ShiftModifier)) - { - _screenWindow->clearSelection(); - - //emit clearSelectionSignal(); - pos.ry() += _scrollBar->value(); - _iPntSel = _pntSel = pos; - _actSel = 1; // left mouse button pressed but nothing selected yet. - - } - else - { - emit mouseSignal( 0, charColumn + 1, charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , 0); - } - } - } - else if ( ev->button() == Qt::MidButton ) - { - if ( _mouseMarks || (!_mouseMarks && (ev->modifiers() & Qt::ShiftModifier)) ) - emitSelection(true,ev->modifiers() & Qt::ControlModifier); - else - emit mouseSignal( 1, charColumn +1, charLine +1 +_scrollBar->value() -_scrollBar->maximum() , 0); - } - else if ( ev->button() == Qt::RightButton ) - { - if (_mouseMarks || (ev->modifiers() & Qt::ShiftModifier)) - { - emit configureRequest( this, - ev->modifiers() & (Qt::ShiftModifier|Qt::ControlModifier), - ev->pos() - ); - } - else - emit mouseSignal( 2, charColumn +1, charLine +1 +_scrollBar->value() -_scrollBar->maximum() , 0); - } -} - -QList<QAction*> TerminalDisplay::filterActions(const QPoint& position) -{ - int charLine, charColumn; - getCharacterPosition(position,charLine,charColumn); - - Filter::HotSpot* spot = _filterChain->hotSpotAt(charLine,charColumn); - - return spot ? spot->actions() : QList<QAction*>(); -} - -void TerminalDisplay::mouseMoveEvent(QMouseEvent* ev) -{ - int charLine = 0; - int charColumn = 0; - - getCharacterPosition(ev->pos(),charLine,charColumn); - - // handle filters - // change link hot-spot appearance on mouse-over - Filter::HotSpot* spot = _filterChain->hotSpotAt(charLine,charColumn); - if ( spot && spot->type() == Filter::HotSpot::Link) - { - QRect previousHotspotArea = _mouseOverHotspotArea; - _mouseOverHotspotArea.setCoords( qMin(spot->startColumn() , spot->endColumn()) * _fontWidth, - spot->startLine() * _fontHeight, - qMax(spot->startColumn() , spot->endColumn()) * _fontHeight, - (spot->endLine()+1) * _fontHeight ); - - // display tooltips when mousing over links - // TODO: Extend this to work with filter types other than links - const QString& tooltip = spot->tooltip(); - if ( !tooltip.isEmpty() ) - { - QToolTip::showText( mapToGlobal(ev->pos()) , tooltip , this , _mouseOverHotspotArea ); - } - - update( _mouseOverHotspotArea | previousHotspotArea ); - } - else if ( _mouseOverHotspotArea.isValid() ) - { - update( _mouseOverHotspotArea ); - // set hotspot area to an invalid rectangle - _mouseOverHotspotArea = QRect(); - } - - // for auto-hiding the cursor, we need mouseTracking - if (ev->buttons() == Qt::NoButton ) return; - - // if the terminal is interested in mouse movements - // then emit a mouse movement signal, unless the shift - // key is being held down, which overrides this. - if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) - { - int button = 3; - if (ev->buttons() & Qt::LeftButton) - button = 0; - if (ev->buttons() & Qt::MidButton) - button = 1; - if (ev->buttons() & Qt::RightButton) - button = 2; - - - emit mouseSignal( button, - charColumn + 1, - charLine + 1 +_scrollBar->value() -_scrollBar->maximum(), - 1 ); - - return; - } - - if (dragInfo.state == diPending) - { - // we had a mouse down, but haven't confirmed a drag yet - // if the mouse has moved sufficiently, we will confirm - - int distance = 10; //KGlobalSettings::dndEventDelay(); - if ( ev->x() > dragInfo.start.x() + distance || ev->x() < dragInfo.start.x() - distance || - ev->y() > dragInfo.start.y() + distance || ev->y() < dragInfo.start.y() - distance) - { - // we've left the drag square, we can start a real drag operation now - emit isBusySelecting(false); // Ok.. we can breath again. - - _screenWindow->clearSelection(); - doDrag(); - } - return; - } - else if (dragInfo.state == diDragging) - { - // this isn't technically needed because mouseMoveEvent is suppressed during - // Qt drag operations, replaced by dragMoveEvent - return; - } - - if (_actSel == 0) return; - - // don't extend selection while pasting - if (ev->buttons() & Qt::MidButton) return; - - extendSelection( ev->pos() ); -} - -#if 0 -void TerminalDisplay::setSelectionEnd() -{ - extendSelection( _configureRequestPoint ); -} -#endif - -void TerminalDisplay::extendSelection( const QPoint& position ) -{ - QPoint pos = position; - - if ( !_screenWindow ) - return; - - //if ( !contentsRect().contains(ev->pos()) ) return; - QPoint tL = contentsRect().topLeft(); - int tLx = tL.x(); - int tLy = tL.y(); - int scroll = _scrollBar->value(); - - // we're in the process of moving the mouse with the left button pressed - // the mouse cursor will kept caught within the bounds of the text in - // this widget. - - // Adjust position within text area bounds. See FIXME above. - QPoint oldpos = pos; - if ( pos.x() < tLx+_leftMargin ) - pos.setX( tLx+_leftMargin ); - if ( pos.x() > tLx+_leftMargin+_usedColumns*_fontWidth-1 ) - pos.setX( tLx+_leftMargin+_usedColumns*_fontWidth ); - if ( pos.y() < tLy+_topMargin ) - pos.setY( tLy+_topMargin ); - if ( pos.y() > tLy+_topMargin+_usedLines*_fontHeight-1 ) - pos.setY( tLy+_topMargin+_usedLines*_fontHeight-1 ); - - if ( pos.y() == tLy+_topMargin+_usedLines*_fontHeight-1 ) - { - _scrollBar->setValue(_scrollBar->value()+yMouseScroll); // scrollforward - } - if ( pos.y() == tLy+_topMargin ) - { - _scrollBar->setValue(_scrollBar->value()-yMouseScroll); // scrollback - } - - int charColumn = 0; - int charLine = 0; - getCharacterPosition(pos,charLine,charColumn); - - QPoint here = QPoint(charColumn,charLine); //QPoint((pos.x()-tLx-_leftMargin+(_fontWidth/2))/_fontWidth,(pos.y()-tLy-_topMargin)/_fontHeight); - QPoint ohere; - QPoint _iPntSelCorr = _iPntSel; - _iPntSelCorr.ry() -= _scrollBar->value(); - QPoint _pntSelCorr = _pntSel; - _pntSelCorr.ry() -= _scrollBar->value(); - bool swapping = false; - - if ( _wordSelectionMode ) - { - // Extend to word boundaries - int i; - int selClass; - - bool left_not_right = ( here.y() < _iPntSelCorr.y() || - here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x() ); - bool old_left_not_right = ( _pntSelCorr.y() < _iPntSelCorr.y() || - _pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x() ); - swapping = left_not_right != old_left_not_right; - - // Find left (left_not_right ? from here : from start) - QPoint left = left_not_right ? here : _iPntSelCorr; - i = loc(left.x(),left.y()); - if (i>=0 && i<=_imageSize) { - selClass = charClass(_image[i].character); - while ( ((left.x()>0) || (left.y()>0 && (_lineProperties[left.y()-1] & LINE_WRAPPED) )) - && charClass(_image[i-1].character) == selClass ) - { i--; if (left.x()>0) left.rx()--; else {left.rx()=_usedColumns-1; left.ry()--;} } - } - - // Find left (left_not_right ? from start : from here) - QPoint right = left_not_right ? _iPntSelCorr : here; - i = loc(right.x(),right.y()); - if (i>=0 && i<=_imageSize) { - selClass = charClass(_image[i].character); - while( ((right.x()<_usedColumns-1) || (right.y()<_usedLines-1 && (_lineProperties[right.y()] & LINE_WRAPPED) )) - && charClass(_image[i+1].character) == selClass ) - { i++; if (right.x()<_usedColumns-1) right.rx()++; else {right.rx()=0; right.ry()++; } } - } - - // Pick which is start (ohere) and which is extension (here) - if ( left_not_right ) - { - here = left; ohere = right; - } - else - { - here = right; ohere = left; - } - ohere.rx()++; - } - - if ( _lineSelectionMode ) - { - // Extend to complete line - bool above_not_below = ( here.y() < _iPntSelCorr.y() ); - - QPoint above = above_not_below ? here : _iPntSelCorr; - QPoint below = above_not_below ? _iPntSelCorr : here; - - while (above.y()>0 && (_lineProperties[above.y()-1] & LINE_WRAPPED) ) - above.ry()--; - while (below.y()<_usedLines-1 && (_lineProperties[below.y()] & LINE_WRAPPED) ) - below.ry()++; - - above.setX(0); - below.setX(_usedColumns-1); - - // Pick which is start (ohere) and which is extension (here) - if ( above_not_below ) - { - here = above; ohere = below; - } - else - { - here = below; ohere = above; - } - - QPoint newSelBegin = QPoint( ohere.x(), ohere.y() ); - swapping = !(_tripleSelBegin==newSelBegin); - _tripleSelBegin = newSelBegin; - - ohere.rx()++; - } - - int offset = 0; - if ( !_wordSelectionMode && !_lineSelectionMode ) - { - int i; - int selClass; - - bool left_not_right = ( here.y() < _iPntSelCorr.y() || - here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x() ); - bool old_left_not_right = ( _pntSelCorr.y() < _iPntSelCorr.y() || - _pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x() ); - swapping = left_not_right != old_left_not_right; - - // Find left (left_not_right ? from here : from start) - QPoint left = left_not_right ? here : _iPntSelCorr; - - // Find left (left_not_right ? from start : from here) - QPoint right = left_not_right ? _iPntSelCorr : here; - if ( right.x() > 0 && !_columnSelectionMode ) - { - i = loc(right.x(),right.y()); - if (i>=0 && i<=_imageSize) { - selClass = charClass(_image[i-1].character); - if (selClass == ' ') - { - while ( right.x() < _usedColumns-1 && charClass(_image[i+1].character) == selClass && (right.y()<_usedLines-1) && - !(_lineProperties[right.y()] & LINE_WRAPPED)) - { i++; right.rx()++; } - if (right.x() < _usedColumns-1) - right = left_not_right ? _iPntSelCorr : here; - else - right.rx()++; // will be balanced later because of offset=-1; - } - } - } - - // Pick which is start (ohere) and which is extension (here) - if ( left_not_right ) - { - here = left; ohere = right; offset = 0; - } - else - { - here = right; ohere = left; offset = -1; - } - } - - if ((here == _pntSelCorr) && (scroll == _scrollBar->value())) return; // not moved - - if (here == ohere) return; // It's not left, it's not right. - - if ( _actSel < 2 || swapping ) - { - if ( _columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode ) - { - _screenWindow->setSelectionStart( ohere.x() , ohere.y() , true ); - } - else - { - _screenWindow->setSelectionStart( ohere.x()-1-offset , ohere.y() , false ); - } - - } - - _actSel = 2; // within selection - _pntSel = here; - _pntSel.ry() += _scrollBar->value(); - - if ( _columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode ) - { - _screenWindow->setSelectionEnd( here.x() , here.y() ); - } - else - { - _screenWindow->setSelectionEnd( here.x()+offset , here.y() ); - } - -} - -void TerminalDisplay::mouseReleaseEvent(QMouseEvent* ev) -{ - if ( !_screenWindow ) - return; - - int charLine; - int charColumn; - getCharacterPosition(ev->pos(),charLine,charColumn); - - if ( ev->button() == Qt::LeftButton) - { - emit isBusySelecting(false); - if(dragInfo.state == diPending) - { - // We had a drag event pending but never confirmed. Kill selection - _screenWindow->clearSelection(); - //emit clearSelectionSignal(); - } - else - { - if ( _actSel > 1 ) - { - setSelection( _screenWindow->selectedText(_preserveLineBreaks) ); - } - - _actSel = 0; - - //FIXME: emits a release event even if the mouse is - // outside the range. The procedure used in `mouseMoveEvent' - // applies here, too. - - if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) - emit mouseSignal( 3, // release - charColumn + 1, - charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , 0); - } - dragInfo.state = diNone; - } - - - if ( !_mouseMarks && - ((ev->button() == Qt::RightButton && !(ev->modifiers() & Qt::ShiftModifier)) - || ev->button() == Qt::MidButton) ) - { - emit mouseSignal( 3, - charColumn + 1, - charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , - 0); - } -} - -void TerminalDisplay::getCharacterPosition(const QPoint& widgetPoint,int& line,int& column) const -{ - - column = (widgetPoint.x() + _fontWidth/2 -contentsRect().left()-_leftMargin) / _fontWidth; - line = (widgetPoint.y()-contentsRect().top()-_topMargin) / _fontHeight; - - if ( line < 0 ) - line = 0; - if ( column < 0 ) - column = 0; - - if ( line >= _usedLines ) - line = _usedLines-1; - - // the column value returned can be equal to _usedColumns, which - // is the position just after the last character displayed in a line. - // - // this is required so that the user can select characters in the right-most - // column (or left-most for right-to-left input) - if ( column > _usedColumns ) - column = _usedColumns; -} - -void TerminalDisplay::updateLineProperties() -{ - if ( !_screenWindow ) - return; - - _lineProperties = _screenWindow->getLineProperties(); -} - -void TerminalDisplay::mouseDoubleClickEvent(QMouseEvent* ev) -{ - if ( ev->button() != Qt::LeftButton) return; - if ( !_screenWindow ) return; - - int charLine = 0; - int charColumn = 0; - - getCharacterPosition(ev->pos(),charLine,charColumn); - - QPoint pos(charColumn,charLine); - - // pass on double click as two clicks. - if (!_mouseMarks && !(ev->modifiers() & Qt::ShiftModifier)) - { - // Send just _ONE_ click event, since the first click of the double click - // was already sent by the click handler - emit mouseSignal( 0, - pos.x()+1, - pos.y()+1 +_scrollBar->value() -_scrollBar->maximum(), - 0 ); // left button - return; - } - - _screenWindow->clearSelection(); - QPoint bgnSel = pos; - QPoint endSel = pos; - int i = loc(bgnSel.x(),bgnSel.y()); - _iPntSel = bgnSel; - _iPntSel.ry() += _scrollBar->value(); - - _wordSelectionMode = true; - - // find word boundaries... - int selClass = charClass(_image[i].character); - { - // find the start of the word - int x = bgnSel.x(); - while ( ((x>0) || (bgnSel.y()>0 && (_lineProperties[bgnSel.y()-1] & LINE_WRAPPED) )) - && charClass(_image[i-1].character) == selClass ) - { - i--; - if (x>0) - x--; - else - { - x=_usedColumns-1; - bgnSel.ry()--; - } - } - - bgnSel.setX(x); - _screenWindow->setSelectionStart( bgnSel.x() , bgnSel.y() , false ); - - // find the end of the word - i = loc( endSel.x(), endSel.y() ); - x = endSel.x(); - while( ((x<_usedColumns-1) || (endSel.y()<_usedLines-1 && (_lineProperties[endSel.y()] & LINE_WRAPPED) )) - && charClass(_image[i+1].character) == selClass ) - { - i++; - if (x<_usedColumns-1) - x++; - else - { - x=0; - endSel.ry()++; - } - } - - endSel.setX(x); - - // In word selection mode don't select @ (64) if at end of word. - if ( ( QChar( _image[i].character ) == '@' ) && ( ( endSel.x() - bgnSel.x() ) > 0 ) ) - endSel.setX( x - 1 ); - - - _actSel = 2; // within selection - - _screenWindow->setSelectionEnd( endSel.x() , endSel.y() ); - - setSelection( _screenWindow->selectedText(_preserveLineBreaks) ); - } - - _possibleTripleClick=true; - - QTimer::singleShot(QApplication::doubleClickInterval(),this, - SLOT(tripleClickTimeout())); -} - -void TerminalDisplay::wheelEvent( QWheelEvent* ev ) -{ - if (ev->orientation() != Qt::Vertical) - return; - - if ( _mouseMarks ) - _scrollBar->event(ev); - else - { - int charLine; - int charColumn; - getCharacterPosition( ev->pos() , charLine , charColumn ); - - emit mouseSignal( ev->delta() > 0 ? 4 : 5, - charColumn + 1, - charLine + 1 +_scrollBar->value() -_scrollBar->maximum() , - 0); - } -} - -void TerminalDisplay::tripleClickTimeout() -{ - _possibleTripleClick=false; -} - -void TerminalDisplay::mouseTripleClickEvent(QMouseEvent* ev) -{ - if ( !_screenWindow ) return; - - int charLine; - int charColumn; - getCharacterPosition(ev->pos(),charLine,charColumn); - _iPntSel = QPoint(charColumn,charLine); - - _screenWindow->clearSelection(); - - _lineSelectionMode = true; - _wordSelectionMode = false; - - _actSel = 2; // within selection - emit isBusySelecting(true); // Keep it steady... - - while (_iPntSel.y()>0 && (_lineProperties[_iPntSel.y()-1] & LINE_WRAPPED) ) - _iPntSel.ry()--; - - if (_tripleClickMode == SelectForwardsFromCursor) { - // find word boundary start - int i = loc(_iPntSel.x(),_iPntSel.y()); - int selClass = charClass(_image[i].character); - int x = _iPntSel.x(); - - while ( ((x>0) || - (_iPntSel.y()>0 && (_lineProperties[_iPntSel.y()-1] & LINE_WRAPPED) ) - ) - && charClass(_image[i-1].character) == selClass ) - { - i--; - if (x>0) - x--; - else - { - x=_columns-1; - _iPntSel.ry()--; - } - } - - _screenWindow->setSelectionStart( x , _iPntSel.y() , false ); - _tripleSelBegin = QPoint( x, _iPntSel.y() ); - } - else if (_tripleClickMode == SelectWholeLine) { - _screenWindow->setSelectionStart( 0 , _iPntSel.y() , false ); - _tripleSelBegin = QPoint( 0, _iPntSel.y() ); - } - - while (_iPntSel.y()<_lines-1 && (_lineProperties[_iPntSel.y()] & LINE_WRAPPED) ) - _iPntSel.ry()++; - - _screenWindow->setSelectionEnd( _columns - 1 , _iPntSel.y() ); - - setSelection(_screenWindow->selectedText(_preserveLineBreaks)); - - _iPntSel.ry() += _scrollBar->value(); -} - - -bool TerminalDisplay::focusNextPrevChild( bool next ) -{ - if (next) - return false; // This disables changing the active part in konqueror - // when pressing Tab - return QWidget::focusNextPrevChild( next ); -} - - -int TerminalDisplay::charClass(quint16 ch) const -{ - QChar qch=QChar(ch); - if ( qch.isSpace() ) return ' '; - - if ( qch.isLetterOrNumber() || _wordCharacters.contains(qch, Qt::CaseInsensitive ) ) - return 'a'; - - // Everything else is weird - return 1; -} - -void TerminalDisplay::setWordCharacters(const QString& wc) -{ - _wordCharacters = wc; -} - -void TerminalDisplay::setUsesMouse(bool on) -{ - _mouseMarks = on; - setCursor( _mouseMarks ? Qt::IBeamCursor : Qt::ArrowCursor ); -} -bool TerminalDisplay::usesMouse() const -{ - return _mouseMarks; -} - -/* ------------------------------------------------------------------------- */ -/* */ -/* Clipboard */ -/* */ -/* ------------------------------------------------------------------------- */ - -#undef KeyPress - -void TerminalDisplay::emitSelection(bool useXselection,bool appendReturn) -{ - if ( !_screenWindow ) - return; - - // Paste Clipboard by simulating keypress events - QString text = QApplication::clipboard()->text(useXselection ? QClipboard::Selection : - QClipboard::Clipboard); - if(appendReturn) - text.append("\r"); - if ( ! text.isEmpty() ) - { - text.replace("\n", "\r"); - QKeyEvent e(QEvent::KeyPress, 0, Qt::NoModifier, text); - emit keyPressedSignal(&e); // expose as a big fat keypress event - - _screenWindow->clearSelection(); - } -} - -void TerminalDisplay::setSelection(const QString& t) -{ - QApplication::clipboard()->setText(t, QClipboard::Selection); -} - -void TerminalDisplay::copyClipboard() -{ - if ( !_screenWindow ) - return; - - QString text = _screenWindow->selectedText(_preserveLineBreaks); - QApplication::clipboard()->setText(text); -} - -void TerminalDisplay::pasteClipboard() -{ - emitSelection(false,false); -} - -void TerminalDisplay::pasteSelection() -{ - emitSelection(true,false); -} - -/* ------------------------------------------------------------------------- */ -/* */ -/* Keyboard */ -/* */ -/* ------------------------------------------------------------------------- */ - -void TerminalDisplay::setFlowControlWarningEnabled( bool enable ) -{ - _flowControlWarningEnabled = enable; - - // if the dialog is currently visible and the flow control warning has - // been disabled then hide the dialog - if (!enable) - outputSuspended(false); -} - -void TerminalDisplay::keyPressEvent( QKeyEvent* event ) -{ -//qDebug("%s %d keyPressEvent and key is %d", __FILE__, __LINE__, event->key()); - - bool emitKeyPressSignal = true; - - // XonXoff flow control - if (event->modifiers() & Qt::ControlModifier && _flowControlWarningEnabled) - { - if ( event->key() == Qt::Key_S ) { - //qDebug("%s %d keyPressEvent, output suspended", __FILE__, __LINE__); - emit flowControlKeyPressed(true /*output suspended*/); - } - else if ( event->key() == Qt::Key_Q ) { - //qDebug("%s %d keyPressEvent, output enabled", __FILE__, __LINE__); - emit flowControlKeyPressed(false /*output enabled*/); - } - } - - // Keyboard-based navigation - if ( event->modifiers() == Qt::ShiftModifier ) - { - bool update = true; - - if ( event->key() == Qt::Key_PageUp ) - { - //qDebug("%s %d pageup", __FILE__, __LINE__); - _screenWindow->scrollBy( ScreenWindow::ScrollPages , -1 ); - } - else if ( event->key() == Qt::Key_PageDown ) - { - //qDebug("%s %d pagedown", __FILE__, __LINE__); - _screenWindow->scrollBy( ScreenWindow::ScrollPages , 1 ); - } - else if ( event->key() == Qt::Key_Up ) - { - //qDebug("%s %d keyup", __FILE__, __LINE__); - _screenWindow->scrollBy( ScreenWindow::ScrollLines , -1 ); - } - else if ( event->key() == Qt::Key_Down ) - { - //qDebug("%s %d keydown", __FILE__, __LINE__); - _screenWindow->scrollBy( ScreenWindow::ScrollLines , 1 ); - } - else { - update = false; - } - - if ( update ) - { - //qDebug("%s %d updating", __FILE__, __LINE__); - _screenWindow->setTrackOutput( _screenWindow->atEndOfOutput() ); - - updateLineProperties(); - updateImage(); - - // do not send key press to terminal - emitKeyPressSignal = false; - } - } - - _screenWindow->setTrackOutput( true ); - - _actSel=0; // Key stroke implies a screen update, so TerminalDisplay won't - // know where the current selection is. - - if (_hasBlinkingCursor) - { - _blinkCursorTimer->start(BLINK_DELAY); - if (_cursorBlinking) - blinkCursorEvent(); - else - _cursorBlinking = false; - } - - if ( emitKeyPressSignal ) - emit keyPressedSignal(event); - - event->accept(); -} - -void TerminalDisplay::inputMethodEvent( QInputMethodEvent* event ) -{ - QKeyEvent keyEvent(QEvent::KeyPress,0,Qt::NoModifier,event->commitString()); - emit keyPressedSignal(&keyEvent); - - _inputMethodData.preeditString = event->preeditString(); - update(preeditRect() | _inputMethodData.previousPreeditRect); - - event->accept(); -} -QVariant TerminalDisplay::inputMethodQuery( Qt::InputMethodQuery query ) const -{ - const QPoint cursorPos = _screenWindow ? _screenWindow->cursorPosition() : QPoint(0,0); - switch ( query ) - { - case Qt::ImMicroFocus: - return imageToWidget(QRect(cursorPos.x(),cursorPos.y(),1,1)); - break; - case Qt::ImFont: - return font(); - break; - case Qt::ImCursorPosition: - // return the cursor position within the current line - return cursorPos.x(); - break; - case Qt::ImSurroundingText: - { - // return the text from the current line - QString lineText; - QTextStream stream(&lineText); - PlainTextDecoder decoder; - decoder.begin(&stream); - decoder.decodeLine(&_image[loc(0,cursorPos.y())],_usedColumns,_lineProperties[cursorPos.y()]); - decoder.end(); - return lineText; - } - break; - case Qt::ImCurrentSelection: - return QString(); - break; - } - - return QVariant(); -} - -bool TerminalDisplay::event( QEvent *e ) -{ - if ( e->type() == QEvent::ShortcutOverride ) - { - QKeyEvent* keyEvent = static_cast<QKeyEvent *>( e ); - - // a check to see if keyEvent->text() is empty is used - // to avoid intercepting the press of the modifier key on its own. - // - // this is important as it allows a press and release of the Alt key - // on its own to focus the menu bar, making it possible to - // work with the menu without using the mouse - if ( (keyEvent->modifiers() == Qt::AltModifier) && - !keyEvent->text().isEmpty() ) - { - keyEvent->accept(); - return true; - } - - // Override any of the following shortcuts because - // they are needed by the terminal - int keyCode = keyEvent->key() | keyEvent->modifiers(); - switch ( keyCode ) - { - // list is taken from the QLineEdit::event() code - case Qt::Key_Tab: - case Qt::Key_Delete: - case Qt::Key_Home: - case Qt::Key_End: - case Qt::Key_Backspace: - case Qt::Key_Left: - case Qt::Key_Right: - keyEvent->accept(); - return true; - } - } - return QWidget::event( e ); -} - -void TerminalDisplay::setBellMode(int mode) -{ - _bellMode=mode; -} - -void TerminalDisplay::enableBell() -{ - _allowBell = true; -} - -void TerminalDisplay::bell(const QString&) -{ - if (_bellMode==NoBell) return; - - //limit the rate at which bells can occur - //...mainly for sound effects where rapid bells in sequence - //produce a horrible noise - if ( _allowBell ) - { - _allowBell = false; - QTimer::singleShot(500,this,SLOT(enableBell())); - - if (_bellMode==SystemBeepBell) - { -// KNotification::beep(); - } - else if (_bellMode==NotifyBell) - { -// KNotification::event("BellVisible", message,QPixmap(),this); - } - else if (_bellMode==VisualBell) - { - swapColorTable(); - QTimer::singleShot(200,this,SLOT(swapColorTable())); - } - } -} - -void TerminalDisplay::swapColorTable() -{ - ColorEntry color = _colorTable[1]; - _colorTable[1]=_colorTable[0]; - _colorTable[0]= color; - _colorsInverted = !_colorsInverted; - update(); -} - -void TerminalDisplay::clearImage() -{ - // We initialize _image[_imageSize] too. See makeImage() - for (int i = 0; i <= _imageSize; i++) - { - _image[i].character = ' '; - _image[i].foregroundColor = CharacterColor(COLOR_SPACE_DEFAULT, - DEFAULT_FORE_COLOR); - _image[i].backgroundColor = CharacterColor(COLOR_SPACE_DEFAULT, - DEFAULT_BACK_COLOR); - _image[i].rendition = DEFAULT_RENDITION; - } -} - -void TerminalDisplay::calcGeometry() -{ - _scrollBar->resize(QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent), - contentsRect().height()); - switch(_scrollbarLocation) - { - case NoScrollBar : - _leftMargin = DEFAULT_LEFT_MARGIN; - _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN; - break; - case ScrollBarLeft : - _leftMargin = DEFAULT_LEFT_MARGIN + _scrollBar->width(); - _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN - _scrollBar->width(); - _scrollBar->move(contentsRect().topLeft()); - break; - case ScrollBarRight: - _leftMargin = DEFAULT_LEFT_MARGIN; - _contentWidth = contentsRect().width() - 2 * DEFAULT_LEFT_MARGIN - _scrollBar->width(); - _scrollBar->move(contentsRect().topRight() - QPoint(_scrollBar->width()-1,0)); - break; - } - - _topMargin = DEFAULT_TOP_MARGIN; - _contentHeight = contentsRect().height() - 2 * DEFAULT_TOP_MARGIN + /* mysterious */ 1; - - if (!_isFixedSize) - { - // ensure that display is always at least one column wide - _columns = qMax(1,_contentWidth / _fontWidth); - _usedColumns = qMin(_usedColumns,_columns); - - // ensure that display is always at least one line high - _lines = qMax(1,_contentHeight / _fontHeight); - _usedLines = qMin(_usedLines,_lines); - } -} - -void TerminalDisplay::makeImage() -{ -//qDebug("%s %d makeImage", __FILE__, __LINE__); - calcGeometry(); - - // confirm that array will be of non-zero size, since the painting code - // assumes a non-zero array length - Q_ASSERT( _lines > 0 && _columns > 0 ); - Q_ASSERT( _usedLines <= _lines && _usedColumns <= _columns ); - - _imageSize=_lines*_columns; - - // We over-commit one character so that we can be more relaxed in dealing with - // certain boundary conditions: _image[_imageSize] is a valid but unused position - _image = new Character[_imageSize+1]; - - clearImage(); -} - -// calculate the needed size -void TerminalDisplay::setSize(int columns, int lines) -{ - //FIXME - Not quite correct, a small amount of additional space - // will be used for margins, the scrollbar etc. - // we need to allow for this so that '_size' does allow - // enough room for the specified number of columns and lines to fit - - QSize newSize = QSize( columns * _fontWidth , - lines * _fontHeight ); - - if ( newSize != size() ) - { - _size = newSize; - updateGeometry(); - } -} - -void TerminalDisplay::setFixedSize(int cols, int lins) -{ - _isFixedSize = true; - - //ensure that display is at least one line by one column in size - _columns = qMax(1,cols); - _lines = qMax(1,lins); - _usedColumns = qMin(_usedColumns,_columns); - _usedLines = qMin(_usedLines,_lines); - - if (_image) - { - delete[] _image; - makeImage(); - } - setSize(cols, lins); - QWidget::setFixedSize(_size); -} - -QSize TerminalDisplay::sizeHint() const -{ - return _size; -} - - -/* --------------------------------------------------------------------- */ -/* */ -/* Drag & Drop */ -/* */ -/* --------------------------------------------------------------------- */ - -void TerminalDisplay::dragEnterEvent(QDragEnterEvent* event) -{ - if (event->mimeData()->hasFormat("text/plain")) - event->acceptProposedAction(); -} - -void TerminalDisplay::dropEvent(QDropEvent* event) -{ -// KUrl::List urls = KUrl::List::fromMimeData(event->mimeData()); - - QString dropText; -/* if (!urls.isEmpty()) - { - for ( int i = 0 ; i < urls.count() ; i++ ) - { - KUrl url = KIO::NetAccess::mostLocalUrl( urls[i] , 0 ); - QString urlText; - - if (url.isLocalFile()) - urlText = url.path(); - else - urlText = url.url(); - - // in future it may be useful to be able to insert file names with drag-and-drop - // without quoting them (this only affects paths with spaces in) - urlText = KShell::quoteArg(urlText); - - dropText += urlText; - - if ( i != urls.count()-1 ) - dropText += ' '; - } - } - else - { - dropText = event->mimeData()->text(); - } -*/ - if(event->mimeData()->hasFormat("text/plain")) - { - emit sendStringToEmu(dropText.toLocal8Bit()); - } -} - -void TerminalDisplay::doDrag() -{ - dragInfo.state = diDragging; - dragInfo.dragObject = new QDrag(this); - QMimeData *mimeData = new QMimeData; - mimeData->setText(QApplication::clipboard()->text(QClipboard::Selection)); - dragInfo.dragObject->setMimeData(mimeData); - dragInfo.dragObject->start(Qt::CopyAction); - // Don't delete the QTextDrag object. Qt will delete it when it's done with it. -} - -void TerminalDisplay::outputSuspended(bool suspended) -{ - //create the label when this function is first called - if (!_outputSuspendedLabel) - { - //This label includes a link to an English language website - //describing the 'flow control' (Xon/Xoff) feature found in almost - //all terminal emulators. - //If there isn't a suitable article available in the target language the link - //can simply be removed. - _outputSuspendedLabel = new QLabel( ("<qt>Output has been " - "<a href=\"http://en.wikipedia.org/wiki/XON\">suspended</a>" - " by pressing Ctrl+S." - " Press <b>Ctrl+Q</b> to resume.</qt>"), - this ); - - QPalette palette(_outputSuspendedLabel->palette()); - - palette.setColor(QPalette::Normal, QPalette::WindowText, QColor(Qt::white)); - palette.setColor(QPalette::Normal, QPalette::Window, QColor(Qt::black)); -// KColorScheme::adjustForeground(palette,KColorScheme::NeutralText); -// KColorScheme::adjustBackground(palette,KColorScheme::NeutralBackground); - _outputSuspendedLabel->setPalette(palette); - _outputSuspendedLabel->setAutoFillBackground(true); - _outputSuspendedLabel->setBackgroundRole(QPalette::Base); - _outputSuspendedLabel->setFont(QApplication::font()); - _outputSuspendedLabel->setMargin(5); - - //enable activation of "Xon/Xoff" link in label - _outputSuspendedLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse | - Qt::LinksAccessibleByKeyboard); - _outputSuspendedLabel->setOpenExternalLinks(true); - _outputSuspendedLabel->setVisible(false); - - _gridLayout->addWidget(_outputSuspendedLabel); - _gridLayout->addItem( new QSpacerItem(0,0,QSizePolicy::Expanding, - QSizePolicy::Expanding), - 1,0); - - } - - _outputSuspendedLabel->setVisible(suspended); -} - -uint TerminalDisplay::lineSpacing() const -{ - return _lineSpacing; -} - -void TerminalDisplay::setLineSpacing(uint i) -{ - _lineSpacing = i; - setVTFont(font()); // Trigger an update. -} - -//#include "moc_TerminalDisplay.cpp"
deleted file mode 100644 --- a/gui/qtermwidget/lib/TerminalDisplay.h +++ /dev/null @@ -1,754 +0,0 @@ -/* - Copyright (C) 2007 by Robert Knight <robertknight@gmail.com> - Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 TERMINALDISPLAY_H -#define TERMINALDISPLAY_H - -// Qt -#include <QtGui/QColor> -#include <QtCore/QPointer> -#include <QtGui/QWidget> - -// Konsole -#include "Filter.h" -#include "Character.h" -#include "ColorTables.h" - -class QDrag; -class QDragEnterEvent; -class QDropEvent; -class QLabel; -class QTimer; -class QEvent; -class QFrame; -class QGridLayout; -class QKeyEvent; -class QScrollBar; -class QShowEvent; -class QHideEvent; -class QWidget; - -//class KMenu; - -namespace Konsole -{ - -extern unsigned short vt100_graphics[32]; - -class ScreenWindow; - -/** - * A widget which displays output from a terminal emulation and sends input keypresses and mouse activity - * to the terminal. - * - * When the terminal emulation receives new output from the program running in the terminal, - * it will update the display by calling updateImage(). - * - * TODO More documentation - */ -class TerminalDisplay : public QWidget -{ - Q_OBJECT - -public: - /** Constructs a new terminal display widget with the specified parent. */ - TerminalDisplay(QWidget *parent=0); - virtual ~TerminalDisplay(); - - /** Returns the terminal color palette used by the display. */ - const ColorEntry* colorTable() const; - /** Sets the terminal color palette used by the display. */ - void setColorTable(const ColorEntry table[]); - /** - * Sets the seed used to generate random colors for the display - * (in color schemes that support them). - */ - void setRandomSeed(uint seed); - /** - * Returns the seed used to generate random colors for the display - * (in color schemes that support them). - */ - uint randomSeed() const; - - /** Sets the opacity of the terminal display. */ - void setOpacity(qreal opacity); - - /** - * This enum describes the location where the scroll bar is positioned in the display widget. - */ - enum ScrollBarPosition - { - /** Do not show the scroll bar. */ - NoScrollBar=0, - /** Show the scroll bar on the left side of the display. */ - ScrollBarLeft=1, - /** Show the scroll bar on the right side of the display. */ - ScrollBarRight=2 - }; - /** - * Specifies whether the terminal display has a vertical scroll bar, and if so whether it - * is shown on the left or right side of the display. - */ - void setScrollBarPosition(ScrollBarPosition position); - - /** - * Sets the current position and range of the display's scroll bar. - * - * @param cursor The position of the scroll bar's thumb. - * @param lines The maximum value of the scroll bar. - */ - void setScroll(int cursor, int lines); - - /** - * Returns the display's filter chain. When the image for the display is updated, - * the text is passed through each filter in the chain. Each filter can define - * hotspots which correspond to certain strings (such as URLs or particular words). - * Depending on the type of the hotspots created by the filter ( returned by Filter::Hotspot::type() ) - * the view will draw visual cues such as underlines on mouse-over for links or translucent - * rectangles for markers. - * - * To add a new filter to the view, call: - * viewWidget->filterChain()->addFilter( filterObject ); - */ - FilterChain* filterChain() const; - - /** - * Updates the filters in the display's filter chain. This will cause - * the hotspots to be updated to match the current image. - * - * WARNING: This function can be expensive depending on the - * image size and number of filters in the filterChain() - * - * TODO - This API does not really allow efficient usage. Revise it so - * that the processing can be done in a better way. - * - * eg: - * - Area of interest may be known ( eg. mouse cursor hovering - * over an area ) - */ - void processFilters(); - - /** - * Returns a list of menu actions created by the filters for the content - * at the given @p position. - */ - QList<QAction*> filterActions(const QPoint& position); - - /** Returns true if the cursor is set to blink or false otherwise. */ - bool blinkingCursor() { return _hasBlinkingCursor; } - /** Specifies whether or not the cursor blinks. */ - void setBlinkingCursor(bool blink); - - void setCtrlDrag(bool enable) { _ctrlDrag=enable; } - bool ctrlDrag() { return _ctrlDrag; } - - /** - * This enum describes the methods for selecting text when - * the user triple-clicks within the display. - */ - enum TripleClickMode - { - /** Select the whole line underneath the cursor. */ - SelectWholeLine, - /** Select from the current cursor position to the end of the line. */ - SelectForwardsFromCursor - }; - /** Sets how the text is selected when the user triple clicks within the display. */ - void setTripleClickMode(TripleClickMode mode) { _tripleClickMode = mode; } - /** See setTripleClickSelectionMode() */ - TripleClickMode tripleClickMode() { return _tripleClickMode; } - - void setLineSpacing(uint); - uint lineSpacing() const; - - void emitSelection(bool useXselection,bool appendReturn); - - /** - * This enum describes the available shapes for the keyboard cursor. - * See setKeyboardCursorShape() - */ - enum KeyboardCursorShape - { - /** A rectangular block which covers the entire area of the cursor character. */ - BlockCursor, - /** - * A single flat line which occupies the space at the bottom of the cursor - * character's area. - */ - UnderlineCursor, - /** - * An cursor shaped like the capital letter 'I', similar to the IBeam - * cursor used in Qt/KDE text editors. - */ - IBeamCursor - }; - /** - * Sets the shape of the keyboard cursor. This is the cursor drawn - * at the position in the terminal where keyboard input will appear. - * - * In addition the terminal display widget also has a cursor for - * the mouse pointer, which can be set using the QWidget::setCursor() - * method. - * - * Defaults to BlockCursor - */ - void setKeyboardCursorShape(KeyboardCursorShape shape); - /** - * Returns the shape of the keyboard cursor. See setKeyboardCursorShape() - */ - KeyboardCursorShape keyboardCursorShape() const; - - /** - * Sets the color used to draw the keyboard cursor. - * - * The keyboard cursor defaults to using the foreground color of the character - * underneath it. - * - * @param useForegroundColor If true, the cursor color will change to match - * the foreground color of the character underneath it as it is moved, in this - * case, the @p color parameter is ignored and the color of the character - * under the cursor is inverted to ensure that it is still readable. - * @param color The color to use to draw the cursor. This is only taken into - * account if @p useForegroundColor is false. - */ - void setKeyboardCursorColor(bool useForegroundColor , const QColor& color); - - /** - * Returns the color of the keyboard cursor, or an invalid color if the keyboard - * cursor color is set to change according to the foreground color of the character - * underneath it. - */ - QColor keyboardCursorColor() const; - - /** - * Returns the number of lines of text which can be displayed in the widget. - * - * This will depend upon the height of the widget and the current font. - * See fontHeight() - */ - int lines() { return _lines; } - /** - * Returns the number of characters of text which can be displayed on - * each line in the widget. - * - * This will depend upon the width of the widget and the current font. - * See fontWidth() - */ - int columns() { return _columns; } - - /** - * Returns the height of the characters in the font used to draw the text in the display. - */ - int fontHeight() { return _fontHeight; } - /** - * Returns the width of the characters in the display. - * This assumes the use of a fixed-width font. - */ - int fontWidth() { return _fontWidth; } - - void setSize(int cols, int lins); - void setFixedSize(int cols, int lins); - - // reimplemented - QSize sizeHint() const; - - /** - * Sets which characters, in addition to letters and numbers, - * are regarded as being part of a word for the purposes - * of selecting words in the display by double clicking on them. - * - * The word boundaries occur at the first and last characters which - * are either a letter, number, or a character in @p wc - * - * @param wc An array of characters which are to be considered parts - * of a word ( in addition to letters and numbers ). - */ - void setWordCharacters(const QString& wc); - /** - * Returns the characters which are considered part of a word for the - * purpose of selecting words in the display with the mouse. - * - * @see setWordCharacters() - */ - QString wordCharacters() { return _wordCharacters; } - - /** - * Sets the type of effect used to alert the user when a 'bell' occurs in the - * terminal session. - * - * The terminal session can trigger the bell effect by calling bell() with - * the alert message. - */ - void setBellMode(int mode); - /** - * Returns the type of effect used to alert the user when a 'bell' occurs in - * the terminal session. - * - * See setBellMode() - */ - int bellMode() { return _bellMode; } - - /** - * This enum describes the different types of sounds and visual effects which - * can be used to alert the user when a 'bell' occurs in the terminal - * session. - */ - enum BellMode - { - /** A system beep. */ - SystemBeepBell=0, - /** - * KDE notification. This may play a sound, show a passive popup - * or perform some other action depending on the user's settings. - */ - NotifyBell=1, - /** A silent, visual bell (eg. inverting the display's colors briefly) */ - VisualBell=2, - /** No bell effects */ - NoBell=3 - }; - - void setSelection(const QString &t); - - /** - * Reimplemented. Has no effect. Use setVTFont() to change the font - * used to draw characters in the display. - */ - virtual void setFont(const QFont &); - - /** Returns the font used to draw characters in the display */ - QFont getVTFont() { return font(); } - - /** - * Sets the font used to draw the display. Has no effect if @p font - * is larger than the size of the display itself. - */ - void setVTFont(const QFont& font); - - /** - * Specified whether anti-aliasing of text in the terminal display - * is enabled or not. Defaults to enabled. - */ - static void setAntialias( bool antialias ) { _antialiasText = antialias; } - /** - * Returns true if anti-aliasing of text in the terminal is enabled. - */ - static bool antialias() { return _antialiasText; } - - /** - * Sets whether or not the current height and width of the - * terminal in lines and columns is displayed whilst the widget - * is being resized. - */ - void setTerminalSizeHint(bool on) { _terminalSizeHint=on; } - /** - * Returns whether or not the current height and width of - * the terminal in lines and columns is displayed whilst the widget - * is being resized. - */ - bool terminalSizeHint() { return _terminalSizeHint; } - /** - * Sets whether the terminal size display is shown briefly - * after the widget is first shown. - * - * See setTerminalSizeHint() , isTerminalSizeHint() - */ - void setTerminalSizeStartup(bool on) { _terminalSizeStartup=on; } - - void setBidiEnabled(bool set) { _bidiEnabled=set; } - bool isBidiEnabled() { return _bidiEnabled; } - - /** - * Sets the terminal screen section which is displayed in this widget. - * When updateImage() is called, the display fetches the latest character image from the - * the associated terminal screen window. - * - * In terms of the model-view paradigm, the ScreenWindow is the model which is rendered - * by the TerminalDisplay. - */ - void setScreenWindow( ScreenWindow* window ); - /** Returns the terminal screen section which is displayed in this widget. See setScreenWindow() */ - ScreenWindow* screenWindow() const; - - static bool HAVE_TRANSPARENCY; - -public slots: - - /** - * Causes the terminal display to fetch the latest character image from the associated - * terminal screen ( see setScreenWindow() ) and redraw the display. - */ - void updateImage(); - /** - * Causes the terminal display to fetch the latest line status flags from the - * associated terminal screen ( see setScreenWindow() ). - */ - void updateLineProperties(); - - /** Copies the selected text to the clipboard. */ - void copyClipboard(); - /** - * Pastes the content of the clipboard into the - * display. - */ - void pasteClipboard(); - /** - * Pastes the content of the selection into the - * display. - */ - void pasteSelection(); - - /** - * Changes whether the flow control warning box should be shown when the flow control - * stop key (Ctrl+S) are pressed. - */ - void setFlowControlWarningEnabled(bool enabled); - - /** - * Causes the widget to display or hide a message informing the user that terminal - * output has been suspended (by using the flow control key combination Ctrl+S) - * - * @param suspended True if terminal output has been suspended and the warning message should - * be shown or false to indicate that terminal output has been resumed and that - * the warning message should disappear. - */ - void outputSuspended(bool suspended); - - /** - * Sets whether the program whoose output is being displayed in the view - * is interested in mouse events. - * - * If this is set to true, mouse signals will be emitted by the view when the user clicks, drags - * or otherwise moves the mouse inside the view. - * The user interaction needed to create selections will also change, and the user will be required - * to hold down the shift key to create a selection or perform other mouse activities inside the - * view area - since the program running in the terminal is being allowed to handle normal mouse - * events itself. - * - * @param usesMouse Set to true if the program running in the terminal is interested in mouse events - * or false otherwise. - */ - void setUsesMouse(bool usesMouse); - - /** See setUsesMouse() */ - bool usesMouse() const; - - /** - * Shows a notification that a bell event has occurred in the terminal. - * TODO: More documentation here - */ - void bell(const QString& message); - -signals: - - /** - * Emitted when the user presses a key whilst the terminal widget has focus. - */ - void keyPressedSignal(QKeyEvent *e); - - /** - * Emitted when the user presses the suspend or resume flow control key combinations - * - * @param suspend true if the user pressed Ctrl+S (the suspend output key combination) or - * false if the user pressed Ctrl+Q (the resume output key combination) - */ - void flowControlKeyPressed(bool suspend); - - /** - * A mouse event occurred. - * @param button The mouse button (0 for left button, 1 for middle button, 2 for right button, 3 for release) - * @param column The character column where the event occurred - * @param line The character row where the event occurred - * @param eventType The type of event. 0 for a mouse press / release or 1 for mouse motion - */ - void mouseSignal(int button, int column, int line, int eventType); - void changedFontMetricSignal(int height, int width); - void changedContentSizeSignal(int height, int width); - - /** - * Emitted when the user right clicks on the display, or right-clicks with the Shift - * key held down if usesMouse() is true. - * - * This can be used to display a context menu. - */ - void configureRequest( TerminalDisplay*, int state, const QPoint& position ); - - void isBusySelecting(bool); - void sendStringToEmu(const char*); - -protected: - virtual bool event( QEvent * ); - - virtual void paintEvent( QPaintEvent * ); - - virtual void showEvent(QShowEvent*); - virtual void hideEvent(QHideEvent*); - virtual void resizeEvent(QResizeEvent*); - - virtual void fontChange(const QFont &font); - - virtual void keyPressEvent(QKeyEvent* event); - virtual void mouseDoubleClickEvent(QMouseEvent* ev); - virtual void mousePressEvent( QMouseEvent* ); - virtual void mouseReleaseEvent( QMouseEvent* ); - virtual void mouseMoveEvent( QMouseEvent* ); - virtual void extendSelection( const QPoint& pos ); - virtual void wheelEvent( QWheelEvent* ); - - virtual bool focusNextPrevChild( bool next ); - - // drag and drop - virtual void dragEnterEvent(QDragEnterEvent* event); - virtual void dropEvent(QDropEvent* event); - void doDrag(); - enum DragState { diNone, diPending, diDragging }; - - struct _dragInfo { - DragState state; - QPoint start; - QDrag *dragObject; - } dragInfo; - - virtual int charClass(quint16) const; - - void clearImage(); - - void mouseTripleClickEvent(QMouseEvent* ev); - - // reimplemented - virtual void inputMethodEvent ( QInputMethodEvent* event ); - virtual QVariant inputMethodQuery( Qt::InputMethodQuery query ) const; - -protected slots: - - void scrollBarPositionChanged(int value); - void blinkEvent(); - void blinkCursorEvent(); - - //Renables bell noises and visuals. Used to disable further bells for a short period of time - //after emitting the first in a sequence of bell events. - void enableBell(); - -private slots: - - void swapColorTable(); - void tripleClickTimeout(); // resets possibleTripleClick - -private: - - // -- Drawing helpers -- - - // divides the part of the display specified by 'rect' into - // fragments according to their colors and styles and calls - // drawTextFragment() to draw the fragments - void drawContents(QPainter &paint, const QRect &rect); - // draws a section of text, all the text in this section - // has a common color and style - void drawTextFragment(QPainter& painter, const QRect& rect, - const QString& text, const Character* style); - // draws the background for a text fragment - // if useOpacitySetting is true then the color's alpha value will be set to - // the display's transparency (set with setOpacity()), otherwise the background - // will be drawn fully opaque - void drawBackground(QPainter& painter, const QRect& rect, const QColor& color, - bool useOpacitySetting); - // draws the cursor character - void drawCursor(QPainter& painter, const QRect& rect , const QColor& foregroundColor, - const QColor& backgroundColor , bool& invertColors); - // draws the characters or line graphics in a text fragment - void drawCharacters(QPainter& painter, const QRect& rect, const QString& text, - const Character* style, bool invertCharacterColor); - // draws a string of line graphics - void drawLineCharString(QPainter& painter, int x, int y, - const QString& str, const Character* attributes); - - // draws the preedit string for input methods - void drawInputMethodPreeditString(QPainter& painter , const QRect& rect); - - // -- - - // maps an area in the character image to an area on the widget - QRect imageToWidget(const QRect& imageArea) const; - - // maps a point on the widget to the position ( ie. line and column ) - // of the character at that point. - void getCharacterPosition(const QPoint& widgetPoint,int& line,int& column) const; - - // the area where the preedit string for input methods will be draw - QRect preeditRect() const; - - // shows a notification window in the middle of the widget indicating the terminal's - // current size in columns and lines - void showResizeNotification(); - - // scrolls the image by a number of lines. - // 'lines' may be positive ( to scroll the image down ) - // or negative ( to scroll the image up ) - // 'region' is the part of the image to scroll - currently only - // the top, bottom and height of 'region' are taken into account, - // the left and right are ignored. - void scrollImage(int lines , const QRect& region); - - void calcGeometry(); - void propagateSize(); - void updateImageSize(); - void makeImage(); - - void paintFilters(QPainter& painter); - - // returns a region covering all of the areas of the widget which contain - // a hotspot - QRegion hotSpotRegion() const; - - // returns the position of the cursor in columns and lines - QPoint cursorPosition() const; - - // the window onto the terminal screen which this display - // is currently showing. - QPointer<ScreenWindow> _screenWindow; - - bool _allowBell; - - QGridLayout* _gridLayout; - - bool _fixedFont; // has fixed pitch - int _fontHeight; // height - int _fontWidth; // width - int _fontAscent; // ascend - - int _leftMargin; // offset - int _topMargin; // offset - - int _lines; // the number of lines that can be displayed in the widget - int _columns; // the number of columns that can be displayed in the widget - - int _usedLines; // the number of lines that are actually being used, this will be less - // than 'lines' if the character image provided with setImage() is smaller - // than the maximum image size which can be displayed - - int _usedColumns; // the number of columns that are actually being used, this will be less - // than 'columns' if the character image provided with setImage() is smaller - // than the maximum image size which can be displayed - - int _contentHeight; - int _contentWidth; - Character* _image; // [lines][columns] - // only the area [usedLines][usedColumns] in the image contains valid data - - int _imageSize; - QVector<LineProperty> _lineProperties; - - ColorEntry _colorTable[TABLE_COLORS]; - uint _randomSeed; - - bool _resizing; - bool _terminalSizeHint; - bool _terminalSizeStartup; - bool _bidiEnabled; - bool _mouseMarks; - - QPoint _iPntSel; // initial selection point - QPoint _pntSel; // current selection point - QPoint _tripleSelBegin; // help avoid flicker - int _actSel; // selection state - bool _wordSelectionMode; - bool _lineSelectionMode; - bool _preserveLineBreaks; - bool _columnSelectionMode; - - QClipboard* _clipboard; - QScrollBar* _scrollBar; - ScrollBarPosition _scrollbarLocation; - QString _wordCharacters; - int _bellMode; - - bool _blinking; // hide text in paintEvent - bool _hasBlinker; // has characters to blink - bool _cursorBlinking; // hide cursor in paintEvent - bool _hasBlinkingCursor; // has blinking cursor enabled - bool _ctrlDrag; // require Ctrl key for drag - TripleClickMode _tripleClickMode; - bool _isFixedSize; //Columns / lines are locked. - QTimer* _blinkTimer; // active when hasBlinker - QTimer* _blinkCursorTimer; // active when hasBlinkingCursor - -// KMenu* _drop; - QString _dropText; - int _dndFileCount; - - bool _possibleTripleClick; // is set in mouseDoubleClickEvent and deleted - // after QApplication::doubleClickInterval() delay - - - QLabel* _resizeWidget; - QTimer* _resizeTimer; - - bool _flowControlWarningEnabled; - - //widgets related to the warning message that appears when the user presses Ctrl+S to suspend - //terminal output - informing them what has happened and how to resume output - QLabel* _outputSuspendedLabel; - - uint _lineSpacing; - - bool _colorsInverted; // true during visual bell - - QSize _size; - - QRgb _blendColor; - - // list of filters currently applied to the display. used for links and - // search highlight - TerminalImageFilterChain* _filterChain; - QRect _mouseOverHotspotArea; - - KeyboardCursorShape _cursorShape; - - // custom cursor color. if this is invalid then the foreground - // color of the character under the cursor is used - QColor _cursorColor; - - - struct InputMethodData - { - QString preeditString; - QRect previousPreeditRect; - }; - InputMethodData _inputMethodData; - - static bool _antialiasText; // do we antialias or not - - //the delay in milliseconds between redrawing blinking text - static const int BLINK_DELAY = 500; - static const int DEFAULT_LEFT_MARGIN = 1; - static const int DEFAULT_TOP_MARGIN = 1; - -public: - static void setTransparencyEnabled(bool enable) - { - HAVE_TRANSPARENCY = enable; - } -}; - -} - -#endif // TERMINALDISPLAY_H
deleted file mode 100644 --- a/gui/qtermwidget/lib/Vt102Emulation.cpp +++ /dev/null @@ -1,1266 +0,0 @@ -/* - This file is part of Konsole, an X terminal. - Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 "Vt102Emulation.h" - -//#include <config-konsole.h> - - -#if defined(__osf__) || defined(__APPLE__) -#define AVOID_XKB -#endif - -// this allows konsole to be compiled without XKB and XTEST extensions -// even though it might be available on a particular system. -#if defined(AVOID_XKB) -#undef HAVE_XKB -#endif - -// Standard -#include <stdio.h> -#include <unistd.h> -#include <assert.h> - -// Qt -#include <QtCore/QEvent> -#include <QtGui/QKeyEvent> -#include <QtCore/QByteRef> - -// KDE -//#include <kdebug.h> -//#include <klocale.h> - -// Konsole -#include "KeyboardTranslator.h" -#include "Screen.h" - -#if defined(HAVE_XKB) -void scrolllock_set_off(); -void scrolllock_set_on(); -#endif - -using namespace Konsole; - -/* VT102 Terminal Emulation - - This class puts together the screens, the pty and the widget to a - complete terminal emulation. Beside combining it's componentes, it - handles the emulations's protocol. - - This module consists of the following sections: - - - Constructor/Destructor - - Incoming Bytes Event pipeline - - Outgoing Bytes - - Mouse Events - - Keyboard Events - - Modes and Charset State - - Diagnostics -*/ - -/* ------------------------------------------------------------------------- */ -/* */ -/* Constructor / Destructor */ -/* */ -/* ------------------------------------------------------------------------- */ - - -Vt102Emulation::Vt102Emulation() - : Emulation(), - _titleUpdateTimer(new QTimer(this)) -{ - _titleUpdateTimer->setSingleShot(true); - - QObject::connect(_titleUpdateTimer , SIGNAL(timeout()) , this , SLOT(updateTitle())); - - initTokenizer(); - reset(); -} - -Vt102Emulation::~Vt102Emulation() -{ -} - -void Vt102Emulation::clearEntireScreen() -{ - _currentScreen->clearEntireScreen(); - - bufferedUpdate(); -} - -void Vt102Emulation::reset() -{ - //kDebug(1211)<<"Vt102Emulation::reset() resetToken()"; - resetToken(); - //kDebug(1211)<<"Vt102Emulation::reset() resetModes()"; - resetModes(); - //kDebug(1211)<<"Vt102Emulation::reset() resetCharSet()"; - resetCharset(0); - //kDebug(1211)<<"Vt102Emulation::reset() reset screen0()"; - _screen[0]->reset(); - //kDebug(1211)<<"Vt102Emulation::reset() resetCharSet()"; - resetCharset(1); - //kDebug(1211)<<"Vt102Emulation::reset() reset _screen 1"; - _screen[1]->reset(); - //kDebug(1211)<<"Vt102Emulation::reset() setCodec()"; - setCodec(LocaleCodec); - //kDebug(1211)<<"Vt102Emulation::reset() done"; - - bufferedUpdate(); -} - -/* ------------------------------------------------------------------------- */ -/* */ -/* Processing the incoming byte stream */ -/* */ -/* ------------------------------------------------------------------------- */ - -/* Incoming Bytes Event pipeline - - This section deals with decoding the incoming character stream. - Decoding means here, that the stream is first separated into `tokens' - which are then mapped to a `meaning' provided as operations by the - `Screen' class or by the emulation class itself. - - The pipeline proceeds as follows: - - - Tokenizing the ESC codes (onReceiveChar) - - VT100 code page translation of plain characters (applyCharset) - - Interpretation of ESC codes (tau) - - The escape codes and their meaning are described in the - technical reference of this program. -*/ - -// Tokens ------------------------------------------------------------------ -- - -/* - Since the tokens are the central notion if this section, we've put them - in front. They provide the syntactical elements used to represent the - terminals operations as byte sequences. - - They are encodes here into a single machine word, so that we can later - switch over them easily. Depending on the token itself, additional - argument variables are filled with parameter values. - - The tokens are defined below: - - - CHR - Printable characters (32..255 but DEL (=127)) - - CTL - Control characters (0..31 but ESC (= 27), DEL) - - ESC - Escape codes of the form <ESC><CHR but `[]()+*#'> - - ESC_DE - Escape codes of the form <ESC><any of `()+*#%'> C - - CSI_PN - Escape codes of the form <ESC>'[' {Pn} ';' {Pn} C - - CSI_PS - Escape codes of the form <ESC>'[' {Pn} ';' ... C - - CSI_PR - Escape codes of the form <ESC>'[' '?' {Pn} ';' ... C - - CSI_PE - Escape codes of the form <ESC>'[' '!' {Pn} ';' ... C - - VT52 - VT52 escape codes - - <ESC><Chr> - - <ESC>'Y'{Pc}{Pc} - - XTE_HA - Xterm hacks <ESC>`]' {Pn} `;' {Text} <BEL> - note that this is handled differently - - The last two forms allow list of arguments. Since the elements of - the lists are treated individually the same way, they are passed - as individual tokens to the interpretation. Further, because the - meaning of the parameters are names (althought represented as numbers), - they are includes within the token ('N'). - -*/ - -#define TY_CONSTR(T,A,N) ( ((((int)N) & 0xffff) << 16) | ((((int)A) & 0xff) << 8) | (((int)T) & 0xff) ) - -#define TY_CHR( ) TY_CONSTR(0,0,0) -#define TY_CTL(A ) TY_CONSTR(1,A,0) -#define TY_ESC(A ) TY_CONSTR(2,A,0) -#define TY_ESC_CS(A,B) TY_CONSTR(3,A,B) -#define TY_ESC_DE(A ) TY_CONSTR(4,A,0) -#define TY_CSI_PS(A,N) TY_CONSTR(5,A,N) -#define TY_CSI_PN(A ) TY_CONSTR(6,A,0) -#define TY_CSI_PR(A,N) TY_CONSTR(7,A,N) - -#define TY_VT52(A ) TY_CONSTR(8,A,0) - -#define TY_CSI_PG(A ) TY_CONSTR(9,A,0) - -#define TY_CSI_PE(A ) TY_CONSTR(10,A,0) - -// Tokenizer --------------------------------------------------------------- -- - -/* The tokenizers state - - The state is represented by the buffer (pbuf, ppos), - and accompanied by decoded arguments kept in (argv,argc). - Note that they are kept internal in the tokenizer. -*/ - -void Vt102Emulation::resetToken() -{ - ppos = 0; argc = 0; argv[0] = 0; argv[1] = 0; -} - -void Vt102Emulation::addDigit(int dig) -{ - argv[argc] = 10*argv[argc] + dig; -} - -void Vt102Emulation::addArgument() -{ - argc = qMin(argc+1,MAXARGS-1); - argv[argc] = 0; -} - -void Vt102Emulation::pushToToken(int cc) -{ - pbuf[ppos] = cc; - ppos = qMin(ppos+1,MAXPBUF-1); -} - -// Character Classes used while decoding - -#define CTL 1 -#define CHR 2 -#define CPN 4 -#define DIG 8 -#define SCS 16 -#define GRP 32 -#define CPS 64 - -void Vt102Emulation::initTokenizer() -{ int i; quint8* s; - for(i = 0; i < 256; i++) tbl[ i] = 0; - for(i = 0; i < 32; i++) tbl[ i] |= CTL; - for(i = 32; i < 256; i++) tbl[ i] |= CHR; - for(s = (quint8*)"@ABCDGHILMPSTXZcdfry"; *s; s++) tbl[*s] |= CPN; -// resize = \e[8;<row>;<col>t - for(s = (quint8*)"t"; *s; s++) tbl[*s] |= CPS; - for(s = (quint8*)"0123456789" ; *s; s++) tbl[*s] |= DIG; - for(s = (quint8*)"()+*%" ; *s; s++) tbl[*s] |= SCS; - for(s = (quint8*)"()+*#[]%" ; *s; s++) tbl[*s] |= GRP; - resetToken(); -} - -/* Ok, here comes the nasty part of the decoder. - - Instead of keeping an explicit state, we deduce it from the - token scanned so far. It is then immediately combined with - the current character to form a scanning decision. - - This is done by the following defines. - - - P is the length of the token scanned so far. - - L (often P-1) is the position on which contents we base a decision. - - C is a character or a group of characters (taken from 'tbl'). - - Note that they need to applied in proper order. -*/ - -#define lec(P,L,C) (p == (P) && s[(L)] == (C)) -#define lun( ) (p == 1 && cc >= 32 ) -#define les(P,L,C) (p == (P) && s[L] < 256 && (tbl[s[(L)]] & (C)) == (C)) -#define eec(C) (p >= 3 && cc == (C)) -#define ees(C) (p >= 3 && cc < 256 && (tbl[ cc ] & (C)) == (C)) -#define eps(C) (p >= 3 && s[2] != '?' && s[2] != '!' && s[2] != '>' && cc < 256 && (tbl[ cc ] & (C)) == (C)) -#define epp( ) (p >= 3 && s[2] == '?' ) -#define epe( ) (p >= 3 && s[2] == '!' ) -#define egt( ) (p >= 3 && s[2] == '>' ) -#define Xpe (ppos>=2 && pbuf[1] == ']' ) -#define Xte (Xpe && cc == 7 ) -#define ces(C) ( cc < 256 && (tbl[ cc ] & (C)) == (C) && !Xte) - -#define ESC 27 -#define CNTL(c) ((c)-'@') - -// process an incoming unicode character - -void Vt102Emulation::receiveChar(int cc) -{ - int i; - if (cc == 127) return; //VT100: ignore. - - if (ces( CTL)) - { // DEC HACK ALERT! Control Characters are allowed *within* esc sequences in VT100 - // This means, they do neither a resetToken nor a pushToToken. Some of them, do - // of course. Guess this originates from a weakly layered handling of the X-on - // X-off protocol, which comes really below this level. - if (cc == CNTL('X') || cc == CNTL('Z') || cc == ESC) resetToken(); //VT100: CAN or SUB - if (cc != ESC) { tau( TY_CTL(cc+'@' ), 0, 0); return; } - } - - pushToToken(cc); // advance the state - - int* s = pbuf; - int p = ppos; - - if (getMode(MODE_Ansi)) // decide on proper action - { - if (lec(1,0,ESC)) { return; } - if (lec(1,0,ESC+128)) { s[0] = ESC; receiveChar('['); return; } - if (les(2,1,GRP)) { return; } - if (Xte ) { XtermHack(); resetToken(); return; } - if (Xpe ) { return; } - if (lec(3,2,'?')) { return; } - if (lec(3,2,'>')) { return; } - if (lec(3,2,'!')) { return; } - if (lun( )) { tau( TY_CHR(), applyCharset(cc), 0); resetToken(); return; } - if (lec(2,0,ESC)) { tau( TY_ESC(s[1]), 0, 0); resetToken(); return; } - if (les(3,1,SCS)) { tau( TY_ESC_CS(s[1],s[2]), 0, 0); resetToken(); return; } - if (lec(3,1,'#')) { tau( TY_ESC_DE(s[2]), 0, 0); resetToken(); return; } - if (eps( CPN)) { tau( TY_CSI_PN(cc), argv[0],argv[1]); resetToken(); return; } - -// resize = \e[8;<row>;<col>t - if (eps( CPS)) { tau( TY_CSI_PS(cc, argv[0]), argv[1], argv[2]); resetToken(); return; } - - if (epe( )) { tau( TY_CSI_PE(cc), 0, 0); resetToken(); return; } - if (ees( DIG)) { addDigit(cc-'0'); return; } - if (eec( ';')) { addArgument(); return; } - for (i=0;i<=argc;i++) - if ( epp( )) { tau( TY_CSI_PR(cc,argv[i]), 0, 0); } - else if(egt( )) { tau( TY_CSI_PG(cc ), 0, 0); } // spec. case for ESC]>0c or ESC]>c - else if (cc == 'm' && argc - i >= 4 && (argv[i] == 38 || argv[i] == 48) && argv[i+1] == 2) - { // ESC[ ... 48;2;<red>;<green>;<blue> ... m -or- ESC[ ... 38;2;<red>;<green>;<blue> ... m - i += 2; - tau( TY_CSI_PS(cc, argv[i-2]), COLOR_SPACE_RGB, (argv[i] << 16) | (argv[i+1] << 8) | argv[i+2]); - i += 2; - } - else if (cc == 'm' && argc - i >= 2 && (argv[i] == 38 || argv[i] == 48) && argv[i+1] == 5) - { // ESC[ ... 48;5;<index> ... m -or- ESC[ ... 38;5;<index> ... m - i += 2; - tau( TY_CSI_PS(cc, argv[i-2]), COLOR_SPACE_256, argv[i]); - } - else { tau( TY_CSI_PS(cc,argv[i]), 0, 0); } - resetToken(); - } - else // mode VT52 - { - if (lec(1,0,ESC)) return; - if (les(1,0,CHR)) { tau( TY_CHR( ), s[0], 0); resetToken(); return; } - if (lec(2,1,'Y')) return; - if (lec(3,1,'Y')) return; - if (p < 4) { tau( TY_VT52(s[1] ), 0, 0); resetToken(); return; } - tau( TY_VT52(s[1] ), s[2],s[3]); resetToken(); return; - } -} - -void Vt102Emulation::XtermHack() -{ int i,arg = 0; - for (i = 2; i < ppos && '0'<=pbuf[i] && pbuf[i]<'9' ; i++) - arg = 10*arg + (pbuf[i]-'0'); - if (pbuf[i] != ';') { ReportErrorToken(); return; } - QChar *str = new QChar[ppos-i-2]; - for (int j = 0; j < ppos-i-2; j++) str[j] = pbuf[i+1+j]; - QString unistr(str,ppos-i-2); - - // arg == 1 doesn't change the title. In XTerm it only changes the icon name - // (btw: arg=0 changes title and icon, arg=1 only icon, arg=2 only title -// emit changeTitle(arg,unistr); - _pendingTitleUpdates[arg] = unistr; - _titleUpdateTimer->start(20); - - delete [] str; -} - -void Vt102Emulation::updateTitle() -{ - QListIterator<int> iter( _pendingTitleUpdates.keys() ); - while (iter.hasNext()) { - int arg = iter.next(); - emit titleChanged( arg , _pendingTitleUpdates[arg] ); - } - - _pendingTitleUpdates.clear(); -} - -// Interpreting Codes --------------------------------------------------------- - -/* - Now that the incoming character stream is properly tokenized, - meaning is assigned to them. These are either operations of - the current _screen, or of the emulation class itself. - - The token to be interpreteted comes in as a machine word - possibly accompanied by two parameters. - - Likewise, the operations assigned to, come with up to two - arguments. One could consider to make up a proper table - from the function below. - - The technical reference manual provides more information - about this mapping. -*/ - -void Vt102Emulation::tau( int token, int p, int q ) -{ -#if 0 -int N = (token>>0)&0xff; -int A = (token>>8)&0xff; -switch( N ) -{ - case 0: printf("%c", (p < 128) ? p : '?'); - break; - case 1: if (A == 'J') printf("\r"); - else if (A == 'M') printf("\n"); - else printf("CTL-%c ", (token>>8)&0xff); - break; - case 2: printf("ESC-%c ", (token>>8)&0xff); - break; - case 3: printf("ESC_CS-%c-%c ", (token>>8)&0xff, (token>>16)&0xff); - break; - case 4: printf("ESC_DE-%c ", (token>>8)&0xff); - break; - case 5: printf("CSI-PS-%c-%d", (token>>8)&0xff, (token>>16)&0xff ); - break; - case 6: printf("CSI-PN-%c [%d]", (token>>8)&0xff, p); - break; - case 7: printf("CSI-PR-%c-%d", (token>>8)&0xff, (token>>16)&0xff ); - break; - case 8: printf("VT52-%c", (token>>8)&0xff); - break; - case 9: printf("CSI-PG-%c", (token>>8)&0xff); - break; - case 10: printf("CSI-PE-%c", (token>>8)&0xff); - break; -} -#endif - - switch (token) - { - - case TY_CHR( ) : _currentScreen->ShowCharacter (p ); break; //UTF16 - - // 127 DEL : ignored on input - - case TY_CTL('@' ) : /* NUL: ignored */ break; - case TY_CTL('A' ) : /* SOH: ignored */ break; - case TY_CTL('B' ) : /* STX: ignored */ break; - case TY_CTL('C' ) : /* ETX: ignored */ break; - case TY_CTL('D' ) : /* EOT: ignored */ break; - case TY_CTL('E' ) : reportAnswerBack ( ); break; //VT100 - case TY_CTL('F' ) : /* ACK: ignored */ break; - case TY_CTL('G' ) : emit stateSet(NOTIFYBELL); - break; //VT100 - case TY_CTL('H' ) : _currentScreen->BackSpace ( ); break; //VT100 - case TY_CTL('I' ) : _currentScreen->Tabulate ( ); break; //VT100 - case TY_CTL('J' ) : _currentScreen->NewLine ( ); break; //VT100 - case TY_CTL('K' ) : _currentScreen->NewLine ( ); break; //VT100 - case TY_CTL('L' ) : _currentScreen->NewLine ( ); break; //VT100 - case TY_CTL('M' ) : _currentScreen->Return ( ); break; //VT100 - - case TY_CTL('N' ) : useCharset ( 1); break; //VT100 - case TY_CTL('O' ) : useCharset ( 0); break; //VT100 - - case TY_CTL('P' ) : /* DLE: ignored */ break; - case TY_CTL('Q' ) : /* DC1: XON continue */ break; //VT100 - case TY_CTL('R' ) : /* DC2: ignored */ break; - case TY_CTL('S' ) : /* DC3: XOFF halt */ break; //VT100 - case TY_CTL('T' ) : /* DC4: ignored */ break; - case TY_CTL('U' ) : /* NAK: ignored */ break; - case TY_CTL('V' ) : /* SYN: ignored */ break; - case TY_CTL('W' ) : /* ETB: ignored */ break; - case TY_CTL('X' ) : _currentScreen->ShowCharacter ( 0x2592); break; //VT100 - case TY_CTL('Y' ) : /* EM : ignored */ break; - case TY_CTL('Z' ) : _currentScreen->ShowCharacter ( 0x2592); break; //VT100 - case TY_CTL('[' ) : /* ESC: cannot be seen here. */ break; - case TY_CTL('\\' ) : /* FS : ignored */ break; - case TY_CTL(']' ) : /* GS : ignored */ break; - case TY_CTL('^' ) : /* RS : ignored */ break; - case TY_CTL('_' ) : /* US : ignored */ break; - - case TY_ESC('D' ) : _currentScreen->index ( ); break; //VT100 - case TY_ESC('E' ) : _currentScreen->NextLine ( ); break; //VT100 - case TY_ESC('H' ) : _currentScreen->changeTabStop (true ); break; //VT100 - case TY_ESC('M' ) : _currentScreen->reverseIndex ( ); break; //VT100 - case TY_ESC('Z' ) : reportTerminalType ( ); break; - case TY_ESC('c' ) : reset ( ); break; - - case TY_ESC('n' ) : useCharset ( 2); break; - case TY_ESC('o' ) : useCharset ( 3); break; - case TY_ESC('7' ) : saveCursor ( ); break; - case TY_ESC('8' ) : restoreCursor ( ); break; - - case TY_ESC('=' ) : setMode (MODE_AppKeyPad); break; - case TY_ESC('>' ) : resetMode (MODE_AppKeyPad); break; - case TY_ESC('<' ) : setMode (MODE_Ansi ); break; //VT100 - - case TY_ESC_CS('(', '0') : setCharset (0, '0'); break; //VT100 - case TY_ESC_CS('(', 'A') : setCharset (0, 'A'); break; //VT100 - case TY_ESC_CS('(', 'B') : setCharset (0, 'B'); break; //VT100 - - case TY_ESC_CS(')', '0') : setCharset (1, '0'); break; //VT100 - case TY_ESC_CS(')', 'A') : setCharset (1, 'A'); break; //VT100 - case TY_ESC_CS(')', 'B') : setCharset (1, 'B'); break; //VT100 - - case TY_ESC_CS('*', '0') : setCharset (2, '0'); break; //VT100 - case TY_ESC_CS('*', 'A') : setCharset (2, 'A'); break; //VT100 - case TY_ESC_CS('*', 'B') : setCharset (2, 'B'); break; //VT100 - - case TY_ESC_CS('+', '0') : setCharset (3, '0'); break; //VT100 - case TY_ESC_CS('+', 'A') : setCharset (3, 'A'); break; //VT100 - case TY_ESC_CS('+', 'B') : setCharset (3, 'B'); break; //VT100 - - case TY_ESC_CS('%', 'G') : setCodec (Utf8Codec ); break; //LINUX - case TY_ESC_CS('%', '@') : setCodec (LocaleCodec ); break; //LINUX - - case TY_ESC_DE('3' ) : /* Double height line, top half */ - _currentScreen->setLineProperty( LINE_DOUBLEWIDTH , true ); - _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT , true ); - break; - case TY_ESC_DE('4' ) : /* Double height line, bottom half */ - _currentScreen->setLineProperty( LINE_DOUBLEWIDTH , true ); - _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT , true ); - break; - case TY_ESC_DE('5' ) : /* Single width, single height line*/ - _currentScreen->setLineProperty( LINE_DOUBLEWIDTH , false); - _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT , false); - break; - case TY_ESC_DE('6' ) : /* Double width, single height line*/ - _currentScreen->setLineProperty( LINE_DOUBLEWIDTH , true); - _currentScreen->setLineProperty( LINE_DOUBLEHEIGHT , false); - break; - case TY_ESC_DE('8' ) : _currentScreen->helpAlign ( ); break; - -// resize = \e[8;<row>;<col>t - case TY_CSI_PS('t', 8) : setImageSize( q /* colums */, p /* lines */ ); break; - -// change tab text color : \e[28;<color>t color: 0-16,777,215 - case TY_CSI_PS('t', 28) : emit changeTabTextColorRequest ( p ); break; - - case TY_CSI_PS('K', 0) : _currentScreen->clearToEndOfLine ( ); break; - case TY_CSI_PS('K', 1) : _currentScreen->clearToBeginOfLine ( ); break; - case TY_CSI_PS('K', 2) : _currentScreen->clearEntireLine ( ); break; - case TY_CSI_PS('J', 0) : _currentScreen->clearToEndOfScreen ( ); break; - case TY_CSI_PS('J', 1) : _currentScreen->clearToBeginOfScreen ( ); break; - case TY_CSI_PS('J', 2) : _currentScreen->clearEntireScreen ( ); break; - case TY_CSI_PS('g', 0) : _currentScreen->changeTabStop (false ); break; //VT100 - case TY_CSI_PS('g', 3) : _currentScreen->clearTabStops ( ); break; //VT100 - case TY_CSI_PS('h', 4) : _currentScreen-> setMode (MODE_Insert ); break; - case TY_CSI_PS('h', 20) : setMode (MODE_NewLine ); break; - case TY_CSI_PS('i', 0) : /* IGNORE: attached printer */ break; //VT100 - case TY_CSI_PS('l', 4) : _currentScreen-> resetMode (MODE_Insert ); break; - case TY_CSI_PS('l', 20) : resetMode (MODE_NewLine ); break; - case TY_CSI_PS('s', 0) : saveCursor ( ); break; - case TY_CSI_PS('u', 0) : restoreCursor ( ); break; - - case TY_CSI_PS('m', 0) : _currentScreen->setDefaultRendition ( ); break; - case TY_CSI_PS('m', 1) : _currentScreen-> setRendition (RE_BOLD ); break; //VT100 - case TY_CSI_PS('m', 4) : _currentScreen-> setRendition (RE_UNDERLINE); break; //VT100 - case TY_CSI_PS('m', 5) : _currentScreen-> setRendition (RE_BLINK ); break; //VT100 - case TY_CSI_PS('m', 7) : _currentScreen-> setRendition (RE_REVERSE ); break; - case TY_CSI_PS('m', 10) : /* IGNORED: mapping related */ break; //LINUX - case TY_CSI_PS('m', 11) : /* IGNORED: mapping related */ break; //LINUX - case TY_CSI_PS('m', 12) : /* IGNORED: mapping related */ break; //LINUX - case TY_CSI_PS('m', 22) : _currentScreen->resetRendition (RE_BOLD ); break; - case TY_CSI_PS('m', 24) : _currentScreen->resetRendition (RE_UNDERLINE); break; - case TY_CSI_PS('m', 25) : _currentScreen->resetRendition (RE_BLINK ); break; - case TY_CSI_PS('m', 27) : _currentScreen->resetRendition (RE_REVERSE ); break; - - case TY_CSI_PS('m', 30) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 0); break; - case TY_CSI_PS('m', 31) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 1); break; - case TY_CSI_PS('m', 32) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 2); break; - case TY_CSI_PS('m', 33) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 3); break; - case TY_CSI_PS('m', 34) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 4); break; - case TY_CSI_PS('m', 35) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 5); break; - case TY_CSI_PS('m', 36) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 6); break; - case TY_CSI_PS('m', 37) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 7); break; - - case TY_CSI_PS('m', 38) : _currentScreen->setForeColor (p, q); break; - - case TY_CSI_PS('m', 39) : _currentScreen->setForeColor (COLOR_SPACE_DEFAULT, 0); break; - - case TY_CSI_PS('m', 40) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 0); break; - case TY_CSI_PS('m', 41) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 1); break; - case TY_CSI_PS('m', 42) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 2); break; - case TY_CSI_PS('m', 43) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 3); break; - case TY_CSI_PS('m', 44) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 4); break; - case TY_CSI_PS('m', 45) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 5); break; - case TY_CSI_PS('m', 46) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 6); break; - case TY_CSI_PS('m', 47) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 7); break; - - case TY_CSI_PS('m', 48) : _currentScreen->setBackColor (p, q); break; - - case TY_CSI_PS('m', 49) : _currentScreen->setBackColor (COLOR_SPACE_DEFAULT, 1); break; - - case TY_CSI_PS('m', 90) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 8); break; - case TY_CSI_PS('m', 91) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 9); break; - case TY_CSI_PS('m', 92) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 10); break; - case TY_CSI_PS('m', 93) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 11); break; - case TY_CSI_PS('m', 94) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 12); break; - case TY_CSI_PS('m', 95) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 13); break; - case TY_CSI_PS('m', 96) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 14); break; - case TY_CSI_PS('m', 97) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 15); break; - - case TY_CSI_PS('m', 100) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 8); break; - case TY_CSI_PS('m', 101) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 9); break; - case TY_CSI_PS('m', 102) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 10); break; - case TY_CSI_PS('m', 103) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 11); break; - case TY_CSI_PS('m', 104) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 12); break; - case TY_CSI_PS('m', 105) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 13); break; - case TY_CSI_PS('m', 106) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 14); break; - case TY_CSI_PS('m', 107) : _currentScreen->setBackColor (COLOR_SPACE_SYSTEM, 15); break; - - case TY_CSI_PS('n', 5) : reportStatus ( ); break; - case TY_CSI_PS('n', 6) : reportCursorPosition ( ); break; - case TY_CSI_PS('q', 0) : /* IGNORED: LEDs off */ break; //VT100 - case TY_CSI_PS('q', 1) : /* IGNORED: LED1 on */ break; //VT100 - case TY_CSI_PS('q', 2) : /* IGNORED: LED2 on */ break; //VT100 - case TY_CSI_PS('q', 3) : /* IGNORED: LED3 on */ break; //VT100 - case TY_CSI_PS('q', 4) : /* IGNORED: LED4 on */ break; //VT100 - case TY_CSI_PS('x', 0) : reportTerminalParms ( 2); break; //VT100 - case TY_CSI_PS('x', 1) : reportTerminalParms ( 3); break; //VT100 - - case TY_CSI_PN('@' ) : _currentScreen->insertChars (p ); break; - case TY_CSI_PN('A' ) : _currentScreen->cursorUp (p ); break; //VT100 - case TY_CSI_PN('B' ) : _currentScreen->cursorDown (p ); break; //VT100 - case TY_CSI_PN('C' ) : _currentScreen->cursorRight (p ); break; //VT100 - case TY_CSI_PN('D' ) : _currentScreen->cursorLeft (p ); break; //VT100 - case TY_CSI_PN('G' ) : _currentScreen->setCursorX (p ); break; //LINUX - case TY_CSI_PN('H' ) : _currentScreen->setCursorYX (p, q); break; //VT100 - case TY_CSI_PN('I' ) : _currentScreen->Tabulate (p ); break; - case TY_CSI_PN('L' ) : _currentScreen->insertLines (p ); break; - case TY_CSI_PN('M' ) : _currentScreen->deleteLines (p ); break; - case TY_CSI_PN('P' ) : _currentScreen->deleteChars (p ); break; - case TY_CSI_PN('S' ) : _currentScreen->scrollUp (p ); break; - case TY_CSI_PN('T' ) : _currentScreen->scrollDown (p ); break; - case TY_CSI_PN('X' ) : _currentScreen->eraseChars (p ); break; - case TY_CSI_PN('Z' ) : _currentScreen->backTabulate (p ); break; - case TY_CSI_PN('c' ) : reportTerminalType ( ); break; //VT100 - case TY_CSI_PN('d' ) : _currentScreen->setCursorY (p ); break; //LINUX - case TY_CSI_PN('f' ) : _currentScreen->setCursorYX (p, q); break; //VT100 - case TY_CSI_PN('r' ) : setMargins (p, q); break; //VT100 - case TY_CSI_PN('y' ) : /* IGNORED: Confidence test */ break; //VT100 - - case TY_CSI_PR('h', 1) : setMode (MODE_AppCuKeys); break; //VT100 - case TY_CSI_PR('l', 1) : resetMode (MODE_AppCuKeys); break; //VT100 - case TY_CSI_PR('s', 1) : saveMode (MODE_AppCuKeys); break; //FIXME - case TY_CSI_PR('r', 1) : restoreMode (MODE_AppCuKeys); break; //FIXME - - case TY_CSI_PR('l', 2) : resetMode (MODE_Ansi ); break; //VT100 - - case TY_CSI_PR('h', 3) : clearScreenAndSetColumns(132); break; //VT100 - case TY_CSI_PR('l', 3) : clearScreenAndSetColumns(80); break; //VT100 - - case TY_CSI_PR('h', 4) : /* IGNORED: soft scrolling */ break; //VT100 - case TY_CSI_PR('l', 4) : /* IGNORED: soft scrolling */ break; //VT100 - - case TY_CSI_PR('h', 5) : _currentScreen-> setMode (MODE_Screen ); break; //VT100 - case TY_CSI_PR('l', 5) : _currentScreen-> resetMode (MODE_Screen ); break; //VT100 - - case TY_CSI_PR('h', 6) : _currentScreen-> setMode (MODE_Origin ); break; //VT100 - case TY_CSI_PR('l', 6) : _currentScreen-> resetMode (MODE_Origin ); break; //VT100 - case TY_CSI_PR('s', 6) : _currentScreen-> saveMode (MODE_Origin ); break; //FIXME - case TY_CSI_PR('r', 6) : _currentScreen->restoreMode (MODE_Origin ); break; //FIXME - - case TY_CSI_PR('h', 7) : _currentScreen-> setMode (MODE_Wrap ); break; //VT100 - case TY_CSI_PR('l', 7) : _currentScreen-> resetMode (MODE_Wrap ); break; //VT100 - case TY_CSI_PR('s', 7) : _currentScreen-> saveMode (MODE_Wrap ); break; //FIXME - case TY_CSI_PR('r', 7) : _currentScreen->restoreMode (MODE_Wrap ); break; //FIXME - - case TY_CSI_PR('h', 8) : /* IGNORED: autorepeat on */ break; //VT100 - case TY_CSI_PR('l', 8) : /* IGNORED: autorepeat off */ break; //VT100 - case TY_CSI_PR('s', 8) : /* IGNORED: autorepeat on */ break; //VT100 - case TY_CSI_PR('r', 8) : /* IGNORED: autorepeat off */ break; //VT100 - - case TY_CSI_PR('h', 9) : /* IGNORED: interlace */ break; //VT100 - case TY_CSI_PR('l', 9) : /* IGNORED: interlace */ break; //VT100 - case TY_CSI_PR('s', 9) : /* IGNORED: interlace */ break; //VT100 - case TY_CSI_PR('r', 9) : /* IGNORED: interlace */ break; //VT100 - - case TY_CSI_PR('h', 12) : /* IGNORED: Cursor blink */ break; //att610 - case TY_CSI_PR('l', 12) : /* IGNORED: Cursor blink */ break; //att610 - case TY_CSI_PR('s', 12) : /* IGNORED: Cursor blink */ break; //att610 - case TY_CSI_PR('r', 12) : /* IGNORED: Cursor blink */ break; //att610 - - case TY_CSI_PR('h', 25) : setMode (MODE_Cursor ); break; //VT100 - case TY_CSI_PR('l', 25) : resetMode (MODE_Cursor ); break; //VT100 - case TY_CSI_PR('s', 25) : saveMode (MODE_Cursor ); break; //VT100 - case TY_CSI_PR('r', 25) : restoreMode (MODE_Cursor ); break; //VT100 - - case TY_CSI_PR('h', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM - case TY_CSI_PR('l', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM - case TY_CSI_PR('s', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM - case TY_CSI_PR('r', 41) : /* IGNORED: obsolete more(1) fix */ break; //XTERM - - case TY_CSI_PR('h', 47) : setMode (MODE_AppScreen); break; //VT100 - case TY_CSI_PR('l', 47) : resetMode (MODE_AppScreen); break; //VT100 - case TY_CSI_PR('s', 47) : saveMode (MODE_AppScreen); break; //XTERM - case TY_CSI_PR('r', 47) : restoreMode (MODE_AppScreen); break; //XTERM - - case TY_CSI_PR('h', 67) : /* IGNORED: DECBKM */ break; //XTERM - case TY_CSI_PR('l', 67) : /* IGNORED: DECBKM */ break; //XTERM - case TY_CSI_PR('s', 67) : /* IGNORED: DECBKM */ break; //XTERM - case TY_CSI_PR('r', 67) : /* IGNORED: DECBKM */ break; //XTERM - - // XTerm defines the following modes: - // SET_VT200_MOUSE 1000 - // SET_VT200_HIGHLIGHT_MOUSE 1001 - // SET_BTN_EVENT_MOUSE 1002 - // SET_ANY_EVENT_MOUSE 1003 - // - - //Note about mouse modes: - //There are four mouse modes which xterm-compatible terminals can support - 1000,1001,1002,1003 - //Konsole currently supports mode 1000 (basic mouse press and release) and mode 1002 (dragging the mouse). - //TODO: Implementation of mouse modes 1001 (something called hilight tracking) and - //1003 (a slight variation on dragging the mouse) - // - - case TY_CSI_PR('h', 1000) : setMode (MODE_Mouse1000); break; //XTERM - case TY_CSI_PR('l', 1000) : resetMode (MODE_Mouse1000); break; //XTERM - case TY_CSI_PR('s', 1000) : saveMode (MODE_Mouse1000); break; //XTERM - case TY_CSI_PR('r', 1000) : restoreMode (MODE_Mouse1000); break; //XTERM - - case TY_CSI_PR('h', 1001) : /* IGNORED: hilite mouse tracking */ break; //XTERM - case TY_CSI_PR('l', 1001) : resetMode (MODE_Mouse1001); break; //XTERM - case TY_CSI_PR('s', 1001) : /* IGNORED: hilite mouse tracking */ break; //XTERM - case TY_CSI_PR('r', 1001) : /* IGNORED: hilite mouse tracking */ break; //XTERM - - case TY_CSI_PR('h', 1002) : setMode (MODE_Mouse1002); break; //XTERM - case TY_CSI_PR('l', 1002) : resetMode (MODE_Mouse1002); break; //XTERM - case TY_CSI_PR('s', 1002) : saveMode (MODE_Mouse1002); break; //XTERM - case TY_CSI_PR('r', 1002) : restoreMode (MODE_Mouse1002); break; //XTERM - - case TY_CSI_PR('h', 1003) : setMode (MODE_Mouse1003); break; //XTERM - case TY_CSI_PR('l', 1003) : resetMode (MODE_Mouse1003); break; //XTERM - case TY_CSI_PR('s', 1003) : saveMode (MODE_Mouse1003); break; //XTERM - case TY_CSI_PR('r', 1003) : restoreMode (MODE_Mouse1003); break; //XTERM - - case TY_CSI_PR('h', 1047) : setMode (MODE_AppScreen); break; //XTERM - case TY_CSI_PR('l', 1047) : _screen[1]->clearEntireScreen(); resetMode(MODE_AppScreen); break; //XTERM - case TY_CSI_PR('s', 1047) : saveMode (MODE_AppScreen); break; //XTERM - case TY_CSI_PR('r', 1047) : restoreMode (MODE_AppScreen); break; //XTERM - - //FIXME: Unitoken: save translations - case TY_CSI_PR('h', 1048) : saveCursor ( ); break; //XTERM - case TY_CSI_PR('l', 1048) : restoreCursor ( ); break; //XTERM - case TY_CSI_PR('s', 1048) : saveCursor ( ); break; //XTERM - case TY_CSI_PR('r', 1048) : restoreCursor ( ); break; //XTERM - - //FIXME: every once new sequences like this pop up in xterm. - // Here's a guess of what they could mean. - case TY_CSI_PR('h', 1049) : saveCursor(); _screen[1]->clearEntireScreen(); setMode(MODE_AppScreen); break; //XTERM - case TY_CSI_PR('l', 1049) : resetMode(MODE_AppScreen); restoreCursor(); break; //XTERM - - //FIXME: weird DEC reset sequence - case TY_CSI_PE('p' ) : /* IGNORED: reset ( ) */ break; - - //FIXME: when changing between vt52 and ansi mode evtl do some resetting. - case TY_VT52('A' ) : _currentScreen->cursorUp ( 1); break; //VT52 - case TY_VT52('B' ) : _currentScreen->cursorDown ( 1); break; //VT52 - case TY_VT52('C' ) : _currentScreen->cursorRight ( 1); break; //VT52 - case TY_VT52('D' ) : _currentScreen->cursorLeft ( 1); break; //VT52 - - case TY_VT52('F' ) : setAndUseCharset (0, '0'); break; //VT52 - case TY_VT52('G' ) : setAndUseCharset (0, 'B'); break; //VT52 - - case TY_VT52('H' ) : _currentScreen->setCursorYX (1,1 ); break; //VT52 - case TY_VT52('I' ) : _currentScreen->reverseIndex ( ); break; //VT52 - case TY_VT52('J' ) : _currentScreen->clearToEndOfScreen ( ); break; //VT52 - case TY_VT52('K' ) : _currentScreen->clearToEndOfLine ( ); break; //VT52 - case TY_VT52('Y' ) : _currentScreen->setCursorYX (p-31,q-31 ); break; //VT52 - case TY_VT52('Z' ) : reportTerminalType ( ); break; //VT52 - case TY_VT52('<' ) : setMode (MODE_Ansi ); break; //VT52 - case TY_VT52('=' ) : setMode (MODE_AppKeyPad); break; //VT52 - case TY_VT52('>' ) : resetMode (MODE_AppKeyPad); break; //VT52 - - case TY_CSI_PG('c' ) : reportSecondaryAttributes( ); break; //VT100 - - default : ReportErrorToken(); break; - }; -} - -void Vt102Emulation::clearScreenAndSetColumns(int columnCount) -{ - setImageSize(_currentScreen->getLines(),columnCount); - clearEntireScreen(); - setDefaultMargins(); - _currentScreen->setCursorYX(0,0); -} - -/* ------------------------------------------------------------------------- */ -/* */ -/* Terminal to Host protocol */ -/* */ -/* ------------------------------------------------------------------------- */ - -/* - Outgoing bytes originate from several sources: - - - Replies to Enquieries. - - Mouse Events - - Keyboard Events -*/ - -/*! -*/ - -void Vt102Emulation::sendString(const char* s , int length) -{ - if ( length >= 0 ) - emit sendData(s,length); - else - emit sendData(s,strlen(s)); -} - -// Replies ----------------------------------------------------------------- -- - -// This section copes with replies send as response to an enquiery control code. - -/*! -*/ - -void Vt102Emulation::reportCursorPosition() -{ char tmp[20]; - sprintf(tmp,"\033[%d;%dR",_currentScreen->getCursorY()+1,_currentScreen->getCursorX()+1); - sendString(tmp); -} - -/* - What follows here is rather obsolete and faked stuff. - The correspondent enquieries are neverthenless issued. -*/ - -/*! -*/ - -void Vt102Emulation::reportTerminalType() -{ - // Primary device attribute response (Request was: ^[[0c or ^[[c (from TT321 Users Guide)) - // VT220: ^[[?63;1;2;3;6;7;8c (list deps on emul. capabilities) - // VT100: ^[[?1;2c - // VT101: ^[[?1;0c - // VT102: ^[[?6v - if (getMode(MODE_Ansi)) - sendString("\033[?1;2c"); // I'm a VT100 - else - sendString("\033/Z"); // I'm a VT52 -} - -void Vt102Emulation::reportSecondaryAttributes() -{ - // Seconday device attribute response (Request was: ^[[>0c or ^[[>c) - if (getMode(MODE_Ansi)) - sendString("\033[>0;115;0c"); // Why 115? ;) - else - sendString("\033/Z"); // FIXME I don't think VT52 knows about it but kept for - // konsoles backward compatibility. -} - -void Vt102Emulation::reportTerminalParms(int p) -// DECREPTPARM -{ char tmp[100]; - sprintf(tmp,"\033[%d;1;1;112;112;1;0x",p); // not really true. - sendString(tmp); -} - -/*! -*/ - -void Vt102Emulation::reportStatus() -{ - sendString("\033[0n"); //VT100. Device status report. 0 = Ready. -} - -/*! -*/ - -#define ANSWER_BACK "" // This is really obsolete VT100 stuff. - -void Vt102Emulation::reportAnswerBack() -{ - sendString(ANSWER_BACK); -} - -// Mouse Handling ---------------------------------------------------------- -- - -/*! - Mouse clicks are possibly reported to the client - application if it has issued interest in them. - They are normally consumed by the widget for copy - and paste, but may be propagated from the widget - when gui->setMouseMarks is set via setMode(MODE_Mouse1000). - - `x',`y' are 1-based. - `ev' (event) indicates the button pressed (0-2) - or a general mouse release (3). - - eventType represents the kind of mouse action that occurred: - 0 = Mouse button press or release - 1 = Mouse drag -*/ - -void Vt102Emulation::sendMouseEvent( int cb, int cx, int cy , int eventType ) -{ char tmp[20]; - if ( cx<1 || cy<1 ) return; - // normal buttons are passed as 0x20 + button, - // mouse wheel (buttons 4,5) as 0x5c + button - if (cb >= 4) cb += 0x3c; - - //Mouse motion handling - if ( (getMode(MODE_Mouse1002) || getMode(MODE_Mouse1003)) && eventType == 1 ) - cb += 0x20; //add 32 to signify motion event - - sprintf(tmp,"\033[M%c%c%c",cb+0x20,cx+0x20,cy+0x20); - sendString(tmp); -} - -// Keyboard Handling ------------------------------------------------------- -- - -#define encodeMode(M,B) BITS(B,getMode(M)) -#define encodeStat(M,B) BITS(B,((ev->modifiers() & (M)) == (M))) - -void Vt102Emulation::sendText( const QString& text ) -{ - if (!text.isEmpty()) { - QKeyEvent event(QEvent::KeyPress, - 0, - Qt::NoModifier, - text); - sendKeyEvent(&event); // expose as a big fat keypress event - } - -} - -void Vt102Emulation::sendKeyEvent( QKeyEvent* event ) -{ - Qt::KeyboardModifiers modifiers = event->modifiers(); - KeyboardTranslator::States states = KeyboardTranslator::NoState; - - // get current states - if ( getMode(MODE_NewLine) ) states |= KeyboardTranslator::NewLineState; - if ( getMode(MODE_Ansi) ) states |= KeyboardTranslator::AnsiState; - if ( getMode(MODE_AppCuKeys)) states |= KeyboardTranslator::CursorKeysState; - if ( getMode(MODE_AppScreen)) states |= KeyboardTranslator::AlternateScreenState; - - // lookup key binding - if ( _keyTranslator ) - { - KeyboardTranslator::Entry entry = _keyTranslator->findEntry( - event->key() , - modifiers, - states ); - - // send result to terminal - QByteArray textToSend; - - // special handling for the Alt (aka. Meta) modifier. pressing - // Alt+[Character] results in Esc+[Character] being sent - // (unless there is an entry defined for this particular combination - // in the keyboard modifier) - bool wantsAltModifier = entry.modifiers() & entry.modifierMask() & Qt::AltModifier; - bool wantsAnyModifier = entry.state() & entry.stateMask() & KeyboardTranslator::AnyModifierState; - - if ( modifiers & Qt::AltModifier && !(wantsAltModifier || wantsAnyModifier) - && !event->text().isEmpty() ) - { - textToSend.prepend("\033"); - } - - if ( entry.command() != KeyboardTranslator::NoCommand ) - { - if (entry.command() & KeyboardTranslator::EraseCommand) - textToSend += getErase(); - // TODO command handling - } - else if ( !entry.text().isEmpty() ) - { - textToSend += _codec->fromUnicode(entry.text(true,modifiers)); - } - else - textToSend += _codec->fromUnicode(event->text()); - - sendData( textToSend.constData() , textToSend.length() ); - } - else - { - // print an error message to the terminal if no key translator has been - // set - QString translatorError = ("No keyboard translator available. " - "The information needed to convert key presses " - "into characters to send to the terminal " - "is missing."); - - reset(); - receiveData( translatorError.toAscii().constData() , translatorError.count() ); - } -} - -/* ------------------------------------------------------------------------- */ -/* */ -/* VT100 Charsets */ -/* */ -/* ------------------------------------------------------------------------- */ - -// Character Set Conversion ------------------------------------------------ -- - -/* - The processing contains a VT100 specific code translation layer. - It's still in use and mainly responsible for the line drawing graphics. - - These and some other glyphs are assigned to codes (0x5f-0xfe) - normally occupied by the latin letters. Since this codes also - appear within control sequences, the extra code conversion - does not permute with the tokenizer and is placed behind it - in the pipeline. It only applies to tokens, which represent - plain characters. - - This conversion it eventually continued in TerminalDisplay.C, since - it might involve VT100 enhanced fonts, which have these - particular glyphs allocated in (0x00-0x1f) in their code page. -*/ - -#define CHARSET _charset[_currentScreen==_screen[1]] - -// Apply current character map. - -unsigned short Vt102Emulation::applyCharset(unsigned short c) -{ - if (CHARSET.graphic && 0x5f <= c && c <= 0x7e) return vt100_graphics[c-0x5f]; - if (CHARSET.pound && c == '#' ) return 0xa3; //This mode is obsolete - return c; -} - -/* - "Charset" related part of the emulation state. - This configures the VT100 _charset filter. - - While most operation work on the current _screen, - the following two are different. -*/ - -void Vt102Emulation::resetCharset(int scrno) -{ - _charset[scrno].cu_cs = 0; - strncpy(_charset[scrno].charset,"BBBB",4); - _charset[scrno].sa_graphic = false; - _charset[scrno].sa_pound = false; - _charset[scrno].graphic = false; - _charset[scrno].pound = false; -} - -void Vt102Emulation::setCharset(int n, int cs) // on both screens. -{ - _charset[0].charset[n&3] = cs; useCharset(_charset[0].cu_cs); - _charset[1].charset[n&3] = cs; useCharset(_charset[1].cu_cs); -} - -void Vt102Emulation::setAndUseCharset(int n, int cs) -{ - CHARSET.charset[n&3] = cs; - useCharset(n&3); -} - -void Vt102Emulation::useCharset(int n) -{ - CHARSET.cu_cs = n&3; - CHARSET.graphic = (CHARSET.charset[n&3] == '0'); - CHARSET.pound = (CHARSET.charset[n&3] == 'A'); //This mode is obsolete -} - -void Vt102Emulation::setDefaultMargins() -{ - _screen[0]->setDefaultMargins(); - _screen[1]->setDefaultMargins(); -} - -void Vt102Emulation::setMargins(int t, int b) -{ - _screen[0]->setMargins(t, b); - _screen[1]->setMargins(t, b); -} - -/*! Save the cursor position and the rendition attribute settings. */ - -void Vt102Emulation::saveCursor() -{ - CHARSET.sa_graphic = CHARSET.graphic; - CHARSET.sa_pound = CHARSET.pound; //This mode is obsolete - // we are not clear about these - //sa_charset = charsets[cScreen->_charset]; - //sa_charset_num = cScreen->_charset; - _currentScreen->saveCursor(); -} - -/*! Restore the cursor position and the rendition attribute settings. */ - -void Vt102Emulation::restoreCursor() -{ - CHARSET.graphic = CHARSET.sa_graphic; - CHARSET.pound = CHARSET.sa_pound; //This mode is obsolete - _currentScreen->restoreCursor(); -} - -/* ------------------------------------------------------------------------- */ -/* */ -/* Mode Operations */ -/* */ -/* ------------------------------------------------------------------------- */ - -/* - Some of the emulations state is either added to the state of the screens. - - This causes some scoping problems, since different emulations choose to - located the mode either to the current _screen or to both. - - For strange reasons, the extend of the rendition attributes ranges over - all screens and not over the actual _screen. - - We decided on the precise precise extend, somehow. -*/ - -// "Mode" related part of the state. These are all booleans. - -void Vt102Emulation::resetModes() -{ - resetMode(MODE_Mouse1000); saveMode(MODE_Mouse1000); - resetMode(MODE_Mouse1001); saveMode(MODE_Mouse1001); - resetMode(MODE_Mouse1002); saveMode(MODE_Mouse1002); - resetMode(MODE_Mouse1003); saveMode(MODE_Mouse1003); - - resetMode(MODE_AppScreen); saveMode(MODE_AppScreen); - // here come obsolete modes - resetMode(MODE_AppCuKeys); saveMode(MODE_AppCuKeys); - resetMode(MODE_NewLine ); - setMode(MODE_Ansi ); -} - -void Vt102Emulation::setMode(int m) -{ - _currParm.mode[m] = true; - switch (m) - { - case MODE_Mouse1000: - case MODE_Mouse1001: - case MODE_Mouse1002: - case MODE_Mouse1003: - emit programUsesMouseChanged(false); - break; - - case MODE_AppScreen : _screen[1]->clearSelection(); - setScreen(1); - break; - } - if (m < MODES_SCREEN || m == MODE_NewLine) - { - _screen[0]->setMode(m); - _screen[1]->setMode(m); - } -} - -void Vt102Emulation::resetMode(int m) -{ - _currParm.mode[m] = false; - switch (m) - { - case MODE_Mouse1000 : - case MODE_Mouse1001 : - case MODE_Mouse1002 : - case MODE_Mouse1003 : - emit programUsesMouseChanged(true); - break; - - case MODE_AppScreen : _screen[0]->clearSelection(); - setScreen(0); - break; - } - if (m < MODES_SCREEN || m == MODE_NewLine) - { - _screen[0]->resetMode(m); - _screen[1]->resetMode(m); - } -} - -void Vt102Emulation::saveMode(int m) -{ - _saveParm.mode[m] = _currParm.mode[m]; -} - -void Vt102Emulation::restoreMode(int m) -{ - if (_saveParm.mode[m]) - setMode(m); - else - resetMode(m); -} - -bool Vt102Emulation::getMode(int m) -{ - return _currParm.mode[m]; -} - -char Vt102Emulation::getErase() const -{ - KeyboardTranslator::Entry entry = _keyTranslator->findEntry( - Qt::Key_Backspace, - 0, - 0); - if ( entry.text().count() > 0 ) - return entry.text()[0]; - else - return '\b'; -} - -/* ------------------------------------------------------------------------- */ -/* */ -/* Diagnostic */ -/* */ -/* ------------------------------------------------------------------------- */ - -/*! shows the contents of the scan buffer. - - This functions is used for diagnostics. It is called by \e ReportErrorToken - to inform about strings that cannot be decoded or handled by the emulation. - - \sa ReportErrorToken -*/ - -static void hexdump(int* s, int len) -{ int i; - for (i = 0; i < len; i++) - { - if (s[i] == '\\') - printf("\\\\"); - else - if ((s[i]) > 32 && s[i] < 127) - printf("%c",s[i]); - else - printf("\\%04x(hex)",s[i]); - } -} - -void Vt102Emulation::scan_buffer_report() -{ - if (ppos == 0 || ppos == 1 && (pbuf[0] & 0xff) >= 32) return; - printf("token: "); hexdump(pbuf,ppos); printf("\n"); -} - -/*! -*/ - -void Vt102Emulation::ReportErrorToken() -{ -#ifndef NDEBUG - printf("undecodable "); scan_buffer_report(); -#endif -} - -//#include "moc_Vt102Emulation.cpp" -
deleted file mode 100644 --- a/gui/qtermwidget/lib/Vt102Emulation.h +++ /dev/null @@ -1,192 +0,0 @@ -/* - This file is part of Konsole, an X terminal. - - Copyright (C) 2007 by Robert Knight <robertknight@gmail.com> - Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - 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 VT102EMULATION_H -#define VT102EMULATION_H - -// Standard Library -#include <stdio.h> - -// Qt -#include <QtGui/QKeyEvent> -#include <QtCore/QHash> -#include <QtCore/QTimer> - -// Konsole -#include "Emulation.h" -#include "Screen.h" - -#define MODE_AppScreen (MODES_SCREEN+0) -#define MODE_AppCuKeys (MODES_SCREEN+1) -#define MODE_AppKeyPad (MODES_SCREEN+2) -#define MODE_Mouse1000 (MODES_SCREEN+3) -#define MODE_Mouse1001 (MODES_SCREEN+4) -#define MODE_Mouse1002 (MODES_SCREEN+5) -#define MODE_Mouse1003 (MODES_SCREEN+6) -#define MODE_Ansi (MODES_SCREEN+7) -#define MODE_total (MODES_SCREEN+8) - -namespace Konsole -{ - -struct DECpar -{ - bool mode[MODE_total]; -}; - -struct CharCodes -{ - // coding info - char charset[4]; // - int cu_cs; // actual charset. - bool graphic; // Some VT100 tricks - bool pound ; // Some VT100 tricks - bool sa_graphic; // saved graphic - bool sa_pound; // saved pound -}; - -/** - * Provides an xterm compatible terminal emulation based on the DEC VT102 terminal. - * A full description of this terminal can be found at http://vt100.net/docs/vt102-ug/ - * - * In addition, various additional xterm escape sequences are supported to provide - * features such as mouse input handling. - * See http://rtfm.etla.org/xterm/ctlseq.html for a description of xterm's escape - * sequences. - * - */ -class Vt102Emulation : public Emulation -{ -Q_OBJECT - -public: - - /** Constructs a new emulation */ - Vt102Emulation(); - ~Vt102Emulation(); - - // reimplemented - virtual void clearEntireScreen(); - virtual void reset(); - - // reimplemented - virtual char getErase() const; - -public slots: - - // reimplemented - virtual void sendString(const char*,int length = -1); - virtual void sendText(const QString& text); - virtual void sendKeyEvent(QKeyEvent*); - virtual void sendMouseEvent( int buttons, int column, int line , int eventType ); - -protected: - // reimplemented - virtual void setMode (int mode); - virtual void resetMode (int mode); - - // reimplemented - virtual void receiveChar(int cc); - - -private slots: - - //causes changeTitle() to be emitted for each (int,QString) pair in pendingTitleUpdates - //used to buffer multiple title updates - void updateTitle(); - - -private: - unsigned short applyCharset(unsigned short c); - void setCharset(int n, int cs); - void useCharset(int n); - void setAndUseCharset(int n, int cs); - void saveCursor(); - void restoreCursor(); - void resetCharset(int scrno); - - void setMargins(int top, int bottom); - //set margins for all screens back to their defaults - void setDefaultMargins(); - - // returns true if 'mode' is set or false otherwise - bool getMode (int mode); - // saves the current boolean value of 'mode' - void saveMode (int mode); - // restores the boolean value of 'mode' - void restoreMode(int mode); - // resets all modes - void resetModes(); - - void resetToken(); -#define MAXPBUF 80 - void pushToToken(int cc); - int pbuf[MAXPBUF]; //FIXME: overflow? - int ppos; -#define MAXARGS 15 - void addDigit(int dig); - void addArgument(); - int argv[MAXARGS]; - int argc; - void initTokenizer(); - int tbl[256]; - - void scan_buffer_report(); //FIXME: rename - void ReportErrorToken(); //FIXME: rename - - void tau(int code, int p, int q); - void XtermHack(); - - void reportTerminalType(); - void reportSecondaryAttributes(); - void reportStatus(); - void reportAnswerBack(); - void reportCursorPosition(); - void reportTerminalParms(int p); - - void onScrollLock(); - void scrollLock(const bool lock); - - // clears the screen and resizes it to the specified - // number of columns - void clearScreenAndSetColumns(int columnCount); - - CharCodes _charset[2]; - - DECpar _currParm; - DECpar _saveParm; - - //hash table and timer for buffering calls to the session instance - //to update the name of the session - //or window title. - //these calls occur when certain escape sequences are seen in the - //output from the terminal - QHash<int,QString> _pendingTitleUpdates; - QTimer* _titleUpdateTimer; - -}; - -} - -#endif // VT102EMULATION_H
deleted file mode 100644 --- a/gui/qtermwidget/lib/default.keytab +++ /dev/null @@ -1,128 +0,0 @@ -# [README.default.Keytab] Buildin Keyboard Table -# -# To customize your keyboard, copy this file to something -# ending with .keytab and change it to meet you needs. -# Please read the README.KeyTab and the README.keyboard -# in this case. -# -# -------------------------------------------------------------- - -keyboard "Default (XFree 4)" - -# -------------------------------------------------------------- -# -# Note that this particular table is a "risc" version made to -# ease customization without bothering with obsolete details. -# See VT100.keytab for the more hairy stuff. -# -# -------------------------------------------------------------- - -# common keys - -key Escape : "\E" - -key Tab -Shift : "\t" -key Tab +Shift+Ansi : "\E[Z" -key Tab +Shift-Ansi : "\t" -key Backtab +Ansi : "\E[Z" -key Backtab -Ansi : "\t" - -key Return-Shift-NewLine : "\r" -key Return-Shift+NewLine : "\r\n" - -key Return+Shift : "\EOM" - -# Backspace and Delete codes are preserving CTRL-H. - -key Backspace : "\x7f" - -# Arrow keys in VT52 mode -# shift up/down are reserved for scrolling. -# shift left/right are reserved for switching between tabs (this is hardcoded). - -key Up -Shift-Ansi : "\EA" -key Down -Shift-Ansi : "\EB" -key Right-Shift-Ansi : "\EC" -key Left -Shift-Ansi : "\ED" - -# Arrow keys in ANSI mode with Application - and Normal Cursor Mode) - -key Up -Shift-AnyMod+Ansi+AppCuKeys : "\EOA" -key Down -Shift-AnyMod+Ansi+AppCuKeys : "\EOB" -key Right -Shift-AnyMod+Ansi+AppCuKeys : "\EOC" -key Left -Shift-AnyMod+Ansi+AppCuKeys : "\EOD" - -key Up -Shift-AnyMod+Ansi-AppCuKeys : "\E[A" -key Down -Shift-AnyMod+Ansi-AppCuKeys : "\E[B" -key Right -Shift-AnyMod+Ansi-AppCuKeys : "\E[C" -key Left -Shift-AnyMod+Ansi-AppCuKeys : "\E[D" - -key Up -Shift+AnyMod+Ansi : "\E[1;*A" -key Down -Shift+AnyMod+Ansi : "\E[1;*B" -key Right -Shift+AnyMod+Ansi : "\E[1;*C" -key Left -Shift+AnyMod+Ansi : "\E[1;*D" - -# other grey PC keys - -key Enter+NewLine : "\r\n" -key Enter-NewLine : "\r" - -key Home -AnyMod -AppCuKeys : "\E[H" -key End -AnyMod -AppCuKeys : "\E[F" -key Home -AnyMod +AppCuKeys : "\EOH" -key End -AnyMod +AppCuKeys : "\EOF" -key Home +AnyMod : "\E[1;*H" -key End +AnyMod : "\E[1;*F" - -key Insert -AnyMod : "\E[2~" -key Delete -AnyMod : "\E[3~" -key Insert +AnyMod : "\E[2;*~" -key Delete +AnyMod : "\E[3;*~" - -key Prior -Shift-AnyMod : "\E[5~" -key Next -Shift-AnyMod : "\E[6~" -key Prior -Shift+AnyMod : "\E[5;*~" -key Next -Shift+AnyMod : "\E[6;*~" - -# Function keys -key F1 -AnyMod : "\EOP" -key F2 -AnyMod : "\EOQ" -key F3 -AnyMod : "\EOR" -key F4 -AnyMod : "\EOS" -key F5 -AnyMod : "\E[15~" -key F6 -AnyMod : "\E[17~" -key F7 -AnyMod : "\E[18~" -key F8 -AnyMod : "\E[19~" -key F9 -AnyMod : "\E[20~" -key F10 -AnyMod : "\E[21~" -key F11 -AnyMod : "\E[23~" -key F12 -AnyMod : "\E[24~" - -key F1 +AnyMod : "\EO*P" -key F2 +AnyMod : "\EO*Q" -key F3 +AnyMod : "\EO*R" -key F4 +AnyMod : "\EO*S" -key F5 +AnyMod : "\E[15;*~" -key F6 +AnyMod : "\E[17;*~" -key F7 +AnyMod : "\E[18;*~" -key F8 +AnyMod : "\E[19;*~" -key F9 +AnyMod : "\E[20;*~" -key F10 +AnyMod : "\E[21;*~" -key F11 +AnyMod : "\E[23;*~" -key F12 +AnyMod : "\E[24;*~" - -# Work around dead keys - -key Space +Control : "\x00" - -# Some keys are used by konsole to cause operations. -# The scroll* operations refer to the history buffer. - -key Up +Shift-AppScreen : scrollLineUp -key Prior +Shift-AppScreen : scrollPageUp -key Down +Shift-AppScreen : scrollLineDown -key Next +Shift-AppScreen : scrollPageDown - -key ScrollLock : scrollLock - -# keypad characters are not offered differently by Qt.
deleted file mode 100644 --- a/gui/qtermwidget/lib/k3process.cpp +++ /dev/null @@ -1,1053 +0,0 @@ -/* - This file is part of the KDE libraries - Copyright (C) 1997 Christian Czezatke (e9025461@student.tuwien.ac.at) - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - - -#include "k3process.h" -//#include <config.h> - -#include "k3processcontroller.h" -#include "kpty.h" - -#ifdef __osf__ -#define _OSF_SOURCE -#include <float.h> -#endif - -#ifdef _AIX -#define _ALL_SOURCE -#endif - -#include <sys/socket.h> -#include <sys/ioctl.h> - -#include <sys/types.h> -#include <sys/time.h> -#include <sys/resource.h> -#include <sys/stat.h> -#include <sys/wait.h> - -#ifdef HAVE_SYS_SELECT_H -#include <sys/select.h> -#endif - -#include <errno.h> -#include <assert.h> -#include <fcntl.h> -#include <time.h> -#include <stdlib.h> -#include <signal.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <pwd.h> -#include <grp.h> - -#include <QtCore/QMap> -#include <QtCore/QFile> -#include <QtCore/QSocketNotifier> - -//#include <kdebug.h> -//#include <kstandarddirs.h> -//#include <kuser.h> - - -////////////////// -// private data // -////////////////// - -class K3ProcessPrivate { -public: - K3ProcessPrivate() : - usePty(K3Process::NoCommunication), - addUtmp(false), useShell(false), - pty(0), - priority(0) - { - } - - K3Process::Communication usePty; - bool addUtmp : 1; - bool useShell : 1; - - KPty *pty; - - int priority; - - QMap<QString,QString> env; - QString wd; - QByteArray shell; - QByteArray executable; -}; - -///////////////////////////// -// public member functions // -///////////////////////////// - -K3Process::K3Process( QObject* parent ) - : QObject( parent ), - run_mode(NotifyOnExit), - runs(false), - pid_(0), - status(0), - keepPrivs(false), - innot(0), - outnot(0), - errnot(0), - communication(NoCommunication), - input_data(0), - input_sent(0), - input_total(0), - d(new K3ProcessPrivate) -{ - K3ProcessController::ref(); - K3ProcessController::instance()->addKProcess(this); - - - out[0] = out[1] = -1; - in[0] = in[1] = -1; - err[0] = err[1] = -1; -} - -void -K3Process::setEnvironment(const QString &name, const QString &value) -{ - d->env.insert(name, value); -} - -void -K3Process::setWorkingDirectory(const QString &dir) -{ - d->wd = dir; -} - -void -K3Process::setupEnvironment() -{ - QMap<QString,QString>::Iterator it; - for(it = d->env.begin(); it != d->env.end(); ++it) - { - setenv(QFile::encodeName(it.key()).data(), - QFile::encodeName(it.value()).data(), 1); - } - if (!d->wd.isEmpty()) - { - chdir(QFile::encodeName(d->wd).data()); - } -} - -void -K3Process::setRunPrivileged(bool keepPrivileges) -{ - keepPrivs = keepPrivileges; -} - -bool -K3Process::runPrivileged() const -{ - return keepPrivs; -} - -bool -K3Process::setPriority(int prio) -{ - if (runs) { - if (setpriority(PRIO_PROCESS, pid_, prio)) - return false; - } else { - if (prio > 19 || prio < (geteuid() ? getpriority(PRIO_PROCESS, 0) : -20)) - return false; - } - d->priority = prio; - return true; -} - -K3Process::~K3Process() -{ - if (run_mode != DontCare) - kill(SIGKILL); - detach(); - - delete d->pty; - delete d; - - K3ProcessController::instance()->removeKProcess(this); - K3ProcessController::deref(); -} - -void K3Process::detach() -{ - if (runs) { - K3ProcessController::instance()->addProcess(pid_); - runs = false; - pid_ = 0; // close without draining - commClose(); // Clean up open fd's and socket notifiers. - } -} - -void K3Process::setBinaryExecutable(const char *filename) -{ - d->executable = filename; -} - -K3Process &K3Process::operator<<(const QStringList& args) -{ - QStringList::ConstIterator it = args.begin(); - for ( ; it != args.end() ; ++it ) - arguments.append(QFile::encodeName(*it)); - return *this; -} - -K3Process &K3Process::operator<<(const QByteArray& arg) -{ - return operator<< (arg.data()); -} - -K3Process &K3Process::operator<<(const char* arg) -{ - arguments.append(arg); - return *this; -} - -K3Process &K3Process::operator<<(const QString& arg) -{ - arguments.append(QFile::encodeName(arg)); - return *this; -} - -void K3Process::clearArguments() -{ - arguments.clear(); -} - -bool K3Process::start(RunMode runmode, Communication comm) -{ - if (runs) { - qDebug() << "Attempted to start an already running process" << endl; - return false; - } - - uint n = arguments.count(); - if (n == 0) { - qDebug() << "Attempted to start a process without arguments" << endl; - return false; - } - char **arglist; - QByteArray shellCmd; - if (d->useShell) - { - if (d->shell.isEmpty()) { - qDebug() << "Invalid shell specified" << endl; - return false; - } - - for (uint i = 0; i < n; i++) { - shellCmd += arguments[i]; - shellCmd += ' '; // CC: to separate the arguments - } - - arglist = static_cast<char **>(malloc( 4 * sizeof(char *))); - arglist[0] = d->shell.data(); - arglist[1] = (char *) "-c"; - arglist[2] = shellCmd.data(); - arglist[3] = 0; - } - else - { - arglist = static_cast<char **>(malloc( (n + 1) * sizeof(char *))); - for (uint i = 0; i < n; i++) - arglist[i] = arguments[i].data(); - arglist[n] = 0; - } - - run_mode = runmode; - - if (!setupCommunication(comm)) - { - qDebug() << "Could not setup Communication!" << endl; - free(arglist); - return false; - } - - // We do this in the parent because if we do it in the child process - // gdb gets confused when the application runs from gdb. -#ifdef HAVE_INITGROUPS - struct passwd *pw = geteuid() ? 0 : getpwuid(getuid()); -#endif - - int fd[2]; - if (pipe(fd)) - fd[0] = fd[1] = -1; // Pipe failed.. continue - - // we don't use vfork() because - // - it has unclear semantics and is not standardized - // - we do way too much magic in the child - pid_ = fork(); - if (pid_ == 0) { - // The child process - - close(fd[0]); - // Closing of fd[1] indicates that the execvp() succeeded! - fcntl(fd[1], F_SETFD, FD_CLOEXEC); - - if (!commSetupDoneC()) - qDebug() << "Could not finish comm setup in child!" << endl; - - // reset all signal handlers - struct sigaction act; - sigemptyset(&act.sa_mask); - act.sa_handler = SIG_DFL; - act.sa_flags = 0; - for (int sig = 1; sig < NSIG; sig++) - sigaction(sig, &act, 0L); - - if (d->priority) - setpriority(PRIO_PROCESS, 0, d->priority); - - if (!runPrivileged()) - { - setgid(getgid()); -#ifdef HAVE_INITGROUPS - if (pw) - initgroups(pw->pw_name, pw->pw_gid); -#endif - if (geteuid() != getuid()) - setuid(getuid()); - if (geteuid() != getuid()) - _exit(1); - } - - setupEnvironment(); - - if (runmode == DontCare || runmode == OwnGroup) - setsid(); - - const char *executable = arglist[0]; - if (!d->executable.isEmpty()) - executable = d->executable.data(); - execvp(executable, arglist); - - char resultByte = 1; - write(fd[1], &resultByte, 1); - _exit(-1); - } else if (pid_ == -1) { - // forking failed - - // commAbort(); - pid_ = 0; - free(arglist); - return false; - } - // the parent continues here - free(arglist); - - if (!commSetupDoneP()) - qDebug() << "Could not finish comm setup in parent!" << endl; - - // Check whether client could be started. - close(fd[1]); - for(;;) - { - char resultByte; - int n = ::read(fd[0], &resultByte, 1); - if (n == 1) - { - // exec() failed - close(fd[0]); - waitpid(pid_, 0, 0); - pid_ = 0; - commClose(); - return false; - } - if (n == -1) - { - if (errno == EINTR) - continue; // Ignore - } - break; // success - } - close(fd[0]); - - runs = true; - switch (runmode) - { - case Block: - for (;;) - { - commClose(); // drain only, unless obsolete reimplementation - if (!runs) - { - // commClose detected data on the process exit notifification pipe - K3ProcessController::instance()->unscheduleCheck(); - if (waitpid(pid_, &status, WNOHANG) != 0) // error finishes, too - { - commClose(); // this time for real (runs is false) - K3ProcessController::instance()->rescheduleCheck(); - break; - } - runs = true; // for next commClose() iteration - } - else - { - // commClose is an obsolete reimplementation and waited until - // all output channels were closed (or it was interrupted). - // there is a chance that it never gets here ... - waitpid(pid_, &status, 0); - runs = false; - break; - } - } - // why do we do this? i think this signal should be emitted _only_ - // after the process has successfully run _asynchronously_ --ossi - emit processExited(this); - break; - default: // NotifyOnExit & OwnGroup - input_data = 0; // Discard any data for stdin that might still be there - break; - } - return true; -} - - - -bool K3Process::kill(int signo) -{ - if (runs && pid_ > 0 && !::kill(run_mode == OwnGroup ? -pid_ : pid_, signo)) - return true; - return false; -} - - - -bool K3Process::isRunning() const -{ - return runs; -} - - - -pid_t K3Process::pid() const -{ - return pid_; -} - -#ifndef timersub -# define timersub(a, b, result) \ - do { \ - (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ - (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ - if ((result)->tv_usec < 0) { \ - --(result)->tv_sec; \ - (result)->tv_usec += 1000000; \ - } \ - } while (0) -#endif - -bool K3Process::wait(int timeout) -{ - if (!runs) - return true; - -#ifndef __linux__ - struct timeval etv; -#endif - struct timeval tv, *tvp; - if (timeout < 0) - tvp = 0; - else - { -#ifndef __linux__ - gettimeofday(&etv, 0); - etv.tv_sec += timeout; -#else - tv.tv_sec = timeout; - tv.tv_usec = 0; -#endif - tvp = &tv; - } - - int fd = K3ProcessController::instance()->notifierFd(); - for(;;) - { - fd_set fds; - FD_ZERO( &fds ); - FD_SET( fd, &fds ); - -#ifndef __linux__ - if (tvp) - { - gettimeofday(&tv, 0); - timersub(&etv, &tv, &tv); - if (tv.tv_sec < 0) - tv.tv_sec = tv.tv_usec = 0; - } -#endif - - switch( select( fd+1, &fds, 0, 0, tvp ) ) - { - case -1: - if( errno == EINTR ) - break; - // fall through; should happen if tvp->tv_sec < 0 - case 0: - K3ProcessController::instance()->rescheduleCheck(); - return false; - default: - K3ProcessController::instance()->unscheduleCheck(); - if (waitpid(pid_, &status, WNOHANG) != 0) // error finishes, too - { - processHasExited(status); - K3ProcessController::instance()->rescheduleCheck(); - return true; - } - } - } - return false; -} - - - -bool K3Process::normalExit() const -{ - return (pid_ != 0) && !runs && WIFEXITED(status); -} - - -bool K3Process::signalled() const -{ - return (pid_ != 0) && !runs && WIFSIGNALED(status); -} - - -bool K3Process::coreDumped() const -{ -#ifdef WCOREDUMP - return signalled() && WCOREDUMP(status); -#else - return false; -#endif -} - - -int K3Process::exitStatus() const -{ - return WEXITSTATUS(status); -} - - -int K3Process::exitSignal() const -{ - return WTERMSIG(status); -} - - -bool K3Process::writeStdin(const char *buffer, int buflen) -{ - // if there is still data pending, writing new data - // to stdout is not allowed (since it could also confuse - // kprocess ...) - if (input_data != 0) - return false; - - if (communication & Stdin) { - input_data = buffer; - input_sent = 0; - input_total = buflen; - innot->setEnabled(true); - if (input_total) - slotSendData(0); - return true; - } else - return false; -} - -void K3Process::suspend() -{ - if (outnot) - outnot->setEnabled(false); -} - -void K3Process::resume() -{ - if (outnot) - outnot->setEnabled(true); -} - -bool K3Process::closeStdin() -{ - if (communication & Stdin) { - communication = communication & ~Stdin; - delete innot; - innot = 0; - if (!(d->usePty & Stdin)) - close(in[1]); - in[1] = -1; - return true; - } else - return false; -} - -bool K3Process::closeStdout() -{ - if (communication & Stdout) { - communication = communication & ~Stdout; - delete outnot; - outnot = 0; - if (!(d->usePty & Stdout)) - close(out[0]); - out[0] = -1; - return true; - } else - return false; -} - -bool K3Process::closeStderr() -{ - if (communication & Stderr) { - communication = communication & ~Stderr; - delete errnot; - errnot = 0; - if (!(d->usePty & Stderr)) - close(err[0]); - err[0] = -1; - return true; - } else - return false; -} - -bool K3Process::closePty() -{ - if (d->pty && d->pty->masterFd() >= 0) { - if (d->addUtmp) - d->pty->logout(); - d->pty->close(); - return true; - } else - return false; -} - -void K3Process::closeAll() -{ - closeStdin(); - closeStdout(); - closeStderr(); - closePty(); -} - -///////////////////////////// -// protected slots // -///////////////////////////// - - - -void K3Process::slotChildOutput(int fdno) -{ - if (!childOutput(fdno)) - closeStdout(); -} - - -void K3Process::slotChildError(int fdno) -{ - if (!childError(fdno)) - closeStderr(); -} - - -void K3Process::slotSendData(int) -{ - if (input_sent == input_total) { - innot->setEnabled(false); - input_data = 0; - emit wroteStdin(this); - } else { - int result = ::write(in[1], input_data+input_sent, input_total-input_sent); - if (result >= 0) - { - input_sent += result; - } - else if ((errno != EAGAIN) && (errno != EINTR)) - { - qDebug() << "Error writing to stdin of child process" << endl; - closeStdin(); - } - } -} - -void K3Process::setUseShell(bool useShell, const char *shell) -{ - d->useShell = useShell; - if (shell && *shell) - d->shell = shell; - else -// #ifdef NON_FREE // ... as they ship non-POSIX /bin/sh -#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__GNU__) && !defined(__DragonFly__) - // Solaris POSIX ... - if (!access( "/usr/xpg4/bin/sh", X_OK )) - d->shell = "/usr/xpg4/bin/sh"; - else - // ... which links here anyway - if (!access( "/bin/ksh", X_OK )) - d->shell = "/bin/ksh"; - else - // dunno, maybe superfluous? - if (!access( "/usr/ucb/sh", X_OK )) - d->shell = "/usr/ucb/sh"; - else -#endif - d->shell = "/bin/sh"; -} - -void K3Process::setUsePty(Communication usePty, bool addUtmp) -{ - d->usePty = usePty; - d->addUtmp = addUtmp; - if (usePty) { - if (!d->pty) - d->pty = new KPty; - } else { - delete d->pty; - d->pty = 0; - } -} - -KPty *K3Process::pty() const -{ - return d->pty; -} - -QString K3Process::quote(const QString &arg) -{ - QChar q('\''); - return QString(arg).replace(q, "'\\''").prepend(q).append(q); -} - - -////////////////////////////// -// private member functions // -////////////////////////////// - - -void K3Process::processHasExited(int state) -{ - // only successfully run NotifyOnExit processes ever get here - - status = state; - runs = false; // do this before commClose, so it knows we're dead - - commClose(); // cleanup communication sockets - - if (run_mode != DontCare) - emit processExited(this); -} - - - -int K3Process::childOutput(int fdno) -{ - if (communication & NoRead) { - int len = -1; - emit receivedStdout(fdno, len); - errno = 0; // Make sure errno doesn't read "EAGAIN" - return len; - } - else - { - char buffer[1025]; - int len; - - len = ::read(fdno, buffer, 1024); - - if (len > 0) { - buffer[len] = 0; // Just in case. - emit receivedStdout(this, buffer, len); - } - return len; - } -} - -int K3Process::childError(int fdno) -{ - char buffer[1025]; - int len; - - len = ::read(fdno, buffer, 1024); - - if (len > 0) { - buffer[len] = 0; // Just in case. - emit receivedStderr(this, buffer, len); - } - return len; -} - - -int K3Process::setupCommunication(Communication comm) -{ - // PTY stuff // - if (d->usePty) - { - // cannot communicate on both stderr and stdout if they are both on the pty - if (!(~(comm & d->usePty) & (Stdout | Stderr))) { - qWarning() << "Invalid usePty/communication combination (" << d->usePty << "/" << comm << ")" << endl; - return 0; - } - if (!d->pty->open()) - return 0; - - int rcomm = comm & d->usePty; - int mfd = d->pty->masterFd(); - if (rcomm & Stdin) - in[1] = mfd; - if (rcomm & Stdout) - out[0] = mfd; - if (rcomm & Stderr) - err[0] = mfd; - } - - communication = comm; - - comm = comm & ~d->usePty; - if (comm & Stdin) { - if (socketpair(AF_UNIX, SOCK_STREAM, 0, in)) - goto fail0; - fcntl(in[0], F_SETFD, FD_CLOEXEC); - fcntl(in[1], F_SETFD, FD_CLOEXEC); - } - if (comm & Stdout) { - if (socketpair(AF_UNIX, SOCK_STREAM, 0, out)) - goto fail1; - fcntl(out[0], F_SETFD, FD_CLOEXEC); - fcntl(out[1], F_SETFD, FD_CLOEXEC); - } - if (comm & Stderr) { - if (socketpair(AF_UNIX, SOCK_STREAM, 0, err)) - goto fail2; - fcntl(err[0], F_SETFD, FD_CLOEXEC); - fcntl(err[1], F_SETFD, FD_CLOEXEC); - } - return 1; // Ok - fail2: - if (comm & Stdout) - { - close(out[0]); - close(out[1]); - out[0] = out[1] = -1; - } - fail1: - if (comm & Stdin) - { - close(in[0]); - close(in[1]); - in[0] = in[1] = -1; - } - fail0: - communication = NoCommunication; - return 0; // Error -} - - - -int K3Process::commSetupDoneP() -{ - int rcomm = communication & ~d->usePty; - if (rcomm & Stdin) - close(in[0]); - if (rcomm & Stdout) - close(out[1]); - if (rcomm & Stderr) - close(err[1]); - in[0] = out[1] = err[1] = -1; - - // Don't create socket notifiers if no interactive comm is to be expected - if (run_mode != NotifyOnExit && run_mode != OwnGroup) - return 1; - - if (communication & Stdin) { - fcntl(in[1], F_SETFL, O_NONBLOCK | fcntl(in[1], F_GETFL)); - innot = new QSocketNotifier(in[1], QSocketNotifier::Write, this); - Q_CHECK_PTR(innot); - innot->setEnabled(false); // will be enabled when data has to be sent - QObject::connect(innot, SIGNAL(activated(int)), - this, SLOT(slotSendData(int))); - } - - if (communication & Stdout) { - outnot = new QSocketNotifier(out[0], QSocketNotifier::Read, this); - Q_CHECK_PTR(outnot); - QObject::connect(outnot, SIGNAL(activated(int)), - this, SLOT(slotChildOutput(int))); - if (communication & NoRead) - suspend(); - } - - if (communication & Stderr) { - errnot = new QSocketNotifier(err[0], QSocketNotifier::Read, this ); - Q_CHECK_PTR(errnot); - QObject::connect(errnot, SIGNAL(activated(int)), - this, SLOT(slotChildError(int))); - } - - return 1; -} - - - -int K3Process::commSetupDoneC() -{ - int ok = 1; - if (d->usePty & Stdin) { - if (dup2(d->pty->slaveFd(), STDIN_FILENO) < 0) ok = 0; - } else if (communication & Stdin) { - if (dup2(in[0], STDIN_FILENO) < 0) ok = 0; - } else { - int null_fd = open( "/dev/null", O_RDONLY ); - if (dup2( null_fd, STDIN_FILENO ) < 0) ok = 0; - close( null_fd ); - } - struct linger so; - memset(&so, 0, sizeof(so)); - if (d->usePty & Stdout) { - if (dup2(d->pty->slaveFd(), STDOUT_FILENO) < 0) ok = 0; - } else if (communication & Stdout) { - if (dup2(out[1], STDOUT_FILENO) < 0 || - setsockopt(out[1], SOL_SOCKET, SO_LINGER, (char *)&so, sizeof(so))) - ok = 0; - if (communication & MergedStderr) { - if (dup2(out[1], STDERR_FILENO) < 0) - ok = 0; - } - } - if (d->usePty & Stderr) { - if (dup2(d->pty->slaveFd(), STDERR_FILENO) < 0) ok = 0; - } else if (communication & Stderr) { - if (dup2(err[1], STDERR_FILENO) < 0 || - setsockopt(err[1], SOL_SOCKET, SO_LINGER, (char *)&so, sizeof(so))) - ok = 0; - } - - // don't even think about closing all open fds here or anywhere else - - // PTY stuff // - if (d->usePty) { - d->pty->setCTty(); - if (d->addUtmp) - d->pty->login(getenv("USER"), getenv("DISPLAY")); - } - - return ok; -} - - - -void K3Process::commClose() -{ - closeStdin(); - - if (pid_) { // detached, failed, and killed processes have no output. basta. :) - // If both channels are being read we need to make sure that one socket - // buffer doesn't fill up whilst we are waiting for data on the other - // (causing a deadlock). Hence we need to use select. - - int notfd = K3ProcessController::instance()->notifierFd(); - - while ((communication & (Stdout | Stderr)) || runs) { - fd_set rfds; - FD_ZERO(&rfds); - struct timeval timeout, *p_timeout; - - int max_fd = 0; - if (communication & Stdout) { - FD_SET(out[0], &rfds); - max_fd = out[0]; - } - if (communication & Stderr) { - FD_SET(err[0], &rfds); - if (err[0] > max_fd) - max_fd = err[0]; - } - if (runs) { - FD_SET(notfd, &rfds); - if (notfd > max_fd) - max_fd = notfd; - // If the process is still running we block until we - // receive data or the process exits. - p_timeout = 0; // no timeout - } else { - // If the process has already exited, we only check - // the available data, we don't wait for more. - timeout.tv_sec = timeout.tv_usec = 0; // timeout immediately - p_timeout = &timeout; - } - - int fds_ready = select(max_fd+1, &rfds, 0, 0, p_timeout); - if (fds_ready < 0) { - if (errno == EINTR) - continue; - break; - } else if (!fds_ready) - break; - - if ((communication & Stdout) && FD_ISSET(out[0], &rfds)) - slotChildOutput(out[0]); - - if ((communication & Stderr) && FD_ISSET(err[0], &rfds)) - slotChildError(err[0]); - - if (runs && FD_ISSET(notfd, &rfds)) { - runs = false; // hack: signal potential exit - return; // don't close anything, we will be called again - } - } - } - - closeStdout(); - closeStderr(); - - closePty(); -} - - - -/////////////////////////// -// CC: Class K3ShellProcess -/////////////////////////// - -K3ShellProcess::K3ShellProcess(const char *shellname): - K3Process(), d(0) -{ - setUseShell( true, shellname ? shellname : getenv("SHELL") ); -} - -K3ShellProcess::~K3ShellProcess() { -} - -QString K3ShellProcess::quote(const QString &arg) -{ - return K3Process::quote(arg); -} - -bool K3ShellProcess::start(RunMode runmode, Communication comm) -{ - return K3Process::start(runmode, comm); -} - - -//#include "moc_k3process.cpp"
deleted file mode 100644 --- a/gui/qtermwidget/lib/k3process.h +++ /dev/null @@ -1,890 +0,0 @@ -/* This file is part of the KDE libraries - Copyright (C) 1997 Christian Czezakte (e9025461@student.tuwien.ac.at) - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#ifndef K3PROCESS_H -#define K3PROCESS_H - -#include <QtCore/QObject> - -#include <sys/types.h> // for pid_t -#include <sys/wait.h> -#include <signal.h> -#include <unistd.h> - -class QSocketNotifier; -class K3ProcessPrivate; -class KPty; - -/** - * @obsolete Use KProcess and KPtyProcess instead. - * - * Child process invocation, monitoring and control. - * This class works only in the application's main thread. - * - * <b>General usage and features:</b>\n - * - * This class allows a KDE application to start child processes without having - * to worry about UN*X signal handling issues and zombie process reaping. - * - * @see K3ProcIO - * - * Basically, this class distinguishes three different ways of running - * child processes: - * - * @li DontCare -- The child process is invoked and both the child - * process and the parent process continue concurrently. - * - * The process is started in an own session (see setsid(2)). - * - * @li NotifyOnExit -- The child process is invoked and both the - * child and the parent process run concurrently. - * - * When the child process exits, the K3Process instance - * corresponding to it emits the Qt signal processExited(). - * Since this signal is @em not emitted from within a UN*X - * signal handler, arbitrary function calls can be made. - * - * Be aware: When the K3Process object gets destructed, the child - * process will be killed if it is still running! - * This means in particular, that it usually makes no sense to use - * a K3Process on the stack with NotifyOnExit. - * - * @li OwnGroup -- like NotifyOnExit, but the child process is started - * in an own process group (and an own session, FWIW). The behavior of - * kill() changes to killing the whole process group - this makes - * this mode useful for implementing primitive job management. It can be - * used to work around broken wrapper scripts that don't propagate signals - * to the "real" program. However, use this with care, as you disturb the - * shell's job management if your program is started from the command line. - * - * @li Block -- The child process starts and the parent process - * is suspended until the child process exits. (@em Really not recommended - * for programs with a GUI.) - * In this mode the parent can read the child's output, but can't send it any - * input. - * - * K3Process also provides several functions for determining the exit status - * and the pid of the child process it represents. - * - * Furthermore it is possible to supply command-line arguments to the process - * in a clean fashion (no null-terminated stringlists and such...) - * - * A small usage example: - * \code - * K3Process *proc = new K3Process; - * - * *proc << "my_executable"; - * *proc << "These" << "are" << "the" << "command" << "line" << "args"; - * QApplication::connect(proc, SIGNAL(processExited(K3Process *)), - * pointer_to_my_object, SLOT(my_objects_slot(K3Process *))); - * proc->start(); - * \endcode - * - * This will start "my_executable" with the commandline arguments "These"... - * - * When the child process exits, the slot will be invoked. - * - * <b>Communication with the child process:</b>\n - * - * K3Process supports communication with the child process through - * stdin/stdout/stderr. - * - * The following functions are provided for getting data from the child - * process or sending data to the child's stdin (For more information, - * have a look at the documentation of each function): - * - * @li writeStdin() - * -- Transmit data to the child process' stdin. When all data was sent, the - * signal wroteStdin() is emitted. - * - * @li When data arrives at stdout or stderr, the signal receivedStdout() - * resp. receivedStderr() is emitted. - * - * @li You can shut down individual communication channels with - * closeStdin(), closeStdout(), and closeStderr(), resp. - * - * @author Christian Czezatke e9025461@student.tuwien.ac.at - * - **/ -class K3Process : public QObject -{ - Q_OBJECT - -public: - - /** - * Modes in which the communication channels can be opened. - * - * If communication for more than one channel is required, - * the values should be or'ed together, for example to get - * communication with stdout as well as with stdin, you would - * specify @p Stdin | @p Stdout - * - */ - enum CommunicationFlag { - NoCommunication = 0, /**< No communication with the process. */ - Stdin = 1, /**< Connect to write to the process with writeStdin(). */ - Stdout = 2, /**< Connect to read from the process' output. */ - Stderr = 4, /**< Connect to read from the process' stderr. */ - AllOutput = 6, /**< Connects to all output channels. */ - All = 7, /**< Connects to all channels. */ - NoRead = 8, /**< If specified with Stdout, no data is actually read from stdout, - * only the signal receivedStdout(int fd, int &len) is emitted. */ - CTtyOnly = NoRead, /**< Tells setUsePty() to create a PTY for the process - * and make it the process' controlling TTY, but does not - * redirect any I/O channel to the PTY. */ - MergedStderr = 16 /**< If specified with Stdout, the process' stderr will be - * redirected onto the same file handle as its stdout, i.e., - * all error output will be signalled with receivedStdout(). - * Don't specify Stderr if you specify MergedStderr. */ - }; - - Q_DECLARE_FLAGS(Communication, CommunicationFlag) - - /** - * Run-modes for a child process. - */ - enum RunMode { - /** - * The application does not receive notifications from the subprocess when - * it is finished or aborted. - */ - DontCare, - /** - * The application is notified when the subprocess dies. - */ - NotifyOnExit, - /** - * The application is suspended until the started process is finished. - */ - Block, - /** - * Same as NotifyOnExit, but the process is run in an own session, - * just like with DontCare. - */ - OwnGroup - }; - - /** - * Constructor - */ - explicit K3Process( QObject* parent=0L ); - - /** - *Destructor: - * - * If the process is running when the destructor for this class - * is called, the child process is killed with a SIGKILL, but - * only if the run mode is not of type @p DontCare. - * Processes started as @p DontCare keep running anyway. - */ - virtual ~K3Process(); - - /** - * Sets the executable and the command line argument list for this process. - * - * For example, doing an "ls -l /usr/local/bin" can be achieved by: - * \code - * K3Process p; - * ... - * p << "ls" << "-l" << "/usr/local/bin" - * \endcode - * - * @param arg the argument to add - * @return a reference to this K3Process - **/ - K3Process &operator<<(const QString& arg); - /** - * Similar to previous method, takes a char *, supposed to be in locale 8 bit already. - */ - K3Process &operator<<(const char * arg); - /** - * Similar to previous method, takes a QByteArray, supposed to be in locale 8 bit already. - * @param arg the argument to add - * @return a reference to this K3Process - */ - K3Process &operator<<(const QByteArray & arg); - - /** - * Sets the executable and the command line argument list for this process, - * in a single method call, or add a list of arguments. - * @param args the arguments to add - * @return a reference to this K3Process - **/ - K3Process &operator<<(const QStringList& args); - - /** - * Clear a command line argument list that has been set by using - * operator<<. - */ - void clearArguments(); - - /** - * Starts the process. - * For a detailed description of the - * various run modes and communication semantics, have a look at the - * general description of the K3Process class. Note that if you use - * setUsePty( Stdout | Stderr, \<bool\> ), you cannot use Stdout | Stderr - * here - instead, use Stdout only to receive the mixed output. - * - * The following problems could cause this function to - * return false: - * - * @li The process is already running. - * @li The command line argument list is empty. - * @li The the @p comm parameter is incompatible with the selected pty usage. - * @li The starting of the process failed (could not fork). - * @li The executable was not found. - * - * @param runmode The Run-mode for the process. - * @param comm Specifies which communication channels should be - * established to the child process (stdin/stdout/stderr). By default, - * no communication takes place and the respective communication - * signals will never get emitted. - * - * @return true on success, false on error - * (see above for error conditions) - **/ - virtual bool start(RunMode runmode = NotifyOnExit, - Communication comm = NoCommunication); - - /** - * Stop the process (by sending it a signal). - * - * @param signo The signal to send. The default is SIGTERM. - * @return true if the signal was delivered successfully. - */ - virtual bool kill(int signo = SIGTERM); - - /** - * Checks whether the process is running. - * @return true if the process is (still) considered to be running - */ - bool isRunning() const; - - /** Returns the process id of the process. - * - * If it is called after - * the process has exited, it returns the process id of the last - * child process that was created by this instance of K3Process. - * - * Calling it before any child process has been started by this - * K3Process instance causes pid() to return 0. - * @return the pid of the process or 0 if no process has been started yet. - **/ - pid_t pid() const; - - /** - * Suspend processing of data from stdout of the child process. - */ - void suspend(); - - /** - * Resume processing of data from stdout of the child process. - */ - void resume(); - - /** - * Suspend execution of the current thread until the child process dies - * or the timeout hits. This function is not recommended for programs - * with a GUI. - * @param timeout timeout in seconds. -1 means wait indefinitely. - * @return true if the process exited, false if the timeout hit. - */ - bool wait(int timeout = -1); - - /** - * Checks whether the process exited cleanly. - * - * @return true if the process has already finished and has exited - * "voluntarily", ie: it has not been killed by a signal. - */ - bool normalExit() const; - - /** - * Checks whether the process was killed by a signal. - * - * @return true if the process has already finished and has not exited - * "voluntarily", ie: it has been killed by a signal. - */ - bool signalled() const; - - /** - * Checks whether a killed process dumped core. - * - * @return true if signalled() returns true and the process - * dumped core. Note that on systems that don't define the - * WCOREDUMP macro, the return value is always false. - */ - bool coreDumped() const; - - /** - * Returns the exit status of the process. - * - * @return the exit status of the process. Note that this value - * is not valid if normalExit() returns false. - */ - int exitStatus() const; - - /** - * Returns the signal the process was killed by. - * - * @return the signal number that caused the process to exit. - * Note that this value is not valid if signalled() returns false. - */ - int exitSignal() const; - - /** - * Transmit data to the child process' stdin. - * - * This function may return false in the following cases: - * - * @li The process is not currently running. - * This implies that you cannot use this function in Block mode. - * - * @li Communication to stdin has not been requested in the start() call. - * - * @li Transmission of data to the child process by a previous call to - * writeStdin() is still in progress. - * - * Please note that the data is sent to the client asynchronously, - * so when this function returns, the data might not have been - * processed by the child process. - * That means that you must not free @p buffer or call writeStdin() - * again until either a wroteStdin() signal indicates that the - * data has been sent or a processExited() signal shows that - * the child process is no longer alive. - * - * If all the data has been sent to the client, the signal - * wroteStdin() will be emitted. - * - * This function does not work when the process is start()ed in Block mode. - * - * @param buffer the buffer to write - * @param buflen the length of the buffer - * @return false if an error has occurred - **/ - bool writeStdin(const char *buffer, int buflen); - - /** - * Shuts down the Stdin communication link. If no pty is used, this - * causes "EOF" to be indicated on the child's stdin file descriptor. - * - * @return false if no Stdin communication link exists (any more). - */ - bool closeStdin(); - - /** - * Shuts down the Stdout communication link. If no pty is used, any further - * attempts by the child to write to its stdout file descriptor will cause - * it to receive a SIGPIPE. - * - * @return false if no Stdout communication link exists (any more). - */ - bool closeStdout(); - - /** - * Shuts down the Stderr communication link. If no pty is used, any further - * attempts by the child to write to its stderr file descriptor will cause - * it to receive a SIGPIPE. - * - * @return false if no Stderr communication link exists (any more). - */ - bool closeStderr(); - - /** - * Deletes the optional utmp entry and closes the pty. - * - * Make sure to shut down any communication links that are using the pty - * before calling this function. - * - * @return false if the pty is not open (any more). - */ - bool closePty(); - - /** - * @brief Close stdin, stdout, stderr and the pty - * - * This is the same that calling all close* functions in a row: - * @see closeStdin, @see closeStdout, @see closeStderr and @see closePty - */ - void closeAll(); - - /** - * Lets you see what your arguments are for debugging. - * @return the list of arguments - */ - const QList<QByteArray> &args() /* const */ { return arguments; } - - /** - * Controls whether the started process should drop any - * setuid/setgid privileges or whether it should keep them. - * Note that this function is mostly a dummy, as the KDE libraries - * currently refuse to run with setuid/setgid privileges. - * - * The default is false: drop privileges - * @param keepPrivileges true to keep the privileges - */ - void setRunPrivileged(bool keepPrivileges); - - /** - * Returns whether the started process will drop any - * setuid/setgid privileges or whether it will keep them. - * @return true if the process runs privileged - */ - bool runPrivileged() const; - - /** - * Adds the variable @p name to the process' environment. - * This function must be called before starting the process. - * @param name the name of the environment variable - * @param value the new value for the environment variable - */ - void setEnvironment(const QString &name, const QString &value); - - /** - * Changes the current working directory (CWD) of the process - * to be started. - * This function must be called before starting the process. - * @param dir the new directory - */ - void setWorkingDirectory(const QString &dir); - - /** - * Specify whether to start the command via a shell or directly. - * The default is to start the command directly. - * If @p useShell is true @p shell will be used as shell, or - * if shell is empty, /bin/sh will be used. - * - * When using a shell, the caller should make sure that all filenames etc. - * are properly quoted when passed as argument. - * @see quote() - * @param useShell true if the command should be started via a shell - * @param shell the path to the shell that will execute the process, or - * 0 to use /bin/sh. Use getenv("SHELL") to use the user's - * default shell, but note that doing so is usually a bad idea - * for shell compatibility reasons. - */ - void setUseShell(bool useShell, const char *shell = 0); - - /** - * This function can be used to quote an argument string such that - * the shell processes it properly. This is e. g. necessary for - * user-provided file names which may contain spaces or quotes. - * It also prevents expansion of wild cards and environment variables. - * @param arg the argument to quote - * @return the quoted argument - */ - static QString quote(const QString &arg); - - /** - * Detaches K3Process from child process. All communication is closed. - * No exit notification is emitted any more for the child process. - * Deleting the K3Process will no longer kill the child process. - * Note that the current process remains the parent process of the - * child process. - */ - void detach(); - - /** - * Specify whether to create a pty (pseudo-terminal) for running the - * command. - * This function should be called before starting the process. - * - * @param comm for which stdio handles to use a pty. Note that it is not - * allowed to specify Stdout and Stderr at the same time both here and to - * start (there is only one pty, so they cannot be distinguished). - * @param addUtmp true if a utmp entry should be created for the pty - */ - void setUsePty(Communication comm, bool addUtmp); - - /** - * Obtains the pty object used by this process. The return value is - * valid only after setUsePty() was used with a non-zero argument. - * The pty is open only while the process is running. - * @return a pointer to the pty object - */ - KPty *pty() const; - - /** - * More or less intuitive constants for use with setPriority(). - */ - enum { PrioLowest = 20, PrioLow = 10, PrioLower = 5, PrioNormal = 0, - PrioHigher = -5, PrioHigh = -10, PrioHighest = -19 }; - - /** - * Sets the scheduling priority of the process. - * @param prio the new priority in the range -20 (high) to 19 (low). - * @return false on error; see setpriority(2) for possible reasons. - */ - bool setPriority(int prio); - -Q_SIGNALS: - /** - * Emitted after the process has terminated when - * the process was run in the @p NotifyOnExit (==default option to - * start() ) or the Block mode. - * @param proc a pointer to the process that has exited - **/ - void processExited(K3Process *proc); - - - /** - * Emitted, when output from the child process has - * been received on stdout. - * - * To actually get this signal, the Stdout communication link - * has to be turned on in start(). - * - * @param proc a pointer to the process that has received the output - * @param buffer The data received. - * @param buflen The number of bytes that are available. - * - * You should copy the information contained in @p buffer to your private - * data structures before returning from the slot. - * Example: - * \code - * QString myBuf = QLatin1String(buffer, buflen); - * \endcode - **/ - void receivedStdout(K3Process *proc, char *buffer, int buflen); - - /** - * Emitted when output from the child process has - * been received on stdout. - * - * To actually get this signal, the Stdout communication link - * has to be turned on in start() and the - * NoRead flag must have been passed. - * - * You will need to explicitly call resume() after your call to start() - * to begin processing data from the child process' stdout. This is - * to ensure that this signal is not emitted when no one is connected - * to it, otherwise this signal will not be emitted. - * - * The data still has to be read from file descriptor @p fd. - * @param fd the file descriptor that provides the data - * @param len the number of bytes that have been read from @p fd must - * be written here - **/ - void receivedStdout(int fd, int &len); // KDE4: change, broken API - - - /** - * Emitted, when output from the child process has - * been received on stderr. - * - * To actually get this signal, the Stderr communication link - * has to be turned on in start(). - * - * You should copy the information contained in @p buffer to your private - * data structures before returning from the slot. - * - * @param proc a pointer to the process that has received the data - * @param buffer The data received. - * @param buflen The number of bytes that are available. - **/ - void receivedStderr(K3Process *proc, char *buffer, int buflen); - - /** - * Emitted after all the data that has been - * specified by a prior call to writeStdin() has actually been - * written to the child process. - * @param proc a pointer to the process - **/ - void wroteStdin(K3Process *proc); - - -protected Q_SLOTS: - - /** - * This slot gets activated when data from the child's stdout arrives. - * It usually calls childOutput(). - * @param fdno the file descriptor for the output - */ - void slotChildOutput(int fdno); - - /** - * This slot gets activated when data from the child's stderr arrives. - * It usually calls childError(). - * @param fdno the file descriptor for the output - */ - void slotChildError(int fdno); - - /** - * Called when another bulk of data can be sent to the child's - * stdin. If there is no more data to be sent to stdin currently - * available, this function must disable the QSocketNotifier innot. - * @param dummy ignore this argument - */ - void slotSendData(int dummy); // KDE 4: remove dummy - -protected: - - /** - * Sets up the environment according to the data passed via - * setEnvironment() - */ - void setupEnvironment(); - - /** - * The list of the process' command line arguments. The first entry - * in this list is the executable itself. - */ - QList<QByteArray> arguments; - /** - * How to run the process (Block, NotifyOnExit, DontCare). You should - * not modify this data member directly from derived classes. - */ - RunMode run_mode; - /** - * true if the process is currently running. You should not - * modify this data member directly from derived classes. Please use - * isRunning() for reading the value of this data member since it - * will probably be made private in later versions of K3Process. - */ - bool runs; - - /** - * The PID of the currently running process. - * You should not modify this data member in derived classes. - * Please use pid() instead of directly accessing this - * member since it will probably be made private in - * later versions of K3Process. - */ - pid_t pid_; - - /** - * The process' exit status as returned by waitpid(). You should not - * modify the value of this data member from derived classes. You should - * rather use exitStatus() than accessing this data member directly - * since it will probably be made private in further versions of - * K3Process. - */ - int status; - - - /** - * If false, the child process' effective uid & gid will be reset to the - * real values. - * @see setRunPrivileged() - */ - bool keepPrivs; - - /** - * This function is called from start() right before a fork() takes - * place. According to the @p comm parameter this function has to initialize - * the in, out and err data members of K3Process. - * - * This function should return 1 if setting the needed communication channels - * was successful. - * - * The default implementation is to create UNIX STREAM sockets for the - * communication, but you could reimplement this function to establish a - * TCP/IP communication for network communication, for example. - */ - virtual int setupCommunication(Communication comm); - - /** - * Called right after a (successful) fork() on the parent side. This function - * will usually do some communications cleanup, like closing in[0], - * out[1] and out[1]. - * - * Furthermore, it must also create the QSocketNotifiers innot, - * outnot and errnot and connect their Qt signals to the respective - * K3Process slots. - * - * For a more detailed explanation, it is best to have a look at the default - * implementation in kprocess.cpp. - */ - virtual int commSetupDoneP(); - - /** - * Called right after a (successful) fork(), but before an exec() on the child - * process' side. It usually duplicates the in[0], out[1] and - * err[1] file handles to the respective standard I/O handles. - */ - virtual int commSetupDoneC(); - - - /** - * Immediately called after a successfully started process in NotifyOnExit - * mode has exited. This function normally calls commClose() - * and emits the processExited() signal. - * @param state the exit code of the process as returned by waitpid() - */ - virtual void processHasExited(int state); - - /** - * Cleans up the communication links to the child after it has exited. - * This function should act upon the values of pid() and runs. - * See the kprocess.cpp source for details. - * @li If pid() returns zero, the communication links should be closed - * only. - * @li if pid() returns non-zero and runs is false, all data - * immediately available from the communication links should be processed - * before closing them. - * @li if pid() returns non-zero and runs is true, the communication - * links should be monitored for data until the file handle returned by - * K3ProcessController::theKProcessController->notifierFd() becomes ready - * for reading - when it triggers, runs should be reset to false, and - * the function should be immediately left without closing anything. - * - * The previous semantics of this function are forward-compatible, but should - * be avoided, as they are prone to race conditions and can cause K3Process - * (and thus the whole program) to lock up under certain circumstances. At the - * end the function closes the communication links in any case. Additionally - * @li if runs is true, the communication links are monitored for data - * until all of them have returned EOF. Note that if any system function is - * interrupted (errno == EINTR) the polling loop should be aborted. - * @li if runs is false, all data immediately available from the - * communication links is processed. - */ - virtual void commClose(); - - /* KDE 4 - commClose will be changed to perform cleanup only in all cases * - * If @p notfd is -1, all data immediately available from the - * communication links should be processed. - * If @p notfd is not -1, the communication links should be monitored - * for data until the file handle @p notfd becomes ready for reading. - */ -// virtual void commDrain(int notfd); - - /** - * Specify the actual executable that should be started (first argument to execve) - * Normally the the first argument is the executable but you can - * override that with this function. - */ - void setBinaryExecutable(const char *filename); - - /** - * The socket descriptors for stdout. - */ - int out[2]; - /** - * The socket descriptors for stdin. - */ - int in[2]; - /** - * The socket descriptors for stderr. - */ - int err[2]; - - /** - * The socket notifier for in[1]. - */ - QSocketNotifier *innot; - /** - * The socket notifier for out[0]. - */ - QSocketNotifier *outnot; - /** - * The socket notifier for err[0]. - */ - QSocketNotifier *errnot; - - /** - * Lists the communication links that are activated for the child - * process. Should not be modified from derived classes. - */ - Communication communication; - - /** - * Called by slotChildOutput() this function copies data arriving from - * the child process' stdout to the respective buffer and emits the signal - * receivedStdout(). - */ - int childOutput(int fdno); - - /** - * Called by slotChildError() this function copies data arriving from - * the child process' stderr to the respective buffer and emits the signal - * receivedStderr(). - */ - int childError(int fdno); - - /** - * The buffer holding the data that has to be sent to the child - */ - const char *input_data; - /** - * The number of bytes already transmitted - */ - int input_sent; - /** - * The total length of input_data - */ - int input_total; - - /** - * K3ProcessController is a friend of K3Process because it has to have - * access to various data members. - */ - friend class K3ProcessController; - -private: - K3ProcessPrivate* const d; -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(K3Process::Communication) - -class K3ShellProcessPrivate; - -/** -* @obsolete -* -* Use K3Process and K3Process::setUseShell(true) instead. -* -* @short A class derived from K3Process to start child -* processes through a shell. -* @author Christian Czezatke <e9025461@student.tuwien.ac.at> -*/ -class K3ShellProcess : public K3Process -{ - Q_OBJECT - -public: - - /** - * Constructor - * - * If no shellname is specified, the user's default shell is used. - */ - explicit K3ShellProcess(const char *shellname=0); - - /** - * Destructor. - */ - ~K3ShellProcess(); - - virtual bool start(RunMode runmode = NotifyOnExit, - Communication comm = NoCommunication); - - static QString quote(const QString &arg); - -private: - K3ShellProcessPrivate* const d; -}; - - - -#endif -
deleted file mode 100644 --- a/gui/qtermwidget/lib/k3processcontroller.cpp +++ /dev/null @@ -1,334 +0,0 @@ -/* This file is part of the KDE libraries - Copyright (C) 1997 Christian Czezakte (e9025461@student.tuwien.ac.at) - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#include "k3processcontroller.h" -#include "k3process.h" - -//#include <config.h> - -#include <sys/time.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <unistd.h> -#include <errno.h> -#include <fcntl.h> -#include <stdio.h> -#include <stdlib.h> - -#include <QtCore/QSocketNotifier> - -class K3ProcessController::Private -{ -public: - Private() - : needcheck( false ), - notifier( 0 ) - { - } - - ~Private() - { - delete notifier; - } - - int fd[2]; - bool needcheck; - QSocketNotifier *notifier; - QList<K3Process*> kProcessList; - QList<int> unixProcessList; - static struct sigaction oldChildHandlerData; - static bool handlerSet; - static int refCount; - static K3ProcessController* instance; -}; - -K3ProcessController *K3ProcessController::Private::instance = 0; -int K3ProcessController::Private::refCount = 0; - -void K3ProcessController::ref() -{ - if ( !Private::refCount ) { - Private::instance = new K3ProcessController; - setupHandlers(); - } - Private::refCount++; -} - -void K3ProcessController::deref() -{ - Private::refCount--; - if( !Private::refCount ) { - resetHandlers(); - delete Private::instance; - Private::instance = 0; - } -} - -K3ProcessController* K3ProcessController::instance() -{ - /* - * there were no safety guards in previous revisions, is that ok? - if ( !Private::instance ) { - ref(); - } - */ - - return Private::instance; -} - -K3ProcessController::K3ProcessController() - : d( new Private ) -{ - if( pipe( d->fd ) ) - { - perror( "pipe" ); - abort(); - } - - fcntl( d->fd[0], F_SETFL, O_NONBLOCK ); // in case slotDoHousekeeping is called without polling first - fcntl( d->fd[1], F_SETFL, O_NONBLOCK ); // in case it fills up - fcntl( d->fd[0], F_SETFD, FD_CLOEXEC ); - fcntl( d->fd[1], F_SETFD, FD_CLOEXEC ); - - d->notifier = new QSocketNotifier( d->fd[0], QSocketNotifier::Read ); - d->notifier->setEnabled( true ); - QObject::connect( d->notifier, SIGNAL(activated(int)), - SLOT(slotDoHousekeeping())); -} - -K3ProcessController::~K3ProcessController() -{ -#ifndef Q_OS_MAC -/* not sure why, but this is causing lockups */ - close( d->fd[0] ); - close( d->fd[1] ); -#else -#warning FIXME: why does close() freeze up destruction? -#endif - - delete d; -} - - -extern "C" { -static void theReaper( int num ) -{ - K3ProcessController::theSigCHLDHandler( num ); -} -} - -#ifdef Q_OS_UNIX -struct sigaction K3ProcessController::Private::oldChildHandlerData; -#endif -bool K3ProcessController::Private::handlerSet = false; - -void K3ProcessController::setupHandlers() -{ - if( Private::handlerSet ) - return; - Private::handlerSet = true; - -#ifdef Q_OS_UNIX - struct sigaction act; - sigemptyset( &act.sa_mask ); - - act.sa_handler = SIG_IGN; - act.sa_flags = 0; - sigaction( SIGPIPE, &act, 0L ); - - act.sa_handler = theReaper; - act.sa_flags = SA_NOCLDSTOP; - // CC: take care of SunOS which automatically restarts interrupted system - // calls (and thus does not have SA_RESTART) -#ifdef SA_RESTART - act.sa_flags |= SA_RESTART; -#endif - sigaction( SIGCHLD, &act, &Private::oldChildHandlerData ); - - sigaddset( &act.sa_mask, SIGCHLD ); - // Make sure we don't block this signal. gdb tends to do that :-( - sigprocmask( SIG_UNBLOCK, &act.sa_mask, 0 ); -#else - //TODO: win32 -#endif -} - -void K3ProcessController::resetHandlers() -{ - if( !Private::handlerSet ) - return; - Private::handlerSet = false; - -#ifdef Q_OS_UNIX - sigset_t mask, omask; - sigemptyset( &mask ); - sigaddset( &mask, SIGCHLD ); - sigprocmask( SIG_BLOCK, &mask, &omask ); - - struct sigaction act; - sigaction( SIGCHLD, &Private::oldChildHandlerData, &act ); - if (act.sa_handler != theReaper) { - sigaction( SIGCHLD, &act, 0 ); - Private::handlerSet = true; - } - - sigprocmask( SIG_SETMASK, &omask, 0 ); -#else - //TODO: win32 -#endif - // there should be no problem with SIGPIPE staying SIG_IGN -} - -// the pipe is needed to sync the child reaping with our event processing, -// as otherwise there are race conditions, locking requirements, and things -// generally get harder -void K3ProcessController::theSigCHLDHandler( int arg ) -{ - int saved_errno = errno; - - char dummy = 0; - ::write( instance()->d->fd[1], &dummy, 1 ); - -#ifdef Q_OS_UNIX - if ( Private::oldChildHandlerData.sa_handler != SIG_IGN && - Private::oldChildHandlerData.sa_handler != SIG_DFL ) { - Private::oldChildHandlerData.sa_handler( arg ); // call the old handler - } -#else - //TODO: win32 -#endif - - errno = saved_errno; -} - -int K3ProcessController::notifierFd() const -{ - return d->fd[0]; -} - -void K3ProcessController::unscheduleCheck() -{ - char dummy[16]; // somewhat bigger - just in case several have queued up - if( ::read( d->fd[0], dummy, sizeof(dummy) ) > 0 ) - d->needcheck = true; -} - -void -K3ProcessController::rescheduleCheck() -{ - if( d->needcheck ) - { - d->needcheck = false; - char dummy = 0; - ::write( d->fd[1], &dummy, 1 ); - } -} - -void K3ProcessController::slotDoHousekeeping() -{ - char dummy[16]; // somewhat bigger - just in case several have queued up - ::read( d->fd[0], dummy, sizeof(dummy) ); - - int status; - again: - QList<K3Process*>::iterator it( d->kProcessList.begin() ); - QList<K3Process*>::iterator eit( d->kProcessList.end() ); - while( it != eit ) - { - K3Process *prc = *it; - if( prc->runs && waitpid( prc->pid_, &status, WNOHANG ) > 0 ) - { - prc->processHasExited( status ); - // the callback can nuke the whole process list and even 'this' - if (!instance()) - return; - goto again; - } - ++it; - } - QList<int>::iterator uit( d->unixProcessList.begin() ); - QList<int>::iterator ueit( d->unixProcessList.end() ); - while( uit != ueit ) - { - if( waitpid( *uit, 0, WNOHANG ) > 0 ) - { - uit = d->unixProcessList.erase( uit ); - deref(); // counterpart to addProcess, can invalidate 'this' - } else - ++uit; - } -} - -bool K3ProcessController::waitForProcessExit( int timeout ) -{ -#ifdef Q_OS_UNIX - for(;;) - { - struct timeval tv, *tvp; - if (timeout < 0) - tvp = 0; - else - { - tv.tv_sec = timeout; - tv.tv_usec = 0; - tvp = &tv; - } - - fd_set fds; - FD_ZERO( &fds ); - FD_SET( d->fd[0], &fds ); - - switch( select( d->fd[0]+1, &fds, 0, 0, tvp ) ) - { - case -1: - if( errno == EINTR ) - continue; - // fall through; should never happen - case 0: - return false; - default: - slotDoHousekeeping(); - return true; - } - } -#else - //TODO: win32 - return false; -#endif -} - -void K3ProcessController::addKProcess( K3Process* p ) -{ - d->kProcessList.append( p ); -} - -void K3ProcessController::removeKProcess( K3Process* p ) -{ - d->kProcessList.removeAll( p ); -} - -void K3ProcessController::addProcess( int pid ) -{ - d->unixProcessList.append( pid ); - ref(); // make sure we stay around when the K3Process goes away -} - -//#include "moc_k3processcontroller.cpp"
deleted file mode 100644 --- a/gui/qtermwidget/lib/k3processcontroller.h +++ /dev/null @@ -1,137 +0,0 @@ -/* This file is part of the KDE libraries - Copyright (C) 1997 Christian Czezakte (e9025461@student.tuwien.ac.at) - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#ifndef K3PROCCTRL_H -#define K3PROCCTRL_H - -#include <QtCore/QList> -#include <k3process.h> - - -/** - * @short Used internally by K3Process - * @internal - * @author Christian Czezatke <e9025461@student.tuwien.ac.at> - * - * A class for internal use by K3Process only. -- Exactly one instance - * of this class is created by KApplication. - * - * This class takes care of the actual (UN*X) signal handling. - */ -class K3ProcessController : public QObject -{ - Q_OBJECT - -public: - /** - * Create an instance if none exists yet. - * Called by KApplication::KApplication() - */ - static void ref(); - - /** - * Destroy the instance if one exists and it is not referenced any more. - * Called by KApplication::~KApplication() - */ - static void deref(); - - /** - * Only a single instance of this class is allowed at a time. - * This method provides access to that instance. - */ - static K3ProcessController *instance(); - - /** - * Automatically called upon SIGCHLD. Never call it directly. - * If your application (or some library it uses) redirects SIGCHLD, - * the new signal handler (and only it) should call the old handler - * returned by sigaction(). - * @internal - */ - static void theSigCHLDHandler(int signal); // KDE4: private - - /** - * Wait for any process to exit and handle their exit without - * starting an event loop. - * This function may cause K3Process to emit any of its signals. - * - * @param timeout the timeout in seconds. -1 means no timeout. - * @return true if a process exited, false - * if no process exited within @p timeout seconds. - */ - bool waitForProcessExit(int timeout); - - /** - * Call this function to defer processing of the data that became available - * on notifierFd(). - */ - void unscheduleCheck(); - - /** - * This function @em must be called at some point after calling - * unscheduleCheck(). - */ - void rescheduleCheck(); - - /* - * Obtain the file descriptor K3ProcessController uses to get notified - * about process exits. select() or poll() on it if you create a custom - * event loop that needs to act upon SIGCHLD. - * @return the file descriptor of the reading end of the notification pipe - */ - int notifierFd() const; - - /** - * @internal - */ - void addKProcess( K3Process* ); - /** - * @internal - */ - void removeKProcess( K3Process* ); - /** - * @internal - */ - void addProcess( int pid ); - -private Q_SLOTS: - void slotDoHousekeeping(); - -private: - friend class I_just_love_gcc; - - static void setupHandlers(); - static void resetHandlers(); - - // Disallow instantiation - K3ProcessController(); - ~K3ProcessController(); - - // Disallow assignment and copy-construction - K3ProcessController( const K3ProcessController& ); - K3ProcessController& operator= ( const K3ProcessController& ); - - class Private; - Private * const d; -}; - -#endif -
deleted file mode 100644 --- a/gui/qtermwidget/lib/kb-layouts/CVS/Entries +++ /dev/null @@ -1,4 +0,0 @@ -/default.keytab/1.1.1.1/Sat May 10 21:27:57 2008// -/linux.keytab/1.1.1.1/Sat May 10 21:27:57 2008// -/vt420pc.keytab/1.1.1.1/Sat May 10 21:27:57 2008// -D
deleted file mode 100644 --- a/gui/qtermwidget/lib/kb-layouts/CVS/Repository +++ /dev/null @@ -1,1 +0,0 @@ -qtermwidget/lib/kb-layouts
deleted file mode 100644 --- a/gui/qtermwidget/lib/kb-layouts/CVS/Root +++ /dev/null @@ -1,1 +0,0 @@ -:ext:e_k@qtermwidget.cvs.sourceforge.net:/cvsroot/qtermwidget
deleted file mode 100644 --- a/gui/qtermwidget/lib/kb-layouts/default.keytab +++ /dev/null @@ -1,133 +0,0 @@ -# [README.default.Keytab] Buildin Keyboard Table -# -# To customize your keyboard, copy this file to something -# ending with .keytab and change it to meet you needs. -# Please read the README.KeyTab and the README.keyboard -# in this case. -# -# -------------------------------------------------------------- - -keyboard "Default (XFree 4)" - -# -------------------------------------------------------------- -# -# Note that this particular table is a "risc" version made to -# ease customization without bothering with obsolete details. -# See VT100.keytab for the more hairy stuff. -# -# -------------------------------------------------------------- - -# common keys - -key Escape : "\E" - -key Tab -Shift : "\t" -key Tab +Shift+Ansi : "\E[Z" -key Tab +Shift-Ansi : "\t" -key Backtab +Ansi : "\E[Z" -key Backtab -Ansi : "\t" - -key Return-Shift-NewLine : "\r" -key Return-Shift+NewLine : "\r\n" - -key Return+Shift : "\EOM" - -# Backspace and Delete codes are preserving CTRL-H. - -key Backspace : "\x7f" - -# Arrow keys in VT52 mode -# shift up/down are reserved for scrolling. -# shift left/right are reserved for switching between tabs (this is hardcoded). - -key Up -Shift-Ansi : "\EA" -key Down -Shift-Ansi : "\EB" -key Right-Shift-Ansi : "\EC" -key Left -Shift-Ansi : "\ED" - -# Arrow keys in ANSI mode with Application - and Normal Cursor Mode) - -key Up -Shift-AnyMod+Ansi+AppCuKeys : "\EOA" -key Down -Shift-AnyMod+Ansi+AppCuKeys : "\EOB" -key Right -Shift-AnyMod+Ansi+AppCuKeys : "\EOC" -key Left -Shift-AnyMod+Ansi+AppCuKeys : "\EOD" - -key Up -Shift-AnyMod+Ansi-AppCuKeys : "\E[A" -key Down -Shift-AnyMod+Ansi-AppCuKeys : "\E[B" -key Right -Shift-AnyMod+Ansi-AppCuKeys : "\E[C" -key Left -Shift-AnyMod+Ansi-AppCuKeys : "\E[D" - -key Up -Shift+AnyMod+Ansi : "\E[1;*A" -key Down -Shift+AnyMod+Ansi : "\E[1;*B" -key Right -Shift+AnyMod+Ansi : "\E[1;*C" -key Left -Shift+AnyMod+Ansi : "\E[1;*D" - -# other grey PC keys - -key Enter+NewLine : "\r\n" -key Enter-NewLine : "\r" - -key Home -AnyMod -AppCuKeys : "\E[H" -key End -AnyMod -AppCuKeys : "\E[F" -key Home -AnyMod +AppCuKeys : "\EOH" -key End -AnyMod +AppCuKeys : "\EOF" -key Home +AnyMod : "\E[1;*H" -key End +AnyMod : "\E[1;*F" - -key Insert -AnyMod : "\E[2~" -key Delete -AnyMod : "\E[3~" -key Insert +AnyMod : "\E[2;*~" -key Delete +AnyMod : "\E[3;*~" - -key Prior -Shift-AnyMod : "\E[5~" -key Next -Shift-AnyMod : "\E[6~" -key Prior -Shift+AnyMod : "\E[5;*~" -key Next -Shift+AnyMod : "\E[6;*~" - -# Function keys -key F1 -AnyMod : "\EOP" -key F2 -AnyMod : "\EOQ" -key F3 -AnyMod : "\EOR" -key F4 -AnyMod : "\EOS" -key F5 -AnyMod : "\E[15~" -key F6 -AnyMod : "\E[17~" -key F7 -AnyMod : "\E[18~" -key F8 -AnyMod : "\E[19~" -key F9 -AnyMod : "\E[20~" -key F10 -AnyMod : "\E[21~" -key F11 -AnyMod : "\E[23~" -key F12 -AnyMod : "\E[24~" - -key F1 +AnyMod : "\EO*P" -key F2 +AnyMod : "\EO*Q" -key F3 +AnyMod : "\EO*R" -key F4 +AnyMod : "\EO*S" -key F5 +AnyMod : "\E[15;*~" -key F6 +AnyMod : "\E[17;*~" -key F7 +AnyMod : "\E[18;*~" -key F8 +AnyMod : "\E[19;*~" -key F9 +AnyMod : "\E[20;*~" -key F10 +AnyMod : "\E[21;*~" -key F11 +AnyMod : "\E[23;*~" -key F12 +AnyMod : "\E[24;*~" - -# Work around dead keys - -key Space +Control : "\x00" - -# Some keys are used by konsole to cause operations. -# The scroll* operations refer to the history buffer. - -key Up +Shift-AppScreen : scrollLineUp -key Prior +Shift-AppScreen : scrollPageUp -key Down +Shift-AppScreen : scrollLineDown -key Next +Shift-AppScreen : scrollPageDown - -#key Up +Shift : scrollLineUp -#key Prior +Shift : scrollPageUp -#key Down +Shift : scrollLineDown -#key Next +Shift : scrollPageDown - -key ScrollLock : scrollLock - -# keypad characters are not offered differently by Qt.
deleted file mode 100644 --- a/gui/qtermwidget/lib/kb-layouts/linux.keytab +++ /dev/null @@ -1,133 +0,0 @@ -# [linux.keytab] Konsole Keyboard Table (Linux console keys) -# -# -------------------------------------------------------------- - -# NOT TESTED, MAY NEED SOME CLEANUPS -keyboard "Linux console" - -# -------------------------------------------------------------- -# -# This configuration table allows to customize the -# meaning of the keys. -# -# The syntax is that each entry has the form : -# -# "key" Keyname { ("+"|"-") Modename } ":" (String|Operation) -# -# Keynames are those defined in <qnamespace.h> with the -# "Qt::Key_" removed. (We'd better insert the list here) -# -# Mode names are : -# -# - Shift -# - Alt -# - Control -# -# The VT100 emulation has two modes that can affect the -# sequences emitted by certain keys. These modes are -# under control of the client program. -# -# - Newline : effects Return and Enter key. -# - Application : effects Up and Down key. -# -# - Ansi : effects Up and Down key (This is for VT52, really). -# -# Operations are -# -# - scrollUpLine -# - scrollUpPage -# - scrollDownLine -# - scrollDownPage -# -# - emitSelection -# -# If the key is not found here, the text of the -# key event as provided by QT is emitted, possibly -# preceeded by ESC if the Alt key is pressed. -# -# -------------------------------------------------------------- - -key Escape : "\E" -key Tab : "\t" - -# VT100 can add an extra \n after return. -# The NewLine mode is set by an escape sequence. - -key Return-NewLine : "\r" -key Return+NewLine : "\r\n" - -# Some desperately try to save the ^H. - -key Backspace : "\x7f" -key Delete : "\E[3~" - -# These codes are for the VT52 mode of VT100 -# The Ansi mode (i.e. VT100 mode) is set by -# an escape sequence - -key Up -Shift-Ansi : "\EA" -key Down -Shift-Ansi : "\EB" -key Right-Shift-Ansi : "\EC" -key Left -Shift-Ansi : "\ED" - -# VT100 emits a mode bit together -# with the arrow keys.The AppCuKeys -# mode is set by an escape sequence. - -key Up -Shift+Ansi+AppCuKeys : "\EOA" -key Down -Shift+Ansi+AppCuKeys : "\EOB" -key Right-Shift+Ansi+AppCuKeys : "\EOC" -key Left -Shift+Ansi+AppCuKeys : "\EOD" - -key Up -Shift+Ansi-AppCuKeys : "\E[A" -key Down -Shift+Ansi-AppCuKeys : "\E[B" -key Right-Shift+Ansi-AppCuKeys : "\E[C" -key Left -Shift+Ansi-AppCuKeys : "\E[D" - -# linux functions keys F1-F5 differ from xterm - -key F1 : "\E[[A" -key F2 : "\E[[B" -key F3 : "\E[[C" -key F4 : "\E[[D" -key F5 : "\E[[E" - -key F6 : "\E[17~" -key F7 : "\E[18~" -key F8 : "\E[19~" -key F9 : "\E[20~" -key F10 : "\E[21~" -key F11 : "\E[23~" -key F12 : "\E[24~" - -key Home : "\E[1~" -key End : "\E[4~" - -key Prior -Shift : "\E[5~" -key Next -Shift : "\E[6~" -key Insert-Shift : "\E[2~" - -# Keypad-Enter. See comment on Return above. - -key Enter+NewLine : "\r\n" -key Enter-NewLine : "\r" - -key Space +Control : "\x00" - -# some of keys are used by konsole. - -key Up +Shift : scrollLineUp -key Prior +Shift : scrollPageUp -key Down +Shift : scrollLineDown -key Next +Shift : scrollPageDown - -key ScrollLock : scrollLock - -#---------------------------------------------------------- - -# keypad characters as offered by Qt -# cannot be recognized as such. - -#---------------------------------------------------------- - -# Following other strings as emitted by konsole.
deleted file mode 100644 --- a/gui/qtermwidget/lib/kb-layouts/vt420pc.keytab +++ /dev/null @@ -1,163 +0,0 @@ -# [vt420pc.keytab] Konsole Keyboard Table (VT420pc keys) -# adapted by ferdinand gassauer f.gassauer@aon.at -# Nov 2000 -# -################################################################ -# -# The escape sequences emmited by the -# keys Shift+F1 to Shift+F12 might not fit your needs -# -################# IMPORTANT NOTICE ############################# -# the key bindings (Kcontrol -> look and feel -> keybindgs) -# overrule the settings in this file. The key bindings might be -# changed by the user WITHOUT notification of the maintainer of -# the keytab file. Konsole will not work as expected by -# the maintainer of the keytab file. -################################################################ -# -# -------------------------------------------------------------- - -keyboard "DEC VT420 Terminal" - -# -------------------------------------------------------------- -# -# This configuration table allows to customize the -# meaning of the keys. -# -# The syntax is that each entry has the form : -# -# "key" Keyname { ("+"|"-") Modename } ":" (String|Operation) -# -# Keynames are those defined in <qnamespace.h> with the -# "Qt::Key_" removed. (We'd better insert the list here) -# -# Mode names are : -# -# - Shift -# - Alt -# - Control -# -# The VT100 emulation has two modes that can affect the -# sequences emitted by certain keys. These modes are -# under control of the client program. -# -# - Newline : effects Return and Enter key. -# - Application : effects Up and Down key. -# -# - Ansi : effects Up and Down key (This is for VT52, really). -# -# Operations are -# -# - scrollUpLine -# - scrollUpPage -# - scrollDownLine -# - scrollDownPage -# -# - emitSelection -# -# If the key is not found here, the text of the -# key event as provided by QT is emitted, possibly -# preceeded by ESC if the Alt key is pressed. -# -# -------------------------------------------------------------- - -key Escape : "\E" -key Tab : "\t" -key Backtab: "\E[Z" - -# VT100 can add an extra \n after return. -# The NewLine mode is set by an escape sequence. - -key Return-NewLine : "\r" -key Return+NewLine : "\r\n" - -# Some desperately try to save the ^H. -# may be not everyone wants this - -key Backspace : "\x08" # Control H -key Delete : "\x7f" - -# These codes are for the VT420pc -# The Ansi mode (i.e. VT100 mode) is set by -# an escape sequence - -key Up -Shift-Ansi : "\EA" -key Down -Shift-Ansi : "\EB" -key Right-Shift-Ansi : "\EC" -key Left -Shift-Ansi : "\ED" - -# VT100 emits a mode bit together -# with the arrow keys.The AppCuKeys -# mode is set by an escape sequence. - -key Up -Shift+Ansi+AppCuKeys : "\EOA" -key Down -Shift+Ansi+AppCuKeys : "\EOB" -key Right-Shift+Ansi+AppCuKeys : "\EOC" -key Left -Shift+Ansi+AppCuKeys : "\EOD" - -key Up -Shift+Ansi-AppCuKeys : "\E[A" -key Down -Shift+Ansi-AppCuKeys : "\E[B" -key Right-Shift+Ansi-AppCuKeys : "\E[C" -key Left -Shift+Ansi-AppCuKeys : "\E[D" - -# function keys - -key F1 -Shift : "\E[11~" -key F2 -Shift : "\E[12~" -key F3 -Shift : "\E[13~" -key F4 -Shift : "\E[14~" -key F5 -Shift : "\E[15~" -key F6 -Shift : "\E[17~" -key F7 -Shift : "\E[18~" -key F8 -Shift : "\E[19~" -key F9 -Shift : "\E[20~" -key F10-Shift : "\E[21~" -key F11-Shift : "\E[23~" -key F12-Shift : "\E[24~" -# -# Shift F1-F12 -# -key F1 +Shift : "\E[11;2~" -key F2 +Shift : "\E[12;2~" -key F3 +Shift : "\E[13;2~" -key F4 +Shift : "\E[14;2~" -key F5 +Shift : "\E[15;2~" -key F6 +Shift : "\E[17;2~" -key F7 +Shift : "\E[18;2~" -key F8 +Shift : "\E[19;2~" -key F9 +Shift : "\E[20;2~" -key F10+Shift : "\E[21;2~" -key F11+Shift : "\E[23;2~" -key F12+Shift : "\E[24;2~" - -key Home : "\E[H" -key End : "\E[F" - -key Prior -Shift : "\E[5~" -key Next -Shift : "\E[6~" -key Insert-Shift : "\E[2~" - -# Keypad-Enter. See comment on Return above. - -key Enter+NewLine : "\r\n" -key Enter-NewLine : "\r" - -key Space +Control : "\x00" - -# some of keys are used by konsole. - -key Up +Shift : scrollLineUp -key Prior +Shift : scrollPageUp -key Down +Shift : scrollLineDown -key Next +Shift : scrollPageDown - -key ScrollLock : scrollLock - -#---------------------------------------------------------- - -# keypad characters as offered by Qt -# cannot be recognized as such. - -#---------------------------------------------------------- - -# Following other strings as emitted by konsole.
deleted file mode 100644 --- a/gui/qtermwidget/lib/konsole_wcwidth.cpp +++ /dev/null @@ -1,216 +0,0 @@ -/* $XFree86: xc/programs/xterm/wcwidth.character,v 1.3 2001/07/29 22:08:16 tsi Exp $ */ -/* - * This is an implementation of wcwidth() and wcswidth() as defined in - * "The Single UNIX Specification, Version 2, The Open Group, 1997" - * <http://www.UNIX-systems.org/online.html> - * - * Markus Kuhn -- 2001-01-12 -- public domain - */ - -#include "konsole_wcwidth.h" - -struct interval { - unsigned short first; - unsigned short last; -}; - -/* auxiliary function for binary search in interval table */ -static int bisearch(quint16 ucs, const struct interval *table, int max) { - int min = 0; - int mid; - - if (ucs < table[0].first || ucs > table[max].last) - return 0; - while (max >= min) { - mid = (min + max) / 2; - if (ucs > table[mid].last) - min = mid + 1; - else if (ucs < table[mid].first) - max = mid - 1; - else - return 1; - } - - return 0; -} - - -/* The following functions define the column width of an ISO 10646 - * character as follows: - * - * - The null character (U+0000) has a column width of 0. - * - * - Other C0/C1 control characters and DEL will lead to a return - * value of -1. - * - * - Non-spacing and enclosing combining characters (general - * category code Mn or Me in the Unicode database) have a - * column width of 0. - * - * - Other format characters (general category code Cf in the Unicode - * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. - * - * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) - * have a column width of 0. - * - * - Spacing characters in the East Asian Wide (W) or East Asian - * FullWidth (F) category as defined in Unicode Technical - * Report #11 have a column width of 2. - * - * - All remaining characters (including all printable - * ISO 8859-1 and WGL4 characters, Unicode control characters, - * etc.) have a column width of 1. - * - * This implementation assumes that quint16 characters are encoded - * in ISO 10646. - */ - -int konsole_wcwidth(quint16 ucs) -{ - /* sorted list of non-overlapping intervals of non-spacing characters */ - static const struct interval combining[] = { - { 0x0300, 0x034E }, { 0x0360, 0x0362 }, { 0x0483, 0x0486 }, - { 0x0488, 0x0489 }, { 0x0591, 0x05A1 }, { 0x05A3, 0x05B9 }, - { 0x05BB, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, - { 0x05C4, 0x05C4 }, { 0x064B, 0x0655 }, { 0x0670, 0x0670 }, - { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, - { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, - { 0x07A6, 0x07B0 }, { 0x0901, 0x0902 }, { 0x093C, 0x093C }, - { 0x0941, 0x0948 }, { 0x094D, 0x094D }, { 0x0951, 0x0954 }, - { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, { 0x09BC, 0x09BC }, - { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, { 0x09E2, 0x09E3 }, - { 0x0A02, 0x0A02 }, { 0x0A3C, 0x0A3C }, { 0x0A41, 0x0A42 }, - { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, { 0x0A70, 0x0A71 }, - { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, { 0x0AC1, 0x0AC5 }, - { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, { 0x0B01, 0x0B01 }, - { 0x0B3C, 0x0B3C }, { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, - { 0x0B4D, 0x0B4D }, { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, - { 0x0BC0, 0x0BC0 }, { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, - { 0x0C46, 0x0C48 }, { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, - { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, - { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, { 0x0DCA, 0x0DCA }, - { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, { 0x0E31, 0x0E31 }, - { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, { 0x0EB1, 0x0EB1 }, - { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, { 0x0EC8, 0x0ECD }, - { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, { 0x0F37, 0x0F37 }, - { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, { 0x0F80, 0x0F84 }, - { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, { 0x0F99, 0x0FBC }, - { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, { 0x1032, 0x1032 }, - { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, { 0x1058, 0x1059 }, - { 0x1160, 0x11FF }, { 0x17B7, 0x17BD }, { 0x17C6, 0x17C6 }, - { 0x17C9, 0x17D3 }, { 0x180B, 0x180E }, { 0x18A9, 0x18A9 }, - { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x206A, 0x206F }, - { 0x20D0, 0x20E3 }, { 0x302A, 0x302F }, { 0x3099, 0x309A }, - { 0xFB1E, 0xFB1E }, { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, - { 0xFFF9, 0xFFFB } - }; - - /* test for 8-bit control characters */ - if (ucs == 0) - return 0; - if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) - return -1; - - /* binary search in table of non-spacing characters */ - if (bisearch(ucs, combining, - sizeof(combining) / sizeof(struct interval) - 1)) - return 0; - - /* if we arrive here, ucs is not a combining or C0/C1 control character */ - - return 1 + - (ucs >= 0x1100 && - (ucs <= 0x115f || /* Hangul Jamo init. consonants */ - (ucs >= 0x2e80 && ucs <= 0xa4cf && (ucs & ~0x0011) != 0x300a && - ucs != 0x303f) || /* CJK ... Yi */ - (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ - (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ - (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ - (ucs >= 0xff00 && ucs <= 0xff5f) || /* Fullwidth Forms */ - (ucs >= 0xffe0 && ucs <= 0xffe6) /* do not compare UINT16 with 0x20000 || - (ucs >= 0x20000 && ucs <= 0x2ffff) */)); -} - -#if 0 -/* - * The following function is the same as konsole_wcwidth(), except that - * spacing characters in the East Asian Ambiguous (A) category as - * defined in Unicode Technical Report #11 have a column width of 2. - * This experimental variant might be useful for users of CJK legacy - * encodings who want to migrate to UCS. It is not otherwise - * recommended for general use. - */ -int konsole_wcwidth_cjk(quint16 ucs) -{ - /* sorted list of non-overlapping intervals of East Asian Ambiguous - * characters */ - static const struct interval ambiguous[] = { - { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 }, - { 0x00AA, 0x00AA }, { 0x00AD, 0x00AD }, { 0x00B0, 0x00B4 }, - { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 }, - { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 }, - { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED }, - { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA }, - { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 }, - { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B }, - { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 }, - { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 }, - { 0x0148, 0x014A }, { 0x014D, 0x014D }, { 0x0152, 0x0153 }, - { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE }, - { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 }, - { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA }, - { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 }, - { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB }, { 0x02CD, 0x02CD }, - { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB }, { 0x02DD, 0x02DD }, - { 0x0391, 0x03A1 }, { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, - { 0x03C3, 0x03C9 }, { 0x0401, 0x0401 }, { 0x0410, 0x044F }, - { 0x0451, 0x0451 }, { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, - { 0x2018, 0x2019 }, { 0x201C, 0x201D }, { 0x2020, 0x2021 }, - { 0x2025, 0x2027 }, { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, - { 0x2035, 0x2035 }, { 0x203B, 0x203B }, { 0x2074, 0x2074 }, - { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC }, - { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 }, - { 0x2113, 0x2113 }, { 0x2121, 0x2122 }, { 0x2126, 0x2126 }, - { 0x212B, 0x212B }, { 0x2154, 0x2155 }, { 0x215B, 0x215B }, - { 0x215E, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 }, - { 0x2190, 0x2199 }, { 0x21D2, 0x21D2 }, { 0x21D4, 0x21D4 }, - { 0x2200, 0x2200 }, { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, - { 0x220B, 0x220B }, { 0x220F, 0x220F }, { 0x2211, 0x2211 }, - { 0x2215, 0x2215 }, { 0x221A, 0x221A }, { 0x221D, 0x2220 }, - { 0x2223, 0x2223 }, { 0x2225, 0x2225 }, { 0x2227, 0x222C }, - { 0x222E, 0x222E }, { 0x2234, 0x2237 }, { 0x223C, 0x223D }, - { 0x2248, 0x2248 }, { 0x224C, 0x224C }, { 0x2252, 0x2252 }, - { 0x2260, 0x2261 }, { 0x2264, 0x2267 }, { 0x226A, 0x226B }, - { 0x226E, 0x226F }, { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, - { 0x2295, 0x2295 }, { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, - { 0x22BF, 0x22BF }, { 0x2312, 0x2312 }, { 0x2460, 0x24BF }, - { 0x24D0, 0x24E9 }, { 0x2500, 0x254B }, { 0x2550, 0x2574 }, - { 0x2580, 0x258F }, { 0x2592, 0x2595 }, { 0x25A0, 0x25A1 }, - { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 }, { 0x25B6, 0x25B7 }, - { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 }, { 0x25C6, 0x25C8 }, - { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 }, { 0x25E2, 0x25E5 }, - { 0x25EF, 0x25EF }, { 0x2605, 0x2606 }, { 0x2609, 0x2609 }, - { 0x260E, 0x260F }, { 0x261C, 0x261C }, { 0x261E, 0x261E }, - { 0x2640, 0x2640 }, { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, - { 0x2663, 0x2665 }, { 0x2667, 0x266A }, { 0x266C, 0x266D }, - { 0x266F, 0x266F }, { 0x300A, 0x300B }, { 0x301A, 0x301B }, - { 0xE000, 0xF8FF }, { 0xFFFD, 0xFFFD } - }; - - /* binary search in table of non-spacing characters */ - if (bisearch(ucs, ambiguous, - sizeof(ambiguous) / sizeof(struct interval) - 1)) - return 2; - - return konsole_wcwidth(ucs); -} -#endif - -// single byte char: +1, multi byte char: +2 -int string_width( const QString &txt ) -{ - int w = 0; - for ( int i = 0; i < txt.length(); ++i ) - w += konsole_wcwidth( txt[ i ].unicode() ); - return w; -}
deleted file mode 100644 --- a/gui/qtermwidget/lib/konsole_wcwidth.h +++ /dev/null @@ -1,24 +0,0 @@ -/* $XFree86: xc/programs/xterm/wcwidth.h,v 1.2 2001/06/18 19:09:27 dickey Exp $ */ - -/* Markus Kuhn -- 2001-01-12 -- public domain */ -/* Adaptions for KDE by Waldo Bastian <bastian@kde.org> */ -/* - Rewritten for QT4 by e_k <e_k at users.sourceforge.net> -*/ - - -#ifndef _KONSOLE_WCWIDTH_H_ -#define _KONSOLE_WCWIDTH_H_ - -// Qt -#include <QtCore/QBool> -#include <QtCore/QString> - -int konsole_wcwidth(quint16 ucs); -#if 0 -int konsole_wcwidth_cjk(Q_UINT16 ucs); -#endif - -int string_width( const QString &txt ); - -#endif
deleted file mode 100644 --- a/gui/qtermwidget/lib/kpty.cpp +++ /dev/null @@ -1,624 +0,0 @@ -/* - - This file is part of the KDE libraries - Copyright (C) 2002 Waldo Bastian <bastian@kde.org> - Copyright (C) 2002-2003,2007 Oswald Buddenhagen <ossi@kde.org> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#include "kpty_p.h" - -#ifdef __sgi -#define __svr4__ -#endif - -#ifdef __osf__ -#define _OSF_SOURCE -#include <float.h> -#endif - -#ifdef _AIX -#define _ALL_SOURCE -#endif - -// __USE_XOPEN isn't defined by default in ICC -// (needed for ptsname(), grantpt() and unlockpt()) -#ifdef __INTEL_COMPILER -# ifndef __USE_XOPEN -# define __USE_XOPEN -# endif -#endif - -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/time.h> -#include <sys/resource.h> -#include <sys/stat.h> -#include <sys/param.h> - -#include <errno.h> -#include <fcntl.h> -#include <time.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <grp.h> - -#if defined(HAVE_PTY_H) -# include <pty.h> -#endif - -#ifdef HAVE_LIBUTIL_H -# include <libutil.h> -#elif defined(HAVE_UTIL_H) -# include <util.h> -#endif - -#ifdef HAVE_UTEMPTER -extern "C" { -# include <utempter.h> -} -#else -# include <utmp.h> -# ifdef HAVE_UTMPX -# include <utmpx.h> -# endif -# if !defined(_PATH_UTMPX) && defined(_UTMPX_FILE) -# define _PATH_UTMPX _UTMPX_FILE -# endif -# if !defined(_PATH_WTMPX) && defined(_WTMPX_FILE) -# define _PATH_WTMPX _WTMPX_FILE -# endif -#endif - -/* for HP-UX (some versions) the extern C is needed, and for other - platforms it doesn't hurt */ -extern "C" { -#include <termios.h> -#if defined(HAVE_TERMIO_H) -# include <termio.h> // struct winsize on some systems -#endif -} - -#if defined (_HPUX_SOURCE) -# define _TERMIOS_INCLUDED -# include <bsdtty.h> -#endif - -#ifdef HAVE_SYS_STROPTS_H -# include <sys/stropts.h> // Defines I_PUSH -# define _NEW_TTY_CTRL -#endif - -#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || defined (__bsdi__) || defined(__APPLE__) || defined (__DragonFly__) -# define _tcgetattr(fd, ttmode) ioctl(fd, TIOCGETA, (char *)ttmode) -#else -# if defined(_HPUX_SOURCE) || defined(__Lynx__) || defined (__CYGWIN__) -# define _tcgetattr(fd, ttmode) tcgetattr(fd, ttmode) -# else -# define _tcgetattr(fd, ttmode) ioctl(fd, TCGETS, (char *)ttmode) -# endif -#endif - -#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || defined (__bsdi__) || defined(__APPLE__) || defined (__DragonFly__) -# define _tcsetattr(fd, ttmode) ioctl(fd, TIOCSETA, (char *)ttmode) -#else -# if defined(_HPUX_SOURCE) || defined(__CYGWIN__) -# define _tcsetattr(fd, ttmode) tcsetattr(fd, TCSANOW, ttmode) -# else -# define _tcsetattr(fd, ttmode) ioctl(fd, TCSETS, (char *)ttmode) -# endif -#endif - -//#include <kdebug.h> -//#include <kstandarddirs.h> // findExe - -#include <QtCore> - -// not defined on HP-UX for example -#ifndef CTRL -# define CTRL(x) ((x) & 037) -#endif - -#define TTY_GROUP "tty" - -/////////////////////// -// private functions // -/////////////////////// - -////////////////// -// private data // -////////////////// - -KPtyPrivate::KPtyPrivate() : - masterFd(-1), slaveFd(-1) -{ -} - -bool KPtyPrivate::chownpty(bool) -{ -// return !QProcess::execute(KStandardDirs::findExe("kgrantpty"), -// QStringList() << (grant?"--grant":"--revoke") << QString::number(masterFd)); - return true; -} - -///////////////////////////// -// public member functions // -///////////////////////////// - -KPty::KPty() : - d_ptr(new KPtyPrivate) -{ - d_ptr->q_ptr = this; -} - -KPty::KPty(KPtyPrivate *d) : - d_ptr(d) -{ - d_ptr->q_ptr = this; -} - -KPty::~KPty() -{ - close(); - delete d_ptr; -} - -bool KPty::open() -{ - Q_D(KPty); - - if (d->masterFd >= 0) - return true; - - QByteArray ptyName; - - // Find a master pty that we can open //////////////////////////////// - - // Because not all the pty animals are created equal, they want to - // be opened by several different methods. - - // We try, as we know them, one by one. - -#ifdef HAVE_OPENPTY - - char ptsn[PATH_MAX]; - if (::openpty( &d->masterFd, &d->slaveFd, ptsn, 0, 0)) - { - d->masterFd = -1; - d->slaveFd = -1; - kWarning(175) << "Can't open a pseudo teletype"; - return false; - } - d->ttyName = ptsn; - -#else - -#ifdef HAVE__GETPTY // irix - - char *ptsn = _getpty(&d->masterFd, O_RDWR|O_NOCTTY, S_IRUSR|S_IWUSR, 0); - if (ptsn) { - d->ttyName = ptsn; - goto grantedpt; - } - -#elif defined(HAVE_PTSNAME) || defined(TIOCGPTN) - -#ifdef HAVE_POSIX_OPENPT - d->masterFd = ::posix_openpt(O_RDWR|O_NOCTTY); -#elif defined(HAVE_GETPT) - d->masterFd = ::getpt(); -#elif defined(PTM_DEVICE) - d->masterFd = ::open(PTM_DEVICE, O_RDWR|O_NOCTTY); -#else -# error No method to open a PTY master detected. -#endif - - if (d->masterFd >= 0) - { - -#ifdef HAVE_PTSNAME - char *ptsn = ptsname(d->masterFd); - if (ptsn) { - d->ttyName = ptsn; -#else - int ptyno; - if (!ioctl(d->masterFd, TIOCGPTN, &ptyno)) { - d->ttyName = QByteArray("/dev/pts/") + QByteArray::number(ptyno); -#endif -#ifdef HAVE_GRANTPT - if (!grantpt(d->masterFd)) - goto grantedpt; -#else - - goto gotpty; -#endif - } - ::close(d->masterFd); - d->masterFd = -1; - } -#endif // HAVE_PTSNAME || TIOCGPTN - - // Linux device names, FIXME: Trouble on other systems? - for (const char* s3 = "pqrstuvwxyzabcde"; *s3; s3++) - { - for (const char* s4 = "0123456789abcdef"; *s4; s4++) - { - ptyName = QString().sprintf("/dev/pty%c%c", *s3, *s4).toAscii(); - d->ttyName = QString().sprintf("/dev/tty%c%c", *s3, *s4).toAscii(); - - d->masterFd = ::open(ptyName.data(), O_RDWR); - if (d->masterFd >= 0) - { -#ifdef Q_OS_SOLARIS - /* Need to check the process group of the pty. - * If it exists, then the slave pty is in use, - * and we need to get another one. - */ - int pgrp_rtn; - if (ioctl(d->masterFd, TIOCGPGRP, &pgrp_rtn) == 0 || errno != EIO) { - ::close(d->masterFd); - d->masterFd = -1; - continue; - } -#endif /* Q_OS_SOLARIS */ - if (!access(d->ttyName.data(),R_OK|W_OK)) // checks availability based on permission bits - { - if (!geteuid()) - { - struct group* p = getgrnam(TTY_GROUP); - if (!p) - p = getgrnam("wheel"); - gid_t gid = p ? p->gr_gid : getgid (); - - chown(d->ttyName.data(), getuid(), gid); - chmod(d->ttyName.data(), S_IRUSR|S_IWUSR|S_IWGRP); - } - goto gotpty; - } - ::close(d->masterFd); - d->masterFd = -1; - } - } - } - - qWarning() << "Can't open a pseudo teletype"; - return false; - - gotpty: - struct stat st; - if (stat(d->ttyName.data(), &st)) { - return false; // this just cannot happen ... *cough* Yeah right, I just - // had it happen when pty #349 was allocated. I guess - // there was some sort of leak? I only had a few open. - } - if (((st.st_uid != getuid()) || - (st.st_mode & (S_IRGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH))) && - !d->chownpty(true)) - { - qWarning() - << "chownpty failed for device " << ptyName << "::" << d->ttyName - << "\nThis means the communication can be eavesdropped." << endl; - } - -#if defined (HAVE__GETPTY) || defined (HAVE_GRANTPT) - grantedpt: -#endif - -#ifdef HAVE_REVOKE - revoke(d->ttyName.data()); -#endif - -#ifdef HAVE_UNLOCKPT - unlockpt(d->masterFd); -#elif defined(TIOCSPTLCK) - int flag = 0; - ioctl(d->masterFd, TIOCSPTLCK, &flag); -#endif - - d->slaveFd = ::open(d->ttyName.data(), O_RDWR | O_NOCTTY); - if (d->slaveFd < 0) - { - qWarning() << "Can't open slave pseudo teletype"; - ::close(d->masterFd); - d->masterFd = -1; - return false; - } - -#if (defined(__svr4__) || defined(__sgi__)) - // Solaris - ioctl(d->slaveFd, I_PUSH, "ptem"); - ioctl(d->slaveFd, I_PUSH, "ldterm"); -#endif - -#endif /* HAVE_OPENPTY */ - - fcntl(d->masterFd, F_SETFD, FD_CLOEXEC); - fcntl(d->slaveFd, F_SETFD, FD_CLOEXEC); - - return true; -} - -void KPty::closeSlave() -{ - Q_D(KPty); - - if (d->slaveFd < 0) - return; - ::close(d->slaveFd); - d->slaveFd = -1; -} - -void KPty::close() -{ - Q_D(KPty); - - if (d->masterFd < 0) - return; - closeSlave(); - // don't bother resetting unix98 pty, it will go away after closing master anyway. - if (memcmp(d->ttyName.data(), "/dev/pts/", 9)) { - if (!geteuid()) { - struct stat st; - if (!stat(d->ttyName.data(), &st)) { - chown(d->ttyName.data(), 0, st.st_gid == getgid() ? 0 : -1); - chmod(d->ttyName.data(), S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); - } - } else { - fcntl(d->masterFd, F_SETFD, 0); - d->chownpty(false); - } - } - ::close(d->masterFd); - d->masterFd = -1; -} - -void KPty::setCTty() -{ - Q_D(KPty); - - // Setup job control ////////////////////////////////// - - // Become session leader, process group leader, - // and get rid of the old controlling terminal. - setsid(); - - // make our slave pty the new controlling terminal. -#ifdef TIOCSCTTY - ioctl(d->slaveFd, TIOCSCTTY, 0); -#else - // __svr4__ hack: the first tty opened after setsid() becomes controlling tty - ::close(::open(d->ttyName, O_WRONLY, 0)); -#endif - - // make our new process group the foreground group on the pty - int pgrp = getpid(); -#if defined(_POSIX_VERSION) || defined(__svr4__) - tcsetpgrp(d->slaveFd, pgrp); -#elif defined(TIOCSPGRP) - ioctl(d->slaveFd, TIOCSPGRP, (char *)&pgrp); -#endif -} - -void KPty::login(const char *user, const char *remotehost) -{ -#ifdef HAVE_UTEMPTER - Q_D(KPty); - - addToUtmp(d->ttyName, remotehost, d->masterFd); - Q_UNUSED(user); -#else -# ifdef HAVE_UTMPX - struct utmpx l_struct; -# else - struct utmp l_struct; -# endif - memset(&l_struct, 0, sizeof(l_struct)); - // note: strncpy without terminators _is_ correct here. man 4 utmp - - if (user) - strncpy(l_struct.ut_name, user, sizeof(l_struct.ut_name)); - - if (remotehost) { - strncpy(l_struct.ut_host, remotehost, sizeof(l_struct.ut_host)); -# ifdef HAVE_STRUCT_UTMP_UT_SYSLEN - l_struct.ut_syslen = qMin(strlen(remotehost), sizeof(l_struct.ut_host)); -# endif - } - -# ifndef __GLIBC__ - Q_D(KPty); - const char *str_ptr = d->ttyName.data(); - if (!memcmp(str_ptr, "/dev/", 5)) - str_ptr += 5; - strncpy(l_struct.ut_line, str_ptr, sizeof(l_struct.ut_line)); -# ifdef HAVE_STRUCT_UTMP_UT_ID - strncpy(l_struct.ut_id, - str_ptr + strlen(str_ptr) - sizeof(l_struct.ut_id), - sizeof(l_struct.ut_id)); -# endif -# endif - -# ifdef HAVE_UTMPX - gettimeofday(&l_struct.ut_tv, 0); -# else - l_struct.ut_time = time(0); -# endif - -# ifdef HAVE_LOGIN -# ifdef HAVE_LOGINX - ::loginx(&l_struct); -# else - ::login(&l_struct); -# endif -# else -# ifdef HAVE_STRUCT_UTMP_UT_TYPE - l_struct.ut_type = USER_PROCESS; -# endif -# ifdef HAVE_STRUCT_UTMP_UT_PID - l_struct.ut_pid = getpid(); -# ifdef HAVE_STRUCT_UTMP_UT_SESSION - l_struct.ut_session = getsid(0); -# endif -# endif -# ifdef HAVE_UTMPX - utmpxname(_PATH_UTMPX); - setutxent(); - pututxline(&l_struct); - endutxent(); - updwtmpx(_PATH_WTMPX, &l_struct); -# else - utmpname(_PATH_UTMP); - setutent(); - pututline(&l_struct); - endutent(); - updwtmp(_PATH_WTMP, &l_struct); -# endif -# endif -#endif -} - -void KPty::logout() -{ -#ifdef HAVE_UTEMPTER - Q_D(KPty); - - removeLineFromUtmp(d->ttyName, d->masterFd); -#else - Q_D(KPty); - - const char *str_ptr = d->ttyName.data(); - if (!memcmp(str_ptr, "/dev/", 5)) - str_ptr += 5; -# ifdef __GLIBC__ - else { - const char *sl_ptr = strrchr(str_ptr, '/'); - if (sl_ptr) - str_ptr = sl_ptr + 1; - } -# endif -# ifdef HAVE_LOGIN -# ifdef HAVE_LOGINX - ::logoutx(str_ptr, 0, DEAD_PROCESS); -# else - ::logout(str_ptr); -# endif -# else -# ifdef HAVE_UTMPX - struct utmpx l_struct, *ut; -# else - struct utmp l_struct, *ut; -# endif - memset(&l_struct, 0, sizeof(l_struct)); - - strncpy(l_struct.ut_line, str_ptr, sizeof(l_struct.ut_line)); - -# ifdef HAVE_UTMPX - utmpxname(_PATH_UTMPX); - setutxent(); - if ((ut = getutxline(&l_struct))) { -# else - utmpname(_PATH_UTMP); - setutent(); - if ((ut = getutline(&l_struct))) { -# endif - memset(ut->ut_name, 0, sizeof(*ut->ut_name)); - memset(ut->ut_host, 0, sizeof(*ut->ut_host)); -# ifdef HAVE_STRUCT_UTMP_UT_SYSLEN - ut->ut_syslen = 0; -# endif -# ifdef HAVE_STRUCT_UTMP_UT_TYPE - ut->ut_type = DEAD_PROCESS; -# endif -# ifdef HAVE_UTMPX - gettimeofday(ut->ut_tv, 0); - pututxline(ut); - } - endutxent(); -# else - ut->ut_time = time(0); - pututline(ut); - } - endutent(); -# endif -# endif -#endif -} - -// XXX Supposedly, tc[gs]etattr do not work with the master on Solaris. -// Please verify. - -bool KPty::tcGetAttr(struct ::termios *ttmode) const -{ - Q_D(const KPty); - - return _tcgetattr(d->masterFd, ttmode) == 0; -} - -bool KPty::tcSetAttr(struct ::termios *ttmode) -{ - Q_D(KPty); - - return _tcsetattr(d->masterFd, ttmode) == 0; -} - -bool KPty::setWinSize(int lines, int columns) -{ - Q_D(KPty); - - struct winsize winSize; - memset(&winSize, 0, sizeof(winSize)); - winSize.ws_row = (unsigned short)lines; - winSize.ws_col = (unsigned short)columns; - return ioctl(d->masterFd, TIOCSWINSZ, (char *)&winSize) == 0; -} - -bool KPty::setEcho(bool echo) -{ - struct ::termios ttmode; - if (!tcGetAttr(&ttmode)) - return false; - if (!echo) - ttmode.c_lflag &= ~ECHO; - else - ttmode.c_lflag |= ECHO; - return tcSetAttr(&ttmode); -} - -const char *KPty::ttyName() const -{ - Q_D(const KPty); - - return d->ttyName.data(); -} - -int KPty::masterFd() const -{ - Q_D(const KPty); - - return d->masterFd; -} - -int KPty::slaveFd() const -{ - Q_D(const KPty); - - return d->slaveFd; -}
deleted file mode 100644 --- a/gui/qtermwidget/lib/kpty.h +++ /dev/null @@ -1,188 +0,0 @@ -/* This file is part of the KDE libraries - - Copyright (C) 2003,2007 Oswald Buddenhagen <ossi@kde.org> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#ifndef kpty_h -#define kpty_h - -#include <QtCore> - -struct KPtyPrivate; -struct termios; - -/** - * Provides primitives for opening & closing a pseudo TTY pair, assigning the - * controlling TTY, utmp registration and setting various terminal attributes. - */ -class KPty { - Q_DECLARE_PRIVATE(KPty) - -public: - - /** - * Constructor - */ - KPty(); - - /** - * Destructor: - * - * If the pty is still open, it will be closed. Note, however, that - * an utmp registration is @em not undone. - */ - ~KPty(); - - /** - * Create a pty master/slave pair. - * - * @return true if a pty pair was successfully opened - */ - bool open(); - - /** - * Close the pty master/slave pair. - */ - void close(); - - /** - * Close the pty slave descriptor. - * - * When creating the pty, KPty also opens the slave and keeps it open. - * Consequently the master will never receive an EOF notification. - * Usually this is the desired behavior, as a closed pty slave can be - * reopened any time - unlike a pipe or socket. However, in some cases - * pipe-alike behavior might be desired. - * - * After this function was called, slaveFd() and setCTty() cannot be - * used. - */ - void closeSlave(); - - /** - * Creates a new session and process group and makes this pty the - * controlling tty. - */ - void setCTty(); - - /** - * Creates an utmp entry for the tty. - * This function must be called after calling setCTty and - * making this pty the stdin. - * @param user the user to be logged on - * @param remotehost the host from which the login is coming. This is - * @em not the local host. For remote logins it should be the hostname - * of the client. For local logins from inside an X session it should - * be the name of the X display. Otherwise it should be empty. - */ - void login(const char *user = 0, const char *remotehost = 0); - - /** - * Removes the utmp entry for this tty. - */ - void logout(); - - /** - * Wrapper around tcgetattr(3). - * - * This function can be used only while the PTY is open. - * You will need an #include <termios.h> to do anything useful - * with it. - * - * @param ttmode a pointer to a termios structure. - * Note: when declaring ttmode, @c struct @c ::termios must be used - - * without the '::' some version of HP-UX thinks, this declares - * the struct in your class, in your method. - * @return @c true on success, false otherwise - */ - bool tcGetAttr(struct ::termios *ttmode) const; - - /** - * Wrapper around tcsetattr(3) with mode TCSANOW. - * - * This function can be used only while the PTY is open. - * - * @param ttmode a pointer to a termios structure. - * @return @c true on success, false otherwise. Note that success means - * that @em at @em least @em one attribute could be set. - */ - bool tcSetAttr(struct ::termios *ttmode); - - /** - * Change the logical (screen) size of the pty. - * The default is 24 lines by 80 columns. - * - * This function can be used only while the PTY is open. - * - * @param lines the number of rows - * @param columns the number of columns - * @return @c true on success, false otherwise - */ - bool setWinSize(int lines, int columns); - - /** - * Set whether the pty should echo input. - * - * Echo is on by default. - * If the output of automatically fed (non-interactive) PTY clients - * needs to be parsed, disabling echo often makes it much simpler. - * - * This function can be used only while the PTY is open. - * - * @param echo true if input should be echoed. - * @return @c true on success, false otherwise - */ - bool setEcho(bool echo); - - /** - * @return the name of the slave pty device. - * - * This function should be called only while the pty is open. - */ - const char *ttyName() const; - - /** - * @return the file descriptor of the master pty - * - * This function should be called only while the pty is open. - */ - int masterFd() const; - - /** - * @return the file descriptor of the slave pty - * - * This function should be called only while the pty slave is open. - */ - int slaveFd() const; - -protected: - /** - * @internal - */ - KPty(KPtyPrivate *d); - - /** - * @internal - */ - KPtyPrivate * const d_ptr; -}; - -#endif -
deleted file mode 100644 --- a/gui/qtermwidget/lib/kpty_p.h +++ /dev/null @@ -1,44 +0,0 @@ -/* This file is part of the KDE libraries - - Copyright (C) 2003,2007 Oswald Buddenhagen <ossi@kde.org> - - Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#ifndef kpty_p_h -#define kpty_p_h - -#include "kpty.h" - -#include <QtCore/QByteArray> - -struct KPtyPrivate { - Q_DECLARE_PUBLIC(KPty) - - KPtyPrivate(); - bool chownpty(bool grant); - - int masterFd; - int slaveFd; - - QByteArray ttyName; - - KPty *q_ptr; -}; - -#endif
deleted file mode 100644 --- a/gui/qtermwidget/lib/lib.pro +++ /dev/null @@ -1,48 +0,0 @@ -TEMPLATE = lib -VERSION = 0.1.0 -DESTDIR = .. - -TARGET = qtermwidget - -CONFIG += qt debug_and_release warn_on build_all staticlib #dll - -QT += core gui - -MOC_DIR = ../.moc - -CONFIG(debug, debug|release) { - OBJECTS_DIR = ../.objs_d - TARGET = qtermwidget_d -} else { - OBJECTS_DIR = ../.objs - TARGET = qtermwidget -} - -DEFINES += HAVE_POSIX_OPENPT -#or DEFINES += HAVE_GETPT - -HEADERS = TerminalCharacterDecoder.h Character.h CharacterColor.h \ - KeyboardTranslator.h \ - ExtendedDefaultTranslator.h \ - Screen.h History.h BlockArray.h konsole_wcwidth.h \ - ScreenWindow.h \ - Emulation.h \ - Vt102Emulation.h TerminalDisplay.h Filter.h LineFont.h \ - Pty.h kpty.h kpty_p.h k3process.h k3processcontroller.h \ - Session.h ShellCommand.h \ - qtermwidget.h - -SOURCES = TerminalCharacterDecoder.cpp \ - KeyboardTranslator.cpp \ - Screen.cpp History.cpp BlockArray.cpp konsole_wcwidth.cpp \ - ScreenWindow.cpp \ - Emulation.cpp \ - Vt102Emulation.cpp TerminalDisplay.cpp Filter.cpp \ - Pty.cpp kpty.cpp k3process.cpp k3processcontroller.cpp \ - Session.cpp ShellCommand.cpp \ - qtermwidget.cpp - - - - - \ No newline at end of file
deleted file mode 100644 --- a/gui/qtermwidget/lib/qtermwidget.cpp +++ /dev/null @@ -1,222 +0,0 @@ -/* Copyright (C) 2008 e_k (e_k@users.sourceforge.net) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - - -#include "qtermwidget.h" - -#include "Session.h" -#include "TerminalDisplay.h" - -using namespace Konsole; - -void *createTermWidget(int startnow, void *parent) -{ - return (void*) new QTermWidget(startnow, (QWidget*)parent); -} - -struct TermWidgetImpl -{ - TermWidgetImpl(QWidget* parent = 0); - - TerminalDisplay *m_terminalDisplay; - Session *m_session; - - Session* createSession(); - TerminalDisplay* createTerminalDisplay(Session *session, QWidget* parent); -}; - -TermWidgetImpl::TermWidgetImpl(QWidget* parent) -{ - this->m_session = createSession(); - this->m_terminalDisplay = createTerminalDisplay(this->m_session, parent); -} - - -Session *TermWidgetImpl::createSession() -{ - Session *session = new Session(); - - session->setTitle(Session::NameRole, "QTermWidget"); - session->setProgram("/bin/bash"); - QStringList args(""); - session->setArguments(args); - session->setAutoClose(true); - - session->setCodec(QTextCodec::codecForName("UTF-8")); - - session->setFlowControlEnabled(true); - session->setHistoryType(HistoryTypeBuffer(1000)); - - session->setDarkBackground(true); - - session->setKeyBindings(""); - return session; -} - -TerminalDisplay *TermWidgetImpl::createTerminalDisplay(Session *session, QWidget* parent) -{ -// TerminalDisplay* display = new TerminalDisplay(this); - TerminalDisplay* display = new TerminalDisplay(parent); - - display->setBellMode(TerminalDisplay::NotifyBell); - display->setTerminalSizeHint(true); - display->setTripleClickMode(TerminalDisplay::SelectWholeLine); - display->setTerminalSizeStartup(true); - - display->setRandomSeed(session->sessionId() * 31); - - return display; -} - - -QTermWidget::QTermWidget(int startnow, QWidget *parent) -:QWidget(parent) -{ - m_impl = new TermWidgetImpl(this); - - init(); - - if (startnow && m_impl->m_session) { - m_impl->m_session->run(); - } - - this->setFocus( Qt::OtherFocusReason ); - m_impl->m_terminalDisplay->resize(this->size()); - - this->setFocusProxy(m_impl->m_terminalDisplay); -} - -void QTermWidget::startShellProgram() -{ - if ( m_impl->m_session->isRunning() ) - return; - - m_impl->m_session->run(); -} - -void QTermWidget::init() -{ - m_impl->m_terminalDisplay->setSize(80, 40); - - QFont font = QApplication::font(); - font.setFamily("Monospace"); - font.setPointSize(10); - font.setStyleHint(QFont::TypeWriter); - setTerminalFont(font); - setScrollBarPosition(NoScrollBar); - - m_impl->m_session->addView(m_impl->m_terminalDisplay); - - connect(m_impl->m_session, SIGNAL(finished()), this, SLOT(sessionFinished())); -} - - -QTermWidget::~QTermWidget() -{ - emit destroyed(); -} - - -void QTermWidget::setTerminalFont(QFont &font) -{ - if (!m_impl->m_terminalDisplay) - return; - m_impl->m_terminalDisplay->setVTFont(font); -} - -void QTermWidget::setShellProgram(QString &progname) -{ - if (!m_impl->m_session) - return; - m_impl->m_session->setProgram(progname); -} - -void QTermWidget::setArgs(QStringList &args) -{ - if (!m_impl->m_session) - return; - m_impl->m_session->setArguments(args); -} - -void QTermWidget::setTextCodec(QTextCodec *codec) -{ - if (!m_impl->m_session) - return; - m_impl->m_session->setCodec(codec); -} - -void QTermWidget::setColorScheme(int scheme) -{ - switch(scheme) { - case COLOR_SCHEME_WHITE_ON_BLACK: - m_impl->m_terminalDisplay->setColorTable(whiteonblack_color_table); - break; - case COLOR_SCHEME_GREEN_ON_BLACK: - m_impl->m_terminalDisplay->setColorTable(greenonblack_color_table); - break; - case COLOR_SCHEME_BLACK_ON_LIGHT_YELLOW: - m_impl->m_terminalDisplay->setColorTable(blackonlightyellow_color_table); - break; - default: //do nothing - break; - }; -} - -void QTermWidget::setSize(int h, int v) -{ - if (!m_impl->m_terminalDisplay) - return; - m_impl->m_terminalDisplay->setSize(h, v); -} - -void QTermWidget::setHistorySize(int lines) -{ - if (lines < 0) - m_impl->m_session->setHistoryType(HistoryTypeFile()); - else - m_impl->m_session->setHistoryType(HistoryTypeBuffer(lines)); -} - -void QTermWidget::setScrollBarPosition(ScrollBarPosition pos) -{ - if (!m_impl->m_terminalDisplay) - return; - m_impl->m_terminalDisplay->setScrollBarPosition((TerminalDisplay::ScrollBarPosition)pos); -} - -void QTermWidget::sendText(QString &text) -{ - m_impl->m_session->sendText(text); -} - -void QTermWidget::resizeEvent(QResizeEvent*) -{ -//qDebug("global window resizing...with %d %d", this->size().width(), this->size().height()); - m_impl->m_terminalDisplay->resize(this->size()); -} - - - -void QTermWidget::sessionFinished() -{ - emit finished(); -} - - -//#include "moc_consoleq.cpp" -
deleted file mode 100644 --- a/gui/qtermwidget/lib/qtermwidget.h +++ /dev/null @@ -1,108 +0,0 @@ -/* Copyright (C) 2008 e_k (e_k@users.sourceforge.net) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - - -#ifndef _Q_TERM_WIDGET -#define _Q_TERM_WIDGET - -#include <QtGui> - -struct TermWidgetImpl; - -enum COLOR_SCHEME { COLOR_SCHEME_WHITE_ON_BLACK = 1, - COLOR_SCHEME_GREEN_ON_BLACK, - COLOR_SCHEME_BLACK_ON_LIGHT_YELLOW }; - -class QTermWidget : public QWidget -{ - Q_OBJECT -public: - - enum ScrollBarPosition - { - /** Do not show the scroll bar. */ - NoScrollBar=0, - /** Show the scroll bar on the left side of the display. */ - ScrollBarLeft=1, - /** Show the scroll bar on the right side of the display. */ - ScrollBarRight=2 - }; - - - //Creation of widget - QTermWidget(int startnow = 1, //start shell programm immediatelly - QWidget *parent = 0); - ~QTermWidget(); - - //start shell program if it was not started in constructor - void startShellProgram(); - - //look-n-feel, if you don`t like defaults - - // Terminal font - // Default is application font with family Monospace, size 10 - void setTerminalFont(QFont &font); - - // Shell program, default is /bin/bash - void setShellProgram(QString &progname); - - // Shell program args, default is none - void setArgs(QStringList &args); - - //Text codec, default is UTF-8 - void setTextCodec(QTextCodec *codec); - - //Color scheme, default is white on black - void setColorScheme(int scheme); - - //set size - void setSize(int h, int v); - - // History size for scrolling - void setHistorySize(int lines); //infinite if lines < 0 - - // Presence of scrollbar - void setScrollBarPosition(ScrollBarPosition); - - // Send some text to terminal - void sendText(QString &text); - -signals: - void finished(); - -protected: - virtual void resizeEvent(QResizeEvent *); - -protected slots: - void sessionFinished(); - -private: - void init(); - TermWidgetImpl *m_impl; -}; - - -//Maybe useful, maybe not - -#ifdef __cplusplus -extern "C" -#endif -void *createTermWidget(int startnow, void *parent); - -#endif -
deleted file mode 100644 --- a/gui/qtermwidget/qtermwidget.pro +++ /dev/null @@ -1,4 +0,0 @@ -TEMPLATE = subdirs -SUBDIRS = lib src - -OPTIONS += ordered \ No newline at end of file
deleted file mode 100644 --- a/gui/qtermwidget/src/CVS/Entries +++ /dev/null @@ -1,4 +0,0 @@ -/README/1.1.1.1/Sat May 10 21:27:57 2008// -/src.pro/1.1.1.1/Sat May 10 21:27:57 2008// -/main.cpp/1.7/Wed Jul 30 21:32:00 2008// -D
deleted file mode 100644 --- a/gui/qtermwidget/src/CVS/Repository +++ /dev/null @@ -1,1 +0,0 @@ -qtermwidget/src
deleted file mode 100644 --- a/gui/qtermwidget/src/CVS/Root +++ /dev/null @@ -1,1 +0,0 @@ -:ext:e_k@qtermwidget.cvs.sourceforge.net:/cvsroot/qtermwidget
deleted file mode 100644 --- a/gui/qtermwidget/src/README +++ /dev/null @@ -1,1 +0,0 @@ -here is sample program which uses QTermWidet for displaying a terminal \ No newline at end of file
deleted file mode 100644 --- a/gui/qtermwidget/src/main.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* Copyright (C) 2008 e_k (e_k@users.sourceforge.net) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - - -#include <QtCore> -#include <QtGui> -#include <QApplication> - -#include "qtermwidget.h" - -int main(int argc, char *argv[]) -{ - QApplication app(argc, argv); - QMainWindow *mainWindow = new QMainWindow(); - - QTermWidget *console = new QTermWidget(); - - QFont font = QApplication::font(); - font.setFamily("Terminus"); - font.setPointSize(12); - - console->setTerminalFont(font); - - //console->setColorScheme(COLOR_SCHEME_BLACK_ON_LIGHT_YELLOW); - console->setScrollBarPosition(QTermWidget::ScrollBarRight); - - mainWindow->setCentralWidget(console); - mainWindow->resize(802, 610); - - QObject::connect(console, SIGNAL(finished()), mainWindow, SLOT(close())); - - mainWindow->show(); - return app.exec(); -} - -
deleted file mode 100644 --- a/gui/qtermwidget/src/src.pro +++ /dev/null @@ -1,27 +0,0 @@ -TEMPLATE = app -DESTDIR = .. - -CONFIG += qt debug_and_release warn_on build_all - -QT += core gui - -MOC_DIR = ../.moc - -CONFIG(debug, debug|release) { - OBJECTS_DIR = ../.objs_d - TARGET = consoleq_d - LIBS += -L.. ../libqtermwidget_d.a -} else { - OBJECTS_DIR = ../.objs - TARGET = consoleq - LIBS += -L.. ../libqtermwidget.a -} - -SOURCES = main.cpp - -INCLUDEPATH = ../lib - -#LIBS += -L.. -lqtermwidget - - - \ No newline at end of file