Freeciv21
Develop your civilization from humble roots to a global empire
themes.cpp
Go to the documentation of this file.
1 /*
2  Copyright (c) 1996-2020 Freeciv21 and Freeciv contributors. This file is
3  part of Freeciv21. Freeciv21 is free software: you can redistribute it
4  and/or modify it under the terms of the GNU General Public License as
5  published by the Free Software Foundation, either version 3 of the
6  License, or (at your option) any later version. You should have received
7  a copy of the GNU General Public License along with Freeciv21. If not,
8  see https://www.gnu.org/licenses/.
9  */
10 
11 // Qt
12 #include <QApplication>
13 #include <QDir>
14 #include <QMetaEnum>
15 #include <QPalette>
16 #include <QSettings>
17 #include <QStyle>
18 #include <QStyleFactory>
19 #include <QTextStream>
20 
21 // utility
22 #include "shared.h"
23 
24 // client
25 #include "chatline.h"
26 #include "colors_common.h"
27 #include "fc_client.h"
28 #include "page_game.h"
29 #include "qtg_cxxside.h"
30 #include "themes_common.h"
31 #include <array>
32 
33 extern QString current_theme;
34 Q_GLOBAL_STATIC(QString, def_app_style)
35 Q_GLOBAL_STATIC(QString, stylestring)
36 
37 namespace {
38 
42 void load_chat_colors(QSettings &settings)
43 {
44  settings.beginGroup("color_mapping");
45  QHash<QString, QString> colors;
46 
47  for (auto k : settings.childKeys()) {
48  auto val = settings.value(k).toString();
49  if (!QColor::isValidColor(val)) {
50  qWarning() << "color invalid: " << val;
51  continue;
52  }
53  colors[QStringLiteral("#") + k] = val;
54  }
55 
57  settings.endGroup();
58 }
59 
60 static std::array<QColor, COLOR_LAST> diag_colors = {};
61 
65 void load_diag_colors(QSettings &settings)
66 {
67  settings.beginGroup("diagram_colors");
68 
69  for (int i = 0; i < COLOR_LAST; ++i) {
70  const auto name = color_std_name(static_cast<color_std>(i));
71  if (settings.contains(name)) {
72  diag_colors[i].setNamedColor(settings.value(name).toString());
73  } else {
74  diag_colors[i] = QColor(); // Invalid, fetch from tileset
75  }
76  }
77 
78  settings.endGroup();
79 }
80 
84 QPalette load_palette(QSettings &settings)
85 {
86  settings.beginGroup("general_colors");
87  QPalette pal;
88 
89  auto meta = QMetaEnum::fromType<QPalette::ColorRole>();
90  for (int i = 0; i < meta.keyCount(); ++i) {
91  const auto name = meta.key(i);
92  const auto role = static_cast<QPalette::ColorRole>(meta.value(i));
93  if (role == QPalette::NColorRoles || role == QPalette::NoRole) {
94  continue;
95  }
96 
97  if (!settings.contains(name)) {
98  qWarning() << "missing color" << name;
99  continue;
100  }
101 
102  const auto val = settings.value(name).toString();
103  if (!QColor::isValidColor(val)) {
104  qWarning() << "color invalid:" << val;
105  continue;
106  }
107 
108  pal.setBrush(role, QColor(val));
109  }
110 
111  settings.endGroup();
112  return pal;
113 }
114 
115 } // namespace
116 
120 void gui_load_theme(const QString &directory, const QString &theme_name)
121 {
122  QString fake_dir;
123  QString data_dir;
124  QFile f;
125  QString lnb = QStringLiteral("LittleFinger");
126 
127  if (def_app_style->isEmpty()) {
128  *def_app_style = QApplication::style()->objectName();
129  }
130 
131  data_dir = QString(directory);
132 
133  f.setFileName(data_dir + "/" + theme_name + "/resource.qss");
134 
135  if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
136  if (QString(theme_name) != QStringLiteral(FC_QT_DEFAULT_THEME_NAME)) {
137  gui_clear_theme();
138  }
139  return;
140  }
141  // Stylesheet uses UNIX separators
142  fake_dir = data_dir;
143  QTextStream in(&f);
144  *stylestring = in.readAll();
145  stylestring->replace(lnb, fake_dir + "/" + theme_name + "/");
146 
147  if (theme_name == QStringLiteral("System")) {
148  QApplication::setStyle(QStyleFactory::create(*def_app_style));
149  } else {
150  QStyle *fstyle = QStyleFactory::create(QStringLiteral("Fusion"));
151 
152  if (fstyle != nullptr) {
153  QApplication::setStyle(fstyle);
154  } else {
155  QApplication::setStyle(QStyleFactory::create(*def_app_style));
156  }
157  }
158 
159  QSettings settings(data_dir + "/" + theme_name + "/theme.conf",
160  QSettings::IniFormat);
161  load_chat_colors(settings);
162  load_diag_colors(settings);
163 
164  current_theme = theme_name;
165  QPixmapCache::clear();
166  if (theme_name != QStringLiteral("System")) {
167  // Need to do this *before* changing the stylesheet.
168  // FIXME How to reset to the system palette?
169  QApplication::setPalette(load_palette(settings));
170  }
171  qApp->setStyleSheet(*stylestring);
172  if (king()) {
174  }
175 }
176 
181 {
183  // TRANS: No full stop after the URL, could cause confusion.
184  qFatal(_("No Qt-client theme was found. For instructions on how to "
185  "get one, please visit %s"),
186  WIKI_URL);
187  exit(EXIT_FAILURE);
188  }
189 }
190 
197 QStringList get_gui_specific_themes_directories(int *count)
198 {
199  auto data_dirs = get_data_dirs();
200  QStringList directories;
201 
202  *count = data_dirs.size();
203  for (const auto &data_dir : data_dirs) {
204  directories.append(QStringLiteral("%1/themes").arg(data_dir));
205  }
206 
207  return directories;
208 }
209 
215 QStringList get_useable_themes_in_directory(QString &directory)
216 {
217  QStringList sl, theme_list, array;
218  QByteArray qba;
219  QString name;
220  QString qtheme_name;
221  QDir dir;
222  QFile f;
223 
224  dir.setPath(directory);
225  sl << dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
226  name = QString(directory);
227 
228  for (auto const &str : qAsConst(sl)) {
229 #ifdef Q_OS_WIN
230  // "System" creates endless problems on Windows, see #1287 #756
231  if (str == QStringLiteral("System")) {
232  continue;
233  }
234 #endif
235  f.setFileName(name + "/" + str + "/resource.qss");
236  if (!f.exists()) {
237  continue;
238  }
239  theme_list << str;
240  }
241 
242  qtheme_name = gui_options->gui_qt_default_theme_name;
243  // move current theme on first position
244  if (theme_list.contains(qtheme_name)) {
245  theme_list.removeAll(qtheme_name);
246  theme_list.prepend(qtheme_name);
247  }
248  return theme_list;
249 }
250 
255 QColor get_diag_color(color_std c)
256 {
257  return diag_colors[c].isValid() ? diag_colors[c] : get_color(tileset, c);
258 }
void set_chat_colors(const QHash< QString, QString > &colors)
Sets color substitution map.
Definition: chatline.cpp:61
void reloadSidebarIcons()
Reloads top bar icons (useful on theme change)
Definition: page_game.cpp:234
QColor get_color(const struct tileset *t, enum color_std stdcolor)
Return a pointer to the given "standard" color.
class fc_client * king()
Return fc_client instance.
Definition: gui_main.cpp:58
#define _(String)
Definition: fcintl.h:50
const char * name
Definition: inputfile.cpp:118
Colors.
client_options * gui_options
Definition: options.cpp:74
#define FC_QT_DEFAULT_THEME_NAME
Definition: options.h:163
pageGame * queen()
Return game instandce.
Definition: page_game.cpp:557
Q_GLOBAL_STATIC(QVector< QString >, future_name_translation)
static struct setting settings[]
Definition: settings.cpp:1338
const QStringList & get_data_dirs()
Returns a list of data directory paths, in the order in which they should be searched.
Definition: shared.cpp:533
char gui_qt_default_theme_name[512]
Definition: options.h:168
char *const value
Definition: settings.cpp:130
QStringList get_gui_specific_themes_directories(int *count)
Each gui has its own themes directories.
Definition: themes.cpp:197
QStringList get_useable_themes_in_directory(QString &directory)
Return an array of names of usable themes in the given directory.
Definition: themes.cpp:215
QColor get_diag_color(color_std c)
Gets a diagram color.
Definition: themes.cpp:255
QString current_theme
Definition: icons.cpp:24
void gui_clear_theme()
Clears a theme (sets default system theme)
Definition: themes.cpp:180
void gui_load_theme(const QString &directory, const QString &theme_name)
Loads a qt theme directory/theme_name.
Definition: themes.cpp:120
struct theme_directory * directories
bool load_theme(const QString &theme_name)
Loads a theme with the given name.