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