Freeciv21
Develop your civilization from humble roots to a global empire
page_load.cpp
Go to the documentation of this file.
1 /*
2  /\ ___ /\ Copyright (c) 1996-2020 Freeciv21 and Freeciv
3  ( o o ) contributors. This file is part of Freeciv21.
4  \ >#< / Freeciv21 is free software: you can redistribute it
5  / \ and/or modify it under the terms of the GNU
6  / \ ^ General Public License as published by the Free
7 | | // Software Foundation, either version 3 of the License,
8  \ / // or (at your option) any later version.
10  GNU General Public License along with Freeciv21.
11  If not, see https://www.gnu.org/licenses/.
12  */
13 
14 #include "page_load.h"
15 
16 // Qt
17 #include <QDateTime>
18 #include <QFileDialog>
19 #include <QPushButton>
20 
21 // utility
22 #include "fcintl.h"
23 #include "section_file.h"
24 
25 // common
26 #include "rgbcolor.h"
27 
28 // client
29 #include "fc_client.h"
30 #include "options.h"
31 
32 static struct terrain *char2terrain(char ch);
33 
38 static struct terrain *char2terrain(char ch)
39 {
40  if (ch == TERRAIN_UNKNOWN_IDENTIFIER) {
41  return T_UNKNOWN;
42  }
43  terrain_type_iterate(pterrain)
44  {
45  if (pterrain->identifier_load == ch) {
46  return pterrain;
47  }
48  }
50  return nullptr;
51 }
52 
53 page_load::page_load(QWidget *parent, fc_client *c) : QWidget(parent)
54 {
55  gui = c;
56  ui.setupUi(this);
57  ui.show_preview->setText(_("Show preview"));
58  ui.load_pix->setProperty("themed_border", true);
59  ui.load_pix->setFixedSize(0, 0);
60  ui.load_save_text->setText(QLatin1String(""));
61  ui.load_save_text->setTextFormat(Qt::RichText);
62  ui.load_save_text->setWordWrap(true);
63  ui.show_preview->setChecked(gui_options->gui_qt_show_preview);
64  ui.saves_load->setRowCount(0);
65  QStringList sav;
66  sav << _("Choose Saved Game to Load") << _("Date");
67  ui.saves_load->setColumnCount(sav.count());
68  ui.saves_load->setHorizontalHeaderLabels(sav);
69  ui.saves_load->horizontalHeader()->setSectionResizeMode(
70  0, QHeaderView::Stretch);
71 
72  connect(ui.saves_load->selectionModel(),
73  &QItemSelectionModel::selectionChanged, this,
75  connect(ui.show_preview, &QCheckBox::stateChanged, this,
77 
78  auto browse =
79  ui.buttons->addButton(_("Browse..."), QDialogButtonBox::ActionRole);
80  browse->setIcon(QIcon::fromTheme(QStringLiteral("document-open-folder")));
81  connect(browse, &QAbstractButton::clicked, this, &page_load::browse_saves);
82 
83  connect(ui.buttons->button(QDialogButtonBox::Cancel),
84  &QAbstractButton::clicked, gui, &fc_client::slot_disconnect);
85 
86  auto load = ui.buttons->button(QDialogButtonBox::Ok);
87  load->setText(_("Load"));
88  connect(load, &QAbstractButton::clicked, this,
90 }
91 
92 page_load::~page_load() = default;
93 
98 {
99  int row;
100 
101  row = 0;
102  ui.saves_load->clearContents();
103  ui.saves_load->setRowCount(0);
104  ui.show_preview->setChecked(gui_options->gui_qt_show_preview);
105 
106  const auto files =
107  find_files_in_path(get_save_dirs(), QStringLiteral("*.sav*"), false);
108  for (const auto &info : files) {
109  auto item = new QTableWidgetItem();
110  item->setData(Qt::UserRole, info.absoluteFilePath());
111  ui.saves_load->insertRow(row);
112  item->setText(info.fileName());
113  ui.saves_load->setItem(row, 0, item);
114  item = new QTableWidgetItem();
115  item->setData(Qt::DisplayRole, QDateTime(info.lastModified()));
116  ui.saves_load->setItem(row, 1, item);
117  row++;
118  }
119 
120  ui.saves_load->sortByColumn(1, Qt::DescendingOrder);
121 
122  if (!files.isEmpty()) {
123  ui.saves_load->setCurrentCell(0, 0); // Select the latest save
124  }
125 }
126 
131 
136 {
137  QString str;
138  str = QString(_("Save Files"))
139  + QStringLiteral(" (*.sav *.sav.bz2 *.sav.gz *.sav.xz *.sav.zst)");
140  current_file = QFileDialog::getOpenFileName(this, _("Open Save File"),
141  QDir::homePath(), str);
142  if (!current_file.isEmpty()) {
143  start_from_save();
144  }
145 }
146 
151 {
153  ui.show_preview->checkState() != Qt::Unchecked;
154  auto selection = ui.saves_load->selectionModel()->selection();
155  ui.saves_load->selectionModel()->clearSelection();
156  ui.saves_load->selectionModel()->select(
157  selection,
158  QItemSelectionModel::Rows | QItemSelectionModel::SelectCurrent);
159 }
160 
161 void page_load::slot_selection_changed(const QItemSelection &selected,
162  const QItemSelection &deselected)
163 {
164  Q_UNUSED(deselected)
165  QModelIndexList indexes = selected.indexes();
166  QStringList sl;
167  QModelIndex index;
168  QVariant qvar;
169  QString str_pixmap;
170 
171  const char *terr_name;
172  int ii = 0;
173  int nat_y, nat_x;
174  QByteArray fn_bytes;
175 
176  if (indexes.isEmpty()) {
177  return;
178  }
179 
180  index = indexes.at(0);
181  qvar = index.data(Qt::UserRole);
182  current_file = qvar.toString();
183  if (ui.show_preview->checkState() == Qt::Unchecked) {
184  ui.load_pix->setPixmap(*(new QPixmap));
185  ui.load_save_text->setText(QLatin1String(""));
186  return;
187  }
188  fn_bytes = current_file.toLocal8Bit();
189 
190  auto sf = std::unique_ptr<section_file, decltype(&secfile_destroy)>(
191  nullptr, &secfile_destroy);
192  sf.reset(
193  secfile_load_section(fn_bytes.data(), QStringLiteral("game"), true));
194  if (sf) {
195  const char *sname;
196  bool sbool;
197  int integer;
198  QString final_str;
199  QString pl_str = nullptr;
200  int num_players = 0;
201  int curr_player = 0;
202  QByteArray pl_bytes;
203 
204  integer = secfile_lookup_int_default(sf.get(), -1, "game.turn");
205  if (integer >= 0) {
206  final_str = QStringLiteral("<b>") + _("Turn") + ":</b> "
207  + QString::number(integer).toHtmlEscaped() + "<br>";
208  }
209  sf.reset(secfile_load_section(fn_bytes.data(), QStringLiteral("players"),
210  true));
211  if (sf) {
212  integer = secfile_lookup_int_default(sf.get(), -1, "players.nplayers");
213  if (integer >= 0) {
214  final_str = final_str + "<b>" + _("Players") + ":</b>" + " "
215  + QString::number(integer).toHtmlEscaped() + "<br>";
216  }
217  num_players = integer;
218  }
219  for (int i = 0; i < num_players; i++) {
220  pl_str = QStringLiteral("player") + QString::number(i);
221  pl_bytes = pl_str.toLocal8Bit();
222  sf.reset(secfile_load_section(fn_bytes.data(), pl_bytes.data(), true));
223  if (sf) {
224  if (!(sbool = secfile_lookup_bool_default(
225  sf.get(), true, "player%d.unassigned_user", i))) {
226  curr_player = i;
227  break;
228  }
229  }
230  }
231  // Break case (and return) if no human player found
232  if (pl_str == nullptr) {
233  ui.load_save_text->setText(final_str);
234  return;
235  }
236 
237  // Information about human player
238  pl_bytes = pl_str.toLocal8Bit();
239  sf.reset(secfile_load_section(fn_bytes.data(), pl_bytes.data(), true));
240  if (sf) {
241  sname = secfile_lookup_str_default(sf.get(), nullptr,
242  "player%d.nation", curr_player);
243  if (sname) {
244  final_str = final_str + "<b>" + _("Nation") + ":</b> "
245  + QString(sname).toHtmlEscaped() + "<br>";
246  }
247  integer = secfile_lookup_int_default(sf.get(), -1, "player%d.ncities",
248  curr_player);
249  if (integer >= 0) {
250  final_str = final_str + "<b>" + _("Cities") + ":</b> "
251  + QString::number(integer).toHtmlEscaped() + "<br>";
252  }
253  integer = secfile_lookup_int_default(sf.get(), -1, "player%d.nunits",
254  curr_player);
255  if (integer >= 0) {
256  final_str = final_str + "<b>" + _("Units") + ":</b> "
257  + QString::number(integer).toHtmlEscaped() + "<br>";
258  }
259  integer = secfile_lookup_int_default(sf.get(), -1, "player%d.gold",
260  curr_player);
261  if (integer >= 0) {
262  final_str = final_str + "<b>" + _("Gold") + ":</b> "
263  + QString::number(integer).toHtmlEscaped() + "<br>";
264  }
265  nat_x = 0;
266  for (nat_y = 0; nat_y > -1; nat_y++) {
267  const char *line = secfile_lookup_str_default(
268  sf.get(), nullptr, "player%d.map_t%04d", curr_player, nat_y);
269  if (line == nullptr) {
270  break;
271  }
272  nat_x = qstrlen(line);
273  str_pixmap = str_pixmap + line;
274  }
275 
276  // Reset terrain information
277  terrain_type_iterate(pterr) { pterr->identifier_load = '\0'; }
279 
280  // Load possible terrains and their identifiers (chars)
281  sf.reset(secfile_load_section(fn_bytes.data(),
282  QStringLiteral("savefile"), true));
283  if (sf) {
284  while ((terr_name = secfile_lookup_str_default(
285  sf.get(), nullptr, "savefile.terrident%d.name", ii))
286  != nullptr) {
287  struct terrain *pterr = terrain_by_rule_name(terr_name);
288  if (pterr != nullptr) {
289  const char *iptr = secfile_lookup_str_default(
290  sf.get(), nullptr, "savefile.terrident%d.identifier", ii);
291  fc_assert_ret(iptr != nullptr);
292  pterr->identifier_load = *iptr;
293  }
294  ii++;
295  }
296  }
297  // Create image
298  QImage img(nat_x, nat_y, QImage::Format_ARGB32_Premultiplied);
299  img.fill(Qt::black);
300  for (int a = 0; a < nat_x; a++) {
301  for (int b = 0; b < nat_y; b++) {
302  struct terrain *tr;
303  struct rgbcolor *rgb;
304  tr = char2terrain(str_pixmap.at(b * nat_x + a).toLatin1());
305  if (tr != nullptr) {
306  rgb = tr->rgb;
307  QColor col;
308  col.setRgb(rgb->r, rgb->g, rgb->b);
309  img.setPixel(a, b, col.rgb());
310  }
311  }
312  }
313  if (img.width() > 1) {
314  ui.load_pix->setPixmap(QPixmap::fromImage(img).scaledToHeight(200));
315  } else {
316  ui.load_pix->setPixmap(*(new QPixmap));
317  }
318  ui.load_pix->setFixedSize(ui.load_pix->pixmap().width(),
319  ui.load_pix->pixmap().height());
320  sf.reset(secfile_load_section(fn_bytes.data(),
321  QStringLiteral("research"), true));
322  if (sf) {
324  sf.get(), nullptr, "research.r%d.now_name", curr_player);
325  if (sname) {
326  final_str = final_str + "<b>" + _("Researching") + ":</b> "
327  + QString(sname).toHtmlEscaped();
328  }
329  }
330  }
331  ui.load_save_text->setText(final_str);
332 
333  ui.load_save_text->show();
334  ui.load_pix->show();
335  ui.buttons->button(QDialogButtonBox::Ok)->setEnabled(true);
336  } else {
337  // Couldn't load the save. Clear the preview and prevent loading it.
338  ui.load_save_text->hide();
339  ui.load_pix->hide();
340  ui.buttons->button(QDialogButtonBox::Ok)->setEnabled(false);
341  }
342 }
void slot_disconnect()
Disconnect from server and return to MAIN PAGE.
Definition: fc_client.cpp:323
void start_from_file(const QString &file)
start from save file
Definition: fc_client.cpp:669
void start_from_save()
Starts game from chosen save - chosen_file (save or scenario)
Definition: page_load.cpp:130
void slot_selection_changed(const QItemSelection &, const QItemSelection &)
Definition: page_load.cpp:161
void state_preview()
State of preview has been changed.
Definition: page_load.cpp:150
fc_client * gui
Definition: page_load.h:33
QString current_file
Definition: page_load.h:35
void browse_saves()
Browse saves directory.
Definition: page_load.cpp:135
Ui::FormPageLoad ui
Definition: page_load.h:34
void update_load_page()
Updates saves to load and updates in tableview = saves_load.
Definition: page_load.cpp:97
~page_load() override
page_load(QWidget *, fc_client *)
Definition: page_load.cpp:53
class fc_client * king()
Return fc_client instance.
Definition: gui_main.cpp:58
#define _(String)
Definition: fcintl.h:50
#define fc_assert_ret(condition)
Definition: log.h:112
#define nat_x
#define nat_y
client_options * gui_options
Definition: options.cpp:74
static struct terrain * char2terrain(char ch)
Helper function for drawing map of savegames.
Definition: page_load.cpp:38
void secfile_destroy(struct section_file *secfile)
Free a section file.
struct section_file * secfile_load_section(const QString &filename, const QString &section, bool allow_duplicates)
Create a section file from a file, read only one particular section.
bool secfile_lookup_bool_default(const struct section_file *secfile, bool def, const char *path,...)
Lookup a boolean value in the secfile.
const char * secfile_lookup_str_default(const struct section_file *secfile, const char *def, const char *path,...)
Lookup a string value in the secfile.
int secfile_lookup_int_default(const struct section_file *secfile, int def, const char *path,...)
Lookup a integer value in the secfile.
const QStringList & get_save_dirs()
Returns a list of save directory paths, in the order in which they should be searched.
Definition: shared.cpp:563
QFileInfoList find_files_in_path(const QStringList &path, const QString &pattern, bool nodups)
Search for file names matching the pattern in the provided list of directories.
Definition: shared.cpp:681
bool gui_qt_show_preview
Definition: options.h:165
Definition: mapimg.cpp:266
Definition: climisc.h:66
int g
Definition: rgbcolor.h:27
int b
Definition: rgbcolor.h:27
int r
Definition: rgbcolor.h:27
char identifier_load
Definition: terrain.h:178
struct rgbcolor * rgb
Definition: terrain.h:240
struct terrain * terrain_by_rule_name(const char *name)
Return the terrain type matching the name, or T_UNKNOWN if none matches.
Definition: terrain.cpp:140
#define terrain_type_iterate(_p)
Definition: terrain.h:331
#define T_UNKNOWN
Definition: terrain.h:51
#define TERRAIN_UNKNOWN_IDENTIFIER
Definition: terrain.h:181
#define terrain_type_iterate_end
Definition: terrain.h:337