#include "dialog.h" #include #include #include #include #include #include #include #include #include #include "unistd.h" #include "stdio.h" #include "sys/wait.h" #include "ui_dialog.h" #include "sessiontreeitem.h" #include "globals.h" #include "vsession.h" #include "choosersettings.h" Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog) { model_[0] = new SessionTreeModel(parent); model_[1] = new SessionTreeModel(parent); model_[2] = new SessionTreeModel(parent); ui->setupUi(this); tabs_[0] = ui->tabButtonLocal; tabs_[1] = ui->tabButtonMyClasses; tabs_[2] = 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_, SIGNAL(timeout()), this, SLOT(onCenterTimer())); centerTimer_->start(1000); activeTab_ = 2; ui->tabButtonAllClasses->setChecked(true); ui->helpBox->hide(); ui->newsBox->hide(); oldRow_ = 0; // this is the old row (when switching to a new tab) this->addStatusString(STR_LOADING); /* * TODO: why connect signal/slot when no item was loaded yet? QObject::connect(ui->treeView->selectionModel(), SIGNAL(currentChanged ( const QModelIndex&, const QModelIndex&)), this, SLOT(treeView_selectionChanged(const QModelIndex&, const QModelIndex&))); */ this->onTabButtonChanged(2); // TODO: Implement bug report dialog :) ui->buttonBugReport->setEnabled(false); } Dialog::~Dialog() { delete ui; delete model_[0]; delete model_[1]; delete model_[2]; } void Dialog::changeEvent(QEvent *e) { QDialog::changeEvent(e); switch (e->type()) { case QEvent::LanguageChange: ui->retranslateUi(this); break; default: break; } } void Dialog::on_treeView_activated(QModelIndex index) { // this method gets called when a Session has been activated SessionTreeItem* item = static_cast(index.internalPointer()); const Session* s(item->session()); if (!s) { // no valid session has been selected, do nothing return; } if (s->run()) { // Run session start script if (QFile::exists(sessionStartScript)) { // Use the current environment variables and add the necessary // information for the startUpScipt. char pvs_ac[128]; sprintf(pvs_ac, "PVS_AUTO_CONNECT=%s", ui->PVS_checkbox->isChecked()?"TRUE":"FALSE"); putenv(pvs_ac); // Fork this process twice to detach pid_t pid = fork(); if (pid == 0){ // Close all filedescriptors for (int i = 3; i < 1024; ++i) ::close(i); // Change process group pid_t sid = setsid(); if (sid < 0) exit(EXIT_FAILURE); // Reopen standard pipes freopen("/dev/null", "r", stdin); freopen("/dev/null", "w", stdout); freopen("/dev/null", "w", stdout); pid = fork(); if (pid == 0){ // At this point we are executing as the 2nd child process // Replace this clone with the process we'd like to start char const * const args[] = {VMCHOOSER_SESSION_START_SCRIPT, NULL}; execv(VMCHOOSER_SESSION_START_SCRIPT, (char**)args); } _exit(0); // Dont use exit hooks } // Wait and cleanup the intermediate process waitpid(pid, NULL, 0); } ChooserSettings::setSetting("last-session", (s->shortDescription())); ChooserSettings::setSetting("last-tab", QString::number(activeTab_)); setVisible(false); } else { QMessageBox::warning( this, trUtf8("vmchooser"), trUtf8("Vmchooser failed to run the selected session!")); } } void Dialog::addItems(const QList& entries, int tab) { if (tab < 0 || tab > 2) { return; } this->model_[tab]->addItems(entries); tabs_[tab]->setEnabled(this->model_[tab]->rowCount() != 0); if (tab == activeTab_) { setListModel(this->model_[tab]); } model_[tab]->updateView(); } void Dialog::addStatusString(const int status) { if (status < 0 || status >= STR__MAX) return; this->model_[1]->addLabelItem(strings_[status]); this->model_[2]->addLabelItem(strings_[status]); tabs_[1]->setEnabled(this->model_[1]->rowCount() > 1); tabs_[2]->setEnabled(this->model_[2]->rowCount() > 1); model_[1]->updateView(); model_[2]->updateView(); } void Dialog::removeStatusString(const int status) { if (status < 0 || status >= STR__MAX) return; this->model_[1]->removeItem(strings_[status]); this->model_[2]->removeItem(strings_[status]); tabs_[1]->setEnabled(this->model_[1]->rowCount() > 1); tabs_[2]->setEnabled(this->model_[1]->rowCount() > 1); model_[1]->updateView(); model_[2]->updateView(); } void Dialog::on_pushButtonAbort_clicked() { close(); } void Dialog::on_pushButtonStart_clicked() { this->on_treeView_activated(ui->treeView->selectionModel()->currentIndex()); } bool Dialog::selectSession(const QString& name) { QModelIndex root(ui->treeView->rootIndex()); for (int tab = 0; tab <= 2; ++tab) { for (int i = 0; i < model_[tab]->rowCount(root); ++i) { QModelIndex index = model_[tab]->index(i, 0, root); if (!index.isValid()) { break; } SessionTreeItem* item = static_cast(index.internalPointer()); const Session* s(item->session()); if (!s) { continue; } if (s->shortDescription() == name) { // change the tab onTabButtonChanged(tab); // set selection ui->treeView->selectionModel()->clearSelection(); ui->treeView->selectionModel() ->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); return true; } } } return false; } void Dialog::selectPreviousSession() { if (!ChooserSettings::getSetting("last-session").isEmpty()) { ui->treeView->clearSelection(); if (!selectSession(ChooserSettings::getSetting("last-session"))) { // could not find last session, change to last used tab this->onTabButtonChanged(ChooserSettings::getSetting("last-tab").toInt()); } } setListModel(this->model_[this->activeTab_]); } 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 (theme.isEmpty()) return; themePathBase = QString("%1/%2/").arg(VMCHOOSER_THEME_BASE).arg(theme); themePathIni = QString("%1%2.ini").arg(themePathBase).arg(theme); if (!QFile::exists(themePathIni)) 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; 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(QRegExp("(.*background-color:)#[^;]*(;.*)"), backgroundColor); label_r_style.replace(QRegExp("(.*background-color:)#[^;]*(;.*)"), backgroundColor); //qDebug() << label_r_style << label_l_style; ui->label_l->setStyleSheet(label_l_style); ui->label_r->setStyleSheet(label_r_style); } void Dialog::onCenterTimer() { if (!autoStartEntry_.isEmpty()) { if (this->selectSession(autoStartEntry_)) { this->on_treeView_activated(ui->treeView->selectionModel()->currentIndex()); } else { QMessageBox::critical(this, "Autostart", QString::fromUtf8("Konnte %1 nicht starten.").arg(autoStartEntry_)); } autoStartEntry_.clear(); return; } // center dialog on primary screen QRect desktopRect = QApplication::desktop()->availableGeometry(this); QPoint center = desktopRect.center(); if (center != oldCenter_) { this->move(center.x() - this->width() / 2, center.y() - this->height() / 2); oldCenter_ = center; } } void Dialog::addSessionsAfterDownload(QNetworkReply* reply) { QString temp_filename; if (reply->error() != QNetworkReply::NoError) { if (debugMode) { qDebug() << "Error reading from URL: " << reply->error(); } QFile backup_file(xml_filename); if (!backup_file.open(QIODevice::ReadOnly)) { if (debugMode) { qDebug() << "Cannot read backup file " << xml_filename << " either"; } this->removeStatusString(STR_LOADING); this->addStatusString(STR_URL_ERROR); return; } if (debugMode) { qDebug() << "Using backup file " << xml_filename; } backup_file.close(); temp_filename = xml_filename; } else { QByteArray data = reply->readAll(); // write xml to temporary file temp_filename = QDir::tempPath() + "/vmchooser2/vmchooser-XXXXXX.xml"; QTemporaryFile tmpfile(temp_filename); if (!tmpfile.open() || tmpfile.write(data) == -1) { return; } tmpfile.close(); tmpfile.setAutoRemove(false); temp_filename = tmpfile.fileName(); } QList sessions = VSession::readXmlFile(temp_filename); this->removeStatusString(STR_LOADING); if (!sessions.isEmpty()) { qSort(sessions.begin(), sessions.end(), myLessThan); this->addItems(sessions, 2); // TODO: No magic number; handle user specific classes } else { this->addStatusString(STR_NO_ITEMS); } // give focus to treeView ui->treeView->setFocus(); // select last-session selectPreviousSession(); } void Dialog::treeView_selectionChanged(const QModelIndex& current, const QModelIndex&) { SessionTreeItem* item = static_cast(current.internalPointer()); const Session* s(item->session()); if (!s) { if (debugMode) { qDebug() << "invalid selection"; } // no valid session has been selected, do nothing return; } if (s->type() == Session::VSESSION) { const VSession* vs = (VSession*) s; ui->label_name->setText(vs->getAttribute("short_description", "param")); ui->label_name->setToolTip(vs->getAttribute("short_description", "param")); ui->label_creator->setText(vs->getAttribute("creator", "param")); ui->label_creator->setToolTip(vs->getAttribute("creator", "param")); ui->label_os->setText(vs->getAttribute("os", "param")); ui->label_os->setToolTip(vs->getAttribute("os", "param")); QString description(vs->getAttribute("long_description", "param") + "\n\nKeywords: "); for (int i = 0; i < vs->keywords().length(); ++i) { description += vs->keywords()[i] + ", "; } ui->textBrowser->setText(description); } else { ui->label_name->setText(s->shortDescription()); ui->label_creator->setText(""); ui->label_os->setText(QCoreApplication::instance()->translate("Dialog", "Native Linux")); ui->textBrowser->setPlainText(QCoreApplication::instance()->translate("Dialog", "Linux X session running on this machine without virtualization.")); } } void Dialog::on_tabButtonLocal_clicked() { onTabButtonChanged(0); } void Dialog::on_tabButtonMyClasses_clicked() { onTabButtonChanged(1); } void Dialog::on_tabButtonAllClasses_clicked() { onTabButtonChanged(2); } void Dialog::onTabButtonChanged(int tab) { if (tab < 0 || tab > 2) { // no valid button return; } // give focus to treeView ui->treeView->setFocus(); // Update pressed status of buttons for (int i = 0; i < 3; ++i) { tabs_[i]->setChecked(tab == i); } // clear filter if necessary if (activeTab_ != tab) { this->ui->filterEdit->setText(""); } // save old row //oldRow_ = ui->treeView->selectionModel()->currentIndex().row(); // load the new list setListModel(model_[tab]); this->activeTab_ = tab; ui->treeView->selectionModel()->select(ui->treeView->model()->index(0, 0, ui->treeView->rootIndex()), QItemSelectionModel::Select); treeView_selectionChanged(ui->treeView->model()->index(oldRow_, 0, ui->treeView->rootIndex()), ui->treeView->model()->index(0, 0, ui->treeView->rootIndex())); } void Dialog::on_filterEdit_textChanged() { SessionTreeModel *newModel; // filter the current model if (ui->filterEdit->text() != "" && ui->filterEdit->text().replace(" ", "").length() > 2) { newModel = new SessionTreeModel(this); newModel->addItems(this->model_[activeTab_]->lookForItem(ui->filterEdit->text())); } else { newModel = model_[activeTab_]; } setListModel(newModel); } void Dialog::setListModel(QAbstractItemModel *model) { if (ui->treeView->model() == model_[0] || ui->treeView->model() == model_[1] || ui->treeView->model() == model_[2]) { } else { ui->treeView->model()->deleteLater(); } ui->treeView->setModel(model); // reconnect the treeModel QObject::connect(ui->treeView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(treeView_selectionChanged(const QModelIndex&, const QModelIndex&))); // 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->isVisible()) { ui->helpBox->hide(); ui->newsBox->hide(); } else { ui->helpBox->show(); ui->newsBox->show(); } } void Dialog::addNewsAfterDownload(QNetworkReply* reply) { if (reply->error() != QNetworkReply::NoError) { if (debugMode) { qDebug() << "Could not get news. Try to get cached news."; } // try to get cached news QFile backup_file(news_backup_filename); if (!backup_file.open(QIODevice::ReadOnly)) { if (debugMode) { qDebug() << "Cannot read backup file " << news_backup_filename << " either"; } this->ui->newsTextBrowser->setText(QCoreApplication::instance()->translate("Dialog", "Could not get news.")); return; } if (debugMode) { qDebug() << "Used backup file " << news_backup_filename; } backup_file.close(); return; } QByteArray data = reply->readAll(); QDomDocument doc; if (!doc.setContent(data)) { qDebug() << "News XML contains errors."; return; } QDomElement newsNode = doc.firstChildElement("news"); QDateTime timestamp; timestamp.setTime_t(newsNode.firstChildElement("date").text().toInt()); // format and print news ui->newsTextBrowser->setText(QString("

" + newsNode.firstChildElement("headline").text() + "

" + timestamp.toString(Qt::SystemLocaleShortDate) + "

" + newsNode.firstChildElement("info").text() + "

")); if (ChooserSettings::getSetting("last-news").toUInt() < timestamp.toTime_t()) { // show news if not seen before on_helpNewsButton_clicked(); } // update ini ChooserSettings::setSetting("last-news", QString::number(timestamp.toTime_t())); // make backup QFile file(news_backup_filename); if (!file.open(QIODevice::WriteOnly)) { if (debugMode) { qDebug() << "Could not write XML to " << news_backup_filename; } return; } if (file.write(data) != data.length()) { return; } if (!file.setPermissions(QFile::ReadUser | QFile::ReadGroup | QFile::ReadOther | QFile::WriteUser | QFile::WriteGroup | QFile::WriteOther)) { if (debugMode) { qDebug() << "Could not change permissions of file: " << news_backup_filename; } } file.close(); } void Dialog::addHelpAfterDownload(QNetworkReply* reply) { if (reply->error() != QNetworkReply::NoError) { if (debugMode) { qDebug() << "Could not get help xml. Try to get cached help..."; } // try to get cached news QFile backup_file(help_backup_filename); if (!backup_file.open(QIODevice::ReadOnly)) { if (debugMode) { qDebug() << "Cannot read backup file " << help_backup_filename << " either"; } this->ui->helpTextBrowser->setText(QCoreApplication::instance()->translate("Dialog", "Could not get help.")); return; } if (debugMode) { qDebug() << "Used backup file " << help_backup_filename; } backup_file.close(); return; } QByteArray data = reply->readAll(); QDomDocument doc; if (!doc.setContent(data)) { if (debugMode) { qDebug() << "Help file contains errors."; } return; } ui->helpTextBrowser->setText(QString(data)); // make backup QFile file(help_backup_filename); if (!file.open(QIODevice::WriteOnly)) { if (debugMode) { qDebug() << "Could not write XML to " << help_backup_filename; } return; } if (file.write(data) != data.length()) { return; } if (!file.setPermissions(QFile::ReadUser | QFile::ReadGroup | QFile::ReadOther | QFile::WriteUser | QFile::WriteGroup | QFile::WriteOther)) { if (debugMode) { qDebug() << "Could not change permissions of file: " << help_backup_filename; } } file.close(); } void Dialog::keyPressEvent(QKeyEvent* event) { switch(event->key()) { case Qt::Key_Return: this->on_pushButtonStart_clicked(); break; case Qt::Key_Escape: this->on_pushButtonAbort_clicked(); break; case Qt::Key_H: this->on_helpNewsButton_clicked(); break; } }