diff --git a/language/duchain/repositories/itemrepository.cpp b/language/duchain/repositories/itemrepository.cpp index 90e2924a8e..ae97d8d652 100644 --- a/language/duchain/repositories/itemrepository.cpp +++ b/language/duchain/repositories/itemrepository.cpp @@ -1,405 +1,411 @@ /* Copyright 2008 David Nolden 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. */ #include "itemrepository.h" #include #include #include #include #include #include #include #include "../duchain.h" namespace KDevelop { //If KDevelop crashed this many times consicutively, clean up the repository const int crashesBeforeCleanup = 2; uint staticItemRepositoryVersion() { //Increase this to reset incompatible item-repositories return 65; } AbstractItemRepository::~AbstractItemRepository() { } ItemRepositoryRegistry::ItemRepositoryRegistry(QString openPath, KLockFile::Ptr lock) : m_mutex(QMutex::Recursive) { if(!openPath.isEmpty()) open(openPath, false, lock); } QMutex& ItemRepositoryRegistry::mutex() { return m_mutex; } QAtomicInt& ItemRepositoryRegistry::getCustomCounter(const QString& identity, int initialValue) { if(!m_customCounters.contains(identity)) m_customCounters.insert(identity, new QAtomicInt(initialValue)); return *m_customCounters[identity]; } bool processExists(int pid) { ///@todo Find a cross-platform way of doing this! QFileInfo f(QString("/proc/%1").arg(pid)); return f.exists(); } QPair allocateRepository() { KLockFile::Ptr lock; QString repoPath; KComponentData component("item repositories temp", QByteArray(), KComponentData::SkipMainComponentRegistration); QString baseDir = QDir::homePath() + "/.kdevduchain"; KStandardDirs::makeDir(baseDir); + + if(getenv("KDEV_SESSION")) + baseDir += "/" + QString(getenv("KDEV_SESSION")); + else + kWarning() << "Missing session environment variable KDEV_SESSION!"; + //Since each instance of kdevelop needs an own directory, iterate until we find a not-yet-used one for(int a = 0; a < 100; ++a) { QString specificDir = baseDir + QString("/%1").arg(a); KStandardDirs::makeDir(specificDir); lock = new KLockFile(specificDir + "/lock", component); KLockFile::LockResult result = lock->lock(KLockFile::NoBlockFlag | KLockFile::ForceFlag); bool useDir = false; if(result != KLockFile::LockOK) { int pid; QString hostname, appname; if(lock->getLockInfo(pid, hostname, appname)) { if(!processExists(pid)) { kDebug() << "The process holding" << specificDir << "with pid" << pid << "does not exists any more. Re-using the directory."; QFile::remove(specificDir + "/lock"); useDir = true; if(lock->lock(KLockFile::NoBlockFlag | KLockFile::ForceFlag) != KLockFile::LockOK) { kWarning() << "Failed to re-establish the lock in" << specificDir; continue; } } } }else { useDir = true; } if(useDir) { repoPath = specificDir; if(result == KLockFile::LockStale) { kWarning() << "stale lock detected:" << specificDir + "/lock"; } break; } } if(repoPath.isEmpty()) { kError() << "could not create a directory for the duchain data"; }else{ kDebug() << "picked duchain directory" << repoPath; } return qMakePair(repoPath, lock); } ///The global item-repository registry that is used by default ItemRepositoryRegistry& allocateGlobalItemRepositoryRegistry() { QPair repo = allocateRepository(); ///We intentionally leak the registry, to prevent problems in the destruction order, where ///the actual repositories might get deleted later than the repository registry. static ItemRepositoryRegistry* global = new ItemRepositoryRegistry(repo.first, repo.second); return *global; } ///The global item-repository registry that is used by default ItemRepositoryRegistry& globalItemRepositoryRegistry() { static ItemRepositoryRegistry& global(allocateGlobalItemRepositoryRegistry()); return global; } void ItemRepositoryRegistry::registerRepository(AbstractItemRepository* repository, AbstractRepositoryManager* manager) { QMutexLocker lock(&m_mutex); m_repositories.insert(repository, manager); if(!m_path.isEmpty()) { if(!repository->open(m_path)) { deleteDataDirectory(); kError() << "failed to open a repository"; abort(); } } } QString ItemRepositoryRegistry::path() const { //We cannot lock the mutex here, since this may be called with one of the repositories locked, //and that may lead to a deadlock when at the same time a storing is requested return m_path; } void ItemRepositoryRegistry::lockForWriting() { QMutexLocker lock(&m_mutex); //Create is_writing QFile f(m_path + "/is_writing"); f.open(QIODevice::WriteOnly); f.close(); } void ItemRepositoryRegistry::unlockForWriting() { QMutexLocker lock(&m_mutex); //Delete is_writing QFile::remove(m_path + "/is_writing"); } void ItemRepositoryRegistry::unRegisterRepository(AbstractItemRepository* repository) { QMutexLocker lock(&m_mutex); Q_ASSERT(m_repositories.contains(repository)); repository->close(); m_repositories.remove(repository); } //Recursive delete, copied from a mailing-list //Returns true on success bool removeDirectory(const QDir &aDir) { bool has_err = false; if (aDir.exists())//QDir::NoDotAndDotDot { QFileInfoList entries = aDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Dirs | QDir::Files); int count = entries.size(); for (int idx = 0; ((idx < count) && !has_err); idx++) { QFileInfo entryInfo = entries[idx]; QString path = entryInfo.absoluteFilePath(); if (entryInfo.isDir()) { has_err = !removeDirectory(QDir(path)); } else { QFile file(path); if (!file.remove()) has_err = true; } } if (!aDir.rmdir(aDir.absolutePath())) has_err = true; } return !has_err; } //After calling this, the data-directory may be a new one void ItemRepositoryRegistry::deleteDataDirectory() { QMutexLocker lock(&m_mutex); QFileInfo pathInfo(m_path); QDir d(m_path); //lockForWriting creates a file, that prevents any other KDevelop instance from using the directory as it is. //Instead, the other instance will try to delete the directory as well. lockForWriting(); // Have to release the lock here, else it will delete the new lock and windows needs all file-handles // to be released before deleting a directory that contains these files m_lock->unlock(); bool result = removeDirectory(d); Q_ASSERT(result); Q_ASSERT(m_lock); //Just remove the old directory, and allocate a new one. Probably it'll be the same one. QPair repo = allocateRepository(); m_path = repo.first; m_lock = repo.second; } bool ItemRepositoryRegistry::open(const QString& path, bool clear, KLockFile::Ptr lock) { QMutexLocker mlock(&m_mutex); if(m_path == path && !clear) return true; m_lock = lock; m_path = path; bool needRepoCheck = true; while(needRepoCheck) { if(QFile::exists(m_path + "/is_writing")) { kWarning() << "repository" << m_path << "was write-locked, it probably is inconsistent"; clear = true; } QDir pathDir(m_path); pathDir.setFilter(QDir::Files); //When there is only one file in the repository, it's the lock-file, and the repository has just been cleared if(pathDir.count() != 1) { if(!QFile::exists( m_path + QString("/version_%1").arg(staticItemRepositoryVersion()) )) { kWarning() << "version-hint not found, seems to be an old version"; clear = true; }else if(getenv("CLEAR_DUCHAIN_DIR")) { kWarning() << "clearing duchain directory because CLEAR_DUCHAIN_DIR is set"; clear = true; } } QFile crashesFile(m_path + QString("/crash_counter")); if(crashesFile.open(QIODevice::ReadOnly)) { int count; QDataStream stream(&crashesFile); stream >> count; kDebug() << "current count of crashes: " << count; if(count >= crashesBeforeCleanup) { ///@todo Ask the user kWarning() << "kdevelop crashed" << count << "times in a row with the duchain repository" << m_path << ", clearing it"; clear = true; }else{ ///Increase the crash-count. It will be reset of kdevelop is shut down cleanly. ++count; crashesFile.close(); crashesFile.open(QIODevice::WriteOnly | QIODevice::Truncate); QDataStream writeStream(&crashesFile); writeStream << count; } }else{ ///Increase the crash-count. It will be reset of kdevelop is shut down cleanly. crashesFile.open(QIODevice::WriteOnly | QIODevice::Truncate); QDataStream writeStream(&crashesFile); writeStream << 1; } if(clear) { kWarning() << QString("The data-repository at %1 has to be cleared.").arg(m_path); // KMessageBox::information( 0, i18n("The data-repository at %1 has to be cleared. Either the disk format has changed, or KDevelop crashed while writing the repository.", m_path ) ); #ifdef Q_OS_WIN /// on Windows a file can't be deleted unless the last file handle gets closed /// deleteDataDirectory would enter a never ending loop crashesFile.close(); #endif deleteDataDirectory(); clear = false; //We need to re-check, because a new data-directory may have been picked }else{ needRepoCheck = false; } } foreach(AbstractItemRepository* repository, m_repositories.keys()) { if(!repository->open(path)) { deleteDataDirectory(); kError() << "failed to open a repository"; abort(); } } QFile f(path + "/Counters"); if(f.open(QIODevice::ReadOnly)) { QDataStream stream(&f); while(!stream.atEnd()) { //Read in all custom counter values QString counterName; stream >> counterName; int counterValue; stream >> counterValue; if(m_customCounters.contains(counterName)) *m_customCounters[counterName] = counterValue; else getCustomCounter(counterName, 0) = counterValue; } }else{ // kDebug() << "Could not open counter file"; } return true; } void ItemRepositoryRegistry::store() { QMutexLocker lock(&m_mutex); foreach(AbstractItemRepository* repository, m_repositories.keys()) repository->store(); QFile versionFile(m_path + QString("/version_%1").arg(staticItemRepositoryVersion())); if(versionFile.open(QIODevice::WriteOnly)) { versionFile.close(); }else{ kWarning() << "Could not open version file for writing"; } //Store all custom counter values QFile f(m_path + "/Counters"); if(f.open(QIODevice::WriteOnly)) { f.resize(0); QDataStream stream(&f); for(QMap::const_iterator it = m_customCounters.constBegin(); it != m_customCounters.constEnd(); ++it) { stream << it.key(); stream << it.value()->fetchAndAddRelaxed(0); } }else{ kWarning() << "Could not open counter file for writing"; } } void ItemRepositoryRegistry::printAllStatistics() const { QMutexLocker lock(&m_mutex); foreach(AbstractItemRepository* repository, m_repositories.keys()) { kDebug() << "statistics in" << repository->repositoryName() << ":"; kDebug() << repository->printStatistics(); } } int ItemRepositoryRegistry::finalCleanup() { QMutexLocker lock(&m_mutex); int changed = false; foreach(AbstractItemRepository* repository, m_repositories.keys()) { int added = repository->finalCleanup(); changed += added; kDebug() << "cleaned in" << repository->repositoryName() << ":" << added; } return changed; } void ItemRepositoryRegistry::close() { QMutexLocker lock(&m_mutex); foreach(AbstractItemRepository* repository, m_repositories.keys()) repository->close(); m_path.clear(); } ItemRepositoryRegistry::~ItemRepositoryRegistry() { close(); QMutexLocker lock(&m_mutex); foreach(QAtomicInt* counter, m_customCounters) delete counter; } void ItemRepositoryRegistry::shutdown() { QFile::remove(m_path + QString("/crash_counter")); if(m_lock) m_lock->unlock(); } } diff --git a/shell/session.cpp b/shell/session.cpp index 7f4929d937..1378c64c5a 100644 --- a/shell/session.cpp +++ b/shell/session.cpp @@ -1,151 +1,172 @@ /* This file is part of KDevelop Copyright 2008 Andreas Pakulat 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 "session.h" #include #include #include #include #include #include #include #include #include #include #include "core.h" #include "sessioncontroller.h" +#include +#include namespace KDevelop { const QString Session::cfgSessionNameEntry = "SessionName"; +const QString Session::cfgSessionPrettyContentsEntry = "SessionPrettyContents"; class SessionPrivate { public: QUuid id; KSharedConfig::Ptr config; QString sessionDirectory; KUrl pluginArea( const IPlugin* plugin ) { QString name = Core::self()->pluginController()->pluginInfo( plugin ).pluginName(); QFileInfo fi( sessionDirectory + '/' + name ); if( !fi.exists() ) { QDir d( sessionDirectory ); d.mkdir( name ); } kDebug() << fi.absolutePath(); return KUrl( fi.absolutePath() ); } void initialize() { sessionDirectory = SessionController::sessionDirectory() + '/' + id.toString(); kDebug() << "got dir:" << sessionDirectory; if( !QFileInfo( sessionDirectory ).exists() ) { kDebug() << "creating dir"; QDir( SessionController::sessionDirectory() ).mkdir( id.toString() ); } config = KSharedConfig::openConfig( sessionDirectory+"/sessionrc" ); } }; Session::Session( const QUuid& id ) : d( new SessionPrivate ) { d->id = id; d->initialize(); } Session::~Session() { delete d; } QString Session::name() const { return d->config->group("").readEntry( cfgSessionNameEntry, "" ); } KUrl::List Session::containedProjects() const { return d->config->group( "General Options" ).readEntry( "Open Projects", QStringList() ); } -QString Session::description() const +void Session::updateDescription() { - QString ret = name(); - KUrl::List openProjects = containedProjects(); + QString prettyContents; + if(!openProjects.isEmpty()) { - if(!ret.isEmpty()) - ret += ": "; QStringList projectNames; foreach(KUrl url, openProjects) { - QString projectName = url.fileName(); - if(projectName.endsWith(".kdev4")) - projectName = projectName.left(projectName.size()-6); - projectNames << projectName; + IProject* project = ICore::self()->projectController()->findProjectForUrl(url); + if(project) { + projectNames << project->name(); + }else{ + QString projectName = url.fileName(); + if(projectName.endsWith(".kdev4")) + projectName = projectName.left(projectName.size()-6); + projectNames << projectName; + } } - ret += projectNames.join(", "); + prettyContents = projectNames.join(", "); } + d->config->group("").writeEntry( cfgSessionPrettyContentsEntry, prettyContents ); +} + +QString Session::description() const +{ + QString ret = name(); + + QString prettyContents = d->config->group("").readEntry( cfgSessionPrettyContentsEntry, "" ); + + if(!prettyContents.isEmpty()) + { + if(!ret.isEmpty()) + ret += ": "; + ret += prettyContents; + } return ret; } KUrl Session::pluginDataArea( const IPlugin* p ) { return d->pluginArea( p ); } KSharedConfig::Ptr Session::config() { return d->config; } QUuid Session::id() const { return d->id; } void Session::deleteFromDisk() { KIO::NetAccess::del( KUrl( d->sessionDirectory ), Core::self()->uiController()->activeMainWindow() ); } void Session::setName( const QString& newname ) { QString oldname = name(); d->config->group("").writeEntry( cfgSessionNameEntry, newname ); d->config->sync(); emit nameChanged( newname, oldname ); } } #include "session.moc" diff --git a/shell/session.h b/shell/session.h index d6c770e9fa..b3cb9c0bf6 100644 --- a/shell/session.h +++ b/shell/session.h @@ -1,60 +1,63 @@ /* This file is part of KDevelop Copyright 2008 Andreas Pakulat 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 SESSION_H #define SESSION_H #include "shellexport.h" #include #include #include #include namespace KDevelop { class KDEVPLATFORMSHELL_EXPORT Session : public ISession { Q_OBJECT public: static const QString cfgSessionNameEntry; + static const QString cfgSessionPrettyContentsEntry; Session( const QUuid& ); virtual ~Session(); virtual KUrl pluginDataArea( const IPlugin* ); virtual KSharedConfig::Ptr config(); void deleteFromDisk(); KUrl::List containedProjects() const; + void updateDescription(); + virtual QString description() const; virtual QString name() const; void setName( const QString& ); QUuid id() const; Q_SIGNALS: void nameChanged( const QString& newname, const QString& oldname ); private: class SessionPrivate* const d; }; } #endif diff --git a/shell/sessioncontroller.cpp b/shell/sessioncontroller.cpp index cbb7397edc..b4526ec288 100644 --- a/shell/sessioncontroller.cpp +++ b/shell/sessioncontroller.cpp @@ -1,348 +1,393 @@ /* This file is part of KDevelop Copyright 2008 Andreas Pakulat 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 "sessioncontroller.h" #include #include #include #include #include +#include #include #include #include #include #include #include #include #include "session.h" #include "core.h" #include "uicontroller.h" #include "sessiondialog.h" #include #include #include #include +#include namespace KDevelop { const QString SessionController::cfgSessionGroup = "Sessions"; -const QString SessionController::cfgActiveSessionEntry("Active Session"); +const QString SessionController::cfgActiveSessionEntry("Active Session ID"); class SessionControllerPrivate : public QObject { Q_OBJECT public: SessionControllerPrivate( SessionController* s ) : q(s) {} Session* findSessionForName( const QString& name ) const { foreach( Session* s, sessionActions.keys() ) { if( s->name() == name ) return s; } return 0; } Session* findSessionForId(QString idString) { QUuid id(idString); foreach( Session* s, sessionActions.keys() ) { if( s->id() == id) return s; } return 0; } void newSession() { Session* session = new Session( QUuid::createUuid() ); - KProcess::startDetached("kdevelop", QStringList() << "--s" << session->id().toString()); + KProcess::startDetached("kdev_starter", QStringList() << KCmdLineArgs::appName() << session->id().toString()); delete session; //Terminate this instance of kdevelop if the user agrees foreach(Sublime::MainWindow* window, Core::self()->uiController()->controller()->mainWindows()) window->close(); } void configureSessions() { SessionDialog dlg(ICore::self()->uiController()-> activeMainWindow()); dlg.exec(); } void loadSessionExternally( Session* s ) { Q_ASSERT( s ); - KProcess::startDetached("kdevelop", QStringList() << "--s" << s->id().toString()); + KProcess::startDetached("kdev_starter", QStringList() << KCmdLineArgs::appName() << s->id().toString()); } void activateSession( Session* s ) { Q_ASSERT( s ); QHash::iterator it = sessionActions.find(s); Q_ASSERT( it != sessionActions.end() ); (*it)->setCheckable(true); (*it)->setChecked(true); for(it = sessionActions.begin(); it != sessionActions.end(); ++it) { if(it.key() != s) (*it)->setCheckable(false); } KConfigGroup grp = KGlobal::config()->group( SessionController::cfgSessionGroup ); - grp.writeEntry( SessionController::cfgActiveSessionEntry, s->name() ); + grp.writeEntry( SessionController::cfgActiveSessionEntry, s->id().toString() ); grp.sync(); activeSession = s; } void loadSessionFromAction( QAction* a ) { foreach( Session* s, sessionActions.keys() ) { if( s->id() == QUuid( a->data().toString() ) && s != activeSession ) { loadSessionExternally( s ); //Terminate this instance of kdevelop if the user agrees foreach(Sublime::MainWindow* window, Core::self()->uiController()->controller()->mainWindows()) window->close(); break; } } } void addSession( Session* s ) { KAction* a = new KAction( grp ); a->setText( s->description() ); a->setCheckable( false ); a->setData( s->id().toString() ); sessionActions[s] = a; q->actionCollection()->addAction( "session_"+s->id().toString(), a ); q->unplugActionList( "available_sessions" ); q->plugActionList( "available_sessions", grp->actions() ); connect(s, SIGNAL(nameChanged(QString, QString)), SLOT(nameChanged())); } QHash sessionActions; ISession* activeSession; SessionController* q; QActionGroup* grp; private slots: void nameChanged() { Q_ASSERT(qobject_cast(sender())); Session* s = static_cast(sender()); sessionActions[s]->setText( s->description() ); } }; void SessionController::updateSessionDescriptions() { - for(QHash< Session*, QAction* >::iterator it = d->sessionActions.begin(); it != d->sessionActions.end(); ++it) + for(QHash< Session*, QAction* >::iterator it = d->sessionActions.begin(); it != d->sessionActions.end(); ++it) { + it.key()->updateDescription(); (*it)->setText(it.key()->description()); + } } SessionController::SessionController( QObject *parent ) : QObject( parent ), d(new SessionControllerPrivate(this)) { setObjectName("SessionController"); setComponentData(KComponentData("kdevsession")); setXMLFile("kdevsessionui.rc"); KAction* action = actionCollection()->addAction( "new_session", this, SLOT( newSession() ) ); action->setText( i18n("Start New Session") ); action->setToolTip( i18n("Start a new KDevelop instance with an empty session") ); action = actionCollection()->addAction( "configure_sessions", this, SLOT( configureSessions() ) ); action->setText( i18n("Configure Sessions...") ); action->setToolTip( i18n("Create/Delete/Activate Sessions") ); action->setWhatsThis( i18n( "Configure Sessions

Shows a dialog to Create/Delete Sessions and set a new active session.

" ) ); d->grp = new QActionGroup( this ); connect( d->grp, SIGNAL(triggered(QAction*)), this, SLOT(loadSessionFromAction(QAction*)) ); } SessionController::~SessionController() { delete d; } void SessionController::startNewSession() { d->newSession(); } void SessionController::cleanup() { qDeleteAll(d->sessionActions); } void SessionController::initialize() { QDir sessiondir( SessionController::sessionDirectory() ); foreach( const QString& s, sessiondir.entryList( QDir::AllDirs ) ) { QUuid id( s ); if( id.isNull() ) continue; // Only create sessions for directories that represent proper uuid's Session* session = new Session( id ); //Delete sessions that have no name and are empty if( session->description().isEmpty() && (session->id().toString() != QString(getenv("KDEV_SESSION")))) { ///@todo Think about when we can do this. Another instance might still be using this session. // session->deleteFromDisk(); delete session; }else{ d->addSession( session ); } } loadDefaultSession(); connect(Core::self()->projectController(), SIGNAL(projectClosed(KDevelop::IProject*)), SLOT(updateSessionDescriptions())); connect(Core::self()->projectController(), SIGNAL(projectOpened(KDevelop::IProject*)), SLOT(updateSessionDescriptions())); } ISession* SessionController::activeSession() const { return d->activeSession; } void SessionController::loadSession( const QString& nameOrId ) { d->loadSessionExternally( session( nameOrId ) ); } QList SessionController::sessionNames() const { QStringList l; foreach( const Session* s, d->sessionActions.keys() ) { l << s->name(); } return l; } QList< const KDevelop::Session* > SessionController::sessions() const { QList< const KDevelop::Session* > ret; foreach( const Session* s, d->sessionActions.keys() ) ret << s; return ret; } Session* SessionController::createSession( const QString& name ) { Session* s = new Session( QUuid::createUuid() ); s->setName( name ); d->addSession( s ); return s; } void SessionController::deleteSession( const QString& nameOrId ) { Session* s = session(nameOrId); Q_ASSERT( s != d->activeSession ) ; QHash::iterator it = d->sessionActions.find(s); Q_ASSERT( it != d->sessionActions.end() ); unplugActionList( "available_sessions" ); d->grp->removeAction(*it); actionCollection()->removeAction(*it); plugActionList( "available_sessions", d->grp->actions() ); s->deleteFromDisk(); emit sessionDeleted( s->name() ); d->sessionActions.remove(s); s->deleteLater(); } void SessionController::loadDefaultSession() { QString load = QString(getenv("KDEV_SESSION")); if(!load.isEmpty()) { if(!session(load)) load = createSession("")->id().toString(); + ///KDEV_SESSION must be the UUID of an existing session, and nothing else. + ///If this assertion fails, that was not the case. + Q_ASSERT(session(load)->id().toString() == load); + d->activateSession( session(load) ); return; } KConfigGroup grp = KGlobal::config()->group( cfgSessionGroup ); load = grp.readEntry( cfgActiveSessionEntry, "default" ); if( !session( load ) ) { createSession( load ); } d->activateSession( session(load) ); } Session* SessionController::session( const QString& nameOrId ) const { Session* ret = d->findSessionForName( nameOrId ); if(ret) return ret; return d->findSessionForId( nameOrId ); } +QString SessionController::defaultSessionId(QString pickSession) +{ + if(!pickSession.isEmpty()) + { + //Try picking the correct session out of the existing ones + + QDir sessiondir( SessionController::sessionDirectory() ); + + foreach( const QString& s, sessiondir.entryList( QDir::AllDirs ) ) + { + QUuid id( s ); + if( id.isNull() ) + continue; + + Session session( id ); + + if(id.toString() == pickSession || session.name() == pickSession) + return id; + } + } + + //No existing session has been picked, try using the session marked as 'active' + + KConfigGroup grp = KGlobal::config()->group( cfgSessionGroup ); + QString load = grp.readEntry( cfgActiveSessionEntry, "" ); + + if(load.isEmpty()) + load = QUuid::createUuid(); + + //No session is marked active, + + //Make sure the session does actually exist as a directory + Session session( load ); + + return load; +} + QString SessionController::sessionDirectory() { return KGlobal::mainComponent().dirs()->saveLocation( "data", KGlobal::mainComponent().componentName()+"/sessions", true ); } QString SessionController::cloneSession( const QString& nameOrid ) { Session* origSession = session( nameOrid ); QUuid id = QUuid::createUuid(); KIO::NetAccess::dircopy( KUrl( sessionDirectory() + '/' + origSession->id().toString() ), KUrl( sessionDirectory() + '/' + id.toString() ), Core::self()->uiController()->activeMainWindow() ); Session* newSession = new Session( id ); newSession->setName( i18n( "Copy of %1", origSession->name() ) ); d->addSession(newSession); return newSession->name(); } void SessionController::plugActions() { unplugActionList( "available_sessions" ); plugActionList( "available_sessions", d->grp->actions() ); } } #include "sessioncontroller.moc" #include "moc_sessioncontroller.cpp" diff --git a/shell/sessioncontroller.h b/shell/sessioncontroller.h index 570423ad62..44e6c0e119 100644 --- a/shell/sessioncontroller.h +++ b/shell/sessioncontroller.h @@ -1,77 +1,82 @@ /* This file is part of KDevelop Copyright 2008 Andreas Pakulat 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 SESSIONCONTROLLER_H #define SESSIONCONTROLLER_H #include "shellexport.h" #include #include namespace KDevelop { class Session; class ISession; class KDEVPLATFORMSHELL_EXPORT SessionController : public QObject, public KXMLGUIClient { Q_OBJECT public: SessionController( QObject *parent = 0 ); virtual ~SessionController(); void initialize(); void cleanup(); ///Finds a session by its name or by its UUID Session* session( const QString& nameOrId ) const; virtual ISession* activeSession() const; QList sessionNames() const; Session* createSession( const QString& name ); QList sessions() const; void loadDefaultSession(); void startNewSession(); void loadSession( const QString& nameOrId ); void deleteSession( const QString& nameOrId ); QString cloneSession( const QString& nameOrid ); static QString sessionDirectory(); static const QString cfgSessionGroup; static const QString cfgActiveSessionEntry; + ///Returns the id of a valid session. Either the one that is currently set as 'active', + ///or a fresh one. + ///@param pickSession Name or UUID of a session that will be respected if possible. + static QString defaultSessionId(QString pickSession = QString()); + void plugActions(); Q_SIGNALS: void sessionLoaded( ISession* ); void sessionDeleted( const QString& ); private slots: void updateSessionDescriptions(); private: Q_PRIVATE_SLOT( d, void newSession() ) Q_PRIVATE_SLOT( d, void configureSessions() ) Q_PRIVATE_SLOT( d, void loadSessionFromAction( QAction* ) ) class SessionControllerPrivate* const d; }; } #endif diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index bb0f70dcd4..9228c9a546 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -1,74 +1,75 @@ add_definitions( -DKDE_DEFAULT_DEBUG_AREA=9508 ) ########### next target ############### set(kdevplatformutil_LIB_SRCS kdevstringhandler.cpp focusedtreeview.cpp processlinemaker.cpp commandexecutor.cpp environmentselectionwidget.cpp environmentgrouplist.cpp activetooltip.cpp executecompositejob.cpp ) set (kdevplatformutil_LIB_UI runoptions.ui ) add_subdirectory(duchainify) +add_subdirectory(kdev_starter) add_subdirectory(tests) kde4_add_ui_files(kdevplatformutil_LIB_SRCS ${kdevplatformutil_LIB_US}) kde4_add_library(kdevplatformutil SHARED ${kdevplatformutil_LIB_SRCS}) target_link_libraries(kdevplatformutil ${KDE4_KDEUI_LIBS} ${KDE4_KUTILS_LIBRARY} kdevplatforminterfaces kdevplatformoutputview ) # Might want to add kdevplatform* when they're exported targets target_link_libraries(kdevplatformutil LINK_INTERFACE_LIBRARIES ${KDE4_KDEUI_LIBS} ${KDE4_KUTILS_LIBRARY}) set_target_properties(kdevplatformutil PROPERTIES VERSION ${KDEVPLATFORM_LIB_VERSION} SOVERSION ${KDEVPLATFORM_LIB_SOVERSION}) install(TARGETS kdevplatformutil EXPORT KDevPlatformTargets ${INSTALL_TARGETS_DEFAULT_ARGS} ) ########### install files ############### install( FILES kdevstringhandler.h ksharedobject.h focusedtreeview.h activetooltip.h processlinemaker.h commandexecutor.h utilexport.h environmentselectionwidget.h environmentgrouplist.h pushvalue.h kdevvarlengtharray.h embeddedfreetree.h executecompositejob.h convenientfreelist.h spinlock.h DESTINATION ${INCLUDE_INSTALL_DIR}/kdevplatform/util COMPONENT Devel) install( FILES google/dense_hash_map google/dense_hash_set google/sparse_hash_map google/sparse_hash_set google/sparsetable google/type_traits.h google/hash_fun.h DESTINATION ${INCLUDE_INSTALL_DIR}/kdevplatform/util/google COMPONENT Devel) install( FILES google/sparsehash/densehashtable.h google/sparsehash/sparseconfig.h google/sparsehash/sparseconfig_windows.h google/sparsehash/sparsehashtable.h DESTINATION ${INCLUDE_INSTALL_DIR}/kdevplatform/util/google/sparsehash COMPONENT Devel) diff --git a/util/kdev_starter/CMakeLists.txt b/util/kdev_starter/CMakeLists.txt new file mode 100644 index 0000000000..224bbe737c --- /dev/null +++ b/util/kdev_starter/CMakeLists.txt @@ -0,0 +1,4 @@ + +kde4_add_executable(kdev_starter main.cpp) +target_link_libraries(kdev_starter QtCore) +install(TARGETS kdev_starter ${INSTALL_TARGETS_DEFAULT_ARGS} ) diff --git a/util/kdev_starter/main.cpp b/util/kdev_starter/main.cpp new file mode 100644 index 0000000000..ac3c10e157 --- /dev/null +++ b/util/kdev_starter/main.cpp @@ -0,0 +1,30 @@ +#include +#include +#include +#include +#include + +/** + * This is a very simple helper application that allows starting a new kdevplatform-based application instance with a specific + * session-id that is given on the command-line. This app is used internally for starting up new instances with specific sessions. + */ + +int main(int argc, char** argv) +{ + if(argc < 3) { + std::cerr << "need at least two arguments: app-name and session-id" << std::endl; + return 1; + } + + setenv("KDEV_SESSION", argv[2], 1); + + QStringList args; + for(int a = 3; a < argc; ++a) + args << QString(argv[a]); + + QProcess process; + process.setProcessChannelMode(QProcess::ForwardedChannels); + process.start(QString(argv[1]), args); + process.waitForFinished(-1); + return 0; +}