File konsole-session-management.diff of Package kdebase4

Index:  apps/konsole/src/ViewManager.h
===================================================================
---  apps/konsole/src/ViewManager.h	(revision 875843)
+++  apps/konsole/src/ViewManager.h	(working copy)
@@ -148,6 +148,12 @@
 	 */
 	SessionController* activeViewController() 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();
@@ -296,3 +302,12 @@
 }
 
 #endif
+
+/*
+  Local Variables:
+  mode: c++
+  c-file-style: "stroustrup"
+  indent-tabs-mode: nil
+  tab-width: 4
+  End:
+*/
Index:  apps/konsole/src/MainWindow.h
===================================================================
---  apps/konsole/src/MainWindow.h	(revision 875843)
+++  apps/konsole/src/MainWindow.h	(working copy)
@@ -132,6 +132,10 @@
     protected:
         // 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();
@@ -176,3 +180,12 @@
 }
 
 #endif // KONSOLEMAINWINDOW_H
+
+/*
+  Local Variables:
+  mode: c++
+  c-file-style: "stroustrup"
+  indent-tabs-mode: nil
+  tab-width: 4
+  End:
+*/
Index:  apps/konsole/src/SessionController.cpp
===================================================================
---  apps/konsole/src/SessionController.cpp	(revision 875843)
+++  apps/konsole/src/SessionController.cpp	(working copy)
@@ -50,7 +50,6 @@
 #include "IncrementalSearchBar.h"
 #include "ScreenWindow.h"
 #include "Session.h"
-#include "ProcessInfo.h"
 #include "ProfileList.h"
 #include "TerminalDisplay.h"
 #include "SessionManager.h"
@@ -199,44 +198,9 @@
 {
     Q_ASSERT( _session != 0 );
 
-    ProcessInfo* process = 0;
-    ProcessInfo* snapshot = ProcessInfo::newInstance(_session->processId());
-    snapshot->update();
+    QString title = _session->getDynamicTitle();
+    title         = title.simplified();
 
-    // use foreground process information if available
-    // fallback to session process otherwise
-    int pid = _session->foregroundProcessId(); //snapshot->foregroundPid(&ok);
-    if ( pid != 0 )
-    {
-       process = ProcessInfo::newInstance(pid);
-       process->update();
-    }
-    else
-       process = snapshot;
-
-    bool ok = false;
-
-    // format tab titles using process info
-    QString title;
-    if ( process->name(&ok) == "ssh" && ok )
-    {
-        SSHProcessInfo sshInfo(*process);
-        title = sshInfo.format(_session->tabTitleFormat(Session::RemoteTabTitle));
-    }
-    else
-        title = process->format(_session->tabTitleFormat(Session::LocalTabTitle) ) ;
-
-
-    if ( snapshot != process )
-    {
-        delete snapshot;
-        delete process;
-    }
-    else
-        delete snapshot;
-
-	title = title.simplified();
-
 	// crude indicator when the session is broadcasting to others
 	if (_copyToGroup && _copyToGroup->sessions().count() > 1)
 		title.append('*');
@@ -250,64 +214,12 @@
 
 QString SessionController::currentDir() const
 {
-    ProcessInfo* info = ProcessInfo::newInstance(_session->processId());
-    info->update();
-
-    bool ok = false;
-    QString path = info->currentDir(&ok);
-
-    delete info;
-
-    if ( ok )
-        return path;
-    else
-        return QString();
+    return _session->currentWorkingDirectory();
 }
 
 KUrl SessionController::url() const
 {
-    ProcessInfo* info = ProcessInfo::newInstance(_session->processId());
-    info->update();
-
-    QString path;
-    if ( info->isValid() )
-    {
-        bool ok = false;
-
-        // check if foreground process is bookmark-able
-        int pid = _session->foregroundProcessId();
-        if ( pid != 0 )
-        {
-            ProcessInfo* foregroundInfo = ProcessInfo::newInstance(pid);
-            foregroundInfo->update();
-
-            // for remote connections, save the user and host
-            // bright ideas to get the directory at the other end are welcome :)
-            if ( foregroundInfo->name(&ok) == "ssh" && ok )
-            {
-                SSHProcessInfo sshInfo(*foregroundInfo);
-                path = "ssh://" + sshInfo.userName() + '@' + sshInfo.host();
-            }
-            else
-            {
-                path = foregroundInfo->currentDir(&ok);
-
-                if (!ok)
-                    path.clear();
-            }
-
-            delete foregroundInfo;
-        }
-        else // otherwise use the current working directory of the shell process
-        {
-            path = info->currentDir(&ok);
-            if (!ok)
-                path.clear();
-        }
-    }
-
-    delete info;
-    return KUrl( path );
+    return _session->getUrl();
 }
 
 void SessionController::rename()
@@ -580,11 +492,6 @@
     _changeProfileMenu = new KMenu(i18n("Change Profile"),_view);
     collection->addAction("change-profile",_changeProfileMenu->menuAction());
     connect( _changeProfileMenu , SIGNAL(aboutToShow()) , this , SLOT(prepareChangeProfileMenu()) );
-
-    // debugging tools
-    //action = collection->addAction("debug-process");
-    //action->setText( "Get Foreground Process" );
-    //connect( action , SIGNAL(triggered()) , this , SLOT(debugProcess()) );
 }
 void SessionController::changeProfile(Profile::Ptr profile)
 {
@@ -610,41 +517,7 @@
 {
     _session->setCodec(codec);
 }
-void SessionController::debugProcess()
-{
-    // testing facility to retrieve process information about
-    // currently active process in the shell
-    ProcessInfo* sessionProcess = ProcessInfo::newInstance(_session->processId());
-    sessionProcess->update();
 
-    bool ok = false;
-    int fpid = sessionProcess->foregroundPid(&ok);
-
-    if ( ok )
-    {
-        ProcessInfo* fp = ProcessInfo::newInstance(fpid);
-        fp->update();
-
-        QString name = fp->name(&ok);
-
-        if ( ok )
-        {
-            _session->setTitle(Session::DisplayedTitleRole,name);
-            sessionTitleChanged();
-        }
-
-        QString currentDir = fp->currentDir(&ok);
-
-        if ( ok )
-            kDebug(1211) << currentDir;
-        else
-            kDebug(1211) << "could not read current dir of foreground process";
-
-        delete fp;
-    }
-    delete sessionProcess;
-}
-
 void SessionController::editCurrentProfile()
 {
     EditProfileDialog* dialog = new EditProfileDialog( QApplication::activeWindow() );
@@ -687,13 +560,9 @@
 }
 bool SessionController::confirmClose() const
 {
-    if (_session->foregroundProcessId() != _session->processId())
+    if (_session->isChildActive())
     {
-        ProcessInfo* foregroundInfo = ProcessInfo::newInstance(_session->foregroundProcessId());
-        foregroundInfo->update();
-        bool ok = false;
-        QString title = foregroundInfo->name(&ok);
-        delete foregroundInfo;
+        QString title = _session->childName();
       
         // 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
@@ -703,12 +572,12 @@
             return true;
 
         QString question;
-        if (ok)
-            question = i18n("The program '%1' is currently running in this session."  
-                            "  Are you sure you want to close it?",title);
-        else
+        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; 
@@ -1486,3 +1355,12 @@
 }
 
 #include "SessionController.moc"
+
+/*
+  Local Variables:
+  mode: c++
+  c-file-style: "stroustrup"
+  indent-tabs-mode: nil
+  tab-width: 4
+  End:
+*/
Index:  apps/konsole/src/ProcessInfo.h
===================================================================
---  apps/konsole/src/ProcessInfo.h	(revision 875843)
+++  apps/konsole/src/ProcessInfo.h	(working copy)
@@ -152,6 +152,11 @@
     QString currentDir(bool* ok) const;
 
     /**
+     * Returns the current working directory of the process (or its parent)
+     */
+    QString validCurrentDir() const;
+
+    /**
      * 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.
@@ -424,3 +429,12 @@
 
 }
 #endif //PROCESSINFO_H
+
+/*
+  Local Variables:
+  mode: c++
+  c-file-style: "stroustrup"
+  indent-tabs-mode: nil
+  tab-width: 4
+  End:
+*/
Index:  apps/konsole/src/ViewManager.cpp
===================================================================
---  apps/konsole/src/ViewManager.cpp	(revision 875843)
+++  apps/konsole/src/ViewManager.cpp	(working copy)
@@ -838,9 +838,91 @@
     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("ActiveTabIndex", 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>());
+
+    if (ids.isEmpty())
+    {
+        // Incomplete session file, e.g. from a KDE3 session. Create a
+        // default session so that we don't end up with an empty window.
+        kWarning() << i18n("Unable to restore saved tabs. Possibly unsupported KDE 3 session data.");
+        Session *session = SessionManager::instance()->createSession();
+        createView(session);
+        session->run();
+    }
+    else
+    {
+        int activeTab = group.readEntry("ActiveTabIndex", 0);
+        int tab = 1;
+        QWidget *focusView = 0;
+
+        foreach(int id, ids)
+        {
+            Session *session = SessionManager::instance()->idToSession(id);
+            createView(session);
+            if (!session->isRunning())
+                session->run();
+            if (tab++ == activeTab)
+                focusView = activeView();
+        }
+
+        if (focusView)
+        {
+            _viewSplitter->activeContainer()->setActiveView(focusView);
+            focusView->setFocus(Qt::OtherFocusReason);
+        }
+    }
+}
+
 uint qHash(QPointer<TerminalDisplay> display)
 {
     return qHash((TerminalDisplay*)display);
 }
 
 #include "ViewManager.moc"
+
+/*
+  Local Variables:
+  mode: c++
+  c-file-style: "stroustrup"
+  indent-tabs-mode: nil
+  tab-width: 4
+  End:
+*/
Index:  apps/konsole/src/SessionManager.cpp
===================================================================
---  apps/konsole/src/SessionManager.cpp	(revision 875843)
+++  apps/konsole/src/SessionManager.cpp	(working copy)
@@ -730,6 +730,69 @@
     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()
 {
@@ -833,3 +896,12 @@
 }
 
 #include "SessionManager.moc"
+
+/*
+  Local Variables:
+  mode: c++
+  c-file-style: "stroustrup"
+  indent-tabs-mode: nil
+  tab-width: 4
+  End:
+*/
Index:  apps/konsole/src/main.cpp
===================================================================
---  apps/konsole/src/main.cpp	(revision 875843)
+++  apps/konsole/src/main.cpp	(working copy)
@@ -19,6 +19,7 @@
 
 // Own
 #include "Application.h"
+#include "MainWindow.h"
 #include <KDebug>
 
 // Unix
@@ -51,6 +52,7 @@
 bool useTransparency();     // returns true if transparency should be enabled
 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.  
@@ -91,12 +93,14 @@
 		getDisplayInformation(display,visual,colormap);
 
 		Application app(display,(Qt::HANDLE)visual,(Qt::HANDLE)colormap);
+        restoreSession(app);
 		return app.exec();
 	}
 	else
 #endif 
 	{
     	Application app;
+        restoreSession(app);
     	return app.exec();
 	}   
 }
@@ -242,4 +246,21 @@
 }
 #endif
 
+void restoreSession(Application& app)
+{
+    if (app.isSessionRestored())
+    {
+        int n = 1;
+        while (KMainWindow::canBeRestored(n))
+            app.newMainWindow()->restore(n++);
+    }
+}
 
+/*
+  Local Variables:
+  mode: c++
+  c-file-style: "stroustrup"
+  indent-tabs-mode: nil
+  tab-width: 4
+  End:
+*/
Index:  apps/konsole/src/Session.h
===================================================================
---  apps/konsole/src/Session.h	(revision 875843)
+++  apps/konsole/src/Session.h	(working copy)
@@ -36,12 +36,14 @@
 #include "History.h"
 
 class KProcess;
+class KUrl;
 
 namespace Konsole
 {
 
 class Emulation;
 class Pty;
+class ProcessInfo;
 class TerminalDisplay;
 class ZModemDialog;
 
@@ -201,6 +203,11 @@
   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
@@ -274,12 +281,17 @@
   QString title(TitleRole role) 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. */
@@ -317,13 +329,12 @@
    */
   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 true if the user has started a program in the session. */
+  bool isChildActive();
 
+  /** Returns the name of the current foreground process. */
+  QString childName();
+
   /** Returns the terminal session's window size in lines and columns. */
   QSize size();
   /**
@@ -378,6 +389,10 @@
 	ProfileChange			= 50 	// this clashes with Xterm's font change command
   };
 
+  // session management
+  void saveSession(KConfigGroup& group);
+  void restoreSession(KConfigGroup& group);
+
 public slots:
 
   /**
@@ -515,6 +530,10 @@
   // 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();
 
   int            _uniqueIdentifier;
 
@@ -553,7 +572,12 @@
   int            _sessionId;
 
   QString        _initialWorkingDir;
+  QString        _currentWorkingDir;
 
+  ProcessInfo*   _sessionProcessInfo;
+  ProcessInfo*   _foregroundProcessInfo;
+  int            _foregroundPid;
+
   // ZModem
   bool           _zmodemBusy;
   KProcess*      _zmodemProc;
@@ -651,3 +675,12 @@
 }
 
 #endif
+
+/*
+  Local Variables:
+  mode: c++
+  c-file-style: "stroustrup"
+  indent-tabs-mode: nil
+  tab-width: 4
+  End:
+*/
Index:  apps/konsole/src/Application.cpp
===================================================================
---  apps/konsole/src/Application.cpp	(revision 875843)
+++  apps/konsole/src/Application.cpp	(working copy)
@@ -103,49 +103,56 @@
 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);
+  
+        // create new session
+        Session* session = createSession( window->defaultProfile() , QString() , window->viewManager() );
+        if ( !args->isSet("close") )
+            session->setAutoClose(false);
 
-    // check for arguments to print help or other information to the terminal,
-    // quit if such an argument was found
-    if ( processHelpArgs(args) ) 
-        return 0;
+        // 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());
 
-    // create a new window or use an existing one 
-    MainWindow* window = processWindowArgs(args);
+            window->show();
+        }
+      }
   
-    // 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);
-
-    // create new session
-    Session* session = createSession( window->defaultProfile() , QString() , window->viewManager() );
-	if ( !args->isSet("close") )
-		session->setAutoClose(false);
-
-    // if the background-mode argument is supplied, start the background session
-    // ( or bring to the front if it already exists )
-    if ( args->isSet("background-mode") )
-        startBackgroundMode(window);
-    else
-	{
-		// Qt constrains top-level windows which have not been manually resized
-		// (via QWidget::resize()) to a maximum of 2/3rds of the screen size.
-		//
-		// This means that the terminal display might not get the width/height
-		// it asks for.  To work around this, the widget must be manually resized
-		// to its sizeHint().
-		//
-		// This problem only affects the first time the application is run.  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;
 }
 
@@ -313,3 +320,12 @@
 }
 
 #include "Application.moc"
+
+/*
+  Local Variables:
+  mode: c++
+  c-file-style: "stroustrup"
+  indent-tabs-mode: nil
+  tab-width: 4
+  End:
+*/
Index:  apps/konsole/src/MainWindow.cpp
===================================================================
---  apps/konsole/src/MainWindow.cpp	(revision 875843)
+++  apps/konsole/src/MainWindow.cpp	(working copy)
@@ -19,6 +19,7 @@
 
 // Own
 #include "MainWindow.h"
+#include "SessionManager.h"
 
 // Qt
 #include <QtGui/QBoxLayout>
@@ -372,6 +373,34 @@
     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::showShortcutsDialog()
 {
     KShortcutsDialog::configure( actionCollection() ,
@@ -419,3 +448,12 @@
 }
 
 #include "MainWindow.moc"
+
+/*
+  Local Variables:
+  mode: c++
+  c-file-style: "stroustrup"
+  indent-tabs-mode: nil
+  tab-width: 4
+  End:
+*/
Index:  apps/konsole/src/Session.cpp
===================================================================
---  apps/konsole/src/Session.cpp	(revision 875843)
+++  apps/konsole/src/Session.cpp	(working copy)
@@ -47,11 +47,13 @@
 #include <KRun>
 #include <kshell.h>
 #include <KStandardDirs>
+#include <KUrl>
 
 // Konsole
 #include <config-konsole.h>
 #include <sessionadaptor.h>
 
+#include "ProcessInfo.h"
 #include "Pty.h"
 #include "TerminalDisplay.h"
 #include "ShellCommand.h"
@@ -76,6 +78,9 @@
    , _flowControl(true)
    , _fullScripting(false)
    , _sessionId(0)
+   , _sessionProcessInfo(0)
+   , _foregroundProcessInfo(0)
+   , _foregroundPid(0)
    , _zmodemBusy(false)
    , _zmodemProc(0)
    , _zmodemProgress(0)
@@ -199,6 +204,19 @@
     _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;
@@ -321,9 +339,13 @@
 {
   //check that everything is in place to run the session
   if (_program.isEmpty())
+  {
       kDebug() << "Session::run() - program to run not set.";
+  }
   if (_arguments.isEmpty())
+  {
       kDebug() << "Session::run() - no command line arguments specified.";
+  }
 
   const int CHOICE_COUNT = 3;
   QString programs[CHOICE_COUNT] = {_program,getenv("SHELL"),"/bin/sh"};
@@ -648,6 +670,10 @@
 
 Session::~Session()
 {
+  if (_foregroundProcessInfo)
+      delete _foregroundProcessInfo;
+  if (_sessionProcessInfo)
+      delete _sessionProcessInfo;
   delete _emulation;
   delete _shellProcess;
   delete _zmodemProc;
@@ -735,6 +761,110 @@
         return QString();
 }
 
+ProcessInfo* Session::getProcessInfo()
+{
+    ProcessInfo* process;
+
+    if (isChildActive())
+        process = _foregroundProcessInfo;
+    else
+    {
+        updateSessionProcessInfo();
+        process = _sessionProcessInfo;
+    }
+
+    return process;
+}
+
+void Session::updateSessionProcessInfo()
+{
+    Q_ASSERT(_shellProcess);
+    if (!_sessionProcessInfo)
+        _sessionProcessInfo = ProcessInfo::newInstance(processId());
+    _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;
+}
+
+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 (isChildActive())
+        {
+            // 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 )
@@ -970,15 +1100,51 @@
 
   emit resizeRequest(size);
 }
-int Session::foregroundProcessId() const
-{
-    return _shellProcess->foregroundProcessGroup();
-}
 int Session::processId() const
 {
     return _shellProcess->pid();
 }
 
+bool Session::isChildActive()
+{
+    // foreground process info is always updated after this
+    return updateForegroundProcessInfo() && (processId() != _foregroundPid);
+}
+
+QString Session::childName()
+{
+    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));
+}
+
+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);
+}
+
 SessionGroup::SessionGroup(QObject* parent)
     : QObject(parent), _masterMode(0)
 {
@@ -1097,3 +1263,12 @@
 }
 
 #include "Session.moc"
+
+/*
+  Local Variables:
+  mode: c++
+  c-file-style: "stroustrup"
+  indent-tabs-mode: nil
+  tab-width: 4
+  End:
+*/
Index:  apps/konsole/src/SessionManager.h
===================================================================
---  apps/konsole/src/SessionManager.h	(revision 875843)
+++  apps/konsole/src/SessionManager.h	(working copy)
@@ -243,6 +243,12 @@
      */
     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);
@@ -318,6 +324,7 @@
 
 	QSet<Profile::Ptr> _types;
     QHash<Session*,Profile::Ptr> _sessionProfiles;
+    QHash<Session*,int> _restoreMapping;
 
     struct ShortcutData
     {
@@ -405,3 +412,11 @@
 }
 #endif //SESSIONMANAGER_H
 
+/*
+  Local Variables:
+  mode: c++
+  c-file-style: "stroustrup"
+  indent-tabs-mode: nil
+  tab-width: 4
+  End:
+*/
Index:  apps/konsole/src/ProcessInfo.cpp
===================================================================
---  apps/konsole/src/ProcessInfo.cpp	(revision 875843)
+++  apps/konsole/src/ProcessInfo.cpp	(working copy)
@@ -64,18 +64,10 @@
     readProcessInfo(_pid,_enableEnvironmentRead);
 }
 
-QString ProcessInfo::format(const QString& input) const
+QString ProcessInfo::validCurrentDir() const
 {
    bool ok = false;
 
-   QString output(input);
-
-   // search for and replace known marker
-   output.replace("%u","NOT IMPLEMENTED YET");
-   output.replace("%n",name(&ok));
-   output.replace("%c",formatCommand(name(&ok),arguments(&ok),ShortCommandFormat));
-   output.replace("%C",formatCommand(name(&ok),arguments(&ok),LongCommandFormat));
-   
    // read current dir, if an error occurs try the parent as the next
    // best option
    int currentPid = parentPid(&ok);
@@ -88,7 +80,23 @@
        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","NOT IMPLEMENTED YET");
+   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();
    output.replace("%D",dir);
    output.replace("%d",formatShortDir(dir));
    
@@ -731,3 +739,11 @@
 #endif
 }
 
+/*
+  Local Variables:
+  mode: c++
+  c-file-style: "stroustrup"
+  indent-tabs-mode: nil
+  tab-width: 4
+  End:
+*/
Index:  apps/konsole/src/SessionController.h
===================================================================
---  apps/konsole/src/SessionController.h	(revision 875843)
+++  apps/konsole/src/SessionController.h	(working copy)
@@ -210,9 +210,6 @@
 
 	void updateSearchFilter();
 
-    // debugging slots
-    void debugProcess();
-
 private:
     // begins the search
     // text - pattern to search for
@@ -440,3 +437,12 @@
 }
 
 #endif //SESSIONCONTROLLER_H
+
+/*
+  Local Variables:
+  mode: c++
+  c-file-style: "stroustrup"
+  indent-tabs-mode: nil
+  tab-width: 4
+  End:
+*/