Changeset View
Changeset View
Standalone View
Standalone View
Modules/about-distro/src/Module.cpp
1 | /* | 1 | /* | ||
---|---|---|---|---|---|
2 | Copyright (C) 2012-2014 Harald Sitter <apachelogger@ubuntu.com> | 2 | Copyright (C) 2012-2020 Harald Sitter <sitter@kde.org> | ||
3 | 3 | | |||
4 | This program is free software; you can redistribute it and/or | 4 | This program is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU General Public License as | 5 | modify it under the terms of the GNU General Public License as | ||
6 | published by the Free Software Foundation; either version 2 of | 6 | published by the Free Software Foundation; either version 2 of | ||
7 | the License or (at your option) version 3 or any later version | 7 | the License or (at your option) version 3 or any later version | ||
8 | accepted by the membership of KDE e.V. (or its successor approved | 8 | accepted by the membership of KDE e.V. (or its successor approved | ||
9 | by the membership of KDE e.V.), which shall act as a proxy | 9 | by the membership of KDE e.V.), which shall act as a proxy | ||
10 | defined in Section 14 of version 3 of the license. | 10 | defined in Section 14 of version 3 of the license. | ||
11 | 11 | | |||
12 | This program is distributed in the hope that it will be useful, | 12 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | 15 | GNU General Public License for more details. | ||
16 | 16 | | |||
17 | You should have received a copy of the GNU General Public License | 17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. | 18 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | */ | 19 | */ | ||
20 | 20 | | |||
21 | #include "Module.h" | 21 | #include "Module.h" | ||
22 | #include "ui_Module.h" | 22 | #include "ui_Module.h" | ||
23 | 23 | | |||
24 | #include <QClipboard> | 24 | #include <QClipboard> | ||
25 | #include <QIcon> | 25 | #include <QIcon> | ||
26 | #include <QLocale> | 26 | #include <QLocale> | ||
27 | #include <QStandardPaths> | | |||
28 | 27 | | |||
29 | #include <KAboutData> | 28 | #include <KAboutData> | ||
30 | #include <KCoreAddons> | 29 | #include <KCoreAddons> | ||
31 | #include <KConfig> | 30 | #include <KConfig> | ||
32 | #include <KConfigGroup> | 31 | #include <KConfigGroup> | ||
33 | #include <KDesktopFile> | | |||
34 | #include <KFormat> | | |||
35 | #include <KLocalizedString> | 32 | #include <KLocalizedString> | ||
36 | #include <KOSRelease> | 33 | #include <KOSRelease> | ||
37 | #include <KSharedConfig> | 34 | #include <KSharedConfig> | ||
38 | 35 | | |||
39 | #include <solid/device.h> | 36 | #include "CPUEntry.h" | ||
40 | #include <solid/processor.h> | 37 | #include "BitEntry.h" | ||
41 | 38 | #include "KernelEntry.h" | |||
42 | #ifdef Q_OS_LINUX | 39 | #include "MemoryEntry.h" | ||
43 | #include <sys/sysinfo.h> | 40 | #include "PlasmaEntry.h" | ||
44 | #elif defined(Q_OS_FREEBSD) | 41 | #include "SectionLabel.h" | ||
45 | #include <sys/types.h> | | |||
46 | #include <sys/sysctl.h> | | |||
47 | #endif | | |||
48 | #include <sys/utsname.h> | | |||
49 | | ||||
50 | #include "Version.h" | 42 | #include "Version.h" | ||
51 | 43 | | |||
52 | static qlonglong calculateTotalRam() | | |||
53 | { | | |||
54 | qlonglong ret = -1; | | |||
55 | #ifdef Q_OS_LINUX | | |||
56 | struct sysinfo info; | | |||
57 | if (sysinfo(&info) == 0) | | |||
58 | // manpage "sizes are given as multiples of mem_unit bytes" | | |||
59 | ret = qlonglong(info.totalram) * info.mem_unit; | | |||
60 | #elif defined(Q_OS_FREEBSD) | | |||
61 | /* Stuff for sysctl */ | | |||
62 | size_t len; | | |||
63 | | ||||
64 | unsigned long memory; | | |||
65 | len = sizeof(memory); | | |||
66 | sysctlbyname("hw.physmem", &memory, &len, NULL, 0); | | |||
67 | | ||||
68 | ret = memory; | | |||
69 | #endif | | |||
70 | return ret; | | |||
71 | } | | |||
72 | | ||||
73 | Module::Module(QWidget *parent, const QVariantList &args) : | 44 | Module::Module(QWidget *parent, const QVariantList &args) : | ||
74 | KCModule(parent, args), | 45 | KCModule(parent, args), | ||
75 | ui(new Ui::Module) | 46 | ui(new Ui::Module) | ||
76 | { | 47 | { | ||
77 | KAboutData *aboutData = new KAboutData(QStringLiteral("kcm-about-distro"), | 48 | KAboutData *aboutData = new KAboutData(QStringLiteral("kcm-about-distro"), | ||
78 | i18nc("@title", "About Distribution"), | 49 | i18nc("@title", "About System"), | ||
79 | QString::fromLatin1(global_s_versionStringFull), | 50 | QString::fromLatin1(global_s_versionStringFull), | ||
80 | QString(), | 51 | QString(), | ||
81 | KAboutLicense::LicenseKey::GPL_V3, | 52 | KAboutLicense::LicenseKey::GPL_V3, | ||
82 | i18nc("@info:credit", "Copyright 2012-2014 Harald Sitter")); | 53 | i18nc("@info:credit", "Copyright 2012-2020 Harald Sitter")); | ||
83 | 54 | | |||
84 | aboutData->addAuthor(i18nc("@info:credit", "Harald Sitter"), | 55 | aboutData->addAuthor(i18nc("@info:credit", "Harald Sitter"), | ||
85 | i18nc("@info:credit", "Author"), | 56 | i18nc("@info:credit", "Author"), | ||
86 | QStringLiteral("apachelogger@kubuntu.org")); | 57 | QStringLiteral("sitter@kde.org")); | ||
87 | 58 | | |||
88 | setAboutData(aboutData); | 59 | setAboutData(aboutData); | ||
89 | 60 | | |||
90 | ui->setupUi(this); | 61 | ui->setupUi(this); | ||
91 | 62 | | |||
92 | QFont font = ui->nameVersionLabel->font(); | 63 | QFont font = ui->nameVersionLabel->font(); | ||
93 | font.setPixelSize(24); | 64 | font.setPixelSize(24); | ||
94 | font.setBold(true); | 65 | font.setBold(true); | ||
Show All 22 Lines | |||||
117 | // Otherwise there is a notciable rendering gap where dummy/no data is | 88 | // Otherwise there is a notciable rendering gap where dummy/no data is | ||
118 | // shown. Makes it look bad. | 89 | // shown. Makes it look bad. | ||
119 | load(); | 90 | load(); | ||
120 | } | 91 | } | ||
121 | 92 | | |||
122 | Module::~Module() | 93 | Module::~Module() | ||
123 | { | 94 | { | ||
124 | delete ui; | 95 | delete ui; | ||
96 | qDeleteAll(m_entries); | ||||
125 | } | 97 | } | ||
126 | 98 | | |||
127 | void Module::load() | 99 | void Module::load() | ||
128 | { | 100 | { | ||
129 | labelsForClipboard.clear(); | 101 | // load is called lazly, but also from the ctor -> prevent double init. | ||
130 | englishTextForClipboard = QStringLiteral(""); | 102 | static bool initd = false; | ||
131 | loadSoftware(); | 103 | if (initd) { | ||
132 | loadHardware(); | 104 | return; | ||
105 | } | ||||
106 | initd = true; | ||||
107 | | ||||
108 | loadOSData(); | ||||
109 | loadEntries(); | ||||
133 | } | 110 | } | ||
134 | 111 | | |||
135 | void Module::save() | 112 | void Module::save() | ||
136 | { | 113 | { | ||
137 | } | 114 | } | ||
138 | 115 | | |||
139 | void Module::defaults() | 116 | void Module::defaults() | ||
140 | { | 117 | { | ||
141 | } | 118 | } | ||
142 | 119 | | |||
143 | void Module::loadSoftware() | 120 | void Module::loadOSData() | ||
144 | { | 121 | { | ||
145 | // NOTE: do not include globals, otherwise kdeglobals could provide values | 122 | // NOTE: do not include globals, otherwise kdeglobals could provide values | ||
146 | // even though we only explicitly want them from our own config. | 123 | // even though we only explicitly want them from our own config. | ||
147 | KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kcm-about-distrorc"), | 124 | KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kcm-about-distrorc"), | ||
148 | KConfig::NoGlobals); | 125 | KConfig::NoGlobals); | ||
149 | KConfigGroup cg = KConfigGroup(config, "General"); | 126 | KConfigGroup cg = KConfigGroup(config, "General"); | ||
150 | 127 | | |||
151 | KOSRelease os; | 128 | KOSRelease os; | ||
Show All 11 Lines | |||||
163 | const QString distroName = cg.readEntry("Name", os.name()); | 140 | const QString distroName = cg.readEntry("Name", os.name()); | ||
164 | const QString osrVersion = cg.readEntry("UseOSReleaseVersion", false) | 141 | const QString osrVersion = cg.readEntry("UseOSReleaseVersion", false) | ||
165 | ? os.version() | 142 | ? os.version() | ||
166 | : os.versionId(); | 143 | : os.versionId(); | ||
167 | const QString versionId = cg.readEntry("Version", osrVersion); | 144 | const QString versionId = cg.readEntry("Version", osrVersion); | ||
168 | const QString distroNameVersion = QStringLiteral("%1 %2").arg(distroName, versionId); | 145 | const QString distroNameVersion = QStringLiteral("%1 %2").arg(distroName, versionId); | ||
169 | ui->nameVersionLabel->setText(distroNameVersion); | 146 | ui->nameVersionLabel->setText(distroNameVersion); | ||
170 | 147 | | |||
171 | const auto dummyDistroDescriptionLabel = new QLabel(i18nc("@title:row", "Operating System:"), this); | 148 | // Insert a dummy entry for debug info dumps. | ||
172 | dummyDistroDescriptionLabel->hide(); | 149 | m_entries.push_back(new Entry(ki18n("Operating System:"), distroNameVersion)); | ||
173 | labelsForClipboard << qMakePair(dummyDistroDescriptionLabel, ui->nameVersionLabel); | | |||
174 | englishTextForClipboard += QStringLiteral("Operating System: %1\n").arg(distroNameVersion); | | |||
175 | 150 | | |||
176 | const QString variant = cg.readEntry("Variant", os.variant()); | 151 | const QString variant = cg.readEntry("Variant", os.variant()); | ||
177 | if (variant.isEmpty()) { | 152 | if (variant.isEmpty()) { | ||
178 | ui->variantLabel->hide(); | 153 | ui->variantLabel->hide(); | ||
179 | } else { | 154 | } else { | ||
180 | ui->variantLabel->setText(variant); | 155 | ui->variantLabel->setText(variant); | ||
181 | } | 156 | } | ||
182 | 157 | | |||
183 | const QString url = cg.readEntry("Website", os.homeUrl()); | 158 | const QString url = cg.readEntry("Website", os.homeUrl()); | ||
184 | if (url.isEmpty()) { | 159 | if (url.isEmpty()) { | ||
185 | ui->urlLabel->hide(); | 160 | ui->urlLabel->hide(); | ||
186 | } else { | 161 | } else { | ||
187 | ui->urlLabel->setText(QStringLiteral("<a href='%1'>%1</a>").arg(url)); | 162 | ui->urlLabel->setText(QStringLiteral("<a href='%1'>%1</a>").arg(url)); | ||
188 | } | 163 | } | ||
189 | | ||||
190 | // Since Plasma version detection isn't based on a library query it can fail | | |||
191 | // in weird cases; instead of admitting defeat we simply hide everything :P | | |||
192 | const QString plasma = plasmaVersion(); | | |||
193 | if (plasma.isEmpty()) { | | |||
194 | ui->plasma->hide(); | | |||
195 | ui->plasmaLabel->hide(); | | |||
196 | } else { | | |||
197 | ui->plasmaLabel->setText(plasma); | | |||
198 | labelsForClipboard << qMakePair(ui->plasma, ui->plasmaLabel); | | |||
199 | englishTextForClipboard += QStringLiteral("KDE Plasma Version: %1\n").arg(plasma); | | |||
200 | } | | |||
201 | | ||||
202 | const QString frameworksVersion = KCoreAddons::versionString(); | | |||
203 | ui->frameworksLabel->setText(frameworksVersion); | | |||
204 | labelsForClipboard << qMakePair(ui->frameworksLabelKey, ui->frameworksLabel); | | |||
205 | englishTextForClipboard += QStringLiteral("KDE Frameworks Version: %1\n").arg(frameworksVersion); | | |||
206 | | ||||
207 | const QString qversion = QString::fromLatin1(qVersion()); | | |||
208 | ui->qtLabel->setText(qversion); | | |||
209 | labelsForClipboard << qMakePair(ui->qt, ui->qtLabel); | | |||
210 | englishTextForClipboard += QStringLiteral("Qt Version: %1\n").arg(qversion); | | |||
211 | } | 164 | } | ||
212 | 165 | | |||
213 | void Module::loadHardware() | 166 | void Module::loadEntries() | ||
214 | { | 167 | { | ||
215 | struct utsname utsName; | 168 | auto addSectionHeader = [this](const QString &text) | ||
216 | if(uname(&utsName) != 0) { | 169 | { | ||
217 | ui->kernel->hide(); | 170 | int row = ui->infoGrid->rowCount(); | ||
218 | ui->kernelLabel->hide(); | 171 | // Random sizes stolen from original UI file values :S | ||
219 | } else { | 172 | ui->infoGrid->addItem(new QSpacerItem(17, 21, QSizePolicy::Minimum, QSizePolicy::Fixed), row, 1, 1, 1); | ||
220 | QString kernelVersion = QString::fromLatin1(utsName.release); | 173 | ++row; | ||
221 | ui->kernelLabel->setText(kernelVersion); | 174 | ui->infoGrid->addWidget(new SectionLabel(text), row, 1, Qt::AlignLeft); | ||
222 | labelsForClipboard << qMakePair(ui->kernel, ui->kernelLabel); | 175 | ++row; | ||
223 | englishTextForClipboard += QStringLiteral("Kernel Version: %1\n").arg(kernelVersion); | 176 | }; | ||
224 | } | 177 | | ||
225 | 178 | auto addEntriesToGrid = [this](std::vector<const Entry *> entries) | |||
226 | const int bits = QT_POINTER_SIZE == 8 ? 64 : 32; | 179 | { | ||
227 | const QString bitsStr = QString::number(bits); | 180 | int row = ui->infoGrid->rowCount(); | ||
228 | ui->bitsLabel->setText(i18nc("@label %1 is the CPU bit width (e.g. 32 or 64)", | 181 | for (auto entry : entries) { | ||
229 | "%1-bit", bitsStr)); | 182 | if (!entry->isValid()) { | ||
230 | labelsForClipboard << qMakePair(ui->bitsKey, ui->bitsLabel); | 183 | delete entry; // since we do not keep it around | ||
231 | englishTextForClipboard += QStringLiteral("OS Type: %1-bit\n").arg(bitsStr); | 184 | continue; | ||
232 | 185 | } | |||
233 | const QList<Solid::Device> list = Solid::Device::listFromType(Solid::DeviceInterface::Processor); | 186 | ui->infoGrid->addWidget(new QLabel(entry->label.toString()), row, 0, Qt::AlignRight); | ||
234 | ui->processor->setText(i18np("Processor:", "Processors:", list.count())); | 187 | ui->infoGrid->addWidget(new QLabel(entry->value), row, 1, Qt::AlignLeft); | ||
235 | // Format processor string | 188 | m_entries.push_back(entry); | ||
236 | // Group by processor name | 189 | ++row; | ||
237 | QMap<QString, int> processorMap; | 190 | } | ||
238 | Q_FOREACH(const Solid::Device &device, list) { | 191 | }; | ||
239 | const QString name = device.product(); | 192 | | ||
240 | auto it = processorMap.find(name); | 193 | // software | ||
241 | if (it == processorMap.end()) { | 194 | addSectionHeader(i18nc("@title:group", "Software")); | ||
242 | processorMap.insert(name, 1); | 195 | addEntriesToGrid({ | ||
243 | } else { | 196 | new PlasmaEntry(), | ||
244 | ++it.value(); | 197 | new Entry(ki18n("KDE Frameworks Version:"), KCoreAddons::versionString()), | ||
245 | } | 198 | new Entry(ki18n("Qt Version:"), QString::fromLatin1(qVersion())), | ||
246 | } | 199 | new KernelEntry(), | ||
247 | // Create a formatted list of grouped processors | 200 | new BitEntry() | ||
248 | QStringList names; | 201 | }); | ||
249 | names.reserve(processorMap.count()); | 202 | | ||
250 | for (auto it = processorMap.constBegin(); it != processorMap.constEnd(); ++it) { | 203 | // hardware | ||
251 | const int count = it.value(); | 204 | addSectionHeader(i18nc("@title:group", "Hardware")); | ||
252 | QString name = it.key(); | 205 | addEntriesToGrid({ | ||
253 | name.replace(QStringLiteral("(TM)"), QChar(8482)); | 206 | new CPUEntry(), | ||
254 | name.replace(QStringLiteral("(R)"), QChar(174)); | 207 | new MemoryEntry() | ||
255 | name = name.simplified(); | 208 | }); | ||
256 | names.append(QStringLiteral("%1 × %2").arg(count).arg(name)); | | |||
257 | } | | |||
258 | | ||||
259 | const QString processorLabel = names.join(QLatin1String(", ")); | | |||
260 | ui->processorLabel->setText(processorLabel); | | |||
261 | if (ui->processorLabel->text().isEmpty()) { | | |||
262 | ui->processor->setHidden(true); | | |||
263 | ui->processorLabel->setHidden(true); | | |||
264 | } else { | | |||
265 | labelsForClipboard << qMakePair(ui->processor, ui->processorLabel); | | |||
266 | englishTextForClipboard += QStringLiteral("Processors: %1\n").arg(processorLabel); | | |||
267 | } | | |||
268 | | ||||
269 | const qlonglong totalRam = calculateTotalRam(); | | |||
270 | const QString memoryLabel = totalRam > 0 | | |||
271 | ? i18nc("@label %1 is the formatted amount of system memory (e.g. 7,7 GiB)", | | |||
272 | "%1 of RAM", KFormat().formatByteSize(totalRam)) | | |||
273 | : i18nc("Unknown amount of RAM", "Unknown"); | | |||
274 | ui->memoryLabel->setText(memoryLabel); | | |||
275 | labelsForClipboard << qMakePair(ui->memory, ui->memoryLabel); | | |||
276 | englishTextForClipboard += QStringLiteral("Memory: %1\n").arg(KFormat().formatByteSize(totalRam)); | | |||
277 | } | 209 | } | ||
278 | 210 | | |||
279 | void Module::copyToClipboard() | 211 | void Module::copyToClipboard() | ||
280 | { | 212 | { | ||
281 | QString text; | 213 | QString text; | ||
282 | // note that this loop does not necessarily represent the same order as in the GUI | 214 | for (auto entry : m_entries) { | ||
283 | for (auto labelPair : qAsConst(labelsForClipboard)) { | 215 | text += entry->diagnosticLine(Entry::Language::System); | ||
284 | const auto valueLabel = labelPair.second; | | |||
285 | if (!valueLabel->isHidden()) { | | |||
286 | const auto descriptionLabelText = labelPair.first->text(); | | |||
287 | const auto valueLabelText = valueLabel->text(); | | |||
288 | text += i18nc("%1 is a label already including a colon, %2 is the corresponding value", "%1 %2", descriptionLabelText, valueLabelText) + QStringLiteral("\n"); | | |||
289 | } | | |||
290 | } | 216 | } | ||
291 | | ||||
292 | QGuiApplication::clipboard()->setText(text); | 217 | QGuiApplication::clipboard()->setText(text); | ||
293 | } | 218 | } | ||
294 | 219 | | |||
295 | void Module::copyToClipboardInEnglish() | 220 | void Module::copyToClipboardInEnglish() | ||
296 | { | 221 | { | ||
297 | QGuiApplication::clipboard()->setText(englishTextForClipboard); | 222 | QString text; | ||
298 | } | 223 | for (auto entry : m_entries) { | ||
299 | 224 | text += entry->diagnosticLine(Entry::Language::English); | |||
300 | QString Module::plasmaVersion() const | | |||
301 | { | | |||
302 | const QStringList &filePaths = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, | | |||
303 | QStringLiteral("xsessions/plasma.desktop")); | | |||
304 | | ||||
305 | if (filePaths.length() < 1) { | | |||
306 | return QString(); | | |||
307 | } | 225 | } | ||
308 | 226 | QGuiApplication::clipboard()->setText(text); | |||
309 | // Despite the fact that there can be multiple desktop files we simply take | | |||
310 | // the first one as users usually don't have xsessions/ in their $HOME | | |||
311 | // data location, so the first match should (usually) be the only one and | | |||
312 | // reflect the plasma session run. | | |||
313 | KDesktopFile desktopFile(filePaths.first()); | | |||
314 | return desktopFile.desktopGroup().readEntry("X-KDE-PluginInfo-Version", QString()); | | |||
315 | } | 227 | } |