1 /*
2     SPDX-FileCopyrightText: 2008 Andreas Pakulat <apaku@gmx.de>
3     SPDX-FileCopyrightText: 2010 David Nolden <david.nolden.kdevelop@art-master.de>
4 
5     SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7 
8 #include "sessioncontroller.h"
9 
10 #include <QDir>
11 #include <QHash>
12 #include <QStringList>
13 #include <QAction>
14 #include <QCoreApplication>
15 #include <QDBusConnection>
16 #include <QInputDialog>
17 #include <QLabel>
18 #include <QLineEdit>
19 #include <QListView>
20 #include <QSortFilterProxyModel>
21 #include <QStandardItemModel>
22 #include <QVBoxLayout>
23 
24 #include <KActionCollection>
25 #include <KConfigGroup>
26 #include <KIO/CopyJob>
27 #include <KJobWidgets>
28 #include <KLocalizedString>
29 #include <KMessageBox>
30 #include <KProcess>
31 #include <KStringHandler>
32 
33 #include "session.h"
34 #include "core.h"
35 #include "uicontroller.h"
36 #include "shellextension.h"
37 #include "sessionlock.h"
38 #include "sessionchooserdialog.h"
39 #include "debug.h"
40 
41 #include <sublime/mainwindow.h>
42 #include <serialization/itemrepositoryregistry.h>
43 #include <duchain/duchain.h>
44 
45 namespace KDevelop
46 {
47 
48 namespace {
49     int argc = 0;
50     char** argv = nullptr;
51 }
52 
setArguments(int _argc,char ** _argv)53 void SessionController::setArguments(int _argc, char** _argv)
54 {
55     argc = _argc;
56     argv = _argv;
57 }
58 
standardArguments()59 static QStringList standardArguments()
60 {
61     QStringList ret;
62     for(int a = 0; a < argc; ++a)
63     {
64         QString arg = QString::fromLocal8Bit(argv[a]);
65         if(arg.startsWith(QLatin1String("-graphicssystem")) || arg.startsWith(QLatin1String("-style")))
66         {
67             ret << QLatin1Char('-') + arg;
68             if(a+1 < argc)
69                 ret << QString::fromLocal8Bit(argv[a+1]);
70         }
71     }
72     return ret;
73 }
74 
75 class SessionControllerPrivate : public QObject
76 {
77     Q_OBJECT
78 public:
SessionControllerPrivate(SessionController * s)79     explicit SessionControllerPrivate( SessionController* s )
80         : q(s)
81         , activeSession(nullptr)
82         , grp(nullptr)
83     {
84     }
85 
~SessionControllerPrivate()86     ~SessionControllerPrivate() override {
87     }
88 
findSessionForName(const QString & name) const89     Session* findSessionForName( const QString& name ) const
90     {
91         for (auto it = sessionActions.begin(), end = sessionActions.end(); it != end; ++it) {
92             Session* s = it.key();
93             if( s->name() == name )
94                 return s;
95         }
96         return nullptr;
97     }
98 
findSessionForId(const QString & idString) const99     Session* findSessionForId(const QString& idString) const
100     {
101         QUuid id(idString);
102 
103         for (auto it = sessionActions.begin(), end = sessionActions.end(); it != end; ++it) {
104             Session* s = it.key();
105             if( s->id() == id)
106                 return s;
107         }
108         return nullptr;
109     }
110 
newSession()111     void newSession()
112     {
113 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
114         qsrand(static_cast<uint>(QDateTime::currentDateTimeUtc().toSecsSinceEpoch()));
115 #endif
116         auto* session = new Session(QUuid::createUuid().toString());
117 
118         KProcess::startDetached(ShellExtension::getInstance()->executableFilePath(), QStringList() << QStringLiteral("-s") << session->id().toString() << standardArguments());
119         delete session;
120 #if 0
121         //Terminate this instance of kdevelop if the user agrees
122         const auto windows = Core::self()->uiController()->controller()->mainWindows();
123         for (Sublime::MainWindow* window : windows) {
124             window->close();
125         }
126 #endif
127     }
128 
deleteCurrentSession()129     void deleteCurrentSession()
130     {
131         int choice = KMessageBox::warningContinueCancel(Core::self()->uiController()->activeMainWindow(), i18n("The current session and all contained settings will be deleted. The projects will stay unaffected. Do you really want to continue?"));
132 
133         if(choice == KMessageBox::Continue)
134         {
135             q->deleteSessionFromDisk(sessionLock);
136             q->emitQuitSession();
137         }
138     }
139 
renameSession()140     void renameSession()
141     {
142         bool ok;
143         auto newSessionName = QInputDialog::getText(Core::self()->uiController()->activeMainWindow(),
144                                                     i18nc("@title:window", "Rename Session"), i18nc("@label:textbox", "New session name:"),
145                                                     QLineEdit::Normal, q->activeSession()->name(), &ok);
146         if (ok) {
147             static_cast<Session*>(q->activeSession())->setName(newSessionName);
148         }
149 
150         q->updateXmlGuiActionList(); // resort
151     }
152 
loadSessionExternally(Session * s)153     bool loadSessionExternally( Session* s )
154     {
155         Q_ASSERT( s );
156         KProcess::startDetached(ShellExtension::getInstance()->executableFilePath(), QStringList() << QStringLiteral("-s") << s->id().toString() << standardArguments());
157         return true;
158     }
159 
activateSession(Session * s)160     TryLockSessionResult activateSession( Session* s )
161     {
162         Q_ASSERT( s );
163 
164         activeSession = s;
165         TryLockSessionResult result = SessionController::tryLockSession( s->id().toString());
166         if( !result.lock ) {
167             activeSession = nullptr;
168             return result;
169         }
170         Q_ASSERT(s->id().toString() == result.lock->id());
171         sessionLock = result.lock;
172 
173         KConfigGroup grp = KSharedConfig::openConfig()->group( SessionController::cfgSessionGroup() );
174         grp.writeEntry( SessionController::cfgActiveSessionEntry(), s->id().toString() );
175         grp.sync();
176         if (Core::self()->setupFlags() & Core::NoUi) return result;
177 
178         QHash<Session*,QAction*>::iterator it = sessionActions.find(s);
179         Q_ASSERT( it != sessionActions.end() );
180         (*it)->setCheckable(true);
181         (*it)->setChecked(true);
182 
183         for(it = sessionActions.begin(); it != sessionActions.end(); ++it)
184         {
185             if(it.key() != s)
186                 (*it)->setCheckable(false);
187         }
188 
189         return result;
190     }
191 
loadSessionFromAction(QAction * action)192     void loadSessionFromAction(QAction* action)
193     {
194         auto session = action->data().value<Session*>();
195         loadSessionExternally(session);
196     }
197 
addSession(Session * s)198     void addSession( Session* s )
199     {
200         if (Core::self()->setupFlags() & Core::NoUi) {
201             sessionActions[s] = nullptr;
202             return;
203         }
204 
205         auto* a = new QAction( grp );
206         a->setText( s->description() );
207         a->setCheckable( false );
208         a->setData(QVariant::fromValue<Session*>(s));
209 
210         sessionActions[s] = a;
211         q->actionCollection()->addAction(QLatin1String("session_") + s->id().toString(), a);
212         connect( s, &Session::sessionUpdated, this, &SessionControllerPrivate::sessionUpdated );
213         sessionUpdated( s );
214     }
215 
216     SessionController* const q;
217 
218     QHash<Session*, QAction*> sessionActions;
219     ISession* activeSession;
220     QActionGroup* grp;
221 
222     ISessionLock::Ptr sessionLock;
223 
sessionBaseDirectory()224     static QString sessionBaseDirectory()
225     {
226         return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)
227             + QLatin1Char('/') + QCoreApplication::applicationName() + QLatin1String("/sessions/");
228     }
229 
ownSessionDirectory() const230     QString ownSessionDirectory() const
231     {
232         Q_ASSERT(activeSession);
233         return q->sessionDirectory( activeSession->id().toString() );
234     }
235 
236 private Q_SLOTS:
sessionUpdated(KDevelop::ISession * s)237     void sessionUpdated( KDevelop::ISession* s )
238     {
239         sessionActions[static_cast<Session*>( s )]->setText( KStringHandler::rsqueeze(s->description()) );
240     }
241 };
242 
243 
SessionController(QObject * parent)244 SessionController::SessionController( QObject *parent )
245     : QObject(parent)
246     , d_ptr(new SessionControllerPrivate(this))
247 {
248     Q_D(SessionController);
249 
250     setObjectName(QStringLiteral("SessionController"));
251     setComponentName(QStringLiteral("kdevsession"), i18n("Session Manager"));
252 
253     setXMLFile(QStringLiteral("kdevsessionui.rc"));
254 
255     QDBusConnection::sessionBus().registerObject( QStringLiteral("/org/kdevelop/SessionController"),
256         this, QDBusConnection::ExportScriptableSlots );
257 
258     if (Core::self()->setupFlags() & Core::NoUi) return;
259 
260     QAction* action = actionCollection()->addAction(QStringLiteral("new_session"));
261     connect(action, &QAction::triggered,
262             this, [this] { Q_D(SessionController); d->newSession(); });
263     action->setText( i18nc("@action:inmenu", "Start New Session") );
264     action->setToolTip( i18nc("@info:tooltip", "Start a new KDevelop instance with an empty session") );
265     action->setIcon(QIcon::fromTheme(QStringLiteral("window-new")));
266 
267     action = actionCollection()->addAction(QStringLiteral("rename_session"));
268     connect(action, &QAction::triggered,
269             this, [this] { Q_D(SessionController); d->renameSession(); });
270     action->setText( i18nc("@action", "Rename Current Session...") );
271     action->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename")));
272 
273     action = actionCollection()->addAction(QStringLiteral("delete_session"));
274     connect(action, &QAction::triggered,
275             this, [this] { Q_D(SessionController); d->deleteCurrentSession(); });
276     action->setText( i18nc("@action", "Delete Current Session...") );
277     action->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
278 
279     action = actionCollection()->addAction( QStringLiteral("quit"), this, SIGNAL(quitSession()) );
280     action->setText( i18nc("@action", "Quit") );
281     action->setMenuRole( QAction::NoRole ); // OSX: prevent QT from hiding this due to conflict with 'Quit KDevelop...'
282     actionCollection()->setDefaultShortcut( action, Qt::CTRL | Qt::Key_Q );
283     action->setIcon(QIcon::fromTheme(QStringLiteral("application-exit")));
284 
285     d->grp = new QActionGroup( this );
286     connect(d->grp, &QActionGroup::triggered,
287             this, [this] (QAction* a) { Q_D(SessionController); d->loadSessionFromAction(a); } );
288 }
289 
290 SessionController::~SessionController() = default;
291 
startNewSession()292 void SessionController::startNewSession()
293 {
294     Q_D(SessionController);
295 
296     d->newSession();
297 }
298 
cleanup()299 void SessionController::cleanup()
300 {
301     Q_D(SessionController);
302 
303     if (d->activeSession) {
304         Q_ASSERT(d->activeSession->id().toString() == d->sessionLock->id());
305 
306         if (d->activeSession->isTemporary()) {
307             deleteSessionFromDisk(d->sessionLock);
308         }
309         d->activeSession = nullptr;
310     }
311 
312     d->sessionLock.clear();
313     qDeleteAll(d->sessionActions);
314     d->sessionActions.clear();
315 }
316 
initialize(const QString & session)317 void SessionController::initialize( const QString& session )
318 {
319     Q_D(SessionController);
320 
321     QDir sessiondir( SessionControllerPrivate::sessionBaseDirectory() );
322 
323     const auto sessionDirs = sessiondir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
324     for (const QString& s : sessionDirs) {
325         QUuid id( s );
326         if( id.isNull() )
327             continue;
328         // Only create sessions for directories that represent proper uuid's
329         auto* ses = new Session(id.toString(), this);
330 
331         //Delete sessions that have no name and are empty
332         if( ses->containedProjects().isEmpty() && ses->name().isEmpty()
333             && (session.isEmpty() || (ses->id().toString() != session && ses->name() != session)) )
334         {
335             TryLockSessionResult result = tryLockSession(s);
336             if (result.lock) {
337                 deleteSessionFromDisk(result.lock);
338             }
339             delete ses;
340         } else {
341             d->addSession( ses );
342         }
343     }
344 
345     loadDefaultSession( session );
346 
347     updateXmlGuiActionList();
348 }
349 
350 
activeSession() const351 ISession* SessionController::activeSession() const
352 {
353     Q_D(const SessionController);
354 
355     return d->activeSession;
356 }
357 
activeSessionLock() const358 ISessionLock::Ptr SessionController::activeSessionLock() const
359 {
360     Q_D(const SessionController);
361 
362     return d->sessionLock;
363 }
364 
loadSession(const QString & nameOrId)365 void SessionController::loadSession( const QString& nameOrId )
366 {
367     Q_D(SessionController);
368 
369     d->loadSessionExternally( session( nameOrId ) );
370 }
371 
sessionNames() const372 QList<QString> SessionController::sessionNames() const
373 {
374     Q_D(const SessionController);
375 
376     QList<QString> l;
377     const auto sessions = d->sessionActions.keys();
378     l.reserve(sessions.size());
379     for(const auto* s : sessions) {
380         l << s->name();
381     }
382     return l;
383 }
384 
sessions() const385 QList< const KDevelop::Session* > SessionController::sessions() const
386 {
387     Q_D(const SessionController);
388 
389     QList< const KDevelop::Session* > ret;
390     const auto sessions = d->sessionActions.keys();
391     ret.reserve(sessions.size());
392     // turn to const pointers
393     for (const auto* s : sessions) {
394         ret << s;
395     }
396     return ret;
397 }
398 
createSession(const QString & name)399 Session* SessionController::createSession( const QString& name )
400 {
401     Q_D(SessionController);
402 
403     Session* s;
404     if(name.startsWith(QLatin1Char('{'))) {
405         s = new Session( QUuid(name).toString(), this );
406     }else{
407 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
408         qsrand(static_cast<uint>(QDateTime::currentDateTimeUtc().toSecsSinceEpoch()));
409 #endif
410         s = new Session( QUuid::createUuid().toString(), this );
411         s->setName( name );
412     }
413     d->addSession( s );
414     updateXmlGuiActionList();
415     return s;
416 }
417 
deleteSession(const ISessionLock::Ptr & lock)418 void SessionController::deleteSession( const ISessionLock::Ptr& lock )
419 {
420     Q_D(SessionController);
421 
422     Session* s  = session(lock->id());
423 
424     QHash<Session*,QAction*>::iterator it = d->sessionActions.find(s);
425     Q_ASSERT( it != d->sessionActions.end() );
426 
427     unplugActionList( QStringLiteral("available_sessions") );
428     actionCollection()->removeAction(*it);
429     if (d->grp) { // happens in unit tests
430         d->grp->removeAction(*it);
431         plugActionList( QStringLiteral("available_sessions"), d->grp->actions() );
432     }
433 
434     if (s == d->activeSession) {
435         d->activeSession = nullptr;
436     }
437     deleteSessionFromDisk(lock);
438 
439     emit sessionDeleted( s->id().toString() );
440     d->sessionActions.remove(s);
441     delete s;
442 }
443 
deleteSessionFromDisk(const ISessionLock::Ptr & lock)444 void SessionController::deleteSessionFromDisk( const ISessionLock::Ptr& lock )
445 {
446     qCDebug(SHELL) << "Deleting session:" << lock->id();
447 
448     static_cast<SessionLock*>(lock.data())->removeFromDisk();
449     ItemRepositoryRegistry::deleteRepositoryFromDisk(DUChain::repositoryPathForSession(lock));
450 }
451 
loadDefaultSession(const QString & session)452 void SessionController::loadDefaultSession( const QString& session )
453 {
454     Q_D(SessionController);
455 
456     QString load = session;
457     if (load.isEmpty()) {
458         KConfigGroup grp = KSharedConfig::openConfig()->group( cfgSessionGroup() );
459         load = grp.readEntry( cfgActiveSessionEntry(), "default" );
460     }
461 
462     // Iteratively try to load the session, asking user what to do in case of failure
463     // If showForceOpenDialog() returns empty string, stop trying
464     do
465     {
466         Session* s = this->session(load);
467         if( !s ) {
468             s = createSession( load );
469         }
470         TryLockSessionResult result = d->activateSession( s );
471         if( result.lock ) {
472             Q_ASSERT(d->activeSession == s);
473             Q_ASSERT(d->sessionLock = result.lock);
474             break;
475         }
476         load = handleLockedSession( s->name(), s->id().toString(), result.runInfo );
477     } while( !load.isEmpty() );
478 }
479 
session(const QString & nameOrId) const480 Session* SessionController::session( const QString& nameOrId ) const
481 {
482     Q_D(const SessionController);
483 
484     Session* ret = d->findSessionForName( nameOrId );
485     if(ret)
486         return ret;
487 
488     return d->findSessionForId( nameOrId );
489 }
490 
cloneSession(const QString & nameOrid)491 QString SessionController::cloneSession( const QString& nameOrid )
492 {
493     Q_D(SessionController);
494 
495     Session* origSession = session( nameOrid );
496 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
497     qsrand(static_cast<uint>(QDateTime::currentDateTimeUtc().toSecsSinceEpoch()));
498 #endif
499     QUuid id = QUuid::createUuid();
500     auto copyJob = KIO::copy(QUrl::fromLocalFile(sessionDirectory(origSession->id().toString())),
501                              QUrl::fromLocalFile(sessionDirectory( id.toString())));
502     KJobWidgets::setWindow(copyJob, Core::self()->uiController()->activeMainWindow());
503     copyJob->exec();
504 
505     auto* newSession = new Session(id.toString(), this);
506     newSession->setName( i18n( "Copy of %1", origSession->name() ) );
507     d->addSession(newSession);
508     updateXmlGuiActionList();
509     return newSession->name();
510 }
511 
updateXmlGuiActionList()512 void SessionController::updateXmlGuiActionList()
513 {
514     Q_D(SessionController);
515 
516     unplugActionList( QStringLiteral("available_sessions") );
517 
518     if (d->grp) {
519         auto actions = d->grp->actions();
520         std::sort(actions.begin(), actions.end(), [](const QAction* lhs, const QAction* rhs) {
521             auto s1 = lhs->data().value<Session*>();
522             auto s2 = rhs->data().value<Session*>();
523             return QString::localeAwareCompare(s1->description(), s2->description()) < 0;
524         });
525         plugActionList(QStringLiteral("available_sessions"), actions);
526     }
527 }
528 
529 
cfgSessionGroup()530 QString SessionController::cfgSessionGroup() { return QStringLiteral("Sessions"); }
cfgActiveSessionEntry()531 QString SessionController::cfgActiveSessionEntry() { return QStringLiteral("Active Session ID"); }
532 
availableSessionInfos()533 SessionInfos SessionController::availableSessionInfos()
534 {
535     SessionInfos sessionInfos;
536     const auto sessionDirs = QDir(SessionControllerPrivate::sessionBaseDirectory()).entryList(QDir::AllDirs);
537     sessionInfos.reserve(sessionDirs.size());
538     for (const QString& sessionId : sessionDirs) {
539         if( !QUuid( sessionId ).isNull() ) {
540             sessionInfos << Session::parse( sessionId );
541         }
542     }
543     sessionInfos.squeeze();
544     return sessionInfos;
545 }
546 
sessionDirectory(const QString & sessionId)547 QString SessionController::sessionDirectory(const QString& sessionId)
548 {
549     return SessionControllerPrivate::sessionBaseDirectory() + sessionId;
550 }
551 
tryLockSession(const QString & id,bool doLocking)552 TryLockSessionResult SessionController::tryLockSession(const QString& id, bool doLocking)
553 {
554     return SessionLock::tryLockSession(id, doLocking);
555 }
556 
isSessionRunning(const QString & id)557 bool SessionController::isSessionRunning(const QString& id)
558 {
559     return sessionRunInfo(id).isRunning;
560 }
561 
sessionRunInfo(const QString & id)562 SessionRunInfo SessionController::sessionRunInfo(const QString& id)
563 {
564     return SessionLock::tryLockSession(id, false).runInfo;
565 }
566 
showSessionChooserDialog(const QString & headerText,bool onlyRunning)567 QString SessionController::showSessionChooserDialog(const QString& headerText, bool onlyRunning)
568 {
569     ///FIXME: move this code into sessiondialog.cpp
570     auto* view = new QListView;
571     auto* filter = new QLineEdit;
572     filter->setClearButtonEnabled( true );
573     filter->setPlaceholderText(i18nc("@info:placeholder", "Search..."));
574 
575     auto* model = new QStandardItemModel(view);
576 
577     auto *proxy = new QSortFilterProxyModel(model);
578     proxy->setSourceModel(model);
579     proxy->setFilterKeyColumn( 1 );
580     proxy->setFilterCaseSensitivity( Qt::CaseInsensitive );
581     connect(filter, &QLineEdit::textChanged, proxy, &QSortFilterProxyModel::setFilterFixedString);
582 
583     SessionChooserDialog dialog(view, proxy, filter);
584     view->setEditTriggers(QAbstractItemView::NoEditTriggers);
585 
586     QVBoxLayout layout(dialog.mainWidget());
587     if(!headerText.isEmpty()) {
588         auto* heading = new QLabel(headerText);
589         QFont font = heading->font();
590         font.setBold(true);
591         heading->setFont(font);
592         layout.addWidget(heading);
593     }
594 
595     model->setColumnCount(4);
596     model->setHeaderData(0, Qt::Horizontal,i18nc("@title:column", "Identity"));
597     model->setHeaderData(1, Qt::Horizontal,i18nc("@title:column", "Contents"));
598     model->setHeaderData(2, Qt::Horizontal,i18nc("@title:column", "State"));
599     model->setHeaderData(3, Qt::Horizontal,i18nc("@title:column", "Name"));
600 
601     view->setModel(proxy);
602     view->setModelColumn(1);
603 
604     auto* filterLayout = new QHBoxLayout();
605     filterLayout->addWidget(new QLabel(i18nc("@label:textbox", "Filter:")));
606     filterLayout->addWidget(filter);
607     layout.addLayout(filterLayout);
608     layout.addWidget(view);
609     filter->setFocus();
610 
611     int row = 0;
612 
613     QString defaultSession = KSharedConfig::openConfig()->group( cfgSessionGroup() ).readEntry( cfgActiveSessionEntry(), "default" );
614 
615     const auto availableSessionInfos = KDevelop::SessionController::availableSessionInfos();
616     for (const KDevelop::SessionInfo& si : availableSessionInfos) {
617         if ( si.name.isEmpty() && si.projects.isEmpty() ) {
618             continue;
619         }
620 
621         bool running = KDevelop::SessionController::isSessionRunning(si.uuid.toString());
622 
623         if(onlyRunning && !running)
624             continue;
625 
626         model->setItem(row, 0, new QStandardItem(si.uuid.toString()));
627         model->setItem(row, 1, new QStandardItem(si.description));
628         model->setItem(row, 2, new QStandardItem);
629         model->setItem(row, 3, new QStandardItem(si.name));
630 
631         ++row;
632     }
633     model->sort(1);
634 
635     if(!onlyRunning) {
636         model->setItem(row, 0, new QStandardItem);
637         model->setItem(row, 1, new QStandardItem(QIcon::fromTheme(QStringLiteral("window-new")), i18n("Create New Session")));
638     }
639 
640     dialog.updateState();
641     dialog.mainWidget()->layout()->setContentsMargins(0,0,0,0);
642 
643     const QModelIndex defaultSessionIndex = model->match(model->index(0, 0), Qt::DisplayRole, defaultSession, 1, Qt::MatchExactly).value(0);
644     view->selectionModel()->setCurrentIndex(proxy->mapFromSource(defaultSessionIndex), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
645     view->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
646     ///@todo We need a way to get a proper size-hint from the view, but unfortunately, that only seems possible after the view was shown.
647     dialog.resize(QSize(900, 600));
648 
649     if(dialog.exec() != QDialog::Accepted) // krazy:exclude=crashy
650     {
651         return QString();
652     }
653 
654     QModelIndex selected = view->selectionModel()->currentIndex();
655     if (!selected.isValid())
656         return QString();
657 
658     const QString selectedSessionId = selected.sibling(selected.row(), 0).data().toString();
659     if (selectedSessionId.isEmpty()) {
660         // "Create New Session" item selected, return a fresh UUID
661 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
662         qsrand(static_cast<uint>(QDateTime::currentDateTimeUtc().toSecsSinceEpoch()));
663 #endif
664         return QUuid::createUuid().toString();
665     }
666     return selectedSessionId;
667 }
668 
handleLockedSession(const QString & sessionName,const QString & sessionId,const SessionRunInfo & runInfo)669 QString SessionController::handleLockedSession( const QString& sessionName, const QString& sessionId,
670                                                 const SessionRunInfo& runInfo )
671 {
672     return SessionLock::handleLockedSession(sessionName, sessionId, runInfo);
673 }
674 
sessionDir()675 QString SessionController::sessionDir()
676 {
677     Q_D(SessionController);
678 
679     if( !activeSession() )
680         return QString();
681     return d->ownSessionDirectory();
682 }
683 
sessionName()684 QString SessionController::sessionName()
685 {
686     if(!activeSession())
687         return QString();
688     return activeSession()->description();
689 }
690 
691 
692 }
693 #include "sessioncontroller.moc"
694 #include "moc_sessioncontroller.cpp"
695