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_SnapDistance
= "View_SnapDistance";
71 const QString
GlobalSettings::Key_View_CursorFillColor
= "View_CursorFillColor";
72 const QString
GlobalSettings::Key_View_CursorShowFrequency
= "View_CursorShowFrequency";
73 const QString
GlobalSettings::Key_View_CursorShowInterval
= "View_CursorShowInterval";
74 const QString
GlobalSettings::Key_View_CursorShowSamples
= "View_CursorShowSamples";
75 const QString
GlobalSettings::Key_Dec_InitialStateConfigurable
= "Dec_InitialStateConfigurable";
76 const QString
GlobalSettings::Key_Dec_ExportFormat
= "Dec_ExportFormat";
77 const QString
GlobalSettings::Key_Dec_AlwaysShowAllRows
= "Dec_AlwaysShowAllRows";
78 const QString
GlobalSettings::Key_Log_BufferSize
= "Log_BufferSize";
79 const QString
GlobalSettings::Key_Log_NotifyOfStacktrace
= "Log_NotifyOfStacktrace";
81 vector
<GlobalSettingsInterface
*> GlobalSettings::callbacks_
;
82 bool GlobalSettings::tracking_
= false;
83 bool GlobalSettings::is_dark_theme_
= false;
84 map
<QString
, QVariant
> GlobalSettings::tracked_changes_
;
85 QString
GlobalSettings::default_style_
;
86 QPalette
GlobalSettings::default_palette_
;
88 GlobalSettings::GlobalSettings() :
91 beginGroup("Settings");
94 void GlobalSettings::save_internal_defaults()
96 default_style_
= qApp
->style()->objectName();
97 if (default_style_
.isEmpty())
98 default_style_
= "fusion";
100 default_palette_
= QApplication::palette();
103 void GlobalSettings::set_defaults_where_needed()
105 if (!contains(Key_General_Language
)) {
106 // Determine and set default UI language
107 QString language
= QLocale().uiLanguages().first(); // May return e.g. en-Latn-US // clazy:exclude=detaching-temporary
108 language
= language
.split("-").first();
110 setValue(Key_General_Language
, language
);
114 // Use no theme by default
115 if (!contains(Key_General_Theme
))
116 setValue(Key_General_Theme
, 0);
117 if (!contains(Key_General_Style
))
118 setValue(Key_General_Style
, "");
120 // Save setup with .sr files by default
121 if (!contains(Key_General_SaveWithSetup
))
122 setValue(Key_General_SaveWithSetup
, true);
124 // Enable zoom-to-fit after acquisition by default
125 if (!contains(Key_View_ZoomToFitAfterAcq
))
126 setValue(Key_View_ZoomToFitAfterAcq
, true);
128 // Allow vertical dragging by default
129 if (!contains(Key_View_AllowVerticalDragging
))
130 setValue(Key_View_AllowVerticalDragging
, true);
132 // Enable colored trace backgrounds by default
133 if (!contains(Key_View_ColoredBG
))
134 setValue(Key_View_ColoredBG
, true);
136 // Enable showing sampling points by default
137 if (!contains(Key_View_ShowSamplingPoints
))
138 setValue(Key_View_ShowSamplingPoints
, true);
140 // Enable filling logic signal high areas by default
141 if (!contains(Key_View_FillSignalHighAreas
))
142 setValue(Key_View_FillSignalHighAreas
, true);
144 if (!contains(Key_View_DefaultDivHeight
))
145 setValue(Key_View_DefaultDivHeight
,
146 3 * QFontMetrics(QApplication::font()).height());
148 if (!contains(Key_View_DefaultLogicHeight
))
149 setValue(Key_View_DefaultLogicHeight
,
150 2 * QFontMetrics(QApplication::font()).height());
152 if (!contains(Key_View_ShowHoverMarker
))
153 setValue(Key_View_ShowHoverMarker
, true);
155 if (!contains(Key_View_SnapDistance
))
156 setValue(Key_View_SnapDistance
, 15);
158 if (!contains(Key_View_CursorShowInterval
))
159 setValue(Key_View_CursorShowInterval
, true);
161 if (!contains(Key_View_CursorShowFrequency
))
162 setValue(Key_View_CursorShowFrequency
, true);
164 // %c was used for the row name in the past so we need to transition such users
165 if (!contains(Key_Dec_ExportFormat
) ||
166 value(Key_Dec_ExportFormat
).toString() == "%s %d: %c: %1")
167 setValue(Key_Dec_ExportFormat
, "%s %d: %r: %1");
169 // Default to 500 lines of backlog
170 if (!contains(Key_Log_BufferSize
))
171 setValue(Key_Log_BufferSize
, 500);
173 // Notify user of existing stack trace by default
174 if (!contains(Key_Log_NotifyOfStacktrace
))
175 setValue(Key_Log_NotifyOfStacktrace
, true);
177 // Default theme is bright, so use its color scheme if undefined
178 if (!contains(Key_View_CursorFillColor
))
179 set_bright_theme_default_colors();
182 void GlobalSettings::set_bright_theme_default_colors()
184 setValue(Key_View_FillSignalHighAreaColor
,
185 QColor(0, 0, 0, 5 * 256 / 100).rgba());
187 setValue(Key_View_CursorFillColor
,
188 QColor(220, 231, 243).rgba());
191 void GlobalSettings::set_dark_theme_default_colors()
193 setValue(Key_View_FillSignalHighAreaColor
,
194 QColor(188, 188, 188, 9 * 256 / 100).rgba());
196 setValue(Key_View_CursorFillColor
,
197 QColor(60, 60, 60).rgba());
200 bool GlobalSettings::current_theme_is_dark()
202 return is_dark_theme_
;
205 void GlobalSettings::apply_theme()
207 QString theme_name
= Themes
.at(value(Key_General_Theme
).toInt()).first
;
208 QString resource_name
= Themes
.at(value(Key_General_Theme
).toInt()).second
;
210 if (!resource_name
.isEmpty()) {
211 QFile
file(resource_name
);
212 file
.open(QFile::ReadOnly
| QFile::Text
);
213 qApp
->setStyleSheet(file
.readAll());
215 qApp
->setStyleSheet("");
217 qApp
->setPalette(default_palette_
);
219 const QString style
= value(Key_General_Style
).toString();
221 qApp
->setStyle(default_style_
);
223 qApp
->setStyle(style
);
225 is_dark_theme_
= false;
227 if (theme_name
.compare("QDarkStyleSheet") == 0) {
228 QPalette dark_palette
;
229 dark_palette
.setColor(QPalette::Window
, QColor(53, 53, 53));
230 dark_palette
.setColor(QPalette::WindowText
, Qt::white
);
231 dark_palette
.setColor(QPalette::Base
, QColor(42, 42, 42));
232 dark_palette
.setColor(QPalette::Dark
, QColor(35, 35, 35));
233 dark_palette
.setColor(QPalette::Highlight
, QColor(42, 130, 218));
234 qApp
->setPalette(dark_palette
);
235 is_dark_theme_
= true;
236 } else if (theme_name
.compare("DarkStyle") == 0) {
237 QPalette dark_palette
;
238 dark_palette
.setColor(QPalette::Window
, QColor(53, 53, 53));
239 dark_palette
.setColor(QPalette::WindowText
, Qt::white
);
240 dark_palette
.setColor(QPalette::Disabled
, QPalette::WindowText
, QColor(127, 127, 127));
241 dark_palette
.setColor(QPalette::Base
, QColor(42, 42, 42));
242 dark_palette
.setColor(QPalette::AlternateBase
, QColor(66, 66, 66));
243 dark_palette
.setColor(QPalette::ToolTipBase
, Qt::white
);
244 dark_palette
.setColor(QPalette::ToolTipText
, QColor(53, 53, 53));
245 dark_palette
.setColor(QPalette::Text
, Qt::white
);
246 dark_palette
.setColor(QPalette::Disabled
, QPalette::Text
, QColor(127, 127, 127));
247 dark_palette
.setColor(QPalette::Dark
, QColor(35, 35, 35));
248 dark_palette
.setColor(QPalette::Shadow
, QColor(20, 20, 20));
249 dark_palette
.setColor(QPalette::Button
, QColor(53, 53, 53));
250 dark_palette
.setColor(QPalette::ButtonText
, Qt::white
);
251 dark_palette
.setColor(QPalette::Disabled
, QPalette::ButtonText
, QColor(127, 127, 127));
252 dark_palette
.setColor(QPalette::BrightText
, Qt::red
);
253 dark_palette
.setColor(QPalette::Link
, QColor(42, 130, 218));
254 dark_palette
.setColor(QPalette::Highlight
, QColor(42, 130, 218));
255 dark_palette
.setColor(QPalette::Disabled
, QPalette::Highlight
, QColor(80, 80, 80));
256 dark_palette
.setColor(QPalette::HighlightedText
, Qt::white
);
257 dark_palette
.setColor(QPalette::Disabled
, QPalette::HighlightedText
, QColor(127, 127, 127));
258 qApp
->setPalette(dark_palette
);
259 is_dark_theme_
= true;
262 QPixmapCache::clear();
265 void GlobalSettings::apply_language()
267 Application
* a
= qobject_cast
<Application
*>(QApplication::instance());
268 a
->switch_language(value(Key_General_Language
).toString());
271 void GlobalSettings::add_change_handler(GlobalSettingsInterface
*cb
)
273 callbacks_
.push_back(cb
);
276 void GlobalSettings::remove_change_handler(GlobalSettingsInterface
*cb
)
278 for (auto cb_it
= callbacks_
.begin(); cb_it
!= callbacks_
.end(); cb_it
++)
280 callbacks_
.erase(cb_it
);
285 void GlobalSettings::setValue(const QString
&key
, const QVariant
&value
)
287 // Save previous value if we're tracking changes,
288 // not altering an already-existing saved setting
290 tracked_changes_
.emplace(key
, QSettings::value(key
));
292 QSettings::setValue(key
, value
);
294 // TODO Emulate noquote()
295 qDebug() << "Setting" << key
<< "changed to" << value
;
297 // Call all registered callbacks
298 for (GlobalSettingsInterface
*cb
: callbacks_
)
299 cb
->on_setting_changed(key
, value
);
302 void GlobalSettings::start_tracking()
305 tracked_changes_
.clear();
308 void GlobalSettings::stop_tracking()
311 tracked_changes_
.clear();
314 void GlobalSettings::undo_tracked_changes()
318 for (auto& entry
: tracked_changes_
)
319 setValue(entry
.first
, entry
.second
);
321 tracked_changes_
.clear();
324 void GlobalSettings::store_gvariant(QSettings
&settings
, GVariant
*v
)
326 const GVariantType
*var_type
= g_variant_get_type(v
);
327 char *var_type_str
= g_variant_type_dup_string(var_type
);
329 QByteArray var_data
= QByteArray((const char*)g_variant_get_data(v
),
330 g_variant_get_size(v
));
332 settings
.setValue("value", var_data
);
333 settings
.setValue("type", var_type_str
);
335 g_free(var_type_str
);
338 GVariant
* GlobalSettings::restore_gvariant(QSettings
&settings
)
340 QString raw_type
= settings
.value("type").toString();
341 GVariantType
*var_type
= g_variant_type_new(raw_type
.toUtf8());
343 QByteArray data
= settings
.value("value").toByteArray();
345 #if GLIB_CHECK_VERSION(2, 67, 3) // See https://discourse.gnome.org/t/port-your-module-from-g-memdup-to-g-memdup2-now/5538
346 gpointer var_data
= g_memdup2((gconstpointer
)data
.constData(), (gsize
)data
.size());
348 gpointer var_data
= g_memdup((gconstpointer
)data
.constData(), (guint
)data
.size());
351 GVariant
*value
= g_variant_new_from_data(var_type
, var_data
,
352 data
.size(), false, g_free
, var_data
);
354 g_variant_type_free(var_type
);
359 void GlobalSettings::store_variantbase(QSettings
&settings
, Glib::VariantBase v
)
361 const QByteArray var_data
= QByteArray((const char*)v
.get_data(), v
.get_size());
363 settings
.setValue("value", var_data
);
364 settings
.setValue("type", QString::fromStdString(v
.get_type_string()));
367 Glib::VariantBase
GlobalSettings::restore_variantbase(QSettings
&settings
)
369 QString raw_type
= settings
.value("type").toString();
370 GVariantType
*var_type
= g_variant_type_new(raw_type
.toUtf8());
372 QByteArray data
= settings
.value("value").toByteArray();
374 #if GLIB_CHECK_VERSION(2, 67, 3) // See https://discourse.gnome.org/t/port-your-module-from-g-memdup-to-g-memdup2-now/5538
375 gpointer var_data
= g_memdup2((gconstpointer
)data
.constData(), (gsize
)data
.size());
377 gpointer var_data
= g_memdup((gconstpointer
)data
.constData(), (guint
)data
.size());
380 GVariant
*value
= g_variant_new_from_data(var_type
, var_data
,
381 data
.size(), false, g_free
, var_data
);
383 Glib::VariantBase ret_val
= Glib::VariantBase(value
, true);
385 g_variant_type_free(var_type
);
386 g_variant_unref(value
);
391 void GlobalSettings::store_timestamp(QSettings
&settings
, const char *name
, const pv::util::Timestamp
&ts
)
394 boost::archive::text_oarchive
oa(ss
);
395 oa
<< boost::serialization::make_nvp(name
, ts
);
396 settings
.setValue(name
, QString::fromStdString(ss
.str()));
399 pv::util::Timestamp
GlobalSettings::restore_timestamp(QSettings
&settings
, const char *name
)
401 util::Timestamp result
;
403 ss
<< settings
.value(name
).toString().toStdString();
406 boost::archive::text_iarchive
ia(ss
);
407 ia
>> boost::serialization::make_nvp(name
, result
);
408 } catch (boost::archive::archive_exception
&) {
409 qDebug() << "Could not restore setting" << name
;