File indexing completed on 2024-04-28 05:46:36
0001 /* 0002 SPDX-FileCopyrightText: 2010 Volker Lanz <vl@fidra.de> 0003 SPDX-FileCopyrightText: 2014-2020 Andrius Štikonas <andrius@stikonas.eu> 0004 0005 SPDX-License-Identifier: GPL-3.0-or-later 0006 */ 0007 0008 #include "gui/smartdialog.h" 0009 #include "gui/smartdialogwidget.h" 0010 0011 #include <core/device.h> 0012 #include <core/smartstatus.h> 0013 #include <core/smartattribute.h> 0014 0015 #include <util/helpers.h> 0016 #include <util/htmlreport.h> 0017 0018 #include <QDialogButtonBox> 0019 #include <QFileDialog> 0020 #include <QFontDatabase> 0021 #include <QPushButton> 0022 #include <QStyle> 0023 #include <QTemporaryFile> 0024 #include <QTextDocumentFragment> 0025 #include <QTextStream> 0026 #include <QTreeWidgetItem> 0027 #include <QtGlobal> 0028 0029 #include <KConfigGroup> 0030 #include <KFormat> 0031 #include <KLocalizedString> 0032 #include <KIO/CopyJob> 0033 #include <KJobUiDelegate> 0034 #include <KMessageBox> 0035 #include <KSharedConfig> 0036 0037 #include <sys/utsname.h> 0038 #include <unistd.h> 0039 0040 #include <config.h> 0041 0042 /** Creates a new SmartDialog 0043 @param parent pointer to the parent widget 0044 @param d the Device 0045 */ 0046 SmartDialog::SmartDialog(QWidget* parent, Device& d) : 0047 QDialog(parent), 0048 m_Device(d), 0049 m_DialogWidget(new SmartDialogWidget(this)) 0050 { 0051 QVBoxLayout *mainLayout = new QVBoxLayout(this); 0052 setLayout(mainLayout); 0053 mainLayout->addWidget(&dialogWidget()); 0054 setWindowTitle(xi18nc("@title:window", "SMART Properties: <filename>%1</filename>", device().deviceNode())); 0055 0056 buttonBox = new QDialogButtonBox(this); 0057 buttonBox->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Close); 0058 buttonBox->button(QDialogButtonBox::Save)->setText(xi18nc("@action:button", "Save SMART Report")); 0059 buttonBox->button(QDialogButtonBox::Save)->setIcon(QIcon::fromTheme(QStringLiteral("document-save"))); 0060 mainLayout->addWidget(buttonBox); 0061 0062 setupDialog(); 0063 setupConnections(); 0064 0065 KConfigGroup kcg(KSharedConfig::openConfig(), QStringLiteral("smartDialog")); 0066 restoreGeometry(kcg.readEntry<QByteArray>("Geometry", QByteArray())); 0067 } 0068 0069 /** Destroys a SmartDialog */ 0070 SmartDialog::~SmartDialog() 0071 { 0072 KConfigGroup kcg(KSharedConfig::openConfig(), QStringLiteral("smartDialog")); 0073 kcg.writeEntry("Geometry", saveGeometry()); 0074 } 0075 0076 void SmartDialog::setupDialog() 0077 { 0078 if (device().smartStatus().isValid()) { 0079 if (device().smartStatus().status()) { 0080 dialogWidget().statusText().setText(xi18nc("@label SMART disk status", "good")); 0081 dialogWidget().statusIcon().setPixmap(QIcon::fromTheme(QStringLiteral("dialog-ok")) 0082 .pixmap(QApplication::style()->pixelMetric(QStyle::PixelMetric::PM_SmallIconSize))); 0083 } else { 0084 dialogWidget().statusText().setText(xi18nc("@label SMART disk status", "BAD")); 0085 dialogWidget().statusIcon().setPixmap(QIcon::fromTheme(QStringLiteral("dialog-warning")) 0086 .pixmap(QApplication::style()->pixelMetric(QStyle::PixelMetric::PM_SmallIconSize))); 0087 } 0088 0089 dialogWidget().modelName().setText(device().smartStatus().modelName()); 0090 dialogWidget().firmware().setText(device().smartStatus().firmware()); 0091 dialogWidget().serialNumber().setText(device().smartStatus().serial()); 0092 0093 dialogWidget().temperature().setText(SmartStatus::tempToString(device().smartStatus().temp())); 0094 const QString badSectors = device().smartStatus().badSectors() > 0 0095 ? QLocale().toString(device().smartStatus().badSectors()) 0096 : xi18nc("@label SMART number of bad sectors", "none"); 0097 dialogWidget().badSectors().setText(badSectors); 0098 dialogWidget().poweredOn().setText(KFormat().formatDuration(device().smartStatus().poweredOn())); 0099 dialogWidget().powerCycles().setText(QLocale().toString(device().smartStatus().powerCycles())); 0100 dialogWidget().overallAssessment().setText(SmartStatus::overallAssessmentToString(device().smartStatus().overall())); 0101 dialogWidget().selfTests().setText(SmartStatus::selfTestStatusToString(device().smartStatus().selfTestStatus())); 0102 0103 dialogWidget().treeSmartAttributes().clear(); 0104 0105 const QFont f = QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont); 0106 const QString size = f.pixelSize() != -1 ? QStringLiteral("%1px").arg(f.pixelSize()) : QStringLiteral("%1pt").arg(f.pointSize()); 0107 0108 const QString st = QStringLiteral("<span style=\"font-family:%1;font-size:%2;\">").arg(f.family()).arg(size); 0109 0110 for (const auto &a : device().smartStatus().attributes()) { 0111 QTreeWidgetItem* item = new QTreeWidgetItem( 0112 QStringList() 0113 << QLocale().toString(a.id()) 0114 << QStringLiteral("<b>%1</b><br/>%2").arg(a.name()).arg(st + a.desc() + QStringLiteral("</span>")) 0115 << (a.failureType() == SmartAttribute::FailureType::PreFailure ? xi18nc("@item:intable", "Pre-Failure") : xi18nc("@item:intable", "Old-Age")) 0116 << (a.updateType() == SmartAttribute::UpdateType::Online ? xi18nc("@item:intable", "Online") : xi18nc("@item:intable", "Offline")) 0117 << QLocale().toString(a.worst()) 0118 << QLocale().toString(a.current()) 0119 << QLocale().toString(a.threshold()) 0120 << a.raw() 0121 << a.assessmentToString() 0122 << a.value() 0123 ); 0124 item->setSizeHint(0, QSize(0, 64)); 0125 item->setToolTip(1, QTextDocumentFragment::fromHtml(a.desc()).toPlainText()); 0126 dialogWidget().treeSmartAttributes().addTopLevelItem(item); 0127 } 0128 } else 0129 dialogWidget().statusText().setText(xi18nc("@label", "(unknown)")); 0130 0131 setMinimumSize(dialogWidget().size()); 0132 resize(dialogWidget().size()); 0133 } 0134 0135 void SmartDialog::setupConnections() 0136 { 0137 connect(buttonBox->button(QDialogButtonBox::Save), &QPushButton::clicked, this, &SmartDialog::saveSmartReport); 0138 connect(buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, this, &SmartDialog::close); 0139 } 0140 0141 QString SmartDialog::toHtml() const 0142 { 0143 QString rval; 0144 QTextStream s(&rval); 0145 0146 if (device().smartStatus().status()) 0147 s << HtmlReport::tableLine(i18n("SMART status:"), xi18nc("@label SMART disk status", "good")); 0148 else 0149 s << HtmlReport::tableLine(i18n("SMART status:"), xi18nc("@label SMART disk status", "BAD")); 0150 0151 const QString badSectors = device().smartStatus().badSectors() > 0 0152 ? QLocale().toString(device().smartStatus().badSectors()) 0153 : i18nc("@label SMART number of bad sectors", "none"); 0154 0155 s << HtmlReport::tableLine(i18n("Model:"), device().smartStatus().modelName()) 0156 << HtmlReport::tableLine(i18n("Serial number:"), device().smartStatus().serial()) 0157 << HtmlReport::tableLine(i18n("Firmware revision:"), device().smartStatus().firmware()) 0158 << HtmlReport::tableLine(i18n("Temperature:"), SmartStatus::tempToString(device().smartStatus().temp())) 0159 << HtmlReport::tableLine(i18n("Bad sectors:"), badSectors) 0160 << HtmlReport::tableLine(i18n("Powered on for:"), KFormat().formatSpelloutDuration(device().smartStatus().poweredOn())) 0161 << HtmlReport::tableLine(i18n("Power cycles:"), QLocale().toString(device().smartStatus().powerCycles())) 0162 << HtmlReport::tableLine(i18n("Self tests:"), SmartStatus::selfTestStatusToString(device().smartStatus().selfTestStatus())) 0163 << HtmlReport::tableLine(i18n("Overall assessment:"), SmartStatus::overallAssessmentToString(device().smartStatus().overall())); 0164 0165 s << "</table><br/>"; 0166 0167 if (device().smartStatus().isValid()) { 0168 0169 const QFont f = QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont); 0170 const QString size = f.pixelSize() != -1 ? QStringLiteral("%1px").arg(f.pixelSize()) : QStringLiteral("%1pt").arg(f.pointSize()); 0171 0172 const QString st = QStringLiteral("<span style=\"font-family:%1;font-size:%2;\">").arg(f.family()).arg(size); 0173 0174 s << "<table>\n"; 0175 0176 for (const auto &a : device().smartStatus().attributes()) { 0177 s << "<tr>\n"; 0178 0179 s << "<td>" << QLocale().toString(a.id()) << "</td>\n" 0180 << "<td>" << QStringLiteral("<b>%1</b><br/>%2").arg(a.name()).arg(st + a.desc() + QStringLiteral("</span>")) << "</td>\n" 0181 << "<td>" << (a.failureType() == SmartAttribute::FailureType::PreFailure ? xi18nc("@item:intable", "Pre-Failure") : xi18nc("@item:intable", "Old-Age")) << "</td>\n" 0182 << "<td>" << (a.updateType() == SmartAttribute::UpdateType::Online ? xi18nc("@item:intable", "Online") : xi18nc("@item:intable", "Offline")) << "</td>\n" 0183 << "<td>" << QLocale().toString(a.worst()) << "</td>\n" 0184 << "<td>" << QLocale().toString(a.current()) << "</td>\n" 0185 << "<td>" << QLocale().toString(a.threshold()) << "</td>\n" 0186 << "<td>" << a.raw() << "</td>\n" 0187 << "<td>" << a.assessmentToString() << "</td>\n" 0188 << "<td>" << a.value() << "</td>\n"; 0189 0190 s << "</tr>\n"; 0191 } 0192 0193 s << "</table>\n"; 0194 } else 0195 s << "(unknown)"; 0196 0197 s.flush(); 0198 0199 return rval; 0200 } 0201 0202 void SmartDialog::saveSmartReport() 0203 { 0204 const QUrl url = QFileDialog::getSaveFileUrl(this, QString(), QUrl(), tr("HTML-Files (*.htm *.html);;All Files (*)")); 0205 0206 if (url.isEmpty()) 0207 return; 0208 0209 QTemporaryFile tempFile; 0210 0211 if (tempFile.open()) { 0212 QTextStream s(&tempFile); 0213 0214 HtmlReport html; 0215 0216 s << html.header() 0217 << toHtml() 0218 << html.footer(); 0219 0220 tempFile.close(); 0221 0222 KIO::CopyJob* job = KIO::move(QUrl::fromLocalFile(tempFile.fileName()), url, KIO::HideProgressInfo); 0223 job->exec(); 0224 if (job->error()) 0225 job->uiDelegate()->showErrorMessage(); 0226 } else 0227 KMessageBox::error(this, xi18nc("@info", "Could not create temporary file when trying to save to <filename>%1</filename>.", url.fileName()), xi18nc("@title:window", "Could Not Save SMART Report.")); 0228 0229 }