PulseView  unreleased development snapshot
A Qt-based sigrok GUI
globalsettings.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the PulseView project.
3  *
4  * Copyright (C) 2017 Soeren Apel <soeren@apelpie.net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <boost/archive/text_iarchive.hpp>
21 #include <boost/archive/text_oarchive.hpp>
22 #include <boost/serialization/serialization.hpp>
23 
24 #include <QApplication>
25 #include <QColor>
26 #include <QDebug>
27 #include <QFile>
28 #include <QFontMetrics>
29 #include <QPixmapCache>
30 #include <QString>
31 #include <QStyle>
32 #include <QtGlobal>
33 
34 #include "globalsettings.hpp"
35 #include "application.hpp"
36 
37 using std::map;
38 using std::pair;
39 using std::string;
40 using std::stringstream;
41 using std::vector;
42 
43 namespace pv {
44 
45 const vector< pair<QString, QString> > Themes {
46  {"None" , ""},
47  {"QDarkStyleSheet", ":/themes/qdarkstyle/style.qss"},
48  {"DarkStyle", ":/themes/darkstyle/darkstyle.qss"}
49 };
50 
51 const QString GlobalSettings::Key_General_Language = "General_Language";
52 const QString GlobalSettings::Key_General_Theme = "General_Theme";
53 const QString GlobalSettings::Key_General_Style = "General_Style";
54 const QString GlobalSettings::Key_General_SaveWithSetup = "General_SaveWithSetup";
55 const QString GlobalSettings::Key_General_StartAllSessions = "General_StartAllSessions";
56 const QString GlobalSettings::Key_View_ZoomToFitDuringAcq = "View_ZoomToFitDuringAcq";
57 const QString GlobalSettings::Key_View_ZoomToFitAfterAcq = "View_ZoomToFitAfterAcq";
58 const QString GlobalSettings::Key_View_TriggerIsZeroTime = "View_TriggerIsZeroTime";
59 const QString GlobalSettings::Key_View_ColoredBG = "View_ColoredBG";
60 const QString GlobalSettings::Key_View_StickyScrolling = "View_StickyScrolling";
61 const QString GlobalSettings::Key_View_AllowVerticalDragging = "View_AllowVerticalDragging";
62 const QString GlobalSettings::Key_View_ShowSamplingPoints = "View_ShowSamplingPoints";
63 const QString GlobalSettings::Key_View_FillSignalHighAreas = "View_FillSignalHighAreas";
64 const QString GlobalSettings::Key_View_FillSignalHighAreaColor = "View_FillSignalHighAreaColor";
65 const QString GlobalSettings::Key_View_ShowAnalogMinorGrid = "View_ShowAnalogMinorGrid";
66 const QString GlobalSettings::Key_View_ConversionThresholdDispMode = "View_ConversionThresholdDispMode";
67 const QString GlobalSettings::Key_View_DefaultDivHeight = "View_DefaultDivHeight";
68 const QString GlobalSettings::Key_View_DefaultLogicHeight = "View_DefaultLogicHeight";
69 const QString GlobalSettings::Key_View_ShowHoverMarker = "View_ShowHoverMarker";
70 const QString GlobalSettings::Key_View_KeepRulerItemSelected = "View_KeepRulerItemSelected";
71 const QString GlobalSettings::Key_View_SnapDistance = "View_SnapDistance";
72 const QString GlobalSettings::Key_View_CursorFillColor = "View_CursorFillColor";
73 const QString GlobalSettings::Key_View_CursorShowFrequency = "View_CursorShowFrequency";
74 const QString GlobalSettings::Key_View_CursorShowInterval = "View_CursorShowInterval";
75 const QString GlobalSettings::Key_View_CursorShowSamples = "View_CursorShowSamples";
76 const QString GlobalSettings::Key_Dec_InitialStateConfigurable = "Dec_InitialStateConfigurable";
77 const QString GlobalSettings::Key_Dec_ExportFormat = "Dec_ExportFormat";
78 const QString GlobalSettings::Key_Dec_AlwaysShowAllRows = "Dec_AlwaysShowAllRows";
79 const QString GlobalSettings::Key_Log_BufferSize = "Log_BufferSize";
80 const QString GlobalSettings::Key_Log_NotifyOfStacktrace = "Log_NotifyOfStacktrace";
81 
82 vector<GlobalSettingsInterface*> GlobalSettings::callbacks_;
83 bool GlobalSettings::tracking_ = false;
85 map<QString, QVariant> GlobalSettings::tracked_changes_;
88 
90  QSettings()
91 {
92  beginGroup("Settings");
93 }
94 
96 {
97  default_style_ = qApp->style()->objectName();
98  if (default_style_.isEmpty())
99  default_style_ = "fusion";
100 
101  default_palette_ = QApplication::palette();
102 }
103 
105 {
106  if (!contains(Key_General_Language)) {
107  // Determine and set default UI language
108  QString language = QLocale().uiLanguages().first(); // May return e.g. en-Latn-US // clazy:exclude=detaching-temporary
109  language = language.split("-").first();
110 
111  setValue(Key_General_Language, language);
112  apply_language();
113  }
114 
115  // Use no theme by default
116  if (!contains(Key_General_Theme))
118  if (!contains(Key_General_Style))
120 
121  // Save setup with .sr files by default
122  if (!contains(Key_General_SaveWithSetup))
124 
125  // Enable zoom-to-fit after acquisition by default
126  if (!contains(Key_View_ZoomToFitAfterAcq))
128 
129  // Allow vertical dragging by default
130  if (!contains(Key_View_AllowVerticalDragging))
132 
133  // Enable colored trace backgrounds by default
134  if (!contains(Key_View_ColoredBG))
136 
137  // Enable showing sampling points by default
138  if (!contains(Key_View_ShowSamplingPoints))
140 
141  // Enable filling logic signal high areas by default
142  if (!contains(Key_View_FillSignalHighAreas))
144 
145  if (!contains(Key_View_DefaultDivHeight))
147  3 * QFontMetrics(QApplication::font()).height());
148 
149  if (!contains(Key_View_DefaultLogicHeight))
151  2 * QFontMetrics(QApplication::font()).height());
152 
153  if (!contains(Key_View_ShowHoverMarker))
155 
156  if (!contains(Key_View_KeepRulerItemSelected))
158 
159  if (!contains(Key_View_SnapDistance))
161 
162  if (!contains(Key_View_CursorShowInterval))
164 
165  if (!contains(Key_View_CursorShowFrequency))
167 
168  // %c was used for the row name in the past so we need to transition such users
169  if (!contains(Key_Dec_ExportFormat) ||
170  value(Key_Dec_ExportFormat).toString() == "%s %d: %c: %1")
171  setValue(Key_Dec_ExportFormat, "%s %d: %r: %1");
172 
173  // Default to 500 lines of backlog
174  if (!contains(Key_Log_BufferSize))
176 
177  // Notify user of existing stack trace by default
178  if (!contains(Key_Log_NotifyOfStacktrace))
180 
181  // Default theme is bright, so use its color scheme if undefined
182  if (!contains(Key_View_CursorFillColor))
184 }
185 
187 {
189  QColor(0, 0, 0, 5 * 256 / 100).rgba());
190 
192  QColor(220, 231, 243).rgba());
193 }
194 
196 {
198  QColor(188, 188, 188, 9 * 256 / 100).rgba());
199 
201  QColor(60, 60, 60).rgba());
202 }
203 
205 {
206  return is_dark_theme_;
207 }
208 
210 {
211  QString theme_name = Themes.at(value(Key_General_Theme).toInt()).first;
212  QString resource_name = Themes.at(value(Key_General_Theme).toInt()).second;
213 
214  if (!resource_name.isEmpty()) {
215  QFile file(resource_name);
216  file.open(QFile::ReadOnly | QFile::Text);
217  qApp->setStyleSheet(file.readAll());
218  } else
219  qApp->setStyleSheet("");
220 
221  qApp->setPalette(default_palette_);
222 
223  const QString style = value(Key_General_Style).toString();
224  if (style.isEmpty())
225  qApp->setStyle(default_style_);
226  else
227  qApp->setStyle(style);
228 
229  is_dark_theme_ = false;
230 
231  if (theme_name.compare("QDarkStyleSheet") == 0) {
232  QPalette dark_palette;
233  dark_palette.setColor(QPalette::Window, QColor(53, 53, 53));
234  dark_palette.setColor(QPalette::WindowText, Qt::white);
235  dark_palette.setColor(QPalette::Base, QColor(42, 42, 42));
236  dark_palette.setColor(QPalette::Dark, QColor(35, 35, 35));
237  dark_palette.setColor(QPalette::Highlight, QColor(42, 130, 218));
238  qApp->setPalette(dark_palette);
239  is_dark_theme_ = true;
240  } else if (theme_name.compare("DarkStyle") == 0) {
241  QPalette dark_palette;
242  dark_palette.setColor(QPalette::Window, QColor(53, 53, 53));
243  dark_palette.setColor(QPalette::WindowText, Qt::white);
244  dark_palette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(127, 127, 127));
245  dark_palette.setColor(QPalette::Base, QColor(42, 42, 42));
246  dark_palette.setColor(QPalette::AlternateBase, QColor(66, 66, 66));
247  dark_palette.setColor(QPalette::ToolTipBase, Qt::white);
248  dark_palette.setColor(QPalette::ToolTipText, QColor(53, 53, 53));
249  dark_palette.setColor(QPalette::Text, Qt::white);
250  dark_palette.setColor(QPalette::Disabled, QPalette::Text, QColor(127, 127, 127));
251  dark_palette.setColor(QPalette::Dark, QColor(35, 35, 35));
252  dark_palette.setColor(QPalette::Shadow, QColor(20, 20, 20));
253  dark_palette.setColor(QPalette::Button, QColor(53, 53, 53));
254  dark_palette.setColor(QPalette::ButtonText, Qt::white);
255  dark_palette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(127, 127, 127));
256  dark_palette.setColor(QPalette::BrightText, Qt::red);
257  dark_palette.setColor(QPalette::Link, QColor(42, 130, 218));
258  dark_palette.setColor(QPalette::Highlight, QColor(42, 130, 218));
259  dark_palette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(80, 80, 80));
260  dark_palette.setColor(QPalette::HighlightedText, Qt::white);
261  dark_palette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(127, 127, 127));
262  qApp->setPalette(dark_palette);
263  is_dark_theme_ = true;
264  }
265 
266  QPixmapCache::clear();
267 }
268 
270 {
271  Application* a = qobject_cast<Application*>(QApplication::instance());
273 }
274 
276 {
277  callbacks_.push_back(cb);
278 }
279 
281 {
282  for (auto cb_it = callbacks_.begin(); cb_it != callbacks_.end(); cb_it++)
283  if (*cb_it == cb) {
284  callbacks_.erase(cb_it);
285  break;
286  }
287 }
288 
289 void GlobalSettings::setValue(const QString &key, const QVariant &value)
290 {
291  // Save previous value if we're tracking changes,
292  // not altering an already-existing saved setting
293  if (tracking_)
294  tracked_changes_.emplace(key, QSettings::value(key));
295 
296  QSettings::setValue(key, value);
297 
298  // TODO Emulate noquote()
299  qDebug() << "Setting" << key << "changed to" << value;
300 
301  // Call all registered callbacks
303  cb->on_setting_changed(key, value);
304 }
305 
307 {
308  tracking_ = true;
309  tracked_changes_.clear();
310 }
311 
313 {
314  tracking_ = false;
315  tracked_changes_.clear();
316 }
317 
319 {
320  tracking_ = false;
321 
322  for (auto& entry : tracked_changes_)
323  setValue(entry.first, entry.second);
324 
325  tracked_changes_.clear();
326 }
327 
328 void GlobalSettings::store_gvariant(QSettings &settings, GVariant *v)
329 {
330  const GVariantType *var_type = g_variant_get_type(v);
331  char *var_type_str = g_variant_type_dup_string(var_type);
332 
333  QByteArray var_data = QByteArray((const char*)g_variant_get_data(v),
334  g_variant_get_size(v));
335 
336  settings.setValue("value", var_data);
337 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
338  settings.setValue("type", (const char *)var_type_str);
339 #else
340  settings.setValue("type", var_type_str);
341 #endif
342 
343  g_free(var_type_str);
344 }
345 
347 {
348  QString raw_type = settings.value("type").toString();
349  GVariantType *var_type = g_variant_type_new(raw_type.toUtf8());
350 
351  QByteArray data = settings.value("value").toByteArray();
352 
353 #if GLIB_CHECK_VERSION(2, 67, 3) // See https://discourse.gnome.org/t/port-your-module-from-g-memdup-to-g-memdup2-now/5538
354  gpointer var_data = g_memdup2((gconstpointer)data.constData(), (gsize)data.size());
355 #else
356  gpointer var_data = g_memdup((gconstpointer)data.constData(), (guint)data.size());
357 #endif
358 
359  GVariant *value = g_variant_new_from_data(var_type, var_data,
360  data.size(), false, g_free, var_data);
361 
362  g_variant_type_free(var_type);
363 
364  return value;
365 }
366 
367 void GlobalSettings::store_variantbase(QSettings &settings, Glib::VariantBase v)
368 {
369  const QByteArray var_data = QByteArray((const char*)v.get_data(), v.get_size());
370 
371  settings.setValue("value", var_data);
372  settings.setValue("type", QString::fromStdString(v.get_type_string()));
373 }
374 
375 Glib::VariantBase GlobalSettings::restore_variantbase(QSettings &settings)
376 {
377  QString raw_type = settings.value("type").toString();
378  GVariantType *var_type = g_variant_type_new(raw_type.toUtf8());
379 
380  QByteArray data = settings.value("value").toByteArray();
381 
382 #if GLIB_CHECK_VERSION(2, 67, 3) // See https://discourse.gnome.org/t/port-your-module-from-g-memdup-to-g-memdup2-now/5538
383  gpointer var_data = g_memdup2((gconstpointer)data.constData(), (gsize)data.size());
384 #else
385  gpointer var_data = g_memdup((gconstpointer)data.constData(), (guint)data.size());
386 #endif
387 
388  GVariant *value = g_variant_new_from_data(var_type, var_data,
389  data.size(), false, g_free, var_data);
390 
391  Glib::VariantBase ret_val = Glib::VariantBase(value, true);
392 
393  g_variant_type_free(var_type);
394  g_variant_unref(value);
395 
396  return ret_val;
397 }
398 
399 void GlobalSettings::store_timestamp(QSettings &settings, const char *name, const pv::util::Timestamp &ts)
400 {
401  stringstream ss;
402  boost::archive::text_oarchive oa(ss);
403  oa << boost::serialization::make_nvp(name, ts);
404  settings.setValue(name, QString::fromStdString(ss.str()));
405 }
406 
408 {
409  util::Timestamp result;
410  stringstream ss;
411  ss << settings.value(name).toString().toStdString();
412 
413  try {
414  boost::archive::text_iarchive ia(ss);
415  ia >> boost::serialization::make_nvp(name, result);
416  } catch (boost::archive::archive_exception&) {
417  qDebug() << "Could not restore setting" << name;
418  }
419 
420  return result;
421 }
422 
423 } // namespace pv
static void store_gvariant(QSettings &settings, GVariant *v)
static const QString Key_General_SaveWithSetup
const vector< pair< QString, QString > > Themes
static bool is_dark_theme_
static map< QString, QVariant > tracked_changes_
static const QString Key_View_KeepRulerItemSelected
static vector< GlobalSettingsInterface * > callbacks_
static QPalette default_palette_
static const QString Key_Log_BufferSize
static const QString Key_View_ShowAnalogMinorGrid
static const QString Key_View_FillSignalHighAreaColor
static const QString Key_View_DefaultDivHeight
static pv::util::Timestamp restore_timestamp(QSettings &settings, const char *name)
T value(details::expression_node< T > *n)
Definition: exprtk.hpp:12358
static const QString Key_General_Style
static const QString Key_Log_NotifyOfStacktrace
static const QString Key_Dec_AlwaysShowAllRows
static const QString Key_View_SnapDistance
static const QString Key_View_TriggerIsZeroTime
static const QString Key_View_CursorShowSamples
static const QString Key_General_StartAllSessions
static const QString Key_General_Language
static bool current_theme_is_dark()
static const QString Key_View_DefaultLogicHeight
static const QString Key_View_CursorFillColor
libsigrok allows users to import and export data from files in various formats some of them as generic as others very specific For a list and make sure to check not so common and outright exotic ways to represent data and sigrok tries to suit as many needs as it can To see which formats your version of PulseView just click on the small arrow next to the _Open_ PulseView will ask for the file name to open Once you picked the file
static QString default_style_
static const QString Key_View_StickyScrolling
static void remove_change_handler(GlobalSettingsInterface *cb)
static std::string data()
Definition: exprtk.hpp:39024
static void store_timestamp(QSettings &settings, const char *name, const pv::util::Timestamp &ts)
static Glib::VariantBase restore_variantbase(QSettings &settings)
void setValue(const QString &key, const QVariant &value)
static const QString Key_General_Theme
pv::util::Timestamp ts
Definition: util.cpp:26
static const QString Key_View_CursorShowFrequency
static const QString Key_View_ShowHoverMarker
static const QString Key_View_AllowVerticalDragging
static const QString Key_Dec_InitialStateConfigurable
static const QString Key_View_ConversionThresholdDispMode
static const QString Key_View_ZoomToFitAfterAcq
static void store_variantbase(QSettings &settings, Glib::VariantBase v)
static const QString Key_View_ShowSamplingPoints
static const QString Key_View_ZoomToFitDuringAcq
static const QString Key_View_FillSignalHighAreas
void switch_language(const QString &language)
Definition: application.cpp:96
static const QString Key_View_CursorShowInterval
static GVariant * restore_gvariant(QSettings &settings)
boost::multiprecision::number< boost::multiprecision::cpp_dec_float< 24 >, boost::multiprecision::et_off > Timestamp
Timestamp type providing yoctosecond resolution.
Definition: util.hpp:67
void set_dark_theme_default_colors()
void set_bright_theme_default_colors()
static const QString Key_Dec_ExportFormat
static void add_change_handler(GlobalSettingsInterface *cb)
static const QString Key_View_ColoredBG