#include "dialog.h"
#include "config.h"
#include <QMessageBox>
#include <QDebug>
#include <QRegExp>
#include <QFile>
#include <QProcess>
#include <QTimer>
#include <QDesktopWidget>
#include <QDateTime>
#include <QTemporaryFile>
#include <QUrlQuery>
#include "unistd.h"
#include "stdio.h"
#include "sys/wait.h"
#include "ui_dialog.h"
#include "sessiontreeitem.h"
#include "globals.h"
#include "vsession.h"
#include "userconfig.h"
#include "filedownloader.h"
#include "windowmanager.h"
static bool isProcessRunning(const QString &binary);
QDialog* Dialog::instance = nullptr;
static QDomDocument toDomDocument(const QString& what, const QByteArray& data, const QString& backupFile, const QString& mandatoryChild);
Dialog::Dialog(QWidget *parent)
: QDialog(parent), ui(new Ui::Dialog) {
instance = this;
model_[TAB_NATIVE] = new SessionTreeModel(parent);
model_[TAB_RECENT_COURSES] = new SessionTreeModel(parent, true);
model_[TAB_ALL_VMS] = new SessionTreeModel(parent);
userInteracted_ = false;
genericExpandedOnce_ = false;
autoQuit_ = g_autoQuitSeconds;
ui->setupUi(this);
tabs_[TAB_NATIVE] = ui->tabButtonLocal;
tabs_[TAB_RECENT_COURSES] = ui->tabButtonMyClasses;
tabs_[TAB_ALL_VMS] = ui->tabButtonAllClasses;
strings_[STR_LOADING] = QCoreApplication::instance()->translate("Dialog", "Loading...");
strings_[STR_URL_ERROR] = QCoreApplication::instance()->translate("Dialog", "URL Error");
strings_[STR_NO_ITEMS] = QCoreApplication::instance()->translate("Dialog", "No Items");
// Re-center dialog every second to account for resolution changes
QRect desktopRect = QApplication::desktop()->availableGeometry(this);
oldCenter_ = desktopRect.center();
centerTimer_ = new QTimer(this);
connect(centerTimer_, &QTimer::timeout, this, &Dialog::onCenterTimer);
centerTimer_->start(1000);
ui->treeView->setFocusProxy(ui->filterEdit);
// ui->filterEdit->installEventFilter(this);
QCoreApplication::instance()->installEventFilter(this);
if (!UserConfig::isNewsHelpOpen()) {
ui->helpBox->hide();
ui->newsBox->hide();
}
ui->lblAutoQuit->hide();
this->addStatusString(STR_LOADING);
/*
* TODO: why connect signal/slot when no item was loaded yet?
* TODO: Change "TODO" in the above line to "QUESTION", as it's just that
QObject::connect(ui->treeView->selectionModel(), SIGNAL(currentChanged ( const QModelIndex&, const QModelIndex&)),
this, SLOT(treeView_selectionChanged(const QModelIndex&, const QModelIndex&)));
*/
ui->PVS_checkbox->setVisible(Config::isSet(Config::PVS));
ui->PVS_checkbox->setChecked(Config::isSet(Config::PVS_CHECKED));
ui->chkNoScreenSaver->setVisible(Config::isSet(Config::ALLOW_SCREENSAVER_DISABLE));
activeTab_ = -1;
if (Config::isSet(Config::EXAM_MODE)) {
ui->tabButtonLocal->setEnabled(false);
this->onTabButtonChanged(TAB_ALL_VMS);
/* modify the pvs checkbox */
ui->PVS_checkbox->setText(tr("join PVS(limited)"));
ui->PVS_checkbox->setEnabled(false);
ui->PVS_checkbox->setChecked(true);
} else {
this->onTabButtonChanged(TAB_NATIVE);
}
ui->chkAdminMode->setVisible(Config::isSet(Config::ALLOW_VM_EDIT));
ui->chkAdminMode->setEnabled(false);
ui->btnScreenSetup->setVisible(isProcessRunning("beamergui"));
if (QApplication::screens().size() > 1) {
QFont copy = ui->btnScreenSetup->font();
copy.setBold(true);
ui->btnScreenSetup->setFont(copy);
}
// TODO: Implement bug report dialog :)
ui->buttonBugReport->setEnabled(false);
QObject::connect(SessionsIconHolder::get(), &SessionsIconHolder::iconDownloaded,
this, &Dialog::iconDownloaded);
}
Dialog::~Dialog() {
instance = nullptr;
delete ui;
}
void Dialog::changeEvent(QEvent *e) {
QDialog::changeEvent(e);
if (e->type() == QEvent::LanguageChange) {
ui->retranslateUi(this);
}
}
void Dialog::on_treeView_doubleClicked(const QModelIndex& index)
{
userInteracted_ = true;
// this method gets called when a Session has been selected to start
SessionTreeItem* item =
static_cast<SessionTreeItem*>(index.internalPointer());
if (item == nullptr)
return; // do nothing if cast failed
const Session* s(item->session());
if (s == nullptr) // no valid session has been selected, do nothing
return;
// Check preconditions. The method might show error messages and return false
if (!s->prepareRun())
return;
WindowManager::stopOwnInstance(true);
// These two are up here in case run-virt cares...
if (Config::isSet(Config::PVS)) {
if (ui->PVS_checkbox->isChecked()) {
setenv("PVS_AUTO_CONNECT", "TRUE", 1);
} else {
setenv("PVS_AUTO_CONNECT", "FALSE", 1);
}
}
if (ui->chkAdminMode->isEnabled() && ui->chkAdminMode->isChecked()) {
setenv("VMCHOOSER_ADMIN_MODE", "TRUE", 1);
} else {
setenv("VMCHOOSER_ADMIN_MODE", "FALSE", 1);
}
if (ui->chkNoScreenSaver->isChecked()) {
setenv("VMCHOOSER_DISABLE_SCREENSAVER", "TRUE", 1);
} else {
setenv("VMCHOOSER_DISABLE_SCREENSAVER", "FALSE", 1);
}
UserConfig::addLastSession(s->uuid().isEmpty() ? s->shortDescription() : s->uuid());
UserConfig::setLastTab(activeTab_);
UserConfig::setNewsHelpOpen(!ui->helpBox->isHidden());
UserConfig::sync();
qint64 scriptPid = 0;
if (QFile::exists(SESSION_START_SCRIPT)) {
// Companion script
setenv("SESSION_NAME", s->shortDescription().toLocal8Bit().constData(), 1);
setenv("SESSION_UUID", s->uuid().toLocal8Bit().constData(), 1);
setenv("SESSION_CMD", s->execCommand().toLocal8Bit().constData(), 1);
if (s->type() == Session::VSESSION) {
setenv("SESSION_TYPE", "VSESSION", 1);
} else if (s->type() == Session::XSESSION) {
setenv("SESSION_TYPE", "XSESSION", 1);
}
if (!QProcess::startDetached(SESSION_START_SCRIPT, QStringList(), QStringLiteral("/"), &scriptPid)) {
scriptPid = 0;
}
}
// Run session
s->run();
// Should not return on success
if (scriptPid > 0) {
::kill(pid_t(scriptPid), SIGTERM);
}
QMessageBox::critical(
this, trUtf8("vmchooser"),
trUtf8("Vmchooser failed to run the selected session!"));
QApplication::instance()->quit();
}
void Dialog::on_treeView_expanded(const QModelIndex& index) {
if (activeTab_ != TAB_ALL_VMS)
return;
SessionTreeItem* item =
static_cast<SessionTreeItem*>(index.internalPointer());
if (item->session() == nullptr && item->sectionType() != SECTION_FOR_LOCATION) {
genericExpandedOnce_ = true;
}
}
void Dialog::addItems(const QList<Session*>& entries, int tab) {
if (tab < 0 || tab >= TAB_COUNT)
return; // Invalid index
if (Config::isSet(Config::EXAM_MODE) && tab == TAB_NATIVE)
return; // Exam mode -- keep native sessions empty
ui->treeView->blockSignals(true);
if (tab != TAB_RECENT_COURSES) {
// If tab is not the recent courses tab, check the list for any recent sessions
auto prev = UserConfig::getLastSessions();
if (!prev.isEmpty()) {
QList<Session*> matches;
for (auto it : entries) {
if ((!it->uuid().isEmpty() && prev.contains(it->uuid()))
|| prev.contains(it->shortDescription())) {
matches.append(it);
}
}
if (!matches.isEmpty()) {
addItems(matches, TAB_RECENT_COURSES);
ui->treeView->blockSignals(true);
}
}
for (auto it : entries) {
// Also check for validity
it->checkCanRun();
}
}
this->model_[tab]->addItems(entries);
tabs_[tab]->setEnabled(this->model_[tab]->rowCount() != 0);
if (tab == activeTab_) {
setListModel(this->model_[tab]);
}
ui->treeView->blockSignals(false);
model_[tab]->updateView();
if (!ui->filterEdit->text().isEmpty()) {
on_filterEdit_textChanged();
}
if (tab != TAB_RECENT_COURSES) {
selectPreviousSession();
}
}
void Dialog::addStatusString(const int status) {
if (status < 0 || status >= STR__MAX)
return;
this->model_[TAB_RECENT_COURSES]->addLabelItem(strings_[status]);
this->model_[TAB_ALL_VMS]->addLabelItem(strings_[status]);
tabs_[TAB_RECENT_COURSES]->setEnabled(this->model_[TAB_RECENT_COURSES]->rowCount() > 1);
tabs_[TAB_ALL_VMS]->setEnabled(this->model_[TAB_ALL_VMS]->rowCount() > 1);
model_[TAB_RECENT_COURSES]->updateView();
model_[TAB_ALL_VMS]->updateView();
}
void Dialog::removeStatusString(const int status) {
if (status < 0 || status >= STR__MAX)
return;
this->model_[TAB_RECENT_COURSES]->removeItem(strings_[status]);
this->model_[TAB_ALL_VMS]->removeItem(strings_[status]);
tabs_[TAB_RECENT_COURSES]->setEnabled(this->model_[TAB_RECENT_COURSES]->rowCount() > 1);
tabs_[TAB_ALL_VMS]->setEnabled(this->model_[TAB_RECENT_COURSES]->rowCount() > 1);
model_[TAB_RECENT_COURSES]->updateView();
model_[TAB_ALL_VMS]->updateView();
}
void Dialog::on_pushButtonAbort_clicked() {
close();
}
void Dialog::on_pushButtonStart_clicked() {
userInteracted_ = true;
this->on_treeView_doubleClicked(ui->treeView->selectionModel()->currentIndex());
}
void Dialog::on_btnScreenSetup_clicked() {
QProcess::startDetached("beamergui", QStringList("-w"));
}
bool Dialog::selectSession(const QString& name, int preferredTab) {
QModelIndex root(ui->treeView->rootIndex());
int bestTab = -1;
QModelIndex bestIndex;
for (int tab = TAB_COUNT - 1; tab >= 0; --tab) {
if (bestTab != -1 && preferredTab != tab) // We already have a potential match, only keep going if this is the desired tab
continue;
for (int i = 0; i < model_[tab]->rowCount(root); ++i) {
QModelIndex section(model_[tab]->index(i, 0, root));
if (!section.isValid())
continue;
for (int j = 0; j < model_[tab]->rowCount(section); ++j) {
QModelIndex index(model_[tab]->index(j, 0, section));
if (!index.isValid())
continue;
SessionTreeItem* item = static_cast<SessionTreeItem*>(index.internalPointer());
const Session* s = item->session();
if (s == nullptr) {
continue;
}
if ((!s->uuid().isEmpty() && s->uuid() == name) || s->shortDescription() == name) {
bestTab = tab;
bestIndex = index;
break; // Break inner, keep checking other tabs
}
}
}
}
if (bestTab != -1) {
// change the tab
qDebug() << "Best tab is" << bestTab;
onTabButtonChanged(bestTab);
// set selection
ui->treeView->selectionModel()->clearSelection();
ui->treeView->selectionModel()->clear();
ui->treeView->selectionModel()->select(bestIndex, QItemSelectionModel::Select);
ui->treeView->selectionModel()->setCurrentIndex(bestIndex, QItemSelectionModel::Select);
ui->treeView->scrollTo(bestIndex);
return true;
}
return false;
}
void Dialog::selectPreviousSession() {
if (userInteracted_) {
qDebug() << "Not selecting previous session as user interacted or session was already selected";
return;
}
int lastTab = UserConfig::getLastTab();
QString lastSession = Config::get(Config::DEFAULT_SESSION);
if (lastSession.isEmpty()) {
auto list = UserConfig::getLastSessions();
if (!list.isEmpty()) {
lastSession = list.back();
}
}
if (!lastSession.isEmpty()) {
qDebug() << "Trying to select last session: " << lastSession;
ui->treeView->clearSelection();
if (selectSession(lastSession, lastTab)) {
qDebug() << "Success";
userInteracted_ = true;
return;
}
}
// could not find last session, change to last used tab
if (lastTab >= 0 && lastTab < TAB_COUNT) {
qDebug() << "Trying to select last tab " << lastTab;
this->onTabButtonChanged(lastTab);
} else {
int defaultTab = Config::get(Config::DEFAULT_TAB).toInt();
qDebug() << "Selected default tab " << defaultTab;
// Select default tab
this->onTabButtonChanged(defaultTab);
}
}
void Dialog::startSession(const QString& name) {
autoStartEntry_ = name;
}
void Dialog::setTheme() {
QString label_l_style, label_r_style;
QString backgroundColor, imageLeft, imageRight;
QString themePathBase, themePathIni, themePathImgLeft, themePathImgRight;
if (!Config::isSet(Config::THEME))
return;
QString theme(Config::get(Config::THEME));
themePathBase = QString("%1/%2/").arg(VMCHOOSER_THEME_BASE).arg(theme);
themePathIni = QString("%1%2.ini").arg(themePathBase).arg(theme);
if (!QFile::exists(themePathIni)) {
qDebug() << "Theme config" << themePathIni << "does not exist";
return;
}
QSettings themeSettings(themePathIni, QSettings::IniFormat);
backgroundColor = themeSettings.value("background-color").toString();
imageLeft = themeSettings.value("image-left").toString();
imageRight = themeSettings.value("image-right").toString();
themePathImgLeft = QString("%1%2").arg(themePathBase).arg(imageLeft);
themePathImgRight = QString("%1%2").arg(themePathBase).arg(imageRight);
QRegExp re("(.*background-color:)#[^;]*(;.*)");
ui->label_l->setPixmap(QPixmap(themePathImgLeft));
ui->label_r->setPixmap(QPixmap(themePathImgRight));
label_l_style = ui->label_l->styleSheet();
label_r_style = ui->label_r->styleSheet();
backgroundColor.prepend("\\1").append("\\2");
label_l_style.replace(re, backgroundColor);
label_r_style.replace(re, backgroundColor);
ui->label_l->setStyleSheet(label_l_style);
ui->label_r->setStyleSheet(label_r_style);
}
void Dialog::onCenterTimer() {
// center dialog on primary screen
if (isVisible()) {
QRect desktopRect = QApplication::desktop()->availableGeometry(this);
QPoint center = desktopRect.center();
if (center != oldCenter_) {
if (g_fullscreen)
this->resize(desktopRect.width(), desktopRect.height());
this->move(center.x() - this->width() / 2, center.y() - this->height() / 2);
oldCenter_ = center;
}
}
// Handle auto-quit timeout
if (autoQuit_ > 0) {
autoQuit_--;
if (autoQuit_ == 0) {
QCoreApplication::instance()->exit(0);
} else if (autoQuit_ < 60) {
ui->lblAutoQuit->setText(trUtf8("Auto logout in %1").arg(autoQuit_));
ui->lblAutoQuit->show();
} else if (!ui->lblAutoQuit->isHidden()) {
ui->lblAutoQuit->hide();
}
}
}
/**
* Download lecture list, news and help
*/
void Dialog::downloadData(const QString& locationIds) {
QUrl listUrl(Config::isSet(Config::URL_LIST)
? Config::get(Config::URL_LIST)
: Config::get(Config::URL_BASE).append("/list"));
QUrlQuery listQuery(listUrl);
if (!locationIds.isEmpty()) {
listQuery.addQueryItem("locations", locationIds);
}
if (Config::isSet(Config::EXAM_MODE)) {
listQuery.addQueryItem("exams", "exam-mode");
}
listUrl.setQuery(listQuery);
//
// Download lecture XML
FileDownloader::download(listUrl, [this](QNetworkReply::NetworkError err, const QByteArray& data) {
QList<Session*> sessions;
QDomDocument doc = toDomDocument(QStringLiteral("lecture list"), data, TEMP_PATH_XML_LIST, QStringLiteral("settings"));
sessions = VSession::loadFromXmlDocument(doc);
this->removeStatusString(STR_LOADING);
if (sessions.isEmpty()) {
if (err == QNetworkReply::NoError) {
this->addStatusString(STR_NO_ITEMS);
} else {
this->addStatusString(STR_URL_ERROR);
}
} else {
qSort(sessions.begin(), sessions.end(), sessionComparator);
this->addItems(sessions, TAB_ALL_VMS);
bool showEdit = false; // Only show edit button if at least one lecture is editable
for (QList<Session*>::const_iterator it = sessions.begin(); it != sessions.end(); ++it) {
if (reinterpret_cast<VSession*>(*it)->canEdit()) {
showEdit = true;
break;
}
}
if (showEdit) {
ui->chkAdminMode->setVisible(true);
}
}
checkAutostart();
// select last-session
selectPreviousSession();
userInteracted_ = true;
});
//
// News
FileDownloader::download(QUrl(Config::isSet(Config::URL_NEWS)
? Config::get(Config::URL_NEWS)
: Config::get(Config::URL_BASE).append("/news")), [this](QNetworkReply::NetworkError err, const QByteArray& data) {
QDomDocument doc = toDomDocument(QStringLiteral("news"), data, TEMP_PATH_NEWS, QStringLiteral("news"));
QDomElement newsNode = doc.firstChildElement("news");
QDomElement timeNode = newsNode.firstChildElement("date");
QDomElement infoNode = newsNode.firstChildElement("info");
QDomElement headlineNode = newsNode.firstChildElement("headline");
QDateTime timestamp;
timestamp.setTime_t(timeNode.text().toUInt());
if (timeNode.isNull() || infoNode.isNull()) {
qDebug() << "Could not load news. Network:" << err;
ui->newsTextBrowser->setText(QCoreApplication::instance()->translate("Dialog", "Could not get news. (//news/date or //news/info missing)"));
} else {
// format and print news
ui->newsTextBrowser->setText(QString("<p style='font-size:16px; margin-bottom: 2px;'>" + headlineNode.text() + "</p> <small>"
+ timestamp.toString(Qt::SystemLocaleShortDate) + "</small><p>"
+ infoNode.text() + "</p>"));
}
if (ui->helpBox->isHidden()
&& (UserConfig::getLastNewsTime() < timestamp.toTime_t() || UserConfig::getLastNewsTime() > QDateTime::currentMSecsSinceEpoch() / 1000)) {
// show news if not seen before
on_helpNewsButton_clicked();
}
// update ini
UserConfig::setLastNewsTime(timestamp.toTime_t());
});
//
// Download help
FileDownloader::download(QUrl(Config::isSet(Config::URL_HELP)
? Config::get(Config::URL_HELP)
: Config::get(Config::URL_BASE).append("/help")), [this](QNetworkReply::NetworkError err, const QByteArray& data) {
QDomDocument doc = toDomDocument(QStringLiteral("help"), data, TEMP_PATH_HELP, QStringLiteral("news"));
QDomElement helpTextNode = doc.firstChildElement("news").firstChildElement("info");
if (helpTextNode.isNull()) {
qDebug() << "Could not load help. Network:" << err;
ui->helpTextBrowser->setText(QCoreApplication::instance()->translate("Dialog", "Could not get help (XML has no //news/info)"));
} else {
ui->helpTextBrowser->setText(helpTextNode.text());
}
});
//
}
void Dialog::mousePressEvent(QMouseEvent * event) {
QDialog::mousePressEvent(event);
userInteracted_ = true;
}
void Dialog::treeView_selectionChanged(const QModelIndex& current, const QModelIndex&) {
SessionTreeItem* item =
static_cast<SessionTreeItem*>(current.internalPointer());
if (item == nullptr) {
ui->chkAdminMode->setEnabled(false);
return;
}
const Session* s(item->session());
if (!s) {
qDebug() << "invalid selection";
// no valid session has been selected, do nothing
ui->chkAdminMode->setEnabled(false);
return;
}
QString description;
if (s->type() == Session::VSESSION) {
const VSession* vs = reinterpret_cast<const VSession*>(s);
ui->label_creator->setText(vs->getAttribute("creator", "param"));
ui->label_creator->setToolTip(vs->getAttribute("creator", "param"));
ui->label_os->setText(vs->getAttribute("os_name", "param"));
ui->label_os->setToolTip(vs->getAttribute("os_name", "param"));
ui->label_platform->setText(vs->getAttribute("virtualizer_name", "param"));
ui->label_platform->setToolTip(vs->getAttribute("virtualizer_name", "param"));
// TODO: This is a bug? vs->canEdit() seems completely pointless right now...
ui->chkAdminMode->setEnabled(vs->canEdit() || Config::isSet(Config::ALLOW_VM_EDIT));
if (vs->keywords().length() > 0) {
description = "\n\nKeywords: ";
for (int i = 0; i < vs->keywords().length(); ++i) {
description += vs->keywords()[i] + ", ";
}
}
} else {
ui->label_creator->setText("");
ui->label_creator->setToolTip("");
ui->label_os->setText(QCoreApplication::instance()->translate("Dialog", "Native Linux"));
ui->label_platform->setText("Linux");
ui->label_platform->setToolTip("");
}
ui->label_name->setText(s->shortDescription());
ui->label_name->setToolTip(s->shortDescription());
ui->textBrowser->setHtml(s->description() + description);
}
void Dialog::on_tabButtonLocal_clicked() {
userInteracted_ = true;
onTabButtonChanged(TAB_NATIVE);
}
void Dialog::on_tabButtonMyClasses_clicked() {
userInteracted_ = true;
onTabButtonChanged(TAB_RECENT_COURSES);
}
void Dialog::on_tabButtonAllClasses_clicked() {
userInteracted_ = true;
onTabButtonChanged(TAB_ALL_VMS);
}
void Dialog::onTabButtonChanged(int tab) {
if (tab < 0 || tab >= TAB_COUNT) {
// no valid button
return;
}
// Block this so it won't trigger the expand signal while refilling, which would
// interfere with LOCATION_EXCLUSIVE mode
ui->treeView->blockSignals(true);
// give focus to treeView
if (!ui->treeView->hasFocus()) {
ui->treeView->setFocus();
}
// Update pressed status of buttons
for (int i = 0; i < TAB_COUNT; ++i) {
tabs_[i]->setChecked(tab == i);
}
// clear filter if necessary
if (activeTab_ != tab) {
this->ui->filterEdit->setText("");
}
// load the new list
setListModel(model_[tab]);
this->activeTab_ = tab;
ui->treeView->blockSignals(false);
/* get the first element ad select it */
selectFirstElement();
}
void Dialog::on_filterEdit_textEdited() {
userInteracted_ = true;
}
void Dialog::on_filterEdit_textChanged() {
if (activeTab_ < 0 || activeTab_ >= TAB_COUNT) return;
SessionTreeModel *newModel = nullptr;
// filter the current model
if (!ui->filterEdit->text().isEmpty() && ui->filterEdit->text().replace(" ", "").length() > 2) {
if (model_[activeTab_] != nullptr) {
newModel = new SessionTreeModel(this);
newModel->addItems(this->model_[activeTab_]->lookForItem(ui->filterEdit->text()));
}
}
if (newModel == nullptr) {
newModel = model_[activeTab_];
}
if (newModel != nullptr) {
setListModel(newModel);
}
}
void Dialog::setListModel(SessionTreeModel *model) {
QAbstractItemModel *old = nullptr;
if (ui->treeView->model() == model_[TAB_NATIVE] || ui->treeView->model() == model_[TAB_RECENT_COURSES] || ui->treeView->model() == model_[TAB_ALL_VMS]) {
// A default model is currently being set; don't delete it
} else {
// Remember currently set model so we can delete it after setting the new one
old = ui->treeView->model();
}
// Disconnect current model
QObject::disconnect(ui->treeView->selectionModel(), &QItemSelectionModel::currentChanged,
this, &Dialog::treeView_selectionChanged);
ui->treeView->setModel(model);
ui->treeView->expandAll();
if (!genericExpandedOnce_ && g_forLocationHandling == LOCATION_EXCLUSIVE && model->rowCount() > 1) {
QModelIndex index = model->getSection(SECTION_GENERIC);
if (index.isValid()) {
ui->treeView->collapse(index);
}
index = model->getSection(SECTION_TEMPLATES);
if (index.isValid()) {
ui->treeView->collapse(index);
}
}
if (old != nullptr) {
old->deleteLater();
}
// reconnect the treeModel
QObject::connect(ui->treeView->selectionModel(), &QItemSelectionModel::currentChanged,
this, &Dialog::treeView_selectionChanged);
// select the first element, if no element is selected
if (ui->treeView->selectionModel()->selectedRows().count() == 0) {
//ui->treeView->selectionModel()->setCurrentIndex(ui->treeView->model()->index(0, 0, ui->treeView->rootIndex()), QItemSelectionModel::Select);
ui->treeView->selectionModel()->select(ui->treeView->model()->index(0, 0, ui->treeView->rootIndex()), QItemSelectionModel::Select);
}
}
void Dialog::on_helpNewsButton_clicked() {
if (!ui->helpBox->isHidden()) {
ui->helpBox->hide();
ui->newsBox->hide();
} else {
ui->helpBox->show();
ui->newsBox->show();
}
}
void Dialog::keyPressEvent(QKeyEvent* event) {
switch(event->key()) {
case Qt::Key_Return:
event->accept();
this->on_pushButtonStart_clicked();
break;
case Qt::Key_Escape:
event->accept();
this->on_pushButtonAbort_clicked();
break;
default:
QDialog::keyPressEvent(event);
break;
}
}
void Dialog::iconDownloaded(const QUrl& url, const QIcon&) {
qDebug() << "Icon downloaded... (" << url << ")";
// TODO: Check which model(s) contain an entry with this icon
model_[TAB_RECENT_COURSES]->updateView();
model_[TAB_ALL_VMS]->updateView();
}
void Dialog::on_leftKey() {
int i = activeTab_;
do {
i = (i - 1 + TAB_COUNT) % TAB_COUNT;
} while (!tabs_[i]->isEnabled() && i != activeTab_);
onTabButtonChanged(i);
}
void Dialog::on_rightKey() {
int i = activeTab_;
do {
i = (i + 1) % TAB_COUNT;
} while (!tabs_[i]->isEnabled() && i != activeTab_);
onTabButtonChanged(i);
}
/* when the user presses the space key, the current selection in the treeview
* should be collapsed/expanded. */
void Dialog::on_spaceKey() {
QModelIndex index = ui->treeView->selectionModel()->currentIndex();
if (ui->treeView->isExpanded(index)) {
ui->treeView->collapse(index);
} else {
ui->treeView->expand(index);
}
}
void Dialog::selectFirstElement() {
QModelIndex root(ui->treeView->rootIndex());
QModelIndex section(model_[activeTab_]->index(0, 0, root));
QModelIndex newIndex = QModelIndex(model_[activeTab_]->index(0, 0, section));
ui->treeView->selectionModel()->setCurrentIndex(newIndex, QItemSelectionModel::Select);
ui->treeView->setFocus();
}
/* install this filter to the filterEdit to listen for arrow keys */
bool Dialog::eventFilter(QObject*, QEvent *event) {
if (event->type() == QEvent::KeyPress) {
autoQuit_ = g_autoQuitSeconds;
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
bool fortv = false;
if (keyEvent->key() == Qt::Key_Right) {
on_rightKey();
fortv = true;
}
if (keyEvent->key() == Qt::Key_Left) {
on_leftKey();
fortv = true;
}
if (keyEvent->key() == Qt::Key_Up) {
ui->treeView->cursorUp();
fortv = true;
}
if (keyEvent->key() == Qt::Key_Down) {
ui->treeView->cursorDown();
fortv = true;
}
if (fortv) {
ui->treeView->setFocus();
return true;
}
} else if (event->type() == QEvent::MouseMove) {
autoQuit_ = g_autoQuitSeconds;
}
return false;
}
void Dialog::checkAutostart() {
if (!autoStartEntry_.isEmpty()) {
if (this->selectSession(autoStartEntry_)) {
this->on_treeView_doubleClicked(ui->treeView->selectionModel()->currentIndex());
} else {
QMessageBox::critical(this, "Autostart", QString::fromUtf8("Konnte %1 nicht starten.").arg(autoStartEntry_));
}
autoStartEntry_.clear();
return;
}
}
void Dialog::showEvent(QShowEvent *e)
{
QDialog::showEvent(e);
// Make sure a window manager is running, so any modals or other popups work properly
QTimer::singleShot(10, []() {
WindowManager::ensureRunning();
});
}
QDialog* Dialog::getInstance()
{
return instance;
}
static bool isProcessRunning(const QString &binary)
{
bool full = binary.contains('/');
QDir proc("/proc");
for (auto entry : proc.entryInfoList(QStringList(), QDir::NoDotAndDotDot | QDir::AllDirs)) {
if (!entry.isReadable())
continue;
QDir dest = QDir(entry.filePath() + "/exe");
if (!dest.isReadable())
continue;
QString wurst = dest.canonicalPath();
if (wurst.isEmpty())
continue;
if (full && wurst == binary)
return true;
if (!full && wurst.mid(wurst.lastIndexOf('/') + 1) == binary)
return true;
}
return false;
}
static QDomDocument toDomDocument(const QString& what, const QByteArray& data, const QString& backupFile, const QString& mandatoryChild)
{
QDomDocument doc;
if (!data.isEmpty()) {
if (!doc.setContent(data)) {
qDebug() << "Downloaded" << what << "XML contains errors.";
}
} else {
qDebug() << "No content downloaded for" << what;
}
QFile backup(backupFile);
if (doc.isNull() || !doc.hasChildNodes()) {
if (backup.open(QFile::ReadOnly)) {
if (!doc.setContent(&backup)) {
qDebug() << "Could not load" << what << "backup.";
}
}
}
if (doc.isNull() || !doc.hasChildNodes())
return QDomDocument(); // Above methods failed
if (doc.firstChildElement(mandatoryChild).isNull()) {
qDebug() << "Downloaded" << what << "xml doesn't contain mandatory root node" << mandatoryChild;
return QDomDocument();
}
if (!backup.isOpen()) {
// If it were already open we'd have read the backup, so no need to write it out again...
if (!backup.open(QFile::WriteOnly)) {
qDebug() << "Cannot open" << what << "backup file" << backupFile << "for writing";
} else {
backup.write(data);
}
}
return doc;
}