Cppcheck
settingsdialog.cpp
Go to the documentation of this file.
1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2024 Cppcheck team.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "settingsdialog.h"
20 
21 #include "application.h"
22 #include "applicationdialog.h"
23 #include "applicationlist.h"
24 #include "codeeditorstyle.h"
25 #include "codeeditstyledialog.h"
26 #include "common.h"
27 #include "translationhandler.h"
28 
29 #include "ui_settings.h"
30 
31 #include <QCheckBox>
32 #include <QDialogButtonBox>
33 #include <QDir>
34 #include <QFileDialog>
35 #include <QFileInfo>
36 #include <QGroupBox>
37 #include <QLabel>
38 #include <QLineEdit>
39 #include <QList>
40 #include <QListWidget>
41 #include <QListWidgetItem>
42 #include <QPushButton>
43 #include <QRadioButton>
44 #include <QSettings>
45 #include <QSize>
46 #include <QThread>
47 #include <QVariant>
48 #include <QWidget>
49 
51  TranslationHandler *translator,
52  bool premium,
53  QWidget *parent) :
54  QDialog(parent),
55  mApplications(list),
56  mTempApplications(new ApplicationList(this)),
57  mTranslator(translator),
58  mUI(new Ui::Settings),
59  mPremium(premium)
60 {
61  mUI->setupUi(this);
62  mUI->mPythonPathWarning->setStyleSheet("color: red");
63  QSettings settings;
64  mTempApplications->copy(list);
65 
66  mUI->mJobs->setText(settings.value(SETTINGS_CHECK_THREADS, 1).toString());
67  mUI->mForce->setCheckState(boolToCheckState(settings.value(SETTINGS_CHECK_FORCE, false).toBool()));
68  mUI->mShowFullPath->setCheckState(boolToCheckState(settings.value(SETTINGS_SHOW_FULL_PATH, false).toBool()));
69  mUI->mShowNoErrorsMessage->setCheckState(boolToCheckState(settings.value(SETTINGS_SHOW_NO_ERRORS, false).toBool()));
70  mUI->mShowDebugWarnings->setCheckState(boolToCheckState(settings.value(SETTINGS_SHOW_DEBUG_WARNINGS, false).toBool()));
71  mUI->mSaveAllErrors->setCheckState(boolToCheckState(settings.value(SETTINGS_SAVE_ALL_ERRORS, false).toBool()));
72  mUI->mSaveFullPath->setCheckState(boolToCheckState(settings.value(SETTINGS_SAVE_FULL_PATH, false).toBool()));
73  mUI->mInlineSuppressions->setCheckState(boolToCheckState(settings.value(SETTINGS_INLINE_SUPPRESSIONS, false).toBool()));
74  mUI->mEnableInconclusive->setCheckState(boolToCheckState(settings.value(SETTINGS_INCONCLUSIVE_ERRORS, false).toBool()));
75  mUI->mShowStatistics->setCheckState(boolToCheckState(settings.value(SETTINGS_SHOW_STATISTICS, false).toBool()));
76  mUI->mShowErrorId->setCheckState(boolToCheckState(settings.value(SETTINGS_SHOW_ERROR_ID, true).toBool()));
77  mUI->mCheckForUpdates->setCheckState(boolToCheckState(settings.value(SETTINGS_CHECK_FOR_UPDATES, false).toBool()));
78  mUI->mEditPythonPath->setText(settings.value(SETTINGS_PYTHON_PATH, QString()).toString());
80  if (premium)
81  mUI->mGroupBoxMisra->setVisible(false);
82  mUI->mEditMisraFile->setText(settings.value(SETTINGS_MISRA_FILE, QString()).toString());
83 
84 #ifdef Q_OS_WIN
85  //mUI->mTabClang->setVisible(true);
86  mUI->mEditClangPath->setText(settings.value(SETTINGS_CLANG_PATH, QString()).toString());
87  mUI->mEditVsIncludePaths->setText(settings.value(SETTINGS_VS_INCLUDE_PATHS, QString()).toString());
88  connect(mUI->mBtnBrowseClangPath, &QPushButton::released, this, &SettingsDialog::browseClangPath);
89 #else
90  mUI->mTabClang->setVisible(false);
91 #endif
94 
95  connect(mUI->mEditPythonPath, SIGNAL(textEdited(QString)),
96  this, SLOT(validateEditPythonPath()));
97 
98  connect(mUI->mButtons, &QDialogButtonBox::accepted, this, &SettingsDialog::ok);
99  connect(mUI->mButtons, &QDialogButtonBox::rejected, this, &SettingsDialog::reject);
100  connect(mUI->mBtnAddApplication, SIGNAL(clicked()),
101  this, SLOT(addApplication()));
102  connect(mUI->mBtnRemoveApplication, SIGNAL(clicked()),
103  this, SLOT(removeApplication()));
104  connect(mUI->mBtnEditApplication, SIGNAL(clicked()),
105  this, SLOT(editApplication()));
106  connect(mUI->mBtnDefaultApplication, SIGNAL(clicked()),
107  this, SLOT(defaultApplication()));
108  connect(mUI->mListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
109  this, SLOT(editApplication()));
110 
111  connect(mUI->mBtnBrowsePythonPath, &QPushButton::clicked, this, &SettingsDialog::browsePythonPath);
112  connect(mUI->mBtnBrowseMisraFile, &QPushButton::clicked, this, &SettingsDialog::browseMisraFile);
113  connect(mUI->mBtnEditTheme, SIGNAL(clicked()), this, SLOT(editCodeEditorStyle()));
114  connect(mUI->mThemeSystem, SIGNAL(released()), this, SLOT(setCodeEditorStyleDefault()));
115  connect(mUI->mThemeDark, SIGNAL(released()), this, SLOT(setCodeEditorStyleDefault()));
116  connect(mUI->mThemeLight, SIGNAL(released()), this, SLOT(setCodeEditorStyleDefault()));
117  connect(mUI->mThemeCustom, SIGNAL(toggled(bool)), mUI->mBtnEditTheme, SLOT(setEnabled(bool)));
118 
119  mUI->mListWidget->setSortingEnabled(false);
121 
122  const int count = QThread::idealThreadCount();
123  if (count != -1)
124  mUI->mLblIdealThreads->setText(QString::number(count));
125  else
126  mUI->mLblIdealThreads->setText(tr("N/A"));
127 
128  loadSettings();
130 }
131 
133 {
134  saveSettings();
135  delete mCurrentStyle;
136  delete mUI;
137 }
138 
140 {
141  const QString current = mTranslator->getCurrentLanguage();
142  for (const TranslationInfo& translation : mTranslator->getTranslations()) {
143  auto *item = new QListWidgetItem;
144  item->setText(translation.mName);
145  item->setData(mLangCodeRole, QVariant(translation.mCode));
146  mUI->mListLanguages->addItem(item);
147  if (translation.mCode == current || translation.mCode == current.mid(0, 2))
148  mUI->mListLanguages->setCurrentItem(item);
149  }
150 }
151 
152 Qt::CheckState SettingsDialog::boolToCheckState(bool yes)
153 {
154  if (yes) {
155  return Qt::Checked;
156  }
157  return Qt::Unchecked;
158 }
159 
160 bool SettingsDialog::checkStateToBool(Qt::CheckState state)
161 {
162  return state == Qt::Checked;
163 }
164 
165 
167 {
168  QSettings settings;
169  resize(settings.value(SETTINGS_CHECK_DIALOG_WIDTH, 800).toInt(),
170  settings.value(SETTINGS_CHECK_DIALOG_HEIGHT, 600).toInt());
171 }
172 
174 {
175  QSettings settings;
176  settings.setValue(SETTINGS_CHECK_DIALOG_WIDTH, size().width());
177  settings.setValue(SETTINGS_CHECK_DIALOG_HEIGHT, size().height());
178 }
179 
181 {
182  int jobs = mUI->mJobs->text().toInt();
183  if (jobs <= 0) {
184  jobs = 1;
185  }
186 
187  QSettings settings;
188  settings.setValue(SETTINGS_CHECK_THREADS, jobs);
189  saveCheckboxValue(&settings, mUI->mForce, SETTINGS_CHECK_FORCE);
190  saveCheckboxValue(&settings, mUI->mSaveAllErrors, SETTINGS_SAVE_ALL_ERRORS);
191  saveCheckboxValue(&settings, mUI->mSaveFullPath, SETTINGS_SAVE_FULL_PATH);
192  saveCheckboxValue(&settings, mUI->mShowFullPath, SETTINGS_SHOW_FULL_PATH);
193  saveCheckboxValue(&settings, mUI->mShowNoErrorsMessage, SETTINGS_SHOW_NO_ERRORS);
194  saveCheckboxValue(&settings, mUI->mShowDebugWarnings, SETTINGS_SHOW_DEBUG_WARNINGS);
195  saveCheckboxValue(&settings, mUI->mInlineSuppressions, SETTINGS_INLINE_SUPPRESSIONS);
196  saveCheckboxValue(&settings, mUI->mEnableInconclusive, SETTINGS_INCONCLUSIVE_ERRORS);
197  saveCheckboxValue(&settings, mUI->mShowStatistics, SETTINGS_SHOW_STATISTICS);
198  saveCheckboxValue(&settings, mUI->mShowErrorId, SETTINGS_SHOW_ERROR_ID);
199  saveCheckboxValue(&settings, mUI->mCheckForUpdates, SETTINGS_CHECK_FOR_UPDATES);
200  settings.setValue(SETTINGS_PYTHON_PATH, mUI->mEditPythonPath->text());
201  if (!mPremium)
202  settings.setValue(SETTINGS_MISRA_FILE, mUI->mEditMisraFile->text());
203 
204 #ifdef Q_OS_WIN
205  settings.setValue(SETTINGS_CLANG_PATH, mUI->mEditClangPath->text());
206  settings.setValue(SETTINGS_VS_INCLUDE_PATHS, mUI->mEditVsIncludePaths->text());
207 #endif
208 
209  const QListWidgetItem *currentLang = mUI->mListLanguages->currentItem();
210  if (currentLang) {
211  const QString langcode = currentLang->data(mLangCodeRole).toString();
212  settings.setValue(SETTINGS_LANGUAGE, langcode);
213  }
215 }
216 
217 void SettingsDialog::saveCheckboxValue(QSettings *settings, const QCheckBox *box,
218  const QString &name)
219 {
220  settings->setValue(name, checkStateToBool(box->checkState()));
221 }
222 
224 {
225  const auto pythonPath = mUI->mEditPythonPath->text();
226  if (pythonPath.isEmpty()) {
227  mUI->mEditPythonPath->setStyleSheet("");
228  mUI->mPythonPathWarning->hide();
229  return;
230  }
231 
232  QFileInfo pythonPathInfo(pythonPath);
233  if (!pythonPathInfo.exists() ||
234  !pythonPathInfo.isFile() ||
235  !pythonPathInfo.isExecutable()) {
236  mUI->mEditPythonPath->setStyleSheet("QLineEdit {border: 1px solid red}");
237  mUI->mPythonPathWarning->setText(tr("The executable file \"%1\" is not available").arg(pythonPath));
238  mUI->mPythonPathWarning->show();
239  } else {
240  mUI->mEditPythonPath->setStyleSheet("");
241  mUI->mPythonPathWarning->hide();
242  }
243 }
244 
246 {
247  Application app;
248  ApplicationDialog dialog(tr("Add a new application"), app, this);
249 
250  if (dialog.exec() == QDialog::Accepted) {
252  mUI->mListWidget->addItem(app.getName());
253  }
254 }
255 
257 {
258  for (QListWidgetItem *item : mUI->mListWidget->selectedItems()) {
259  const int removeIndex = mUI->mListWidget->row(item);
260  const int currentDefault = mTempApplications->getDefaultApplication();
261  mTempApplications->removeApplication(removeIndex);
262  if (removeIndex == currentDefault)
263  // If default app is removed set default to unknown
265  else if (removeIndex < currentDefault)
266  // Move default app one up if earlier app was removed
267  mTempApplications->setDefault(currentDefault - 1);
268  }
269  mUI->mListWidget->clear();
271 }
272 
274 {
275  for (QListWidgetItem *item : mUI->mListWidget->selectedItems()) {
276  const int row = mUI->mListWidget->row(item);
278  ApplicationDialog dialog(tr("Modify an application"), app, this);
279 
280  if (dialog.exec() == QDialog::Accepted) {
281  QString name = app.getName();
283  name += tr(" [Default]");
284  item->setText(name);
285  }
286  }
287 }
288 
290 {
291  QList<QListWidgetItem *> selected = mUI->mListWidget->selectedItems();
292  if (!selected.isEmpty()) {
293  const int index = mUI->mListWidget->row(selected[0]);
295  mUI->mListWidget->clear();
297  }
298 }
299 
301 {
302  const int defapp = mTempApplications->getDefaultApplication();
303  for (int i = 0; i < mTempApplications->getApplicationCount(); i++) {
305  QString name = app.getName();
306  if (i == defapp) {
307  name += " ";
308  name += tr("[Default]");
309  }
310  mUI->mListWidget->addItem(name);
311  }
312 
313  // Select default application, or if there is no default app then the
314  // first item.
315  if (defapp == -1)
316  mUI->mListWidget->setCurrentRow(0);
317  else {
318  if (mTempApplications->getApplicationCount() > defapp)
319  mUI->mListWidget->setCurrentRow(defapp);
320  else
321  mUI->mListWidget->setCurrentRow(0);
322  }
323 }
324 
326 {
328  accept();
329 }
330 
332 {
333  return checkStateToBool(mUI->mShowFullPath->checkState());
334 }
335 
337 {
338  return checkStateToBool(mUI->mSaveFullPath->checkState());
339 }
340 
342 {
343  return checkStateToBool(mUI->mSaveAllErrors->checkState());
344 }
345 
347 {
348  return checkStateToBool(mUI->mShowNoErrorsMessage->checkState());
349 }
350 
352 {
353  return checkStateToBool(mUI->mShowErrorId->checkState());
354 }
355 
357 {
358  return checkStateToBool(mUI->mEnableInconclusive->checkState());
359 }
360 
362 {
363  QString fileName = QFileDialog::getOpenFileName(this, tr("Select python binary"), QDir::rootPath());
364  if (fileName.contains("python", Qt::CaseInsensitive))
365  mUI->mEditPythonPath->setText(fileName);
366 }
367 
369 {
370  const QString fileName = QFileDialog::getOpenFileName(this, tr("Select MISRA File"), QDir::homePath(), "Misra File (*.pdf *.txt)");
371  if (!fileName.isEmpty())
372  mUI->mEditMisraFile->setText(fileName);
373 }
374 
375 // Slot to set default light style
377 {
378  if (mUI->mThemeSystem->isChecked())
380  if (mUI->mThemeLight->isChecked())
382  if (mUI->mThemeDark->isChecked())
385 }
386 
387 // Slot to edit custom style
389 {
390  StyleEditDialog dlg(*mCurrentStyle, this);
391  const int nResult = dlg.exec();
392  if (nResult == QDialog::Accepted) {
393  *mCurrentStyle = dlg.getStyle();
395  }
396 }
397 
399 {
400  QString selectedDir = QFileDialog::getExistingDirectory(this,
401  tr("Select clang path"),
402  QDir::rootPath());
403 
404  if (!selectedDir.isEmpty()) {
405  mUI->mEditClangPath->setText(selectedDir);
406  }
407 }
408 
410 {
411  const bool isSystemTheme = mCurrentStyle->isSystemTheme();
412  const bool isDefaultLight = !isSystemTheme && *mCurrentStyle == defaultStyleLight;
413  const bool isDefaultDark = !isSystemTheme && *mCurrentStyle == defaultStyleDark;
414  mUI->mThemeSystem->setChecked(isSystemTheme);
415  mUI->mThemeLight->setChecked(isDefaultLight && !isDefaultDark);
416  mUI->mThemeDark->setChecked(!isDefaultLight && isDefaultDark);
417  mUI->mThemeCustom->setChecked(!isSystemTheme && !isDefaultLight && !isDefaultDark);
418  mUI->mBtnEditTheme->setEnabled(!isSystemTheme && !isDefaultLight && !isDefaultDark);
419 }
420 
Dialog to edit a startable application.
List of applications user has specified to open errors with.
void copy(const ApplicationList *list)
Remove all applications from this list and copy all applications from list given as a parameter.
void setDefault(const int index)
Set application as default application.
int getApplicationCount() const
Get the amount of applications in the list.
const Application & getApplication(const int index) const
Get specific application's name.
void removeApplication(const int index)
Remove an application from the list.
void addApplication(const Application &app)
Add a new application.
int getDefaultApplication() const
Return the default application.
A class containing information of the application to execute.
Definition: application.h:43
const QString & getName() const
Get application name.
Definition: application.h:52
static void saveSettings(QSettings *settings, const CodeEditorStyle &theStyle)
static CodeEditorStyle getSystemTheme()
static CodeEditorStyle loadSettings(QSettings *settings)
bool isSystemTheme() const
bool showNoErrorsMessage() const
Get checkbox value for mNoErrorsMessage.
void browsePythonPath()
Slot for browsing for the python binary.
void ok()
Slot for clicking OK.
bool saveAllErrors() const
Get checkbox value for mSaveAllErrors.
void browseClangPath()
Slot for browsing for the clang binary.
void saveSettingValues() const
Save all values to QSettings.
static void saveCheckboxValue(QSettings *settings, const QCheckBox *box, const QString &name)
Save a single checkboxes value.
void populateApplicationList()
Clear all applications from the list and re insert them from mTempApplications.
bool saveFullPath() const
Get checkbox value for mSaveFullPath.
CodeEditorStyle * mCurrentStyle
Current Code Editor Style.
void editApplication()
Slot for modifying an application in the list.
void addApplication()
Slot for adding a new application to the list.
void removeApplication()
Slot for deleting an application from the list.
void browseMisraFile()
Browse for MISRA file.
void editCodeEditorStyle()
Edit Custom Code Editor Style.
ApplicationList * mTempApplications
Temporary list of applications This will be copied to actual list of applications (mApplications) whe...
TranslationHandler * mTranslator
List of translations.
void validateEditPythonPath()
Slot for validating input value in editPythonPath.
~SettingsDialog() override
void loadSettings()
Load saved values Loads dialog size and column widths.
void initTranslationsList()
Populate the translations list.
static constexpr int mLangCodeRole
void manageStyleControls()
static bool checkStateToBool(Qt::CheckState state)
Converts Qt::CheckState to bool.
void saveSettings() const
Save settings Save dialog size and column widths.
SettingsDialog(ApplicationList *list, TranslationHandler *translator, bool premium, QWidget *parent=nullptr)
bool showInconclusive() const
Get checkbox value for mEnableInconclusive.
Ui::Settings * mUI
Dialog from UI designer.
static Qt::CheckState boolToCheckState(bool yes)
Convert bool to Qt::CheckState.
ApplicationList * mApplications
List of applications user has specified.
void defaultApplication()
Slot for making the selected application as the default (first)
void setCodeEditorStyleDefault()
Set Code Editor Style to Default.
bool showFullPath() const
Get checkbox value for mShowFullPath.
bool showErrorId() const
Get checkbox value for mShowIdColumn.
This is just a container for general settings so that we don't need to pass individual values to func...
Definition: settings.h:95
CodeEditorStyle getStyle()
A class handling the available translations.
const QList< TranslationInfo > & getTranslations() const
Get a list of available translations.
const QString & getCurrentLanguage() const
Get currently selected translation.
static const CodeEditorStyle defaultStyleLight(Qt::black, QColor(240, 240, 240), QColor(255, 220, 220), Qt::black, QColor(240, 240, 240), Qt::darkBlue, QFont::Bold, Qt::darkMagenta, QFont::Bold, Qt::darkGreen, QFont::Normal, Qt::gray, QFont::Normal, Qt::red, QColor(220, 220, 255), QFont::Normal)
static const CodeEditorStyle defaultStyleDark(QColor(218, 218, 218), QColor(16, 16, 32), QColor(64, 64, 64), QColor(43, 145, 175), QColor(16, 16, 32), QColor(0, 204, 204), QFont::Bold, QColor(218, 0, 218), QFont::Bold, QColor(0, 204, 0), QFont::Normal, QColor(180, 180, 180), QFont::Normal, QColor(218, 32, 32), QColor(32, 32, 108), QFont::Normal)
std::string toString(Color c)
Definition: color.cpp:54
#define SETTINGS_PYTHON_PATH
Definition: common.h:80
#define SETTINGS_CHECK_DIALOG_HEIGHT
Definition: common.h:41
#define SETTINGS_INLINE_SUPPRESSIONS
Definition: common.h:84
#define SETTINGS_CLANG_PATH
Definition: common.h:82
#define SETTINGS_SHOW_NO_ERRORS
Definition: common.h:70
#define SETTINGS_SHOW_FULL_PATH
Definition: common.h:69
#define SETTINGS_CHECK_DIALOG_WIDTH
Definition: common.h:40
#define SETTINGS_CHECK_FOR_UPDATES
Definition: common.h:91
#define SETTINGS_CHECK_FORCE
Definition: common.h:67
#define SETTINGS_VS_INCLUDE_PATHS
Definition: common.h:83
#define SETTINGS_SAVE_FULL_PATH
Definition: common.h:73
#define SETTINGS_LANGUAGE
Definition: common.h:78
#define SETTINGS_SHOW_DEBUG_WARNINGS
Definition: common.h:71
#define SETTINGS_INCONCLUSIVE_ERRORS
Definition: common.h:85
#define SETTINGS_MISRA_FILE
Definition: common.h:81
#define SETTINGS_SHOW_ERROR_ID
Definition: common.h:87
#define SETTINGS_SHOW_STATISTICS
Definition: common.h:88
#define SETTINGS_SAVE_ALL_ERRORS
Definition: common.h:72
#define SETTINGS_CHECK_THREADS
Definition: common.h:68
Definition: aboutdialog.h:27
Information for one translation.