Changeset View
Changeset View
Standalone View
Standalone View
src/gtkconfigkcmodule.cpp
Show All 14 Lines | |||||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * Lesser General Public License for more details. | 17 | * Lesser General Public License for more details. | ||
18 | * | 18 | * | ||
19 | * You should have received a copy of the GNU Lesser General Public | 19 | * You should have received a copy of the GNU Lesser General Public | ||
20 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. | 20 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. | ||
21 | */ | 21 | */ | ||
22 | 22 | | |||
23 | #include <QStandardPaths> | | |||
24 | #include <QFile> | | |||
25 | #include <QMenu> | 23 | #include <QMenu> | ||
26 | #include <QDebug> | | |||
27 | #include <QDir> | 24 | #include <QDir> | ||
28 | #include <QSortFilterProxyModel> | 25 | #include <QDBusMessage> | ||
29 | #include <QStringListModel> | 26 | #include <QDBusConnection> | ||
30 | #include <QSvgRenderer> | 27 | #include <QDBusInterface> | ||
31 | #include <QPainter> | 28 | #include <QDBusReply> | ||
29 | #include <QAbstractButton> | ||||
30 | #include <QStandardPaths> | ||||
31 | #include <QFileDialog> | ||||
32 | #include <QMessageBox> | ||||
33 | #include <QUrl> | ||||
32 | 34 | | |||
33 | #include <KAboutData> | 35 | #include <KAboutData> | ||
34 | #include <KPluginFactory> | 36 | #include <KPluginFactory> | ||
35 | #include <KProcess> | 37 | #include <DownloadDialog> | ||
36 | #include <KMessageBox> | | |||
37 | #include <KIconTheme> | | |||
38 | #include <KLocalizedString> | 38 | #include <KLocalizedString> | ||
39 | #include <KTar> | ||||
40 | #include <KIO/DeleteJob> | ||||
39 | 41 | | |||
40 | #include "config.h" | 42 | #include "config.h" | ||
41 | #include "ui_gui.h" | 43 | #include "ui_gui.h" | ||
42 | #include "abstractappearance.h" | | |||
43 | #include "gtkconfigkcmodule.h" | 44 | #include "gtkconfigkcmodule.h" | ||
44 | 45 | | |||
45 | K_PLUGIN_FACTORY_WITH_JSON(GTKConfigKCModuleFactory, "kde-gtk-config.json", registerPlugin<GTKConfigKCModule>();) | 46 | K_PLUGIN_FACTORY_WITH_JSON(GTKConfigKCModuleFactory, "kde-gtk-config.json", registerPlugin<GTKConfigKCModule>();) | ||
46 | 47 | | |||
47 | GTKConfigKCModule::GTKConfigKCModule(QWidget* parent, const QVariantList& args ) | 48 | GTKConfigKCModule::GTKConfigKCModule(QWidget* parent, const QVariantList& args ) | ||
48 | : KCModule(parent) | 49 | : KCModule(parent) | ||
50 | , currentGtk2Theme() | ||||
51 | , currentGtk3Theme() | ||||
52 | , gtk2ThemesModel() | ||||
53 | , gtk3ThemesModel() | ||||
54 | , gtkConfigInterface( | ||||
55 | QStringLiteral("org.kde.GtkConfig"), | ||||
56 | QStringLiteral("/GtkConfig"), | ||||
57 | QStringLiteral("org.kde.GtkConfig") | ||||
58 | ) | ||||
49 | , ui(new Ui::GUI) | 59 | , ui(new Ui::GUI) | ||
50 | , installer(0) | | |||
51 | , uninstaller(0) | | |||
52 | , m_saveEnabled(true) | | |||
53 | { | 60 | { | ||
54 | Q_UNUSED(args); | 61 | Q_UNUSED(args) | ||
55 | KAboutData *acercade = new KAboutData("cgc", i18n("GTK Application Style"), PROJECT_VERSION, | 62 | auto *about = new KAboutData("cgc", i18n("GTK Application Style"), PROJECT_VERSION, | ||
56 | QString(), KAboutLicense::LGPL_V3, i18n("Copyright 2011 José Antonio Sánchez Reynaga")); | 63 | QString(), KAboutLicense::LGPL_V3, i18n("Copyright 2011 José Antonio Sánchez Reynaga")); | ||
57 | acercade->addAuthor(i18n("José Antonio Sánchez Reynaga (antonioJASR)"),i18n("Main Developer"), "joanzare@gmail.com"); | 64 | about->addAuthor(i18n("José Antonio Sánchez Reynaga (antonioJASR)"),i18n("Main Developer"), "joanzare@gmail.com"); | ||
58 | acercade->addAuthor(i18n("Aleix Pol i Gonzalez"), i18n("Feature development. Previews, code refactoring."), "aleixpol@blue-systems.com"); | 65 | about->addAuthor(i18n("Aleix Pol i Gonzalez"), i18n("Feature development. Previews, code refactoring."), "aleixpol@blue-systems.com"); | ||
59 | acercade->addCredit(i18n("Manuel Tortosa (manutortosa)"), i18n("Ideas, tester, internationalization")); | 66 | about->addCredit(i18n("Manuel Tortosa (manutortosa)"), i18n("Ideas, tester, internationalization")); | ||
60 | acercade->addCredit(i18n("Adrián Chaves Fernández (Gallaecio)"), i18n("Internationalization")); | 67 | about->addCredit(i18n("Adrián Chaves Fernández (Gallaecio)"), i18n("Internationalization")); | ||
61 | setAboutData(acercade); | 68 | setAboutData(about); | ||
62 | setButtons(KCModule::Default | KCModule::Apply); | 69 | setButtons(KCModule::Default | KCModule::Apply); | ||
63 | ui->setupUi(this); | 70 | ui->setupUi(this); | ||
64 | appareance = new AppearenceGTK; | | |||
65 | | ||||
66 | m_tempGtk2Preview = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/gtkrc-2.0"; | | |||
67 | m_tempGtk3Preview = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/.config/gtk-3.0/settings.ini"; | | |||
68 | | ||||
69 | const QIcon previewIcon = QIcon::fromTheme("document-preview"); | | |||
70 | ui->gtk2Preview->setIcon(previewIcon); | | |||
71 | ui->gtk3Preview->setIcon(previewIcon); | | |||
72 | | ||||
73 | QString gtk2Preview = QStandardPaths::findExecutable("gtk_preview", {CMAKE_INSTALL_FULL_LIBEXECDIR}); | | |||
74 | QString gtk3Preview = QStandardPaths::findExecutable("gtk3_preview", {CMAKE_INSTALL_FULL_LIBEXECDIR}); | | |||
75 | 71 | | |||
76 | m_p2 = new KProcess(this); | 72 | ui->cb_theme->setModel(>k2ThemesModel); | ||
77 | m_p2->setEnv("GTK2_RC_FILES", m_tempGtk2Preview, true); | 73 | ui->cb_theme_gtk3->setModel(>k3ThemesModel); | ||
78 | if(!gtk2Preview.isEmpty()) { | | |||
79 | *m_p2 << gtk2Preview; | | |||
80 | connect(m_p2, SIGNAL(finished(int)), this, SLOT(untogglePreview())); | | |||
81 | } | | |||
82 | 74 | | |||
83 | m_p3 = new KProcess(this); | 75 | connect(ui->cb_theme, SIGNAL(currentTextChanged(const QString &)), this, SLOT(themesSelectionsChanged())); | ||
84 | m_p3->setEnv("XDG_CONFIG_HOME", QStandardPaths::writableLocation(QStandardPaths::TempLocation)+"/.config"); | 76 | connect(ui->cb_theme_gtk3, SIGNAL(currentTextChanged(const QString &)), this, SLOT(themesSelectionsChanged())); | ||
85 | if(!gtk3Preview.isEmpty()) { | | |||
86 | *m_p3 << gtk3Preview; | | |||
87 | connect(m_p3, SIGNAL(finished(int)), this, SLOT(untogglePreview())); | | |||
88 | } | | |||
89 | 77 | | |||
90 | ui->gtk2Preview->setVisible(!gtk2Preview.isEmpty()); | 78 | connect(ui->removeGtk2Theme, &QAbstractButton::clicked, this, >KConfigKCModule::removeGtk2Theme); | ||
91 | ui->gtk3Preview->setVisible(!gtk3Preview.isEmpty()); | 79 | connect(ui->removeGtk3Theme, &QAbstractButton::clicked, this, >KConfigKCModule::removeGtk3Theme); | ||
92 | 80 | | |||
93 | // UI changes | 81 | connect(ui->gtk2Preview, &QAbstractButton::clicked, this, >KConfigKCModule::showGtk2Preview); | ||
94 | connect(ui->cb_theme, SIGNAL(currentIndexChanged(int)), this, SLOT(appChanged())); | 82 | connect(ui->gtk3Preview, &QAbstractButton::clicked, this, >KConfigKCModule::showGtk3Preview); | ||
95 | connect(ui->cb_theme_gtk3, SIGNAL(currentIndexChanged(int)), this, SLOT(appChanged())); | | |||
96 | 83 | | |||
97 | // Preview updates | 84 | connect(ui->installFromFile, &QAbstractButton::clicked, this, >KConfigKCModule::installGtkThemeFromFile); | ||
98 | connect(ui->gtk2Preview, &QAbstractButton::clicked, this, >KConfigKCModule::runGtk2IfNecessary); | | |||
99 | connect(ui->gtk3Preview, &QAbstractButton::clicked, this, >KConfigKCModule::runGtk3IfNecessary); | | |||
100 | 85 | | |||
101 | QMenu *m = new QMenu(this); | 86 | auto *ghnsMenu = new QMenu(this); | ||
102 | m->addAction(QIcon::fromTheme("get-hot-new-stuff"), i18n("Download GTK2 themes..."), this, >KConfigKCModule::showThemeGHNS); | 87 | ghnsMenu->addAction(QIcon::fromTheme("get-hot-new-stuff"), i18n("Get New GNOME/GTK2 Application Styles..."), this, >KConfigKCModule::installGtk2ThemeFromGHNS); | ||
103 | m->addAction(QIcon::fromTheme("get-hot-new-stuff"), i18n("Download GTK3 themes..."), this, >KConfigKCModule::installThemeGTK3GHNS); | 88 | ghnsMenu->addAction(QIcon::fromTheme("get-hot-new-stuff"), i18n("Get New GNOME/GTK3 Application Styles..."), this, >KConfigKCModule::installGtk3ThemeFromGHNS); | ||
104 | m->addAction(QIcon::fromTheme("archive-insert"), i18n("Install a local theme..."), this, >KConfigKCModule::showDialogForInstall); | 89 | ui->newThemes->setMenu(ghnsMenu); | ||
105 | m->addAction(QIcon::fromTheme("archive-remove"), i18n("Uninstall a local theme..."), this, >KConfigKCModule::showDialogForUninstall); | | |||
106 | ui->newThemes->setMenu(m); | | |||
107 | ui->newThemes->setIcon(QIcon::fromTheme("get-hot-new-stuff")); | 90 | ui->newThemes->setIcon(QIcon::fromTheme("get-hot-new-stuff")); | ||
108 | } | 91 | } | ||
109 | 92 | | |||
110 | GTKConfigKCModule::~GTKConfigKCModule() | 93 | GTKConfigKCModule::~GTKConfigKCModule() | ||
111 | { | 94 | { | ||
112 | m_p2->kill(); | | |||
113 | m_p3->kill(); | | |||
114 | | ||||
115 | QFile::remove(m_tempGtk2Preview); | | |||
116 | QFile::remove(m_tempGtk3Preview); | | |||
117 | delete appareance; | | |||
118 | | ||||
119 | m_p2->waitForFinished(); | | |||
120 | m_p3->waitForFinished(); | | |||
121 | delete ui; | 95 | delete ui; | ||
122 | } | 96 | } | ||
123 | 97 | | |||
124 | void GTKConfigKCModule::syncUI() | 98 | void GTKConfigKCModule::installGtk2ThemeFromGHNS() | ||
125 | { | 99 | { | ||
126 | appareance->setThemeGtk3(ui->cb_theme_gtk3->currentText()); | 100 | KNS3::DownloadDialog downloadDialog("cgctheme.knsrc", this); | ||
127 | appareance->setTheme(ui->cb_theme->currentText()); | 101 | if (downloadDialog.exec()) { | ||
102 | updateThemesListsWithoutLoosingSelection(); | ||||
103 | } | ||||
128 | } | 104 | } | ||
129 | 105 | | |||
130 | void GTKConfigKCModule::showThemeGHNS() | 106 | void GTKConfigKCModule::installGtk3ThemeFromGHNS() | ||
131 | { | 107 | { | ||
132 | KNS3::DownloadDialog d("cgctheme.knsrc", this); | 108 | KNS3::DownloadDialog downloadDialog("cgcgtk3.knsrc", this); | ||
133 | if(d.exec()) { | 109 | if (downloadDialog.exec()) { | ||
134 | refreshLists(); | 110 | updateThemesListsWithoutLoosingSelection(); | ||
135 | } | 111 | } | ||
136 | } | 112 | } | ||
137 | 113 | | |||
138 | void GTKConfigKCModule::installThemeGTK3GHNS() | 114 | void GTKConfigKCModule::installGtkThemeFromFile() | ||
139 | { | 115 | { | ||
140 | KNS3::DownloadDialog d("cgcgtk3.knsrc", this); | 116 | QString themeArchivePath = QFileDialog::getOpenFileName( | ||
141 | if(d.exec()) { | 117 | this, | ||
142 | refreshLists(); | 118 | i18n("Select GTK Theme Archive"), | ||
119 | QDir::home().path(), | ||||
120 | i18n("GTK Theme Archive (*.tar.xz *.tar.gz *.tar.bz2)") | ||||
121 | ); | ||||
122 | | ||||
123 | if (themeArchivePath.isEmpty()) { | ||||
124 | return; | ||||
143 | } | 125 | } | ||
126 | | ||||
127 | QString themesInstallDirectoryPath(QDir::homePath() + QStringLiteral("/.themes")); | ||||
128 | QDir::home().mkpath(themesInstallDirectoryPath); | ||||
129 | KTar themeArchive(themeArchivePath); | ||||
130 | themeArchive.open(QIODevice::ReadOnly); | ||||
131 | | ||||
132 | QStringList archiveSubitems = themeArchive.directory()->entries(); | ||||
133 | if (!archiveSubitems.contains(QStringLiteral("gtk-2.0")) && archiveSubitems.indexOf(QRegExp("gtk-3.*")) == -1) { | ||||
134 | QMessageBox::warning(this, i18n("Invalid GTK Theme archive"), i18n("%1 is not a valid GTK Theme archive.", QDir(themeArchivePath).dirName())); | ||||
135 | return; | ||||
144 | } | 136 | } | ||
145 | 137 | | |||
146 | void GTKConfigKCModule::refreshLists() | 138 | themeArchive.directory()->copyTo(themesInstallDirectoryPath); | ||
139 | | ||||
140 | updateThemesListsWithoutLoosingSelection(); | ||||
141 | } | ||||
142 | | ||||
143 | void GTKConfigKCModule::removeGtk2Theme() | ||||
147 | { | 144 | { | ||
148 | refreshThemesUi(true); | 145 | const QModelIndex index = gtk2ThemesModel.index(ui->cb_theme->currentIndex(), 1); | ||
146 | const QString themePath = gtk2ThemesModel.data(index).value<QString>(); | ||||
147 | KIO::DeleteJob* deleteJob = KIO::del(QUrl::fromLocalFile(themePath), KIO::HideProgressInfo); | ||||
148 | connect(deleteJob, &KJob::finished, this, >KConfigKCModule::updateThemesListsWithoutLoosingSelection); | ||||
149 | } | 149 | } | ||
150 | 150 | | |||
151 | void GTKConfigKCModule::appChanged() | 151 | void GTKConfigKCModule::removeGtk3Theme() | ||
152 | { | 152 | { | ||
153 | if (m_loading) { | 153 | const QModelIndex index = gtk3ThemesModel.index(ui->cb_theme_gtk3->currentIndex(), 1); | ||
154 | return; | 154 | const QString themePath = gtk3ThemesModel.data(index).value<QString>(); | ||
155 | KIO::del(QUrl::fromLocalFile(themePath), KIO::HideProgressInfo); | ||||
156 | KIO::DeleteJob* deleteJob = KIO::del(QUrl::fromLocalFile(themePath), KIO::HideProgressInfo); | ||||
157 | connect(deleteJob, &KJob::finished, this, >KConfigKCModule::updateThemesListsWithoutLoosingSelection); | ||||
155 | } | 158 | } | ||
156 | 159 | | |||
157 | savePreviewConfig(); | 160 | void GTKConfigKCModule::showGtk2Preview() | ||
158 | emit changed(true); | 161 | { | ||
162 | gtkConfigInterface.call(QStringLiteral("showGtk2ThemePreview"), ui->cb_theme->currentText()); | ||||
159 | } | 163 | } | ||
160 | 164 | | |||
165 | void GTKConfigKCModule::showGtk3Preview() | ||||
166 | { | ||||
167 | gtkConfigInterface.call(QStringLiteral("showGtk3ThemePreview"), ui->cb_theme_gtk3->currentText()); | ||||
168 | } | ||||
161 | 169 | | |||
162 | void GTKConfigKCModule::savePreviewConfig() | 170 | void GTKConfigKCModule::save() | ||
163 | { | 171 | { | ||
164 | if(!m_saveEnabled || !(ui->gtk2Preview->isChecked() || ui->gtk3Preview->isChecked())) { | 172 | saveGtk2Theme(); | ||
165 | return; | 173 | saveGtk3Theme(); | ||
166 | } | 174 | } | ||
167 | 175 | | |||
168 | syncUI(); | 176 | void GTKConfigKCModule::defaults() | ||
177 | { | ||||
178 | int gtk2DefaultThemeIndex = ui->cb_theme->findText(QStringLiteral("Breeze")); | ||||
179 | ui->cb_theme->setCurrentIndex(gtk2DefaultThemeIndex); | ||||
169 | 180 | | |||
170 | if(ui->gtk3Preview->isChecked()) { | 181 | int gtk3DefaultThemeIndex = ui->cb_theme_gtk3->findText(QStringLiteral("Breeze")); | ||
171 | // We don't want to recursively loop between savePreviewConfig and runIfNecessary | 182 | ui->cb_theme_gtk3->setCurrentIndex(gtk3DefaultThemeIndex); | ||
172 | m_saveEnabled = false; | 183 | } | ||
173 | m_p3->kill(); | | |||
174 | appareance->gtk3Appearance()->saveSettings(m_tempGtk3Preview); | | |||
175 | 184 | | |||
176 | // Need to make sure runIfNecessary() to know that it's not running | 185 | void GTKConfigKCModule::load() | ||
177 | m_p3->waitForFinished(); | 186 | { | ||
187 | loadGtkThemes(); | ||||
178 | 188 | | |||
179 | m_p3->start(); | 189 | selectCurrentGtk2ThemeInCheckbox(); | ||
180 | ui->gtk3Preview->setChecked(true); | 190 | selectCurrentGtk3ThemeInCheckbox(); | ||
181 | m_saveEnabled = true; | | |||
182 | } else if(ui->gtk2Preview->isChecked()) { | | |||
183 | appareance->gtk2Appearance()->saveSettings(m_tempGtk2Preview); | | |||
184 | } | | |||
185 | } | 191 | } | ||
186 | 192 | | |||
187 | void GTKConfigKCModule::runGtk2IfNecessary(bool checked) | 193 | void GTKConfigKCModule::updateThemesListsWithoutLoosingSelection() | ||
188 | { | 194 | { | ||
189 | KProcess* p = m_p2; | 195 | QString currentGtk2ThemeChoice = ui->cb_theme->currentText(); | ||
190 | KProcess* np = m_p3; | 196 | QString currentGtk3ThemeChoice = ui->cb_theme_gtk3->currentText(); | ||
191 | 197 | | |||
192 | if (checked) { | 198 | loadGtkThemes(); | ||
193 | np->kill(); | 199 | | ||
194 | np->waitForFinished(); | 200 | // Restore correct gtk configuration, if the theme that was being used is deleted | ||
195 | savePreviewConfig(); | 201 | if (!gtk2ThemesModel.containsTheme(currentGtk2Theme)) { | ||
196 | if(p->state() == QProcess::NotRunning) { | 202 | ui->cb_theme->setCurrentText(QStringLiteral("Breeze")); | ||
197 | p->start(); | 203 | saveGtk2Theme(); | ||
198 | } | | |||
199 | } else { | | |||
200 | p->kill(); | | |||
201 | p->waitForFinished(); | | |||
202 | } | | |||
203 | } | 204 | } | ||
204 | 205 | | |||
205 | void GTKConfigKCModule::runGtk3IfNecessary(bool checked) | 206 | if (!gtk3ThemesModel.containsTheme(currentGtk3Theme)) { | ||
206 | { | 207 | ui->cb_theme_gtk3->setCurrentText(QStringLiteral("Breeze")); | ||
207 | KProcess* p = m_p3; | 208 | saveGtk3Theme(); | ||
208 | KProcess* np = m_p2; | 209 | } | ||
209 | 210 | | |||
210 | if (checked) { | 211 | // Restore choices, that was before possible themes deletion | ||
211 | np->kill(); | 212 | if (gtk2ThemesModel.containsTheme(currentGtk2ThemeChoice)) { | ||
212 | np->waitForFinished(); | 213 | ui->cb_theme->setCurrentText(currentGtk2ThemeChoice); | ||
213 | savePreviewConfig(); | 214 | } else { | ||
214 | if(p->state() == QProcess::NotRunning) { | 215 | ui->cb_theme->setCurrentText(QStringLiteral("Breeze")); | ||
215 | p->start(); | | |||
216 | } | 216 | } | ||
217 | | ||||
218 | if (gtk3ThemesModel.containsTheme(currentGtk3ThemeChoice)) { | ||||
219 | ui->cb_theme_gtk3->setCurrentText(currentGtk3ThemeChoice); | ||||
217 | } else { | 220 | } else { | ||
218 | p->kill(); | 221 | ui->cb_theme_gtk3->setCurrentText(QStringLiteral("Breeze")); | ||
219 | p->waitForFinished(); | | |||
220 | } | 222 | } | ||
221 | } | 223 | } | ||
222 | 224 | | |||
223 | void GTKConfigKCModule::save() | 225 | void GTKConfigKCModule::themesSelectionsChanged() | ||
224 | { | 226 | { | ||
225 | syncUI(); | 227 | if (ui->cb_theme->currentText() == currentGtk2Theme && | ||
226 | if(!appareance->saveFileConfig()) { | 228 | ui->cb_theme_gtk3->currentText() == currentGtk3Theme) { | ||
227 | KMessageBox::error(this, i18n("Failed to save configuration.")); | 229 | emit changed(false); | ||
230 | } else { | ||||
231 | markAsChanged(); | ||||
228 | } | 232 | } | ||
233 | updateDeletionPossibilityForSelectedGtk2Theme(); | ||||
234 | updateDeletionPossibilityForSelectedGtk3Theme(); | ||||
229 | } | 235 | } | ||
230 | 236 | | |||
231 | void setComboItem(QComboBox* combo, const QStringList& texts) | 237 | void GTKConfigKCModule::saveGtk2Theme() | ||
232 | { | 238 | { | ||
233 | for (const QString &text : texts) { | 239 | currentGtk2Theme = ui->cb_theme->currentText(); | ||
234 | int pos = combo->findText(text); | 240 | gtkConfigInterface.call(QStringLiteral("setGtk2Theme"), currentGtk2Theme); | ||
235 | if(pos >= 0) { | | |||
236 | combo->setCurrentIndex(pos); | | |||
237 | return; | | |||
238 | } | | |||
239 | } | | |||
240 | } | 241 | } | ||
241 | 242 | | |||
242 | void GTKConfigKCModule::defaults() | 243 | void GTKConfigKCModule::saveGtk3Theme() | ||
243 | { | 244 | { | ||
244 | refreshThemesUi(false); | 245 | currentGtk3Theme = ui->cb_theme_gtk3->currentText(); | ||
245 | 246 | gtkConfigInterface.call(QStringLiteral("setGtk3Theme"), currentGtk3Theme); | |||
246 | m_saveEnabled = false; | | |||
247 | | ||||
248 | setComboItem(ui->cb_theme, QStringList("oxygen-gtk") << "Clearlooks"); | | |||
249 | setComboItem(ui->cb_theme_gtk3, QStringList("oxygen-gtk") << "Adwaita"); | | |||
250 | | ||||
251 | m_saveEnabled = true; | | |||
252 | | ||||
253 | appChanged(); | | |||
254 | } | 247 | } | ||
255 | 248 | | |||
256 | void GTKConfigKCModule::load() | 249 | void GTKConfigKCModule::loadGtkThemes() | ||
257 | { | 250 | { | ||
258 | m_saveEnabled = false; | 251 | QStringList possibleThemesPaths = possiblePathsToGtkThemes(); | ||
259 | bool someCorrect = appareance->loadFileConfig(); | 252 | loadGtk2Themes(possibleThemesPaths); | ||
260 | m_loading = true; | 253 | loadGtk3Themes(possibleThemesPaths); | ||
261 | if(someCorrect) { | | |||
262 | refreshLists(); | | |||
263 | } else { | | |||
264 | defaults(); | | |||
265 | } | 254 | } | ||
266 | m_loading = false; | | |||
267 | 255 | | |||
268 | m_saveEnabled = true; | 256 | void GTKConfigKCModule::loadGtk2Themes(const QStringList &possibleThemesPaths) | ||
257 | { | ||||
258 | QMap<QString, QString> gtk2ThemesNames; | ||||
259 | | ||||
260 | for (const QString &possibleThemePath : possibleThemesPaths) { | ||||
261 | // If the directory has a gtk-2.0 directory inside, it is the GTK2 theme for sure | ||||
262 | QDir possibleThemeDirectory(possibleThemePath); | ||||
263 | bool hasGtk2DirectoryInside = possibleThemeDirectory.exists(QStringLiteral("gtk-2.0")); | ||||
264 | if (hasGtk2DirectoryInside) { | ||||
265 | gtk2ThemesNames.insert(possibleThemeDirectory.dirName(), possibleThemeDirectory.path()); | ||||
266 | } | ||||
269 | } | 267 | } | ||
270 | 268 | | |||
271 | class MyStringListModel : public QAbstractListModel | 269 | gtk2ThemesModel.setThemesList(gtk2ThemesNames); | ||
272 | { | | |||
273 | public: | | |||
274 | MyStringListModel(const QStringList &texts, QObject* parent) : QAbstractListModel(parent), m_texts(texts) | | |||
275 | { | | |||
276 | } | 270 | } | ||
277 | 271 | | |||
278 | QVariant data(const QModelIndex & index, int role) const override | 272 | void GTKConfigKCModule::loadGtk3Themes(const QStringList &possibleThemesPaths) | ||
279 | { | 273 | { | ||
280 | if (role != Qt::DisplayRole || !index.isValid() || index.row()>=m_texts.count()) { | 274 | QMap<QString, QString> gtk3ThemesNames; | ||
281 | return {}; | | |||
282 | } | | |||
283 | 275 | | |||
284 | return m_texts[index.row()]; | 276 | static const QStringList gtk3SubdirPattern(QStringLiteral("gtk-3.*")); | ||
277 | for (const QString &possibleThemePath : possibleThemesPaths) { | ||||
278 | // If the directory contains any of gtk-3.X folders, it is the GTK3 theme for sure | ||||
279 | QDir possibleThemeDirectory(possibleThemePath); | ||||
280 | if (!possibleThemeDirectory.entryList(gtk3SubdirPattern, QDir::Dirs).isEmpty()) { | ||||
281 | gtk3ThemesNames.insert(possibleThemeDirectory.dirName(), possibleThemeDirectory.path()); | ||||
282 | } | ||||
285 | } | 283 | } | ||
286 | 284 | | |||
287 | int rowCount(const QModelIndex &parent) const override { | 285 | gtk3ThemesModel.setThemesList(gtk3ThemesNames); | ||
288 | return parent.isValid() ? 0 : m_texts.count(); | | |||
289 | } | 286 | } | ||
290 | 287 | | |||
291 | void setStrings(const QSet<QString> &list) { | 288 | QStringList GTKConfigKCModule::possiblePathsToGtkThemes() { | ||
292 | const auto current = m_texts.toSet(); | 289 | QStringList possibleThemesPaths; | ||
293 | 290 | | |||
294 | const auto oldRows = QSet<QString>(current).subtract(list); | 291 | QStringList themesLocationsPaths = QStandardPaths::locateAll( | ||
295 | const auto newRows = QSet<QString>(list).subtract(current); | 292 | QStandardPaths::GenericDataLocation, | ||
296 | if (!newRows.isEmpty()) { | 293 | QStringLiteral("themes"), | ||
297 | beginInsertRows({}, m_texts.count(), m_texts.count() + newRows.count()); | 294 | QStandardPaths::LocateDirectory); | ||
298 | m_texts += newRows.values(); | 295 | themesLocationsPaths << QDir::homePath() + QStringLiteral("/.themes"); | ||
299 | endInsertRows(); | | |||
300 | } | | |||
301 | 296 | | |||
302 | int from = -1; | 297 | for (const QString& themesLocationPath : themesLocationsPaths) { | ||
303 | for (const auto &row: oldRows) { | 298 | QStringList possibleThemesDirectoriesNames = QDir(themesLocationPath).entryList(QDir::NoDotAndDotDot | QDir::AllDirs); | ||
304 | for(; from < m_texts.count();) { | 299 | for (const QString &possibleThemeDirectoryName : possibleThemesDirectoriesNames) { | ||
305 | const auto idx = m_texts.indexOf(row, from); | 300 | possibleThemesPaths += themesLocationPath + '/' + possibleThemeDirectoryName; | ||
306 | if (idx < 0) { | | |||
307 | break; | | |||
308 | } | 301 | } | ||
309 | beginRemoveRows({}, idx, idx); | | |||
310 | m_texts.removeAt(idx); | | |||
311 | endRemoveRows(); | | |||
312 | from = idx + 1; | | |||
313 | } | 302 | } | ||
303 | | ||||
304 | return possibleThemesPaths; | ||||
314 | } | 305 | } | ||
306 | | ||||
307 | void GTKConfigKCModule::selectCurrentGtk2ThemeInCheckbox() | ||||
308 | { | ||||
309 | QDBusReply<QString> dbusReply = gtkConfigInterface.call(QStringLiteral("gtk2Theme")); | ||||
310 | currentGtk2Theme = dbusReply.value(); | ||||
311 | ui->cb_theme->setCurrentText(currentGtk2Theme); | ||||
315 | } | 312 | } | ||
316 | 313 | | |||
317 | private: | 314 | void GTKConfigKCModule::selectCurrentGtk3ThemeInCheckbox() | ||
318 | QStringList m_texts; | 315 | { | ||
319 | }; | 316 | QDBusReply<QString> dbusReply = gtkConfigInterface.call(QStringLiteral("gtk3Theme")); | ||
317 | currentGtk3Theme = dbusReply.value(); | ||||
318 | ui->cb_theme_gtk3->setCurrentText(currentGtk3Theme); | ||||
319 | } | ||||
320 | 320 | | |||
321 | void refreshComboSameCurrentValue(QComboBox* combo, const QString& temp, const QStringList& texts) | 321 | void GTKConfigKCModule::updateDeletionPossibilityForSelectedGtk2Theme() | ||
322 | { | 322 | { | ||
323 | const auto model = dynamic_cast<MyStringListModel*>(combo->model()); | 323 | const QModelIndex index = gtk2ThemesModel.index(ui->cb_theme->currentIndex(), 1); | ||
324 | if (!model) { | 324 | const QString themePath = gtk2ThemesModel.data(index).value<QString>(); | ||
325 | combo->setModel(new MyStringListModel(texts, combo)); | 325 | if (themePath.contains(QDir::homePath())) { | ||
326 | ui->removeGtk2Theme->setDisabled(false); | ||||
326 | } else { | 327 | } else { | ||
327 | model->setStrings(texts.toSet()); | 328 | ui->removeGtk2Theme->setDisabled(true); | ||
328 | } | 329 | } | ||
329 | | ||||
330 | const int idx = combo->findText(temp); | | |||
331 | combo->setCurrentIndex(qMax(0, idx)); | | |||
332 | } | 330 | } | ||
333 | 331 | | |||
334 | void GTKConfigKCModule::refreshThemesUi(bool useConfig) | 332 | void GTKConfigKCModule::updateDeletionPossibilityForSelectedGtk3Theme() | ||
335 | { | 333 | { | ||
336 | // Theme gtk2 | 334 | const QModelIndex index = gtk3ThemesModel.index(ui->cb_theme_gtk3->currentIndex(), 1); | ||
337 | bool wasenabled = m_saveEnabled; | 335 | const QString themePath = gtk3ThemesModel.data(index).value<QString>(); | ||
338 | m_saveEnabled = false; | 336 | if (themePath.contains(QDir::homePath())) { | ||
337 | ui->removeGtk3Theme->setDisabled(false); | ||||
338 | } else { | ||||
339 | ui->removeGtk3Theme->setDisabled(true); | ||||
340 | } | ||||
341 | } | ||||
339 | 342 | | |||
340 | refreshComboSameCurrentValue(ui->cb_theme, | | |||
341 | useConfig ? appareance->getTheme() : ui->cb_theme->currentText(), | | |||
342 | appareance->gtk2Appearance()->installedThemesNames()); | | |||
343 | 343 | | |||
344 | // Theme gtk3 | 344 | GtkThemesListModel::GtkThemesListModel(QObject* parent) | ||
345 | refreshComboSameCurrentValue(ui->cb_theme_gtk3, | 345 | : QAbstractTableModel(parent), | ||
346 | useConfig ? appareance->getThemeGtk3() : ui->cb_theme_gtk3->currentText(), | 346 | themesList() | ||
347 | appareance->gtk3Appearance()->installedThemesNames()); | 347 | { | ||
348 | 348 | | |||
349 | m_saveEnabled = wasenabled; | | |||
350 | emit changed(true); | | |||
351 | } | 349 | } | ||
352 | 350 | | |||
353 | void GTKConfigKCModule::showDialogForInstall() | 351 | QVariant GtkThemesListModel::data(const QModelIndex& index, int role) const | ||
354 | { | 352 | { | ||
355 | if(!installer) { | 353 | if (role == Qt::DisplayRole) { | ||
356 | installer = new DialogInstaller(this); | 354 | if (index.row() < 0 || index.row() > themesList.count()) { | ||
357 | connect(installer, &DialogInstaller::themeInstalled, this, >KConfigKCModule::refreshLists); | 355 | return QVariant(); | ||
358 | } | 356 | } | ||
359 | 357 | | |||
360 | installer->exec(); | 358 | if (index.column() == 0) { | ||
361 | refreshThemesUi(); | 359 | return themesList.keys().at(index.row()); | ||
360 | } else { | ||||
361 | return themesList.values().at(index.row()); | ||||
362 | } | ||||
363 | } else { | ||||
364 | return QVariant(); | ||||
365 | } | ||||
362 | } | 366 | } | ||
363 | 367 | | |||
364 | void GTKConfigKCModule::showDialogForUninstall() | 368 | int GtkThemesListModel::rowCount(const QModelIndex& parent) const | ||
365 | { | 369 | { | ||
366 | if(!uninstaller) { | 370 | Q_UNUSED(parent) | ||
367 | uninstaller = new DialogUninstaller(this, appareance); | 371 | return themesList.count(); | ||
368 | connect(uninstaller, &DialogUninstaller::themeUninstalled, this, >KConfigKCModule::refreshLists); | | |||
369 | } | 372 | } | ||
370 | 373 | | |||
371 | uninstaller->refreshListsForUninstall(); | 374 | int GtkThemesListModel::columnCount(const QModelIndex& parent) const | ||
372 | uninstaller->exec(); | 375 | { | ||
373 | 376 | Q_UNUSED(parent) | |||
374 | refreshThemesUi(); | 377 | return 2; | ||
375 | } | 378 | } | ||
376 | 379 | | |||
377 | void GTKConfigKCModule::untogglePreview() | 380 | void GtkThemesListModel::setThemesList(const QMap<QString, QString>& themes) | ||
378 | { | 381 | { | ||
379 | if(sender() == m_p2) { | 382 | beginResetModel(); | ||
380 | ui->gtk2Preview->setChecked(false); | 383 | themesList = themes; | ||
381 | } else { | 384 | endResetModel(); | ||
382 | ui->gtk3Preview->setChecked(false); | | |||
383 | } | 385 | } | ||
386 | | ||||
387 | bool GtkThemesListModel::containsTheme(const QString &themeName) | ||||
388 | { | ||||
389 | return themesList.contains(themeName); | ||||
384 | } | 390 | } | ||
385 | 391 | | |||
386 | #include "gtkconfigkcmodule.moc" | 392 | #include "gtkconfigkcmodule.moc" |