1 /*
2 This file is part of Lokalize
3
4 SPDX-FileCopyrightText: 2007-2011 Nick Shaforostoff <shafff@ukr.net>
5 SPDX-FileCopyrightText: 2018-2019 Simon Depiets <sdepiets@gmail.com>
6
7 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
8 */
9
10 #include "prefs.h"
11
12 #include "lokalize_debug.h"
13
14 #include "prefs_lokalize.h"
15 #include "project.h"
16 #include "projectlocal.h"
17 #include "projectmodel.h"
18 #include "languagelistmodel.h"
19 #include "dbfilesmodel.h"
20
21 #include "ui_prefs_identity.h"
22 #include "ui_prefs_editor.h"
23 #include "ui_prefs_general.h"
24 #include "ui_prefs_appearance.h"
25 #include "ui_prefs_pology.h"
26 #include "ui_prefs_languagetool.h"
27 #include "ui_prefs_tm.h"
28 #include "ui_prefs_projectmain.h"
29 #include "ui_prefs_project_advanced.h"
30 #include "ui_prefs_project_local.h"
31
32
33 #include <KLocalizedString>
34 #include <KMessageBox>
35 #include <KEditListWidget>
36 #include <KConfigDialog>
37
38 #include <QIcon>
39 #include <QBoxLayout>
40 #include <QFileDialog>
41 #include <QDragEnterEvent>
42 #include <QDropEvent>
43 #include <QTimer>
44 #include <QMimeData>
45
46 //#include <sonnet/configwidget.h>
47
48 SettingsController* SettingsController::_instance = nullptr;
cleanupSettingsController()49 void SettingsController::cleanupSettingsController()
50 {
51 delete SettingsController::_instance;
52 SettingsController::_instance = nullptr;
53 }
54
instance()55 SettingsController* SettingsController::instance()
56 {
57 if (_instance == nullptr) {
58 _instance = new SettingsController;
59 qAddPostRoutine(SettingsController::cleanupSettingsController);
60 }
61
62 return _instance;
63 }
64
SettingsController()65 SettingsController::SettingsController()
66 : QObject(Project::instance())
67 , dirty(false)
68 , m_mainWindowPtr(nullptr)
69 {}
70
~SettingsController()71 SettingsController::~SettingsController()
72 {}
73
74
showSettingsDialog()75 void SettingsController::showSettingsDialog()
76 {
77 if (KConfigDialog::showDialog("lokalize_settings"))
78 return;
79
80 KConfigDialog *dialog = new KConfigDialog(m_mainWindowPtr, "lokalize_settings", Settings::self());
81 dialog->setFaceType(KPageDialog::List);
82
83 // Identity
84 QWidget *w = new QWidget(dialog);
85 Ui_prefs_identity ui_prefs_identity;
86 ui_prefs_identity.setupUi(w);
87
88
89 KConfigGroup grp = Settings::self()->config()->group("Identity");
90
91 ui_prefs_identity.DefaultLangCode->setModel(LanguageListModel::instance()->sortModel());
92 ui_prefs_identity.DefaultLangCode->setCurrentIndex(LanguageListModel::instance()->sortModelRowForLangCode(grp.readEntry("DefaultLangCode",
93 QLocale::system().name())));
94
95 connect(ui_prefs_identity.DefaultLangCode, QOverload<int>::of(&KComboBox::activated), ui_prefs_identity.kcfg_DefaultLangCode, &LangCodeSaver::setLangCode);
96 ui_prefs_identity.kcfg_DefaultLangCode->hide();
97
98 connect(ui_prefs_identity.kcfg_overrideLangTeam, &QCheckBox::toggled, ui_prefs_identity.kcfg_userLangTeam, &QLineEdit::setEnabled);
99 connect(ui_prefs_identity.kcfg_overrideLangTeam, &QCheckBox::toggled, ui_prefs_identity.kcfg_userLangTeam, &QLineEdit::focusWidget);
100
101 dialog->addPage(w, i18nc("@title:tab", "Identity"), "preferences-desktop-user");
102
103 //General
104 w = new QWidget(dialog);
105 Ui_prefs_general ui_prefs_general;
106 ui_prefs_general.setupUi(w);
107 connect(ui_prefs_general.kcfg_CustomEditorEnabled, &QCheckBox::toggled, ui_prefs_general.kcfg_CustomEditorCommand, &QLineEdit::setEnabled);
108 ui_prefs_general.kcfg_CustomEditorCommand->setEnabled(Settings::self()->customEditorEnabled());
109 //Set here to avoid I18N_ARGUMENT_MISSING if set in ui file
110 ui_prefs_general.kcfg_CustomEditorCommand->setToolTip(i18n(
111 "The following parameters are available\n%1 - Path of the source file\n%2 - Line number"
112 , QStringLiteral("%1"), QStringLiteral("%2")));
113 dialog->addPage(w, i18nc("@title:tab", "General"), "preferences-system-windows");
114
115 //Editor
116 w = new QWidget(dialog);
117 Ui_prefs_editor ui_prefs_editor;
118 ui_prefs_editor.setupUi(w);
119 dialog->addPage(w, i18nc("@title:tab", "Editing"), "accessories-text-editor");
120
121 //Font
122 w = new QWidget(dialog);
123 Ui_prefs_appearance ui_prefs_appearance;
124 ui_prefs_appearance.setupUi(w);
125 dialog->addPage(w, i18nc("@title:tab", "Appearance"), "preferences-desktop-font");
126
127 //TM
128 w = new QWidget(dialog);
129 Ui_prefs_tm ui_prefs_tm;
130 ui_prefs_tm.setupUi(w);
131 dialog->addPage(w, i18nc("@title:tab", "Translation Memory"), "configure");
132
133 //Pology
134 w = new QWidget(dialog);
135 Ui_prefs_pology ui_prefs_pology;
136 ui_prefs_pology.setupUi(w);
137 dialog->addPage(w, i18nc("@title:tab", "Pology"), "preferences-desktop-filetype-association");
138
139 //LanguageTool
140 w = new QWidget(dialog);
141 Ui_prefs_languagetool ui_prefs_languagetool;
142 ui_prefs_languagetool.setupUi(w);
143 dialog->addPage(w, i18nc("@title:tab", "LanguageTool"), "lokalize");
144
145 connect(dialog, &KConfigDialog::settingsChanged, this, &SettingsController::generalSettingsChanged);
146
147
148 //Spellcheck
149 #if 0
150 w = new Sonnet::ConfigWidget(Settings::self()->config(), dialog);
151 w->setParent(this);
152 dialog->addPage(w, i18nc("@title:tab", "Spellcheck"), "spellcheck_setting");
153 connect(dialog, SIGNAL(okClicked()), w, SLOT(save()));
154 connect(dialog, SIGNAL(applyClicked()), w, SLOT(save()));
155 connect(dialog, SIGNAL(defaultClicked()), w, SLOT(slotDefault()));
156 #endif
157
158
159
160
161 //connect(dialog,SIGNAL(settingsChanged(const QString&)),m_view, SLOT(settingsChanged()));
162
163 dialog->show();
164 // dialog->addPage(new General(0, "General"), i18n("General") );
165 // dialog->addPage(new Appearance(0, "Style"), i18n("Appearance") );
166 // connect(dialog, SIGNAL(settingsChanged(const QString&)), mainWidget, SLOT(loadSettings()));
167 // connect(dialog, SIGNAL(settingsChanged(const QString&)), this, SLOT(loadSettings()));
168
169 }
170
ensureProjectIsLoaded()171 bool SettingsController::ensureProjectIsLoaded()
172 {
173 if (Project::instance()->isLoaded())
174 return true;
175
176 int answer = KMessageBox::questionYesNoCancel(m_mainWindowPtr, i18n("You have accessed a feature that requires a project to be loaded. Do you want to create a new project or open an existing project?"),
177 QString(), KGuiItem(i18nc("@action", "New"), QIcon::fromTheme("document-new")), KGuiItem(i18nc("@action", "Open"), QIcon::fromTheme("project-open"))
178 );
179 if (answer == KMessageBox::Yes)
180 return projectCreate();
181 if (answer == KMessageBox::No)
182 return !projectOpen().isEmpty();
183 return false;
184 }
185
projectOpen(QString path,bool doOpen)186 QString SettingsController::projectOpen(QString path, bool doOpen)
187 {
188 if (path.isEmpty()) {
189 //Project::instance()->model()->weaver()->suspend();
190 //KDE5PORT mutex if needed
191 path = QFileDialog::getOpenFileName(m_mainWindowPtr, QString(), QDir::homePath()/*_catalog->url().directory()*/,
192 i18n("Lokalize translation project (*.lokalize)")/*"text/x-lokalize-project"*/);
193 //Project::instance()->model()->weaver()->resume();
194 }
195
196 if (!path.isEmpty() && doOpen)
197 Project::instance()->load(path);
198
199 return path;
200 }
201
projectCreate()202 bool SettingsController::projectCreate()
203 {
204 //Project::instance()->model()->weaver()->suspend();
205 //KDE5PORT mutex if needed
206 QString desirablePath = Project::instance()->desirablePath();
207 if (desirablePath.isEmpty())
208 desirablePath = QDir::homePath() + "/index.lokalize";
209 QString path = QFileDialog::getSaveFileName(m_mainWindowPtr, i18nc("@window:title", "Select folder with Gettext .po files to translate"), desirablePath, i18n("Lokalize translation project (*.lokalize)") /*"text/x-lokalize-project"*/);
210 //Project::instance()->model()->weaver()->resume();
211 if (path.isEmpty())
212 return false;
213
214 //TODO ask-n-save
215 QDir projectFolder = QFileInfo(path).absoluteDir();
216 QString projectId = projectFolder.dirName();
217 if (projectFolder.cdUp()) projectId = projectFolder.dirName() + '-' + projectId;;
218 Project::instance()->load(path, QString(), projectId);
219 //Project::instance()->setDefaults(); //NOTE will this be an obstacle?
220 //Project::instance()->setProjectID();
221
222 QTimer::singleShot(500, this, &SettingsController::projectConfigure);
223 return true;
224 }
225
projectConfigure()226 void SettingsController::projectConfigure()
227 {
228 if (Project::instance()->path().isEmpty()) {
229 KMessageBox::error(mainWindowPtr(),
230 i18n("Create software or OpenDocument translation project first."));
231 return;
232 }
233
234 KConfigDialog *dialog = new KConfigDialog(m_mainWindowPtr, "project_settings", Project::instance());
235 dialog->setFaceType(KPageDialog::List);
236
237
238 // Main
239 QWidget *w = new QWidget(dialog);
240 Ui_prefs_projectmain ui_prefs_projectmain;
241 ui_prefs_projectmain.setupUi(w);
242 dialog->addPage(w, i18nc("@title:tab", "General"), "preferences-desktop-locale");
243
244 ui_prefs_projectmain.kcfg_LangCode->hide();
245 ui_prefs_projectmain.kcfg_PoBaseDir->hide();
246 ui_prefs_projectmain.kcfg_GlossaryTbx->hide();
247
248 Project& p = *(Project::instance());
249 ui_prefs_projectmain.LangCode->setModel(LanguageListModel::instance()->sortModel());
250 ui_prefs_projectmain.LangCode->setCurrentIndex(LanguageListModel::instance()->sortModelRowForLangCode(p.langCode()));
251 connect(ui_prefs_projectmain.LangCode, QOverload<int>::of(&KComboBox::activated), ui_prefs_projectmain.kcfg_LangCode, &LangCodeSaver::setLangCode);
252
253 ui_prefs_projectmain.poBaseDir->setMode(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly);
254 ui_prefs_projectmain.glossaryTbx->setMode(KFile::File | KFile::LocalOnly);
255 ui_prefs_projectmain.glossaryTbx->setFilter("*.tbx\n*.xml");
256 connect(ui_prefs_projectmain.poBaseDir, &KUrlRequester::textChanged, ui_prefs_projectmain.kcfg_PoBaseDir, &RelPathSaver::setText);
257 connect(ui_prefs_projectmain.glossaryTbx, &KUrlRequester::textChanged, ui_prefs_projectmain.kcfg_GlossaryTbx, &RelPathSaver::setText);
258 ui_prefs_projectmain.poBaseDir->setUrl(QUrl::fromLocalFile(p.poDir()));
259 ui_prefs_projectmain.glossaryTbx->setUrl(QUrl::fromLocalFile(p.glossaryPath()));
260
261 auto kcfg_ProjLangTeam = ui_prefs_projectmain.kcfg_ProjLangTeam;
262 connect(ui_prefs_projectmain.kcfg_LanguageSource, static_cast<void(KComboBox::*)(int)>(&KComboBox::currentIndexChanged),
263 this, [kcfg_ProjLangTeam](int index) {
264 kcfg_ProjLangTeam->setEnabled(static_cast<Project::LangSource>(index) == Project::LangSource::Project);
265 });
266 connect(ui_prefs_projectmain.kcfg_LanguageSource, static_cast<void(KComboBox::*)(const QString &)>(&KComboBox::currentIndexChanged),
267 this, [kcfg_ProjLangTeam] { kcfg_ProjLangTeam->setFocus(); });
268
269
270
271
272 // RegExps
273 w = new QWidget(dialog);
274 Ui_project_advanced ui_project_advanced;
275 ui_project_advanced.setupUi(w);
276 ui_project_advanced.kcfg_PotBaseDir->hide();
277 ui_project_advanced.kcfg_BranchDir->hide();
278 ui_project_advanced.kcfg_AltDir->hide();
279 ui_project_advanced.potBaseDir->setMode(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly);
280 ui_project_advanced.branchDir->setMode(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly);
281 ui_project_advanced.altDir->setMode(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly);
282 connect(ui_project_advanced.potBaseDir, &KUrlRequester::textChanged, ui_project_advanced.kcfg_PotBaseDir, &RelPathSaver::setText);
283 connect(ui_project_advanced.branchDir, &KUrlRequester::textChanged, ui_project_advanced.kcfg_BranchDir, &RelPathSaver::setText);
284 connect(ui_project_advanced.altDir, &KUrlRequester::textChanged, ui_project_advanced.kcfg_AltDir, &RelPathSaver::setText);
285 ui_project_advanced.potBaseDir->setUrl(QUrl::fromLocalFile(p.potDir()));
286 ui_project_advanced.branchDir->setUrl(QUrl::fromLocalFile(p.branchDir()));
287 ui_project_advanced.altDir->setUrl(QUrl::fromLocalFile(p.altTransDir()));
288 dialog->addPage(w, i18nc("@title:tab", "Advanced"), "applications-development-translation");
289
290 //Scripts
291 w = new QWidget(dialog);
292 QVBoxLayout* layout = new QVBoxLayout(w);
293 layout->setSpacing(6);
294 layout->setContentsMargins(11, 11, 11, 11);
295
296 w = new QWidget(dialog);
297 Ui_prefs_project_local ui_prefs_project_local;
298 ui_prefs_project_local.setupUi(w);
299 dialog->addPage(w, Project::local(), i18nc("@title:tab", "Personal"), "preferences-desktop-user");
300
301 connect(dialog, &KConfigDialog::settingsChanged, Project::instance(), &Project::reinit);
302 connect(dialog, &KConfigDialog::settingsChanged, Project::instance(), &Project::save, Qt::QueuedConnection);
303 connect(dialog, &KConfigDialog::settingsChanged, TM::DBFilesModel::instance(), &TM::DBFilesModel::updateProjectTmIndex);
304 connect(dialog, &KConfigDialog::settingsChanged, this, &SettingsController::reflectProjectConfigChange);
305
306 dialog->show();
307 }
308
reflectProjectConfigChange()309 void SettingsController::reflectProjectConfigChange()
310 {
311 //TODO check target language change: reflect changes in TM and glossary
312 TM::DBFilesModel::instance()->openDB(Project::instance()->projectID());
313 }
314
reflectRelativePathsHack()315 void SettingsController::reflectRelativePathsHack()
316 {
317 //m_scriptsRelPrefWidget->clear();
318 QStringList actionz(m_scriptsPrefWidget->items());
319 QString projectDir(Project::instance()->projectDir());
320 int i = actionz.size();
321 while (--i >= 0)
322 actionz[i] = QDir(projectDir).relativeFilePath(actionz.at(i));
323 m_scriptsRelPrefWidget->setItems(actionz);
324 }
325
setLangCode(int index)326 void LangCodeSaver::setLangCode(int index)
327 {
328 setText(LanguageListModel::instance()->langCodeForSortModelRow(index));
329 }
330
setText(const QString & txt)331 void RelPathSaver::setText(const QString& txt)
332 {
333 QLineEdit::setText(QDir(Project::instance()->projectDir()).relativeFilePath(txt));
334 }
335
336
writeUiState(const char * elementName,const QByteArray & state)337 void writeUiState(const char* elementName, const QByteArray& state)
338 {
339 KConfig config;
340 KConfigGroup cg(&config, "MainWindow");
341 cg.writeEntry(elementName, state.toBase64());
342 }
343
readUiState(const char * elementName)344 QByteArray readUiState(const char* elementName)
345 {
346 KConfig config;
347 KConfigGroup cg(&config, "MainWindow");
348 return QByteArray::fromBase64(cg.readEntry(elementName, QByteArray()));
349 }
350
351
352
353