2 * This file is part of the PulseView project.
4 * Copyright (C) 2017 Soeren Apel <soeren@apelpie.net>
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.
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.
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/>.
20 #include <boost/archive/text_iarchive.hpp>
21 #include <boost/archive/text_oarchive.hpp>
22 #include <boost/serialization/serialization.hpp>
24 #include <QApplication>
28 #include <QFontMetrics>
29 #include <QPixmapCache>
34 #include "globalsettings.hpp"
35 #include "application.hpp"
40 using std::stringstream
;
45 const vector
< pair
<QString
, QString
> > Themes
{
47 {"QDarkStyleSheet", ":/themes/qdarkstyle/style.qss"},
48 {"DarkStyle", ":/themes/darkstyle/darkstyle.qss"}
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";
82 vector
<GlobalSettingsInterface
*> GlobalSettings::callbacks_
;
83 bool GlobalSettings::tracking_
= false;
84 bool GlobalSettings::is_dark_theme_
= false;
85 map
<QString
, QVariant
> GlobalSettings::tracked_changes_
;
86 QString
GlobalSettings::default_style_
;
87 QPalette
GlobalSettings::default_palette_
;
89 GlobalSettings::GlobalSettings() :
92 beginGroup("Settings");
95 void GlobalSettings::save_internal_defaults()
97 default_style_
= qApp
->style()->objectName();
98 if (default_style_
.isEmpty())
99 default_style_
= "fusion";
101 default_palette_
= QApplication::palette();
104 void GlobalSettings::set_defaults_where_needed()
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();
111 setValue(Key_General_Language
, language
);
115 // Use no theme by default
116 if (!contains(Key_General_Theme
))
117 setValue(Key_General_Theme
, 0);
118 if (!contains(Key_General_Style
))
119 setValue(Key_General_Style
, "");
121 // Save setup with .sr files by default
122 if (!contains(Key_General_SaveWithSetup
))
123 setValue(Key_General_SaveWithSetup
, true);
125 // Enable zoom-to-fit after acquisition by default
126 if (!contains(Key_View_ZoomToFitAfterAcq
))
127 setValue(Key_View_ZoomToFitAfterAcq
, true);
129 // Allow vertical dragging by default
130 if (!contains(Key_View_AllowVerticalDragging
))
131 setValue(Key_View_AllowVerticalDragging
, true);
133 // Enable colored trace backgrounds by default
134 if (!contains(Key_View_ColoredBG
))
135 setValue(Key_View_ColoredBG
, true);
137 // Enable showing sampling points by default
138 if (!contains(Key_View_ShowSamplingPoints
))
139 setValue(Key_View_ShowSamplingPoints
, true);
141 // Enable filling logic signal high areas by default
142 if (!contains(Key_View_FillSignalHighAreas
))
143 setValue(Key_View_FillSignalHighAreas
, true);
145 if (!contains(Key_View_DefaultDivHeight
))
146 setValue(Key_View_DefaultDivHeight
,
147 3 * QFontMetrics(QApplication::font()).height());
149 if (!contains(Key_View_DefaultLogicHeight
))
150 setValue(Key_View_DefaultLogicHeight
,
151 2 * QFontMetrics(QApplication::font()).height());
153 if (!contains(Key_View_ShowHoverMarker
))
154 setValue(Key_View_ShowHoverMarker
, true);
156 if (!contains(Key_View_KeepRulerItemSelected
))
157 setValue(Key_View_KeepRulerItemSelected
, false);
159 if (!contains(Key_View_SnapDistance
))
160 setValue(Key_View_SnapDistance
, 15);
162 if (!contains(Key_View_CursorShowInterval
))
163 setValue(Key_View_CursorShowInterval
, true);
165 if (!contains(Key_View_CursorShowFrequency
))
166 setValue(Key_View_CursorShowFrequency
, true);
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");
173 // Default to 500 lines of backlog
174 if (!contains(Key_Log_BufferSize
))
175 setValue(Key_Log_BufferSize
, 500);
177 // Notify user of existing stack trace by default
178 if (!contains(Key_Log_NotifyOfStacktrace
))
179 setValue(Key_Log_NotifyOfStacktrace
, true);
181 // Default theme is bright, so use its color scheme if undefined
182 if (!contains(Key_View_CursorFillColor
))
183 set_bright_theme_default_colors();
186 void GlobalSettings::set_bright_theme_default_colors()
188 setValue(Key_View_FillSignalHighAreaColor
,
189 QColor(0, 0, 0, 5 * 256 / 100).rgba());
191 setValue(Key_View_CursorFillColor
,
192 QColor(220, 231, 243).rgba());
195 void GlobalSettings::set_dark_theme_default_colors()
197 setValue(Key_View_FillSignalHighAreaColor
,
198 QColor(188, 188, 188, 9 * 256 / 100).rgba());
200 setValue(Key_View_CursorFillColor
,
201 QColor(60, 60, 60).rgba());
204 bool GlobalSettings::current_theme_is_dark()
206 return is_dark_theme_
;
209 void GlobalSettings::apply_theme()
211 QString theme_name
= Themes
.at(value(Key_General_Theme
).toInt()).first
;
212 QString resource_name
= Themes
.at(value(Key_General_Theme
).toInt()).second
;
214 if (!resource_name
.isEmpty()) {
215 QFile
file(resource_name
);
216 file
.open(QFile::ReadOnly
| QFile::Text
);
217 qApp
->setStyleSheet(file
.readAll());
219 qApp
->setStyleSheet("");
221 qApp
->setPalette(default_palette_
);
223 const QString style
= value(Key_General_Style
).toString();
225 qApp
->setStyle(default_style_
);
227 qApp
->setStyle(style
);
229 is_dark_theme_
= false;
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;
266 QPixmapCache::clear();
269 void GlobalSettings::apply_language()
271 Application
* a
= qobject_cast
<Application
*>(QApplication::instance());
272 a
->switch_language(value(Key_General_Language
).toString());
275 void GlobalSettings::add_change_handler(GlobalSettingsInterface
*cb
)
277 callbacks_
.push_back(cb
);
280 void GlobalSettings::remove_change_handler(GlobalSettingsInterface
*cb
)
282 for (auto cb_it
= callbacks_
.begin(); cb_it
!= callbacks_
.end(); cb_it
++)
284 callbacks_
.erase(cb_it
);
289 void GlobalSettings::setValue(const QString
&key
, const QVariant
&value
)
291 // Save previous value if we're tracking changes,
292 // not altering an already-existing saved setting
294 tracked_changes_
.emplace(key
, QSettings::value(key
));
296 QSettings::setValue(key
, value
);
298 // TODO Emulate noquote()
299 qDebug() << "Setting" << key
<< "changed to" << value
;
301 // Call all registered callbacks
302 for (GlobalSettingsInterface
*cb
: callbacks_
)
303 cb
->on_setting_changed(key
, value
);
306 void GlobalSettings::start_tracking()
309 tracked_changes_
.clear();
312 void GlobalSettings::stop_tracking()
315 tracked_changes_
.clear();
318 void GlobalSettings::undo_tracked_changes()
322 for (auto& entry
: tracked_changes_
)
323 setValue(entry
.first
, entry
.second
);
325 tracked_changes_
.clear();
328 void GlobalSettings::store_gvariant(QSettings
&settings
, GVariant
*v
)
330 const GVariantType
*var_type
= g_variant_get_type(v
);
331 char *var_type_str
= g_variant_type_dup_string(var_type
);
333 QByteArray var_data
= QByteArray((const char*)g_variant_get_data(v
),
334 g_variant_get_size(v
));
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
);
340 settings
.setValue("type", var_type_str
);
343 g_free(var_type_str
);
346 GVariant
* GlobalSettings::restore_gvariant(QSettings
&settings
)
348 QString raw_type
= settings
.value("type").toString();
349 GVariantType
*var_type
= g_variant_type_new(raw_type
.toUtf8());
351 QByteArray data
= settings
.value("value").toByteArray();
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());
356 gpointer var_data
= g_memdup((gconstpointer
)data
.constData(), (guint
)data
.size());
359 GVariant
*value
= g_variant_new_from_data(var_type
, var_data
,
360 data
.size(), false, g_free
, var_data
);
362 g_variant_type_free(var_type
);
367 void GlobalSettings::store_variantbase(QSettings
&settings
, Glib::VariantBase v
)
369 const QByteArray var_data
= QByteArray((const char*)v
.get_data(), v
.get_size());
371 settings
.setValue("value", var_data
);
372 settings
.setValue("type", QString::fromStdString(v
.get_type_string()));
375 Glib::VariantBase
GlobalSettings::restore_variantbase(QSettings
&settings
)
377 QString raw_type
= settings
.value("type").toString();
378 GVariantType
*var_type
= g_variant_type_new(raw_type
.toUtf8());
380 QByteArray data
= settings
.value("value").toByteArray();
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());
385 gpointer var_data
= g_memdup((gconstpointer
)data
.constData(), (guint
)data
.size());
388 GVariant
*value
= g_variant_new_from_data(var_type
, var_data
,
389 data
.size(), false, g_free
, var_data
);
391 Glib::VariantBase ret_val
= Glib::VariantBase(value
, true);
393 g_variant_type_free(var_type
);
394 g_variant_unref(value
);
399 void GlobalSettings::store_timestamp(QSettings
&settings
, const char *name
, const pv::util::Timestamp
&ts
)
402 boost::archive::text_oarchive
oa(ss
);
403 oa
<< boost::serialization::make_nvp(name
, ts
);
404 settings
.setValue(name
, QString::fromStdString(ss
.str()));
407 pv::util::Timestamp
GlobalSettings::restore_timestamp(QSettings
&settings
, const char *name
)
409 util::Timestamp result
;
411 ss
<< settings
.value(name
).toString().toStdString();
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
;