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_ShowSamplingPoints
= "View_ShowSamplingPoints";
62 const QString
GlobalSettings::Key_View_FillSignalHighAreas
= "View_FillSignalHighAreas";
63 const QString
GlobalSettings::Key_View_FillSignalHighAreaColor
= "View_FillSignalHighAreaColor";
64 const QString
GlobalSettings::Key_View_ShowAnalogMinorGrid
= "View_ShowAnalogMinorGrid";
65 const QString
GlobalSettings::Key_View_ConversionThresholdDispMode
= "View_ConversionThresholdDispMode";
66 const QString
GlobalSettings::Key_View_DefaultDivHeight
= "View_DefaultDivHeight";
67 const QString
GlobalSettings::Key_View_DefaultLogicHeight
= "View_DefaultLogicHeight";
68 const QString
GlobalSettings::Key_View_ShowHoverMarker
= "View_ShowHoverMarker";
69 const QString
GlobalSettings::Key_View_SnapDistance
= "View_SnapDistance";
70 const QString
GlobalSettings::Key_View_CursorFillColor
= "View_CursorFillColor";
71 const QString
GlobalSettings::Key_View_CursorShowFrequency
= "View_CursorShowFrequency";
72 const QString
GlobalSettings::Key_View_CursorShowInterval
= "View_CursorShowInterval";
73 const QString
GlobalSettings::Key_View_CursorShowSamples
= "View_CursorShowSamples";
74 const QString
GlobalSettings::Key_Dec_InitialStateConfigurable
= "Dec_InitialStateConfigurable";
75 const QString
GlobalSettings::Key_Dec_ExportFormat
= "Dec_ExportFormat";
76 const QString
GlobalSettings::Key_Dec_AlwaysShowAllRows
= "Dec_AlwaysShowAllRows";
77 const QString
GlobalSettings::Key_Log_BufferSize
= "Log_BufferSize";
78 const QString
GlobalSettings::Key_Log_NotifyOfStacktrace
= "Log_NotifyOfStacktrace";
80 vector
<GlobalSettingsInterface
*> GlobalSettings::callbacks_
;
81 bool GlobalSettings::tracking_
= false;
82 bool GlobalSettings::is_dark_theme_
= false;
83 map
<QString
, QVariant
> GlobalSettings::tracked_changes_
;
84 QString
GlobalSettings::default_style_
;
85 QPalette
GlobalSettings::default_palette_
;
87 GlobalSettings::GlobalSettings() :
90 beginGroup("Settings");
93 void GlobalSettings::save_internal_defaults()
95 default_style_
= qApp
->style()->objectName();
96 if (default_style_
.isEmpty())
97 default_style_
= "fusion";
99 default_palette_
= QApplication::palette();
102 void GlobalSettings::set_defaults_where_needed()
104 if (!contains(Key_General_Language
)) {
105 // Determine and set default UI language
106 QString language
= QLocale().uiLanguages().first(); // May return e.g. en-Latn-US // clazy:exclude=detaching-temporary
107 language
= language
.split("-").first();
109 setValue(Key_General_Language
, language
);
113 // Use no theme by default
114 if (!contains(Key_General_Theme
))
115 setValue(Key_General_Theme
, 0);
116 if (!contains(Key_General_Style
))
117 setValue(Key_General_Style
, "");
119 // Save setup with .sr files by default
120 if (!contains(Key_General_SaveWithSetup
))
121 setValue(Key_General_SaveWithSetup
, true);
123 // Enable zoom-to-fit after acquisition by default
124 if (!contains(Key_View_ZoomToFitAfterAcq
))
125 setValue(Key_View_ZoomToFitAfterAcq
, true);
127 // Enable colored trace backgrounds by default
128 if (!contains(Key_View_ColoredBG
))
129 setValue(Key_View_ColoredBG
, true);
131 // Enable showing sampling points by default
132 if (!contains(Key_View_ShowSamplingPoints
))
133 setValue(Key_View_ShowSamplingPoints
, true);
135 // Enable filling logic signal high areas by default
136 if (!contains(Key_View_FillSignalHighAreas
))
137 setValue(Key_View_FillSignalHighAreas
, true);
139 if (!contains(Key_View_DefaultDivHeight
))
140 setValue(Key_View_DefaultDivHeight
,
141 3 * QFontMetrics(QApplication::font()).height());
143 if (!contains(Key_View_DefaultLogicHeight
))
144 setValue(Key_View_DefaultLogicHeight
,
145 2 * QFontMetrics(QApplication::font()).height());
147 if (!contains(Key_View_ShowHoverMarker
))
148 setValue(Key_View_ShowHoverMarker
, true);
150 if (!contains(Key_View_SnapDistance
))
151 setValue(Key_View_SnapDistance
, 15);
153 if (!contains(Key_View_CursorShowInterval
))
154 setValue(Key_View_CursorShowInterval
, true);
156 if (!contains(Key_View_CursorShowFrequency
))
157 setValue(Key_View_CursorShowFrequency
, true);
159 // %c was used for the row name in the past so we need to transition such users
160 if (!contains(Key_Dec_ExportFormat
) ||
161 value(Key_Dec_ExportFormat
).toString() == "%s %d: %c: %1")
162 setValue(Key_Dec_ExportFormat
, "%s %d: %r: %1");
164 // Default to 500 lines of backlog
165 if (!contains(Key_Log_BufferSize
))
166 setValue(Key_Log_BufferSize
, 500);
168 // Notify user of existing stack trace by default
169 if (!contains(Key_Log_NotifyOfStacktrace
))
170 setValue(Key_Log_NotifyOfStacktrace
, true);
172 // Default theme is bright, so use its color scheme if undefined
173 if (!contains(Key_View_CursorFillColor
))
174 set_bright_theme_default_colors();
177 void GlobalSettings::set_bright_theme_default_colors()
179 setValue(Key_View_FillSignalHighAreaColor
,
180 QColor(0, 0, 0, 5 * 256 / 100).rgba());
182 setValue(Key_View_CursorFillColor
,
183 QColor(220, 231, 243).rgba());
186 void GlobalSettings::set_dark_theme_default_colors()
188 setValue(Key_View_FillSignalHighAreaColor
,
189 QColor(188, 188, 188, 9 * 256 / 100).rgba());
191 setValue(Key_View_CursorFillColor
,
192 QColor(60, 60, 60).rgba());
195 bool GlobalSettings::current_theme_is_dark()
197 return is_dark_theme_
;
200 void GlobalSettings::apply_theme()
202 QString theme_name
= Themes
.at(value(Key_General_Theme
).toInt()).first
;
203 QString resource_name
= Themes
.at(value(Key_General_Theme
).toInt()).second
;
205 if (!resource_name
.isEmpty()) {
206 QFile
file(resource_name
);
207 file
.open(QFile::ReadOnly
| QFile::Text
);
208 qApp
->setStyleSheet(file
.readAll());
210 qApp
->setStyleSheet("");
212 qApp
->setPalette(default_palette_
);
214 const QString style
= value(Key_General_Style
).toString();
216 qApp
->setStyle(default_style_
);
218 qApp
->setStyle(style
);
220 is_dark_theme_
= false;
222 if (theme_name
.compare("QDarkStyleSheet") == 0) {
223 QPalette dark_palette
;
224 dark_palette
.setColor(QPalette::Window
, QColor(53, 53, 53));
225 dark_palette
.setColor(QPalette::WindowText
, Qt::white
);
226 dark_palette
.setColor(QPalette::Base
, QColor(42, 42, 42));
227 dark_palette
.setColor(QPalette::Dark
, QColor(35, 35, 35));
228 dark_palette
.setColor(QPalette::Highlight
, QColor(42, 130, 218));
229 qApp
->setPalette(dark_palette
);
230 is_dark_theme_
= true;
231 } else if (theme_name
.compare("DarkStyle") == 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::Disabled
, QPalette::WindowText
, QColor(127, 127, 127));
236 dark_palette
.setColor(QPalette::Base
, QColor(42, 42, 42));
237 dark_palette
.setColor(QPalette::AlternateBase
, QColor(66, 66, 66));
238 dark_palette
.setColor(QPalette::ToolTipBase
, Qt::white
);
239 dark_palette
.setColor(QPalette::ToolTipText
, QColor(53, 53, 53));
240 dark_palette
.setColor(QPalette::Text
, Qt::white
);
241 dark_palette
.setColor(QPalette::Disabled
, QPalette::Text
, QColor(127, 127, 127));
242 dark_palette
.setColor(QPalette::Dark
, QColor(35, 35, 35));
243 dark_palette
.setColor(QPalette::Shadow
, QColor(20, 20, 20));
244 dark_palette
.setColor(QPalette::Button
, QColor(53, 53, 53));
245 dark_palette
.setColor(QPalette::ButtonText
, Qt::white
);
246 dark_palette
.setColor(QPalette::Disabled
, QPalette::ButtonText
, QColor(127, 127, 127));
247 dark_palette
.setColor(QPalette::BrightText
, Qt::red
);
248 dark_palette
.setColor(QPalette::Link
, QColor(42, 130, 218));
249 dark_palette
.setColor(QPalette::Highlight
, QColor(42, 130, 218));
250 dark_palette
.setColor(QPalette::Disabled
, QPalette::Highlight
, QColor(80, 80, 80));
251 dark_palette
.setColor(QPalette::HighlightedText
, Qt::white
);
252 dark_palette
.setColor(QPalette::Disabled
, QPalette::HighlightedText
, QColor(127, 127, 127));
253 qApp
->setPalette(dark_palette
);
254 is_dark_theme_
= true;
257 QPixmapCache::clear();
260 void GlobalSettings::apply_language()
262 Application
* a
= qobject_cast
<Application
*>(QApplication::instance());
263 a
->switch_language(value(Key_General_Language
).toString());
266 void GlobalSettings::add_change_handler(GlobalSettingsInterface
*cb
)
268 callbacks_
.push_back(cb
);
271 void GlobalSettings::remove_change_handler(GlobalSettingsInterface
*cb
)
273 for (auto cb_it
= callbacks_
.begin(); cb_it
!= callbacks_
.end(); cb_it
++)
275 callbacks_
.erase(cb_it
);
280 void GlobalSettings::setValue(const QString
&key
, const QVariant
&value
)
282 // Save previous value if we're tracking changes,
283 // not altering an already-existing saved setting
285 tracked_changes_
.emplace(key
, QSettings::value(key
));
287 QSettings::setValue(key
, value
);
289 // TODO Emulate noquote()
290 qDebug() << "Setting" << key
<< "changed to" << value
;
292 // Call all registered callbacks
293 for (GlobalSettingsInterface
*cb
: callbacks_
)
294 cb
->on_setting_changed(key
, value
);
297 void GlobalSettings::start_tracking()
300 tracked_changes_
.clear();
303 void GlobalSettings::stop_tracking()
306 tracked_changes_
.clear();
309 void GlobalSettings::undo_tracked_changes()
313 for (auto& entry
: tracked_changes_
)
314 setValue(entry
.first
, entry
.second
);
316 tracked_changes_
.clear();
319 void GlobalSettings::store_gvariant(QSettings
&settings
, GVariant
*v
)
321 const GVariantType
*var_type
= g_variant_get_type(v
);
322 char *var_type_str
= g_variant_type_dup_string(var_type
);
324 QByteArray var_data
= QByteArray((const char*)g_variant_get_data(v
),
325 g_variant_get_size(v
));
327 settings
.setValue("value", var_data
);
328 settings
.setValue("type", var_type_str
);
330 g_free(var_type_str
);
333 GVariant
* GlobalSettings::restore_gvariant(QSettings
&settings
)
335 QString raw_type
= settings
.value("type").toString();
336 GVariantType
*var_type
= g_variant_type_new(raw_type
.toUtf8());
338 QByteArray data
= settings
.value("value").toByteArray();
340 gpointer var_data
= g_memdup((gconstpointer
)data
.constData(),
343 GVariant
*value
= g_variant_new_from_data(var_type
, var_data
,
344 data
.size(), false, g_free
, var_data
);
346 g_variant_type_free(var_type
);
351 void GlobalSettings::store_variantbase(QSettings
&settings
, Glib::VariantBase v
)
353 const QByteArray var_data
= QByteArray((const char*)v
.get_data(), v
.get_size());
355 settings
.setValue("value", var_data
);
356 settings
.setValue("type", QString::fromStdString(v
.get_type_string()));
359 Glib::VariantBase
GlobalSettings::restore_variantbase(QSettings
&settings
)
361 QString raw_type
= settings
.value("type").toString();
362 GVariantType
*var_type
= g_variant_type_new(raw_type
.toUtf8());
364 QByteArray data
= settings
.value("value").toByteArray();
366 gpointer var_data
= g_memdup((gconstpointer
)data
.constData(),
369 GVariant
*value
= g_variant_new_from_data(var_type
, var_data
,
370 data
.size(), false, g_free
, var_data
);
372 Glib::VariantBase ret_val
= Glib::VariantBase(value
, true);
374 g_variant_type_free(var_type
);
375 g_variant_unref(value
);
380 void GlobalSettings::store_timestamp(QSettings
&settings
, const char *name
, const pv::util::Timestamp
&ts
)
383 boost::archive::text_oarchive
oa(ss
);
384 oa
<< boost::serialization::make_nvp(name
, ts
);
385 settings
.setValue(name
, QString::fromStdString(ss
.str()));
388 pv::util::Timestamp
GlobalSettings::restore_timestamp(QSettings
&settings
, const char *name
)
390 util::Timestamp result
;
392 ss
<< settings
.value(name
).toString().toStdString();
395 boost::archive::text_iarchive
ia(ss
);
396 ia
>> boost::serialization::make_nvp(name
, result
);
397 } catch (boost::archive::archive_exception
&) {
398 qDebug() << "Could not restore setting" << name
;