1 /***********************************************************************
2  Freeciv - Copyright (C) 1996-2004 - The Freeciv Team
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 2, or (at your option)
6    any later version.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 ***********************************************************************/
13 
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
17 
18 // Qt
19 #include <QAction>
20 #include <QApplication>
21 #include <QCheckBox>
22 #include <QDateTime>
23 #include <QFileDialog>
24 #include <QGridLayout>
25 #include <QHeaderView>
26 #include <QLineEdit>
27 #include <QPainter>
28 #include <QSignalMapper>
29 #include <QSplitter>
30 #include <QStackedWidget>
31 #include <QTableWidget>
32 #include <QTextEdit>
33 #include <QTreeWidget>
34 
35 // common
36 #include "game.h"
37 #include "version.h"
38 
39 // client
40 #include "client_main.h"
41 #include "connectdlg_common.h"
42 
43 // gui-qt
44 #include "colors.h"
45 #include "dialogs.h"
46 #include "fc_client.h"
47 #include "pages.h"
48 #include "plrdlg.h"
49 #include "qtg_cxxside.h"
50 #include "sidebar.h"
51 #include "sprite.h"
52 #include "voteinfo_bar.h"
53 
54 extern "C" {
55 #include "repodlgs_g.h"
56 #include "cityrep_g.h"
57 const char *science_dialog_text(void);
58 const char *get_bulb_tooltip(void);
59 const char *get_global_warming_tooltip(void);
60 const char *get_nuclear_winter_tooltip(void);
61 const char *get_government_tooltip(void);
62 const char *get_info_label_text_popup(void);
63 const char *get_info_label_text(bool);
64 const char *text_happiness_cities(const struct city *pcity);
65 }
66 
67 int last_center_capital = 0;
68 int last_center_player_city = 0;
69 int last_center_enemy_city = 0;
70 extern void toggle_units_report(bool);
71 extern void popup_shortcuts_dialog();
72 static void center_next_enemy_city();
73 static void center_next_player_city();
74 static void center_next_player_capital();
75 static struct server_scan *meta_scan, *lan_scan;
76 static bool holding_srv_list_mutex = false;
77 static enum connection_state connection_status;
78 static struct terrain *char2terrain(char ch);
79 static void cycle_enemy_units();
80 int last_center_enemy = 0;
81 
82 /****************************************************************************
83   Helper function for drawing map of savegames. Converts stored map char in
84   savefile to proper terrain.
85 ****************************************************************************/
char2terrain(char ch)86 static struct terrain *char2terrain(char ch)
87 {
88   if (ch == TERRAIN_UNKNOWN_IDENTIFIER) {
89     return T_UNKNOWN;
90   }
91   terrain_type_iterate(pterrain) {
92     if (pterrain->identifier_load == ch) {
93       return pterrain;
94     }
95   } terrain_type_iterate_end;
96   return nullptr;
97 }
98 
99 
100 /**************************************************************************
101   Sets the "page" that the client should show.  See also pages_g.h.
102 **************************************************************************/
qtg_real_set_client_page(enum client_pages page)103 void qtg_real_set_client_page(enum client_pages page)
104 {
105   gui()->switch_page(page);
106 }
107 
108 /****************************************************************************
109   Set the list of available rulesets.  The default ruleset should be
110   "default", and if the user changes this then set_ruleset() should be
111   called.
112 ****************************************************************************/
qtg_set_rulesets(int num_rulesets,char ** rulesets)113 void qtg_set_rulesets(int num_rulesets, char **rulesets)
114 {
115   gui()->pr_options->set_rulesets(num_rulesets, rulesets);
116 }
117 
118 /**************************************************************************
119   Returns current client page
120 **************************************************************************/
qtg_get_current_client_page()121 enum client_pages qtg_get_current_client_page()
122 {
123   return gui()->current_page();
124 }
125 
126 /**************************************************************************
127   update the start page.
128 **************************************************************************/
update_start_page(void)129 void update_start_page(void)
130 {
131   gui()->update_start_page();
132 }
133 
134 
135 /**************************************************************************
136   Creates buttons and layouts for start page.
137 **************************************************************************/
create_main_page(void)138 void fc_client::create_main_page(void)
139 {
140 
141   QPixmap main_graphics(tileset_main_intro_filename(tileset));
142   QLabel* free_main_pic = new QLabel;
143   QPainter painter(&main_graphics);
144   QStringList buttons_names;
145   int buttons_nr;
146   char msgbuf[128];
147   const char *rev_ver;
148   QFont f = QApplication::font();
149   QFontMetrics fm(f);
150   int row = 0;
151 #if IS_BETA_VERSION
152   QPalette warn_color;
153   QLabel *beta_label = new QLabel(beta_message());
154 #endif /* IS_BETA_VERSION */
155 
156   pages_layout[PAGE_MAIN] = new QGridLayout;
157 
158   painter.setPen(Qt::white);
159 
160   rev_ver = fc_git_revision();
161 
162   if (rev_ver == NULL) {
163     /* TRANS: "version 2.6.0, Qt client" */
164     fc_snprintf(msgbuf, sizeof(msgbuf), _("%s%s, Qt client"),
165                 word_version(), VERSION_STRING);
166   } else {
167     fc_snprintf(msgbuf, sizeof(msgbuf), "%s%s",
168                 word_version(), VERSION_STRING);
169     painter.drawText(10,
170                      main_graphics.height() - fm.descent() - fm.height() * 2,
171                      msgbuf);
172 
173     /* TRANS: "commit: [modified] <git commit id>" */
174     fc_snprintf(msgbuf, sizeof(msgbuf), _("commit: %s"), rev_ver);
175     painter.drawText(10,
176                      main_graphics.height() - fm.descent() - fm.height(),
177                      msgbuf);
178 
179     strncpy(msgbuf, _("Qt client"), sizeof(msgbuf) - 1);
180   }
181 
182   painter.drawText(main_graphics.width() - fm.horizontalAdvance(msgbuf) - 10,
183                    main_graphics.height() - fm.descent(), msgbuf);
184   free_main_pic->setPixmap(main_graphics);
185   pages_layout[PAGE_MAIN]->addWidget(free_main_pic,
186                                      row++, 0, 1, 2, Qt::AlignCenter);
187 
188 #if IS_BETA_VERSION
189   warn_color.setColor(QPalette::WindowText, Qt::red);
190   beta_label->setPalette(warn_color);
191   beta_label->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Maximum);
192   beta_label->setAlignment(Qt::AlignCenter);
193   pages_layout[PAGE_MAIN]->addWidget(beta_label,
194                                      row++, 0, 1, 2, Qt::AlignHCenter);
195 #endif
196 
197   buttons_names << _("Start new game") << _("Start scenario game")
198                 << _("Load saved game") << _("Connect to network game")
199                 << _("Options") << _("Quit");
200 
201   buttons_nr = buttons_names.count();
202 
203   for (int iter = 0; iter < buttons_nr; iter++) {
204     button = new QPushButton(buttons_names[iter]);
205 
206     switch (iter) {
207     case 0:
208       pages_layout[PAGE_MAIN]->addWidget(button, row + 1, 0);
209       connect(button, &QAbstractButton::clicked, this, &fc_client::start_new_game);
210       break;
211     case 1:
212       pages_layout[PAGE_MAIN]->addWidget(button, row + 2, 0);
213       connect(button, SIGNAL(clicked()), switch_page_mapper, SLOT(map()));
214       switch_page_mapper->setMapping(button, PAGE_SCENARIO);
215       break;
216     case 2:
217       pages_layout[PAGE_MAIN]->addWidget(button, row + 3, 0);
218       connect(button, SIGNAL(clicked()), switch_page_mapper, SLOT(map()));
219       switch_page_mapper->setMapping(button, PAGE_LOAD);
220       break;
221     case 3:
222       pages_layout[PAGE_MAIN]->addWidget(button, row + 1, 1);
223       connect(button, SIGNAL(clicked()), switch_page_mapper, SLOT(map()));
224       switch_page_mapper->setMapping(button, PAGE_NETWORK);
225       break;
226     case 4:
227       pages_layout[PAGE_MAIN]->addWidget(button, row + 2, 1);
228       connect(button, &QAbstractButton::clicked, this, &fc_client::popup_client_options);
229       break;
230     case 5:
231       pages_layout[PAGE_MAIN]->addWidget(button, row + 3, 1);
232       QObject::connect(button, &QAbstractButton::clicked, this, &fc_client::quit);
233       break;
234     default:
235       break;
236     }
237   }
238 }
239 
240 /**************************************************************************
241   Update network page connection state.
242 **************************************************************************/
set_connection_state(enum connection_state state)243 void fc_client::set_connection_state(enum connection_state state)
244 {
245   switch (state) {
246   case LOGIN_TYPE:
247     set_status_bar("");
248     connect_password_edit->setText("");
249     connect_password_edit->setDisabled(true);
250     connect_confirm_password_edit->setText("");
251     connect_confirm_password_edit->setDisabled(true);
252     break;
253   case NEW_PASSWORD_TYPE:
254     connect_password_edit->setText("");
255     connect_confirm_password_edit->setText("");
256     connect_password_edit->setDisabled(false);
257     connect_confirm_password_edit->setDisabled(false);
258     connect_password_edit->setFocus(Qt::OtherFocusReason);
259     break;
260   case ENTER_PASSWORD_TYPE:
261     connect_password_edit->setText("");
262     connect_confirm_password_edit->setText("");
263     connect_password_edit->setDisabled(false);
264     connect_confirm_password_edit->setDisabled(true);
265     connect_password_edit->setFocus(Qt::OtherFocusReason);
266 
267 
268     break;
269   case WAITING_TYPE:
270     set_status_bar("");
271     break;
272   }
273 
274   connection_status = state;
275 }
276 
277 /***************************************************************************
278   Creates buttons and layouts for network page.
279 ***************************************************************************/
create_network_page(void)280 void fc_client::create_network_page(void)
281 {
282   QHeaderView *header;
283   QLabel *connect_msg;
284   QLabel *lan_label;
285   QPushButton *network_button;
286 
287   pages_layout[PAGE_NETWORK] = new QGridLayout;
288   QVBoxLayout *page_network_layout = new QVBoxLayout;
289   QGridLayout *page_network_grid_layout = new QGridLayout;
290   QHBoxLayout *page_network_lan_layout = new QHBoxLayout;
291   QHBoxLayout *page_network_wan_layout = new QHBoxLayout;
292 
293   connect_host_edit = new QLineEdit;
294   connect_port_edit = new QLineEdit;
295   connect_login_edit = new QLineEdit;
296   connect_password_edit = new QLineEdit;
297   connect_confirm_password_edit = new QLineEdit;
298 
299   connect_password_edit->setEchoMode(QLineEdit::Password);
300   connect_confirm_password_edit->setEchoMode(QLineEdit::Password);
301 
302   connect_password_edit->setDisabled(true);
303   connect_confirm_password_edit->setDisabled(true);
304   connect_lan = new QWidget;
305   connect_metaserver = new QWidget;
306   lan_widget = new QTableWidget;
307   wan_widget = new QTableWidget;
308   info_widget = new QTableWidget;
309 
310   QStringList servers_list;
311   servers_list << _("Server Name") << _("Port") << _("Version")
312                << _("Status") << Q_("?count:Players") << _("Comment");
313   QStringList server_info;
314   server_info << _("Name") << _("Type") << _("Host") << _("Nation");
315 
316   lan_widget->setRowCount(0);
317   lan_widget->setColumnCount(servers_list.count());
318   lan_widget->verticalHeader()->setVisible(false);
319   lan_widget->setAutoScroll(false);
320 
321   wan_widget->setRowCount(0);
322   wan_widget->setColumnCount(servers_list.count());
323   wan_widget->verticalHeader()->setVisible(false);
324   wan_widget->setAutoScroll(false);
325 
326   info_widget->setRowCount(0);
327   info_widget->setColumnCount(server_info.count());
328   info_widget->verticalHeader()->setVisible(false);
329 
330   lan_widget->setHorizontalHeaderLabels(servers_list);
331   lan_widget->setProperty("showGrid", "false");
332   lan_widget->setProperty("selectionBehavior", "SelectRows");
333   lan_widget->setEditTriggers(QAbstractItemView::NoEditTriggers);
334   lan_widget->setSelectionMode(QAbstractItemView::SingleSelection);
335 
336   wan_widget->setHorizontalHeaderLabels(servers_list);
337   wan_widget->setProperty("showGrid", "false");
338   wan_widget->setProperty("selectionBehavior", "SelectRows");
339   wan_widget->setEditTriggers(QAbstractItemView::NoEditTriggers);
340   wan_widget->setSelectionMode(QAbstractItemView::SingleSelection);
341 
342   connect(wan_widget->selectionModel(),
343           &QItemSelectionModel::selectionChanged, this,
344           &fc_client::slot_selection_changed);
345 
346   connect(lan_widget->selectionModel(),
347           &QItemSelectionModel::selectionChanged, this,
348           &fc_client::slot_selection_changed);
349   connect(wan_widget, &QTableWidget::itemDoubleClicked, this,
350           &fc_client::slot_connect);
351   connect(lan_widget, &QTableWidget::itemDoubleClicked, this,
352           &fc_client::slot_connect);
353 
354   info_widget->setHorizontalHeaderLabels(server_info);
355   info_widget->setProperty("selectionBehavior", "SelectRows");
356   info_widget->setEditTriggers(QAbstractItemView::NoEditTriggers);
357   info_widget->setSelectionMode(QAbstractItemView::SingleSelection);
358   info_widget->setProperty("showGrid", "false");
359   info_widget->setAlternatingRowColors(true);
360 
361   header = lan_widget->horizontalHeader();
362   header->setSectionResizeMode(0, QHeaderView::Stretch);
363   header->setStretchLastSection(true);
364   header = wan_widget->horizontalHeader();
365   header->setSectionResizeMode(0, QHeaderView::Stretch);
366   header->setStretchLastSection(true);
367   header = info_widget->horizontalHeader();
368   header->setSectionResizeMode(0, QHeaderView::Stretch);
369   header->setStretchLastSection(true);
370 
371   QStringList label_names;
372   label_names << _("Connect") << _("Port") << _("Login")
373               << _("Password") << _("Confirm Password");
374 
375   for (int i = 0; i < label_names.count(); i++) {
376     connect_msg = new QLabel;
377     connect_msg->setText(label_names[i]);
378     page_network_grid_layout->addWidget(connect_msg, i, 0, Qt::AlignHCenter);
379   }
380 
381   page_network_grid_layout->addWidget(connect_host_edit, 0, 1);
382   page_network_grid_layout->addWidget(connect_port_edit, 1, 1);
383   page_network_grid_layout->addWidget(connect_login_edit, 2, 1);
384   page_network_grid_layout->addWidget(connect_password_edit, 3, 1);
385   page_network_grid_layout->addWidget(connect_confirm_password_edit, 4, 1);
386   page_network_grid_layout->addWidget(info_widget, 0, 2, 5, 4);
387 
388   network_button = new QPushButton(_("Refresh"));
389   QObject::connect(network_button, &QAbstractButton::clicked, this,
390                    &fc_client::update_network_lists);
391   page_network_grid_layout->addWidget(network_button, 5, 0);
392 
393   network_button = new QPushButton(_("Cancel"));
394   connect(network_button, SIGNAL(clicked()), switch_page_mapper,
395           SLOT(map()));
396   switch_page_mapper->setMapping(network_button, PAGE_MAIN);
397   page_network_grid_layout->addWidget(network_button, 5, 2, 1, 1);
398 
399   network_button = new QPushButton(_("Connect"));
400   page_network_grid_layout->addWidget(network_button, 5, 5, 1, 1);
401   connect(network_button, &QAbstractButton::clicked, this, &fc_client::slot_connect);
402   connect(connect_login_edit, &QLineEdit::returnPressed,
403           this, &fc_client::slot_connect);
404   connect(connect_password_edit, &QLineEdit::returnPressed,
405           this, &fc_client::slot_connect);
406   connect(connect_confirm_password_edit, &QLineEdit::returnPressed,
407           this, &fc_client::slot_connect);
408 
409   connect_lan->setLayout(page_network_lan_layout);
410   connect_metaserver->setLayout(page_network_wan_layout);
411   page_network_lan_layout->addWidget(lan_widget, 0);
412   page_network_wan_layout->addWidget(wan_widget, 1);
413   lan_label = new QLabel(_("Internet servers:"));
414   page_network_layout->addWidget(lan_label, 1);
415   page_network_layout->addWidget(wan_widget, 10);
416   lan_label = new QLabel(_("Local servers:"));
417   page_network_layout->addWidget(lan_label, 1);
418   page_network_layout->addWidget(lan_widget, 1);
419   page_network_grid_layout->setColumnStretch(3, 4);
420   pages_layout[PAGE_NETWORK]->addLayout(page_network_layout, 1, 1);
421   pages_layout[PAGE_NETWORK]->addLayout(page_network_grid_layout, 2, 1);
422 
423 }
424 
425 /***************************************************************************
426   Sets application status bar for given time in miliseconds
427 ***************************************************************************/
set_status_bar(QString message,int timeout)428 void fc_client::set_status_bar(QString message, int timeout)
429 {
430   if (status_bar_label->text().isEmpty()) {
431     status_bar_label->setText(message);
432     QTimer::singleShot(timeout, this, SLOT(clear_status_bar()));
433   } else {
434     status_bar_queue.append(message);
435     while (status_bar_queue.count() > 3){
436       status_bar_queue.removeFirst();
437     }
438   }
439 }
440 
441 /***************************************************************************
442   Clears status bar or shows next message in queue if exists
443 ***************************************************************************/
clear_status_bar()444 void fc_client::clear_status_bar()
445 {
446   QString str;
447 
448   if (!status_bar_queue.isEmpty()) {
449     str = status_bar_queue.takeFirst();
450     status_bar_label->setText(str);
451     QTimer::singleShot(2000, this, SLOT(clear_status_bar()));
452   } else {
453     status_bar_label->setText("");
454   }
455 }
456 
457 /***************************************************************************
458   Creates page LOADING, showing label with Loading text
459 ***************************************************************************/
create_loading_page()460 void fc_client::create_loading_page()
461 {
462   pages_layout[PAGE_GAME + 1] = new QGridLayout;
463   QLabel *label = new QLabel(_("Loading..."));
464   pages_layout[PAGE_GAME + 1]->addWidget(label, 0, 0, 1, 1,
465                                          Qt::AlignHCenter);
466 }
467 
468 /***************************************************************************
469   Creates buttons and layouts for load page.
470 ***************************************************************************/
create_load_page()471 void fc_client::create_load_page()
472 {
473   pages_layout[PAGE_LOAD] = new QGridLayout;
474   QPushButton *but;
475   QHeaderView *header;
476   QLabel *lbl_show_preview;
477   QWidget *wdg;
478   QHBoxLayout *hbox;
479 
480   saves_load = new QTableWidget;
481   wdg = new QWidget;
482   hbox = new QHBoxLayout;
483   QStringList sav;
484   lbl_show_preview = new QLabel(_("Show preview"));
485   sav << _("Choose Saved Game to Load") << _("Date");
486   load_pix = new QLabel;
487   load_pix->setProperty("themed_border", true);
488   load_pix->setFixedSize(0 ,0);
489   load_save_text = new QLabel;
490   load_save_text->setTextFormat(Qt::RichText);
491   load_save_text->setWordWrap(true);
492   show_preview = new QCheckBox;
493   show_preview->setChecked(gui_options.gui_qt_show_preview);
494   saves_load->setAlternatingRowColors(true);
495   saves_load->setRowCount(0);
496   saves_load->setColumnCount(sav.count());
497   saves_load->setHorizontalHeaderLabels(sav);
498   hbox->addWidget(show_preview);
499   hbox->addWidget(lbl_show_preview, Qt::AlignLeft);
500   wdg->setLayout(hbox);
501 
502   saves_load->setProperty("showGrid", "false");
503   saves_load->setProperty("selectionBehavior", "SelectRows");
504   saves_load->setEditTriggers(QAbstractItemView::NoEditTriggers);
505   saves_load->setSelectionMode(QAbstractItemView::SingleSelection);
506   saves_load->verticalHeader()->setVisible(false);
507 
508   header = saves_load->horizontalHeader();
509   header->setSectionResizeMode(0, QHeaderView::Stretch);
510   header->setStretchLastSection(true);
511 
512   pages_layout[PAGE_LOAD]->addWidget(saves_load, 0, 0, 1, 4);
513   connect(saves_load->selectionModel(),
514           &QItemSelectionModel::selectionChanged, this,
515           &fc_client::slot_selection_changed);
516   connect(show_preview, &QCheckBox::stateChanged, this,
517           &fc_client::state_preview);
518   pages_layout[PAGE_LOAD]->addWidget(wdg, 1, 0);
519   pages_layout[PAGE_LOAD]->addWidget(load_save_text, 2, 0, 1, 2);
520   pages_layout[PAGE_LOAD]->addWidget(load_pix, 2, 2, 1, 2);
521 
522   but = new QPushButton;
523   but->setText(_("Browse..."));
524   but->setIcon(QApplication::style()->standardIcon(QStyle::SP_DirIcon));
525   connect(but, &QAbstractButton::clicked, this, &fc_client::browse_saves);
526   pages_layout[PAGE_LOAD]->addWidget (but, 3, 0);
527 
528   but = new QPushButton;
529   but->setText(_("Cancel"));
530   but->setIcon(QApplication::style()->standardIcon(
531                                       QStyle::SP_DialogCancelButton));
532   connect(but, &QAbstractButton::clicked, this, &fc_client::slot_disconnect);
533   switch_page_mapper->setMapping(but, PAGE_MAIN);
534   pages_layout[PAGE_LOAD]->addWidget(but, 3, 2);
535 
536   but = new QPushButton;
537   but->setText(_("Load"));
538   but->setIcon(QApplication::style()->standardIcon(
539                                       QStyle::SP_DialogOkButton));
540   connect(but, &QAbstractButton::clicked, this, &fc_client::start_from_save);
541   pages_layout[PAGE_LOAD]->addWidget(but, 3, 3);
542   pages_layout[PAGE_LOAD]->setColumnStretch(3, 10);
543   pages_layout[PAGE_LOAD]->setColumnStretch(2, 10);
544   pages_layout[PAGE_LOAD]->setColumnStretch(0, 10);
545 
546 }
547 
548 /***************************************************************************
549  * Creates buttons and layouts for scenario page.
550  **************************************************************************/
create_scenario_page()551 void fc_client::create_scenario_page()
552 {
553   QPushButton *but;
554   QHeaderView *header;
555   QStringList sav;
556 
557   pages_layout[PAGE_SCENARIO] = new QGridLayout;
558   scenarios_load = new QTableWidget;
559   scenarios_view = new QTextEdit;
560   scenarios_text = new QLabel;
561 
562   scenarios_view->setObjectName("scenarios_view");
563   scenarios_text->setTextFormat(Qt::RichText);
564   scenarios_text->setWordWrap(true);
565   sav << _("Choose a Scenario");
566   scenarios_load->setRowCount(0);
567   scenarios_load->setColumnCount(sav.count());
568   scenarios_load->setHorizontalHeaderLabels(sav);
569   scenarios_load->setProperty("showGrid", "false");
570   scenarios_load->setProperty("selectionBehavior", "SelectRows");
571   scenarios_load->setEditTriggers(QAbstractItemView::NoEditTriggers);
572   scenarios_load->setSelectionMode(QAbstractItemView::SingleSelection);
573   scenarios_load->verticalHeader()->setVisible(false);
574   pages_layout[PAGE_SCENARIO]->addWidget(scenarios_load, 0, 0, 3, 3,
575                                          Qt::AlignLeft);
576   pages_layout[PAGE_SCENARIO]->addWidget(scenarios_view, 1, 3, 2, 3);
577   pages_layout[PAGE_SCENARIO]->addWidget(scenarios_text, 0, 3, 1, 2,
578                                          Qt::AlignTop);
579   scenarios_view->setReadOnly(true);
580   scenarios_view->setWordWrapMode(QTextOption::WordWrap);
581   scenarios_text->setAlignment(Qt::AlignCenter);
582 
583   header = scenarios_load->horizontalHeader();
584   header->setSectionResizeMode(0, QHeaderView::Stretch);
585   header->setStretchLastSection(true);
586   connect(scenarios_load->selectionModel(),
587           &QItemSelectionModel::selectionChanged, this,
588           &fc_client::slot_selection_changed);
589 
590   but = new QPushButton;
591   but->setText(_("Browse..."));
592   but->setIcon(QApplication::style()->standardIcon(QStyle::SP_DirIcon));
593   connect(but, &QAbstractButton::clicked, this, &fc_client::browse_scenarios);
594   pages_layout[PAGE_SCENARIO]->addWidget(but, 4, 0);
595 
596   but = new QPushButton;
597   but->setText(_("Cancel"));
598   but->setIcon(QApplication::style()->standardIcon(
599                                         QStyle::SP_DialogCancelButton));
600   connect(but, &QAbstractButton::clicked, this, &fc_client::slot_disconnect);
601   switch_page_mapper->setMapping(but, PAGE_MAIN);
602   pages_layout[PAGE_SCENARIO]->addWidget(but, 4, 3);
603 
604   pages_layout[PAGE_SCENARIO]->setColumnStretch(2, 10);
605   pages_layout[PAGE_SCENARIO]->setColumnStretch(4, 20);
606   pages_layout[PAGE_SCENARIO]->setColumnStretch(3, 20);
607   pages_layout[PAGE_SCENARIO]->setRowStretch(1, 5);
608   but = new QPushButton;
609   but->setText(_("Load Scenario"));
610   but->setIcon(QApplication::style()->standardIcon(
611                                         QStyle::SP_DialogOkButton));
612   connect(but, &QAbstractButton::clicked, this, &fc_client::start_scenario);
613   pages_layout[PAGE_SCENARIO]->addWidget(but, 4, 4);
614 }
615 
616 /***************************************************************************
617   Creates buttons and layouts for start page.
618 ***************************************************************************/
create_start_page()619 void fc_client::create_start_page()
620 {
621   QPushButton *but;
622   QSplitter *splitter;
623   QGridLayout *up_layout;
624   QGridLayout *down_layout;
625   QWidget *up_widget;
626   QWidget *down_widget;
627   QFont f;
628 
629   QStringList player_widget_list;
630   pages_layout[PAGE_START] = new QGridLayout;
631   up_layout = new QGridLayout;
632   down_layout = new QGridLayout;
633   start_players_tree = new QTreeWidget;
634   pr_options = new pregame_options(this);
635   chat_line = new chat_input;
636   chat_line->setProperty("doomchat", true);
637   output_window = new QTextEdit;
638   output_window->setReadOnly(false);
639   f.setBold(true);
640   output_window->setFont(f);
641 
642   pr_options->init();
643   player_widget_list << _("Name") << _("Ready") << Q_("?player:Leader")
644                      << _("Flag") << _("Border") << _("Nation") << _("Team")
645                      << _("Host");
646 
647   start_players_tree->setColumnCount(player_widget_list.count());
648   start_players_tree->setHeaderLabels(player_widget_list);
649   start_players_tree->setContextMenuPolicy(Qt::CustomContextMenu);
650   start_players_tree->setProperty("selectionBehavior", "SelectRows");
651   start_players_tree->setEditTriggers(QAbstractItemView::NoEditTriggers);
652   start_players_tree->setRootIsDecorated(false);
653 
654   connect(start_players_tree,
655           SIGNAL(customContextMenuRequested(const QPoint&)),
656           SLOT(start_page_menu(QPoint)));
657 
658   up_layout->addWidget(start_players_tree, 0, 0, 3, 6);
659   up_layout->addWidget(pr_options, 0, 6, 3, 2);
660   but = new QPushButton;
661   but->setText(_("Disconnect"));
662   but->setIcon(style()->standardPixmap(QStyle::SP_DialogCancelButton));
663   QObject::connect(but, &QAbstractButton::clicked, this, &fc_client::slot_disconnect);
664   down_layout->addWidget(but, 5, 4);
665   nation_button = new QPushButton;
666   nation_button->setText(_("Pick Nation"));
667   nation_button->setIcon(fc_icons::instance()->get_icon("flag"));
668   down_layout->addWidget(nation_button, 5, 5);
669   QObject::connect(nation_button, &QAbstractButton::clicked, this,
670                    &fc_client::slot_pick_nation);
671 
672   obs_button = new QPushButton;
673   obs_button->setText(_("Observe"));
674   obs_button->setIcon(fc_icons::instance()->get_icon("meeting-observer"));
675   down_layout->addWidget(obs_button, 5, 6);
676   QObject::connect(obs_button, &QAbstractButton::clicked, this,
677                    &fc_client::slot_pregame_observe);
678   start_button = new QPushButton;
679   start_button->setText(_("Start"));
680   start_button->setIcon(style()->standardPixmap(QStyle::SP_DialogOkButton));
681   down_layout->addWidget(start_button, 5, 7);
682   QObject::connect(start_button, &QAbstractButton::clicked, this,
683                    &fc_client::slot_pregame_start);
684   pre_vote = new pregamevote;
685 
686   down_layout->addWidget(pre_vote, 4, 0, 1, 4);
687   down_layout->addWidget(chat_line, 5, 0, 1, 4);
688   down_layout->addWidget(output_window, 3, 0, 1, 8);
689   splitter = new QSplitter;
690   up_widget = new QWidget();
691   down_widget = new QWidget();
692   up_widget->setLayout(up_layout);
693   down_widget->setLayout(down_layout);
694   splitter->addWidget(up_widget);
695   splitter->addWidget(down_widget);
696   splitter->setOrientation(Qt::Vertical);
697   pages_layout[PAGE_START]->addWidget(splitter);
698 }
699 
700 /***************************************************************************
701   Creates buttons and layouts for game page.
702 ***************************************************************************/
create_game_page()703 void fc_client::create_game_page()
704 {
705   QGridLayout *game_layout;
706 
707   pages_layout[PAGE_GAME] = new QGridLayout;
708   game_main_widget = new QWidget;
709   game_layout = new QGridLayout;
710   game_layout->setContentsMargins(0, 0, 0, 0);
711   game_layout->setSpacing(0);
712   mapview_wdg = new map_view();
713   mapview_wdg->setFocusPolicy(Qt::WheelFocus);
714   sidebar_wdg = new fc_sidebar();
715 
716   sw_map = new fc_sidewidget(fc_icons::instance()->get_pixmap("view"),
717                              Q_("?noun:View"), "MAP", side_show_map);
718   sw_tax = new fc_sidewidget(nullptr, nullptr, "", side_rates_wdg, SW_TAX);
719   sw_indicators = new fc_sidewidget(nullptr, nullptr, "", side_show_map,
720                                     SW_INDICATORS);
721   sw_indicators->set_right_click(side_indicators_menu);
722   sw_cunit = new fc_sidewidget(fc_icons::instance()->get_pixmap("units"),
723                                _("Units"), "",
724                                toggle_units_report);
725   sw_cities = new fc_sidewidget(fc_icons::instance()->get_pixmap("cities"),
726                                 _("Cities"), "CTS",
727                                 city_report_dialog_popup);
728   sw_cities->set_wheel_up(center_next_enemy_city);
729   sw_cities->set_wheel_down(center_next_player_city);
730   sw_diplo = new fc_sidewidget(fc_icons::instance()->get_pixmap("nations"),
731                                _("Nations"), "PLR", popup_players_dialog);
732   sw_diplo->set_wheel_up(center_next_player_capital);
733   sw_diplo->set_wheel_down(key_center_capital);
734   sw_science = new fc_sidewidget(fc_icons::instance()->get_pixmap("research"),
735                                  _("Research"), "SCI",
736                                  side_left_click_science);
737   sw_economy = new fc_sidewidget(fc_icons::instance()->get_pixmap("economy"),
738                                  _("Economy"), "ECO",
739                                  economy_report_dialog_popup);
740   sw_endturn = new fc_sidewidget(fc_icons::instance()->get_pixmap("endturn"),
741                                  _("Turn Done"), "", side_finish_turn);
742   sw_cunit->set_right_click(side_center_unit);
743   sw_cunit->set_wheel_up(cycle_enemy_units);
744   sw_cunit->set_wheel_down(key_unit_wait);
745   sw_diplo->set_right_click(side_right_click_diplomacy);
746   sw_science->set_right_click(side_right_click_science);
747 
748   sidebar_wdg->add_widget(sw_map);
749   sidebar_wdg->add_widget(sw_cunit);
750   sidebar_wdg->add_widget(sw_cities);
751   sidebar_wdg->add_widget(sw_diplo);
752   sidebar_wdg->add_widget(sw_science);
753   sidebar_wdg->add_widget(sw_economy);
754   sidebar_wdg->add_widget(sw_tax);
755   sidebar_wdg->add_widget(sw_indicators);
756   sidebar_wdg->add_widget(sw_endturn);
757 
758   minimapview_wdg = new minimap_view(mapview_wdg);
759   minimapview_wdg->show();
760   unitinfo_wdg = new hud_units(mapview_wdg);
761   battlelog_wdg = new hud_battle_log(mapview_wdg);
762   battlelog_wdg->hide();
763   infotab = new info_tab(mapview_wdg);
764   infotab->show();
765   x_vote = new xvote(mapview_wdg);
766   x_vote->hide();
767   gtd = new goto_dialog(mapview_wdg);
768   gtd->hide();
769 
770   game_layout->addWidget(mapview_wdg, 1, 0);
771   game_main_widget->setLayout(game_layout);
772   game_tab_widget = new fc_game_tab_widget;
773   game_tab_widget->setMinimumSize(600,400);
774   game_tab_widget->setContentsMargins(0, 0, 0, 0);
775   add_game_tab(game_main_widget);
776   if (gui_options.gui_qt_sidebar_left) {
777     pages_layout[PAGE_GAME]->addWidget(sidebar_wdg, 1, 0);
778   } else {
779     pages_layout[PAGE_GAME]->addWidget(sidebar_wdg, 1, 2);
780   }
781   pages_layout[PAGE_GAME]->addWidget(game_tab_widget, 1, 1);
782   pages_layout[PAGE_GAME]->setContentsMargins(0, 0, 0, 0);
783   pages_layout[PAGE_GAME]->setSpacing(0);
784 }
785 
786 /***************************************************************************
787   Inserts tab widget to game view page
788 ***************************************************************************/
add_game_tab(QWidget * widget)789 int fc_client::add_game_tab(QWidget *widget)
790 {
791   int i;
792 
793   i = game_tab_widget->addWidget(widget);
794   game_tab_widget->setCurrentWidget(widget);
795   return i;
796 }
797 
798 /***************************************************************************
799   Removes given tab widget from game page
800 ***************************************************************************/
rm_game_tab(int index)801 void fc_client::rm_game_tab(int index)
802 {
803   game_tab_widget->removeWidget(game_tab_widget->widget(index));
804 }
805 
806 /***************************************************************************
807   Browse saves directory
808 ***************************************************************************/
browse_saves(void)809 void fc_client::browse_saves(void)
810 {
811   QString str;
812   str = QString(_("Save Files"))
813         + QString(" (*.sav *.sav.bz2 *.sav.gz *.sav.xz)");
814   current_file = QFileDialog::getOpenFileName(gui()->central_wdg,
815                                               _("Open Save File"),
816                                               QDir::homePath(), str);
817   if (!current_file.isEmpty()) {
818     start_from_save();
819   }
820 }
821 
822 /***************************************************************************
823   State of preview has been changed
824 ***************************************************************************/
state_preview(int new_state)825 void fc_client::state_preview(int new_state)
826 {
827  QItemSelection slctn;
828  if (show_preview->checkState() == Qt::Unchecked) {
829    gui_options.gui_qt_show_preview = false;
830  } else {
831    gui_options.gui_qt_show_preview = true;
832  }
833  slctn = saves_load->selectionModel()->selection();
834  saves_load->selectionModel()->clearSelection();
835  saves_load->selectionModel()->select(slctn, QItemSelectionModel::Rows
836                                     | QItemSelectionModel::SelectCurrent);
837 }
838 
839 /***************************************************************************
840   Browse scenarios directory
841 ***************************************************************************/
browse_scenarios(void)842 void fc_client::browse_scenarios(void)
843 {
844   QString str;
845   str = QString(_("Scenarios Files"))
846         + QString(" (*.sav *.sav.bz2 *.sav.gz *.sav.xz)");
847   current_file = QFileDialog::getOpenFileName(gui()->central_wdg,
848                                               _("Open Scenario File"),
849                                               QDir::homePath(), str);
850   if (!current_file.isEmpty()) {
851     start_scenario();
852   }
853 }
854 
855 /***************************************************************************
856   Updates list of servers in network page in proper QTableViews
857 ***************************************************************************/
update_server_list(enum server_scan_type sstype,const struct server_list * list)858 void fc_client::update_server_list(enum server_scan_type sstype,
859                                    const struct server_list *list)
860 {
861   QTableWidget* sel = NULL;
862   QString host, portstr;
863   int port;
864   int row;
865   int old_row_count;
866 
867   switch (sstype) {
868   case SERVER_SCAN_LOCAL:
869     sel = lan_widget;
870     break;
871   case SERVER_SCAN_GLOBAL:
872     sel = wan_widget;
873     break;
874   default:
875     break;
876   }
877 
878   if (!sel) {
879     return;
880   }
881 
882   if (!list) {
883     return;
884   }
885 
886   host = connect_host_edit->text();
887   portstr = connect_port_edit->text();
888   port = portstr.toInt();
889   old_row_count = sel->rowCount();
890   sel->clearContents();
891   row = 0;
892   server_list_iterate(list, pserver) {
893     char buf[20];
894     int tmp;
895     QString tstring;
896 
897     if (old_row_count <= row) {
898       sel->insertRow(row);
899     }
900 
901     if (pserver->humans >= 0) {
902       fc_snprintf(buf, sizeof(buf), "%d", pserver->humans);
903     } else {
904       strncpy(buf, _("Unknown"), sizeof(buf) - 1);
905     }
906 
907     tmp = pserver->port;
908     tstring = QString::number(tmp);
909 
910     for (int col = 0; col < 6; col++) {
911       QTableWidgetItem *item;
912 
913       item = new QTableWidgetItem();
914 
915       switch (col) {
916       case 0:
917         item->setText(pserver->host);
918         break;
919       case 1:
920         item->setText(tstring);
921         break;
922       case 2:
923         item->setText(pserver->version);
924         break;
925       case 3:
926         item->setText(_(pserver->state));
927         break;
928       case 4:
929         item->setText(buf);
930         break;
931       case 5:
932         item->setText(pserver->message);
933         break;
934       default:
935         break;
936       }
937       sel->setItem(row, col, item);
938     }
939 
940     if (host == pserver->host && port == pserver->port) {
941       sel->selectRow(row);
942     }
943 
944     row++;
945   } server_list_iterate_end;
946 
947   /* Remove unneeded rows, if there are any */
948   while (old_row_count - row > 0) {
949     sel->removeRow(old_row_count - 1);
950     old_row_count--;
951   }
952 
953 }
954 
955 /**************************************************************************
956   Callback function for when there's an error in the server scan.
957 **************************************************************************/
server_scan_error(struct server_scan * scan,const char * message)958 void server_scan_error(struct server_scan *scan, const char *message)
959 {
960   qtg_version_message(message);
961   log_error("%s", message);
962 
963   /* Main thread will finalize the scan later (or even concurrently) -
964    * do not do anything here to cause double free or raze condition. */
965 }
966 
967 
968 /**************************************************************************
969   Free the server scans.
970 **************************************************************************/
destroy_server_scans(void)971 void fc_client::destroy_server_scans(void)
972 {
973   if (meta_scan) {
974     server_scan_finish(meta_scan);
975     meta_scan = NULL;
976   }
977 
978   if (meta_scan_timer != NULL) {
979     meta_scan_timer->stop();
980     meta_scan_timer->disconnect();
981     delete meta_scan_timer;
982     meta_scan_timer = NULL;
983   }
984 
985   if (lan_scan) {
986     server_scan_finish(lan_scan);
987     lan_scan = NULL;
988   }
989 
990   if (lan_scan_timer != NULL) {
991     lan_scan_timer->stop();
992     lan_scan_timer->disconnect();
993     delete lan_scan_timer;
994     lan_scan_timer = NULL;
995   }
996 }
997 
998 /**************************************************************************
999   Stop and restart the metaserver and lan server scans.
1000 **************************************************************************/
update_network_lists(void)1001 void fc_client::update_network_lists(void)
1002 {
1003   destroy_server_scans();
1004 
1005   lan_scan_timer = new QTimer(this);
1006   lan_scan = server_scan_begin(SERVER_SCAN_LOCAL, server_scan_error);
1007   connect(lan_scan_timer, &QTimer::timeout, this, &fc_client::slot_lan_scan);
1008   lan_scan_timer->start(500);
1009 
1010   meta_scan_timer = new QTimer(this);
1011   meta_scan = server_scan_begin(SERVER_SCAN_GLOBAL, server_scan_error);
1012   connect(meta_scan_timer, &QTimer::timeout, this, &fc_client::slot_meta_scan);
1013   meta_scan_timer->start(800);
1014 
1015 }
1016 
1017 /**************************************************************************
1018   This function updates the list of servers every so often.
1019 **************************************************************************/
check_server_scan(server_scan * scan_data)1020 bool fc_client::check_server_scan(server_scan *scan_data)
1021 {
1022   struct server_scan *scan = scan_data;
1023   enum server_scan_status stat;
1024 
1025   if (!scan) {
1026     return false;
1027   }
1028 
1029   stat = server_scan_poll(scan);
1030 
1031   if (stat >= SCAN_STATUS_PARTIAL) {
1032     enum server_scan_type type;
1033     struct srv_list *srvrs;
1034 
1035     type = server_scan_get_type(scan);
1036     srvrs = server_scan_get_list(scan);
1037     fc_allocate_mutex(&srvrs->mutex);
1038     holding_srv_list_mutex = true;
1039     update_server_list(type, srvrs->servers);
1040     holding_srv_list_mutex = false;
1041     fc_release_mutex(&srvrs->mutex);
1042   }
1043 
1044   if (stat == SCAN_STATUS_ERROR || stat == SCAN_STATUS_DONE) {
1045     return false;
1046   }
1047 
1048   return true;
1049 }
1050 
1051 /***************************************************************************
1052   Executes lan scan network
1053 ***************************************************************************/
slot_lan_scan()1054 void fc_client::slot_lan_scan()
1055 {
1056   if (lan_scan_timer == NULL) {
1057     return;
1058   }
1059   check_server_scan(lan_scan);
1060 }
1061 
1062 /***************************************************************************
1063   Executes metaserver scan network
1064 ***************************************************************************/
slot_meta_scan()1065 void fc_client::slot_meta_scan()
1066 {
1067   if (meta_scan_timer == NULL) {
1068     return;
1069   }
1070   check_server_scan(meta_scan);
1071 }
1072 
1073 /**************************************************************************
1074   spawn a server, if there isn't one, using the default settings.
1075 **************************************************************************/
start_new_game()1076 void fc_client::start_new_game()
1077 {
1078   if (is_server_running() || client_start_server()) {
1079     /* saved settings are sent in client/options.c load_settable_options() */
1080   }
1081 }
1082 
1083 /**************************************************************************
1084   Starts game from chosen scenario - chosen_file (save or scenario)
1085 **************************************************************************/
start_scenario()1086 void fc_client::start_scenario()
1087 {
1088   if (!is_server_running()){
1089     client_start_server();
1090     send_chat("/detach");
1091   }
1092   if (is_server_running() && !current_file.isEmpty()) {
1093     QByteArray c_bytes;
1094 
1095     c_bytes = current_file.toLocal8Bit();
1096     send_chat_printf("/load %s", c_bytes.data());
1097     switch_page(PAGE_GAME + 1);
1098   }
1099 }
1100 
1101 /**************************************************************************
1102   Starts game from chosen save - chosen_file (save or scenario)
1103 **************************************************************************/
start_from_save()1104 void fc_client::start_from_save()
1105 {
1106   if (!is_server_running()){
1107     client_start_server();
1108     send_chat("/detach");
1109   }
1110   if (is_server_running() && !current_file.isEmpty()) {
1111     QByteArray c_bytes;
1112 
1113     c_bytes = current_file.toLocal8Bit();
1114     send_chat_printf("/load %s", c_bytes.data());
1115     switch_page(PAGE_GAME + 1);
1116   }
1117 }
1118 
1119 /***************************************************************************
1120   Selection chnaged in some tableview on some page
1121 ***************************************************************************/
slot_selection_changed(const QItemSelection & selected,const QItemSelection & deselected)1122 void fc_client::slot_selection_changed(const QItemSelection &selected,
1123                                        const QItemSelection &deselected)
1124 {
1125 
1126   QModelIndexList indexes = selected.indexes();
1127   QStringList sl;
1128   QModelIndex index;
1129   QTableWidgetItem *item;
1130   QItemSelectionModel *tw;
1131   QVariant qvar;
1132   QString str_pixmap;
1133 
1134   client_pages cpage = current_page();
1135   const char *terr_name;
1136   const struct server *pserver = NULL;
1137   int ii = 0;
1138   int k, col, n, nat_y, nat_x;
1139   struct section_file *sf;
1140   struct srv_list *srvrs;
1141   QByteArray fn_bytes;
1142 
1143   if (indexes.isEmpty()) {
1144     return;
1145   }
1146 
1147   switch (cpage) {
1148   case PAGE_NETWORK:
1149     index = indexes.at(0);
1150     connect_host_edit->setText(index.data().toString());
1151     index = indexes.at(1);
1152     connect_port_edit->setText(index.data().toString());
1153 
1154     tw = qobject_cast<QItemSelectionModel *>(sender());
1155 
1156     if (tw == lan_widget->selectionModel()) {
1157       wan_widget->clearSelection();
1158     } else {
1159       lan_widget->clearSelection();
1160     }
1161 
1162     srvrs = server_scan_get_list(meta_scan);
1163     if (!holding_srv_list_mutex) {
1164       fc_allocate_mutex(&srvrs->mutex);
1165     }
1166     if (srvrs->servers) {
1167       pserver = server_list_get(srvrs->servers, index.row());
1168     }
1169     if (!holding_srv_list_mutex) {
1170       fc_release_mutex(&srvrs->mutex);
1171     }
1172     if (!pserver || !pserver->players) {
1173       return;
1174     }
1175     n = pserver->nplayers;
1176     info_widget->clearContents();
1177     info_widget->setRowCount(0);
1178     for (k = 0; k < n; k++) {
1179       info_widget->insertRow(k);
1180       for (col = 0; col < 4; col++) {
1181         item = new QTableWidgetItem();
1182         switch (col) {
1183         case 0:
1184           item->setText(pserver->players[k].name);
1185           break;
1186         case 1:
1187           item->setText(pserver->players[k].type);
1188           break;
1189         case 2:
1190           item->setText(pserver->players[k].host);
1191           break;
1192         case 3:
1193           item->setText(pserver->players[k].nation);
1194           break;
1195         default:
1196           break;
1197         }
1198         info_widget->setItem(k, col, item);
1199       }
1200     }
1201   break;
1202   case PAGE_SCENARIO:
1203     index = indexes.at(0);
1204     qvar = index.data(Qt::UserRole);
1205     sl = qvar.toStringList();
1206     scenarios_text->setText(sl.at(0));
1207     if (sl.count() > 1) {
1208       scenarios_view->setText(sl.at(2));
1209       current_file = sl.at(1);
1210     }
1211     break;
1212   case PAGE_LOAD:
1213     index = indexes.at(0);
1214     qvar = index.data(Qt::UserRole);
1215     current_file = qvar.toString();
1216     if (show_preview->checkState() == Qt::Unchecked) {
1217       load_pix->setPixmap(*(new QPixmap));
1218       load_save_text->setText("");
1219       break;
1220     }
1221     fn_bytes = current_file.toLocal8Bit();
1222     if ((sf = secfile_load_section(fn_bytes.data(),
1223                                    "game", TRUE))) {
1224       const char *sname;
1225       bool sbool;
1226       int integer;
1227       QString final_str;
1228       QString pl_str = nullptr;
1229       int num_players = 0;
1230       int curr_player = 0;
1231       QByteArray pl_bytes;
1232 
1233       integer = secfile_lookup_int_default(sf, -1, "game.turn");
1234       if (integer >= 0) {
1235         final_str = QString("<b>") + _("Turn") + ":</b> "
1236                     + QString::number(integer).toHtmlEscaped() + "<br>";
1237       }
1238       if ((sf = secfile_load_section(fn_bytes.data(),
1239                                      "players", TRUE))) {
1240         integer = secfile_lookup_int_default(sf, -1, "players.nplayers");
1241         if (integer >= 0) {
1242           final_str = final_str + "<b>" + _("Players") + ":</b>" + " "
1243                       + QString::number(integer).toHtmlEscaped() + "<br>";
1244         }
1245         num_players = integer;
1246       }
1247       for (int i = 0; i < num_players; i++) {
1248         pl_str = QString("player") + QString::number(i);
1249         pl_bytes = pl_str.toLocal8Bit();
1250         if ((sf = secfile_load_section(fn_bytes.data(),
1251                                        pl_bytes.data(), true))) {
1252           if (!(sbool = secfile_lookup_bool_default(sf, true,
1253                                        "player%d.unassigned_user",
1254                                        i))) {
1255               curr_player = i;
1256               break;
1257           }
1258         }
1259       }
1260       /* Break case (and return) if no human player found */
1261       if (pl_str == nullptr) {
1262         load_save_text->setText(final_str);
1263         break;
1264       }
1265 
1266       /* Information about human player */
1267       pl_bytes = pl_str.toLocal8Bit();
1268       if ((sf = secfile_load_section(fn_bytes.data(),
1269                                      pl_bytes.data(), true))) {
1270         sname = secfile_lookup_str_default(sf, nullptr, "player%d.nation",
1271                                            curr_player);
1272         if (sname) {
1273           final_str = final_str + "<b>" + _("Nation") + ":</b> "
1274                       + QString(sname).toHtmlEscaped() + "<br>";
1275         }
1276         integer = secfile_lookup_int_default(sf, -1, "player%d.ncities",
1277                                              curr_player);
1278         if (integer >= 0) {
1279           final_str = final_str + "<b>" + _("Cities") + ":</b> "
1280                       + QString::number(integer).toHtmlEscaped() + "<br>";
1281         }
1282         integer = secfile_lookup_int_default(sf, -1, "player%d.nunits",
1283                                              curr_player);
1284         if (integer >= 0) {
1285           final_str = final_str + "<b>" + _("Units") + ":</b> "
1286                       + QString::number(integer).toHtmlEscaped() + "<br>";
1287         }
1288         integer = secfile_lookup_int_default(sf, -1, "player%d.gold",
1289                                              curr_player);
1290         if (integer >= 0) {
1291           final_str = final_str + "<b>" + _("Gold") + ":</b> "
1292                       + QString::number(integer).toHtmlEscaped() + "<br>";
1293         }
1294         nat_x = 0;
1295         for (nat_y = 0; nat_y > -1; nat_y++) {
1296           const char *line = secfile_lookup_str_default(sf, nullptr,
1297                                                         "player%d.map_t%04d",
1298                                                         curr_player, nat_y);
1299           if (line == nullptr) {
1300             break;
1301           }
1302           nat_x = strlen(line);
1303           str_pixmap = str_pixmap + line;
1304         }
1305 
1306         /* Reset terrain information */
1307         terrain_type_iterate(pterr) {
1308           pterr->identifier_load = '\0';
1309         } terrain_type_iterate_end;
1310 
1311         /* Load possible terrains and their identifiers (chars) */
1312         if ((sf = secfile_load_section(fn_bytes.data(),
1313                                        "savefile", true)))
1314           while ((terr_name = secfile_lookup_str_default(sf, NULL,
1315                                  "savefile.terrident%d.name", ii)) != NULL) {
1316             struct terrain *pterr = terrain_by_rule_name(terr_name);
1317             if (pterr != NULL) {
1318               const char *iptr = secfile_lookup_str_default(sf, NULL,
1319                                       "savefile.terrident%d.identifier", ii);
1320               pterr->identifier_load = *iptr;
1321             }
1322             ii++;
1323           }
1324 
1325         /* Create image */
1326         QImage img(nat_x, nat_y, QImage::Format_ARGB32_Premultiplied);
1327 
1328         img.fill(Qt::black);
1329         for (int a = 0 ; a < nat_x; a++) {
1330           for (int b = 0; b < nat_y; b++) {
1331             struct terrain *tr;
1332             struct rgbcolor *rgb;
1333 
1334             tr = char2terrain(str_pixmap.at(b * nat_x + a).toLatin1());
1335             if (tr != nullptr) {
1336               rgb = tr->rgb;
1337               QColor color;
1338 
1339               color.setRgb(rgb->r, rgb->g, rgb->b);
1340               img.setPixel(a, b, color.rgb());
1341             }
1342           }
1343         }
1344         if (img.width() > 1) {
1345           load_pix->setPixmap(QPixmap::fromImage(img).scaledToHeight(200));
1346         } else {
1347           load_pix->setPixmap(*(new QPixmap));
1348         }
1349         load_pix->setFixedSize(load_pix->pixmap()->width(),
1350                                load_pix->pixmap()->height());
1351         if ((sf = secfile_load_section(fn_bytes.data(),
1352                                        "research", TRUE))) {
1353           sname = secfile_lookup_str_default(sf, nullptr,
1354                                              "research.r%d.now_name",
1355                                              curr_player);
1356           if (sname) {
1357             final_str = final_str + "<b>" + _("Researching") + ":</b> "
1358                         + QString(sname).toHtmlEscaped();
1359           }
1360         }
1361       }
1362       load_save_text->setText(final_str);
1363     }
1364     break;
1365   default:
1366     break;
1367   }
1368 
1369 }
1370 
1371 /***************************************************************************
1372   Updates saves to load and updates in tableview = saves_load
1373 ***************************************************************************/
update_load_page(void)1374 void fc_client::update_load_page(void)
1375 {
1376   struct fileinfo_list *files;
1377   int row;
1378 
1379   row = 0;
1380   files = fileinfolist_infix(get_save_dirs(), ".sav", FALSE);
1381   saves_load->clearContents();
1382   saves_load->setRowCount(0);
1383   show_preview->setChecked(gui_options.gui_qt_show_preview);
1384   fileinfo_list_iterate(files, pfile) {
1385     QTableWidgetItem *item;
1386     item = new QTableWidgetItem();
1387     item->setData(Qt::UserRole, pfile->fullname);
1388     saves_load->insertRow(row);
1389     item->setText(pfile->name);
1390     saves_load->setItem(row, 0, item);
1391     item = new QTableWidgetItem();
1392     QDateTime dt = QDateTime::fromTime_t(pfile->mtime);
1393     item->setText(dt.toString(Qt::TextDate));
1394     saves_load->setItem(row, 1, item);
1395     row++;
1396   } fileinfo_list_iterate_end;
1397   fileinfo_list_destroy(files);
1398 }
1399 
1400 /***************************************************************************
1401   Gets scenarios list and updates it in TableWidget = scenarios_load
1402 ***************************************************************************/
update_scenarios_page(void)1403 void fc_client::update_scenarios_page(void)
1404 {
1405   struct fileinfo_list *files;
1406   int row = 0;
1407 
1408   scenarios_load->clearContents();
1409   scenarios_load->setRowCount(0);
1410   scenarios_text->setText("");
1411   scenarios_view->setText("");
1412 
1413   files = fileinfolist_infix(get_scenario_dirs(), ".sav", false);
1414   fileinfo_list_iterate(files, pfile) {
1415     struct section_file *sf;
1416 
1417     if ((sf = secfile_load_section(pfile->fullname, "scenario", TRUE))
1418         && secfile_lookup_bool_default(sf, TRUE, "scenario.is_scenario")) {
1419 
1420       const char *sname, *sdescription, *sauthors;
1421       QTableWidgetItem *item;
1422       QString format;
1423       QString st;
1424       int fcver;
1425       int current_ver = MAJOR_VERSION *10000 + MINOR_VERSION *100;
1426 
1427       fcver = secfile_lookup_int_default(sf, 0, "scenario.game_version");
1428       fcver -= (fcver % 100); /* Patch level does not affect compatibility */
1429       sname = secfile_lookup_str_default(sf, NULL, "scenario.name");
1430       sdescription = secfile_lookup_str_default(sf, NULL,
1431                      "scenario.description");
1432       sauthors = secfile_lookup_str_default(sf, NULL,
1433                                             "scenario.authors");
1434       if (fcver <= current_ver) {
1435         QString version;
1436         bool add_item = true;
1437         bool found = false;
1438         QStringList sl;
1439         int rows;
1440         int found_ver;
1441         int i;
1442 
1443         if (fcver > 0) {
1444           int maj;
1445           int min;
1446 
1447           maj = fcver / 10000;
1448           fcver %= 10000;
1449           min = fcver / 100;
1450           version = QString("%1.%2").arg(maj).arg(min);
1451         } else {
1452           /* TRANS: Unknown scenario format */
1453           version = QString(_("pre-2.6"));
1454         }
1455 
1456         rows = scenarios_load->rowCount();
1457         for (i = 0; i < rows; ++i) {
1458           if (scenarios_load->item(i, 0)
1459               && scenarios_load->item(i, 0)->text() == pfile->name) {
1460             found = true;
1461             item = scenarios_load->takeItem(i, 0);
1462             break;
1463           }
1464         }
1465 
1466         if (found) {
1467           sl = item->data(Qt::UserRole).toStringList();
1468           found_ver = sl.at(3).toInt();
1469           if (found_ver < fcver) {
1470             continue;
1471           }
1472           add_item = false;
1473         }
1474         if (add_item) {
1475           item = new QTableWidgetItem();
1476           scenarios_load->insertRow(row);
1477         }
1478         item->setText(QString(pfile->name));
1479         format = QString("<br>") + QString(_("Format:")) + " "
1480                  + version.toHtmlEscaped();
1481         if (sauthors) {
1482           st = QString("\n") + QString("<b>") + _("Authors: ")
1483                + QString("</b>") + QString(sauthors).toHtmlEscaped();
1484         } else {
1485           st = "";
1486         }
1487         sl << "<b>"
1488            + QString(sname && strlen(sname) ? Q_(sname) : pfile->name)
1489              .toHtmlEscaped()
1490            + "</b>"
1491            << QString(pfile->fullname).toHtmlEscaped()
1492            << QString(NULL != sdescription && '\0' != sdescription[0]
1493                       ? Q_(sdescription) : "").toHtmlEscaped() + st + format
1494            << QString::number(fcver).toHtmlEscaped();
1495         sl.replaceInStrings("\n", "<br>");
1496         item->setData(Qt::UserRole, sl);
1497         if (add_item) {
1498           scenarios_load->setItem(row, 0, item);
1499           row++;
1500         } else {
1501           scenarios_load->setItem(i, 0, item);
1502         }
1503       }
1504       secfile_destroy(sf);
1505     }
1506   } fileinfo_list_iterate_end;
1507   fileinfo_list_destroy(files);
1508   scenarios_load->sortItems(0);
1509   scenarios_load->update();
1510 }
1511 
1512 
1513 /**************************************************************************
1514   configure the dialog depending on what type of authentication request the
1515   server is making.
1516 **************************************************************************/
handle_authentication_req(enum authentication_type type,const char * message)1517 void fc_client::handle_authentication_req(enum authentication_type type,
1518                                           const char *message)
1519 {
1520   set_status_bar(QString::fromUtf8(message));
1521   destroy_server_scans();
1522 
1523   switch (type) {
1524   case AUTH_NEWUSER_FIRST:
1525   case AUTH_NEWUSER_RETRY:
1526     set_connection_state(NEW_PASSWORD_TYPE);
1527     return;
1528   case AUTH_LOGIN_FIRST:
1529     /* if we magically have a password already present in 'fc_password'
1530      * then, use that and skip the password entry dialog */
1531     if (fc_password[0] != '\0') {
1532       struct packet_authentication_reply reply;
1533 
1534       sz_strlcpy(reply.password, fc_password);
1535       send_packet_authentication_reply(&client.conn, &reply);
1536       return;
1537     } else {
1538       set_connection_state(ENTER_PASSWORD_TYPE);
1539     }
1540 
1541     return;
1542   case AUTH_LOGIN_RETRY:
1543     set_connection_state(ENTER_PASSWORD_TYPE);
1544     return;
1545   }
1546 
1547   log_error("Unsupported authentication type %d: %s.", type, message);
1548 }
1549 
1550 /**************************************************************************
1551   If on the network page, switch page to the login page (with new server
1552   and port). if on the login page, send connect and/or authentication
1553   requests to the server.
1554 **************************************************************************/
slot_connect()1555 void fc_client::slot_connect()
1556 {
1557   char errbuf [512];
1558   struct packet_authentication_reply reply;
1559   QByteArray ba_bytes;
1560 
1561   switch (connection_status) {
1562   case LOGIN_TYPE:
1563     ba_bytes = connect_login_edit->text().toLocal8Bit();
1564     sz_strlcpy(user_name, ba_bytes.data());
1565     ba_bytes = connect_host_edit->text().toLocal8Bit();
1566     sz_strlcpy(server_host, ba_bytes.data());
1567     server_port = connect_port_edit->text().toInt();
1568 
1569     if (connect_to_server(user_name, server_host, server_port,
1570                           errbuf, sizeof(errbuf)) != -1) {
1571     } else {
1572       set_status_bar(QString::fromUtf8(errbuf));
1573       output_window_append(ftc_client, errbuf);
1574     }
1575 
1576     return;
1577   case NEW_PASSWORD_TYPE:
1578     ba_bytes = connect_password_edit->text().toLatin1();
1579     sz_strlcpy(fc_password, ba_bytes.data());
1580     ba_bytes = connect_confirm_password_edit->text().toLatin1();
1581     sz_strlcpy(reply.password,
1582                ba_bytes.data());
1583 
1584     if (strncmp(reply.password, fc_password, MAX_LEN_NAME) == 0) {
1585       fc_password[0] = '\0';
1586       send_packet_authentication_reply(&client.conn, &reply);
1587       set_connection_state(WAITING_TYPE);
1588     } else {
1589       set_status_bar(_("Passwords don't match, enter password."));
1590       set_connection_state(NEW_PASSWORD_TYPE);
1591     }
1592 
1593     return;
1594   case ENTER_PASSWORD_TYPE:
1595     ba_bytes = connect_password_edit->text().toLatin1();
1596     sz_strlcpy(reply.password,
1597                ba_bytes.data());
1598     send_packet_authentication_reply(&client.conn, &reply);
1599     set_connection_state(WAITING_TYPE);
1600     return;
1601   case WAITING_TYPE:
1602     return;
1603   }
1604 
1605   log_error("Unsupported connection status: %d", connection_status);
1606 }
1607 
1608 /***************************************************************************
1609  Updates start page (start page = client connected to server, but game not
1610  started)
1611 ***************************************************************************/
update_start_page()1612 void fc_client::update_start_page()
1613 {
1614   int conn_num, i;
1615   QVariant qvar, qvar2;
1616   bool is_ready;
1617   QString host, nation, leader, team, str;
1618   QPixmap *pixmap;
1619   QPainter p;
1620   struct sprite *psprite;
1621   QTreeWidgetItem *item;
1622   QTreeWidgetItem *item_r;
1623   QList <QTreeWidgetItem*> items;
1624   QList <QTreeWidgetItem*> recursed_items;
1625   QTreeWidgetItem *player_item;
1626   QTreeWidgetItem *global_item;
1627   QTreeWidgetItem *detach_item;
1628   int conn_id;
1629   conn_num = conn_list_size(game.est_connections);
1630 
1631   if (conn_num == 0) {
1632     return;
1633   }
1634 
1635   start_players_tree->clear();
1636   qvar2 = 0;
1637 
1638   player_item = new QTreeWidgetItem();
1639   player_item->setText(0, Q_("?header:Players"));
1640   player_item->setData(0, Qt::UserRole, qvar2);
1641 
1642   i = 0;
1643   players_iterate(pplayer) {
1644     i++;
1645   } players_iterate_end;
1646   gui()->pr_options->set_aifill(i);
1647   /**
1648    * Inserts playing players, observing custom players, and AI)
1649    */
1650 
1651   players_iterate(pplayer) {
1652     host = "";
1653     conn_id = -1;
1654     conn_list_iterate(pplayer->connections, pconn) {
1655       if (pconn->playing == pplayer && !pconn->observer) {
1656         conn_id = pconn->id;
1657         host = pconn->addr;
1658         break;
1659       }
1660     } conn_list_iterate_end;
1661     if (is_barbarian(pplayer)) {
1662       continue;
1663     }
1664     if (pplayer->ai_controlled) {
1665       is_ready = true;
1666     } else {
1667       is_ready = pplayer->is_ready;
1668     }
1669 
1670     if (pplayer->nation == NO_NATION_SELECTED) {
1671       nation = _("Random");
1672 
1673       if (pplayer->was_created) {
1674         leader = player_name(pplayer);
1675       } else {
1676         leader = "";
1677       }
1678     } else {
1679       nation = nation_adjective_for_player(pplayer);
1680       leader = player_name(pplayer);
1681     }
1682 
1683     if (pplayer->team) {
1684       team = team_name_translation(pplayer->team);
1685     } else {
1686       team = "";
1687     }
1688 
1689     item = new QTreeWidgetItem();
1690     for (int col = 0; col < 8; col++) {
1691       item->setTextAlignment(col, Qt::AlignVCenter);
1692       switch (col) {
1693       case 0:
1694         str = pplayer->username;
1695 
1696         if (pplayer->ai_controlled) {
1697           str = str + " <" + (ai_level_translated_name(pplayer->ai_common.skill_level))
1698               + ">";
1699           item->setIcon(col, fc_icons::instance()->get_icon("ai"));
1700         } else {
1701           item->setIcon(col, fc_icons::instance()->get_icon("human"));
1702         }
1703 
1704         item->setText(col, str);
1705         qvar = QVariant::fromValue((void *) pplayer);
1706         qvar2 = 1;
1707         item->setData(0, Qt::UserRole, qvar2);
1708         item->setData(1, Qt::UserRole, qvar);
1709         break;
1710       case 1:
1711         if (is_ready) {
1712           item->setText(col, _("Yes"));
1713         } else {
1714           item->setText(col, _("No"));
1715         }
1716         break;
1717       case 2:
1718         item->setText(col, leader);
1719         break;
1720       case 3:
1721         if (!pplayer->nation) {
1722           break;
1723         }
1724         psprite = get_nation_flag_sprite(tileset, pplayer->nation);
1725         pixmap = psprite->pm;
1726         item->setData(col, Qt::DecorationRole, *pixmap);
1727         break;
1728       case 4:
1729         if (!player_has_color(tileset, pplayer)) {
1730           break;
1731         }
1732         pixmap = new QPixmap(
1733                      start_players_tree->header()->sectionSizeHint(col), 16);
1734         pixmap->fill(Qt::transparent);
1735         p.begin(pixmap);
1736         p.fillRect(pixmap->width() / 2 - 8, 0, 16, 16, Qt::black);
1737         p.fillRect(pixmap->width() / 2 - 7, 1, 14, 14,
1738                    get_player_color(tileset, pplayer)->qcolor);
1739         p.end();
1740         item->setData(col, Qt::DecorationRole, *pixmap);
1741         delete pixmap;
1742         break;
1743       case 5:
1744         item->setText(col, nation);
1745         break;
1746       case 6:
1747         item->setText(col, team);
1748         break;
1749       case 7:
1750         item->setText(col, host);
1751         break;
1752       default:
1753         break;
1754       }
1755     }
1756 
1757     /**
1758      * find any custom observers
1759      */
1760     recursed_items.clear();
1761     conn_list_iterate(pplayer->connections, pconn) {
1762       if (pconn->id == conn_id) {
1763         continue;
1764       }
1765       item_r = new QTreeWidgetItem();
1766       item_r->setText(0, pconn->username);
1767       item_r->setText(5, _("Observer"));
1768       item_r->setText(7, pconn->addr);
1769       recursed_items.append(item_r);
1770       item->addChildren(recursed_items);
1771     } conn_list_iterate_end;
1772     items.append(item);
1773   } players_iterate_end;
1774 
1775   player_item->addChildren(items);
1776   start_players_tree->insertTopLevelItem(0, player_item);
1777 
1778   /**
1779    * Insert global observers
1780    */
1781   items.clear();
1782   global_item = new QTreeWidgetItem();
1783   global_item->setText(0, _("Global observers"));
1784   qvar2 = 0;
1785   global_item->setData(0, Qt::UserRole, qvar2);
1786 
1787   conn_list_iterate(game.est_connections, pconn) {
1788     if (NULL != pconn->playing || !pconn->observer) {
1789       continue;
1790     }
1791     item = new QTreeWidgetItem();
1792     for (int col = 0; col < 8; col++) {
1793       switch (col) {
1794       case 0:
1795         item->setText(col, pconn->username);
1796         break;
1797       case 5:
1798         item->setText(col, _("Observer"));
1799         break;
1800       case 7:
1801         item->setText(col, pconn->addr);
1802         break;
1803       default:
1804         break;
1805       }
1806       items.append(item);
1807     }
1808   } conn_list_iterate_end;
1809 
1810   global_item->addChildren(items);
1811   start_players_tree->insertTopLevelItem(1, global_item);
1812   items.clear();
1813 
1814   /**
1815   * Insert detached
1816   */
1817   detach_item = new QTreeWidgetItem();
1818   detach_item->setText(0, _("Detached"));
1819   qvar2 = 0;
1820   detach_item->setData(0, Qt::UserRole, qvar2);
1821 
1822   conn_list_iterate(game.all_connections, pconn) {
1823     if (NULL != pconn->playing || pconn->observer) {
1824       continue;
1825     }
1826     item = new QTreeWidgetItem();
1827     item->setText(0, pconn->username);
1828     item->setText(7, pconn->addr);
1829     items.append(item);
1830   } conn_list_iterate_end;
1831 
1832   detach_item->addChildren(items);
1833   start_players_tree->insertTopLevelItem(2, detach_item);
1834   start_players_tree->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
1835   start_players_tree->expandAll();
1836   update_buttons();
1837 }
1838 
1839 /***************************************************************************
1840   Updates observe button in case user started observing manually
1841 ***************************************************************************/
update_buttons()1842 void fc_client::update_buttons()
1843 {
1844   bool sensitive;
1845   QString text;
1846 
1847   /* Observe button */
1848   if (client_is_observer() || client_is_global_observer()) {
1849     obs_button->setText(_("Don't Observe"));
1850   } else {
1851     obs_button->setText(_("Observe"));
1852   }
1853 
1854   /* Ready button */
1855   if (can_client_control()) {
1856     sensitive = true;
1857     if (client_player()->is_ready) {
1858       text = _("Not ready");
1859     } else {
1860       int num_unready = 0;
1861 
1862       players_iterate(pplayer) {
1863         if (!pplayer->ai_controlled && !pplayer->is_ready) {
1864           num_unready++;
1865         }
1866       } players_iterate_end;
1867 
1868       if (num_unready > 1) {
1869         text = _("Ready");
1870       } else {
1871         /* We are the last unready player so clicking here will
1872          * immediately start the game. */
1873         text = ("Start");
1874       }
1875     }
1876   } else {
1877     text = _("Start");
1878     if (can_client_access_hack() && client.conn.observer) {
1879       sensitive = true;
1880       players_iterate(plr) {
1881         if (!plr->ai_controlled) {
1882           /* There's human controlled player(s) in game, so it's their
1883            * job to start the game. */
1884           sensitive = false;
1885           break;
1886         }
1887       } players_iterate_end;
1888     } else {
1889       sensitive = false;
1890     }
1891   }
1892   start_button->setEnabled(sensitive);
1893   start_button->setText(text);
1894 
1895   /* Nation button */
1896   sensitive = game.info.is_new_game && can_client_control();
1897   nation_button->setEnabled(sensitive);
1898 
1899   sensitive = game.info.is_new_game;
1900   pr_options->setEnabled(sensitive);
1901 
1902   gui()->pr_options->update_buttons();
1903   gui()->pr_options->update_ai_level();
1904 }
1905 
1906 /***************************************************************************
1907   Context menu on some player, arg Qpoint specifies some pixel on screen
1908 ***************************************************************************/
start_page_menu(QPoint pos)1909 void fc_client::start_page_menu(QPoint pos)
1910 {
1911   QAction *action;
1912   QMenu *menu, *submenu_AI, *submenu_team;
1913   QPoint global_pos = start_players_tree->mapToGlobal(pos);
1914   QString me, splayer, str, sp;
1915   bool need_empty_team;
1916   const char *level_cmd, *level_name;
1917   int level, count;
1918   QSignalMapper *player_menu_mapper;
1919   player *selected_player;
1920   QVariant qvar, qvar2;
1921 
1922   me = client.conn.username;
1923   QTreeWidgetItem *item = start_players_tree->itemAt(pos);
1924 
1925   menu = new QMenu(this);
1926   submenu_AI = new QMenu(this);
1927   submenu_team = new QMenu(this);
1928   if (!item) {
1929     return;
1930   }
1931 
1932   qvar = item->data(0, Qt::UserRole);
1933   qvar2 = item->data(1, Qt::UserRole);
1934 
1935   /**
1936    * qvar = 0 -> selected label -> do nothing
1937    * qvar = 1 -> selected player (stored in qvar2)
1938    */
1939 
1940   selected_player = NULL;
1941   if (qvar == 0) {
1942     return;
1943   }
1944   if (qvar == 1) {
1945     selected_player = (player *) qvar2.value < void *>();
1946   }
1947   player_menu_mapper = new QSignalMapper;
1948   players_iterate(pplayer) {
1949     if (selected_player && selected_player == pplayer) {
1950       splayer = QString(pplayer->name);
1951       sp = "\"" + splayer + "\"";
1952       if (me != splayer) {
1953         str = QString(_("Observe"));
1954         action = new QAction(str, start_players_tree);
1955         str = "/observe " + sp;
1956         connect(action, SIGNAL(triggered()), player_menu_mapper,
1957                 SLOT(map()));
1958         player_menu_mapper->setMapping(action, str);
1959         menu->addAction(action);
1960 
1961         if (ALLOW_CTRL <= client.conn.access_level) {
1962           str = QString(_("Remove player"));
1963           action = new QAction(str, start_players_tree);
1964           str = "/remove " + sp;
1965           connect(action, SIGNAL(triggered()), player_menu_mapper,
1966                   SLOT(map()));
1967           player_menu_mapper->setMapping(action, str);
1968           menu->addAction(action);
1969         }
1970         str = QString(_("Take this player"));
1971         action = new QAction(str, start_players_tree);
1972         str = "/take " + sp;
1973         connect(action, SIGNAL(triggered()), player_menu_mapper,
1974                 SLOT(map()));
1975         player_menu_mapper->setMapping(action, str);
1976         menu->addAction(action);
1977       }
1978 
1979       if (can_conn_edit_players_nation(&client.conn, pplayer)) {
1980         str = QString(_("Pick nation"));
1981         action = new QAction(str, start_players_tree);
1982         str = "PICK:" + QString(player_name(pplayer));  /* PICK is a key */
1983         connect(action, SIGNAL(triggered()), player_menu_mapper,
1984                 SLOT(map()));
1985         player_menu_mapper->setMapping(action, str);
1986         menu->addAction(action);
1987       }
1988 
1989       if (pplayer->ai_controlled) {
1990         /**
1991          * Set AI difficulty submenu
1992          */
1993         if (ALLOW_CTRL <= client.conn.access_level
1994             && NULL != pplayer && pplayer->ai_controlled) {
1995           submenu_AI->setTitle(_("Set difficulty"));
1996           menu->addMenu(submenu_AI);
1997 
1998           for (level = 0; level < AI_LEVEL_COUNT; level++) {
1999             if (is_settable_ai_level(static_cast < ai_level > (level))) {
2000               level_name = ai_level_translated_name(static_cast < ai_level > (level));
2001               level_cmd = ai_level_cmd(static_cast < ai_level > (level));
2002               action = new QAction(QString(level_name), start_players_tree);
2003               str = "/" + QString(level_cmd) + " " + sp;
2004               connect(action, SIGNAL(triggered()), player_menu_mapper,
2005                       SLOT(map()));
2006               player_menu_mapper->setMapping(action, str);
2007               submenu_AI->addAction(action);
2008             }
2009           }
2010         }
2011       }
2012 
2013       /**
2014       * Put to Team X submenu
2015       */
2016       if (pplayer && game.info.is_new_game) {
2017         menu->addMenu(submenu_team);
2018         submenu_team->setTitle(_("Put on team"));
2019         menu->addMenu(submenu_team);
2020         count = pplayer->team ?
2021             player_list_size(team_members(pplayer->team)) : 0;
2022         need_empty_team = (count != 1);
2023         team_slots_iterate(tslot) {
2024           if (!team_slot_is_used(tslot)) {
2025             if (!need_empty_team) {
2026               continue;
2027             }
2028             need_empty_team = false;
2029           }
2030           str = team_slot_name_translation(tslot);
2031           action = new QAction(str, start_players_tree);
2032           str = "/team" + sp + " \"" + QString(team_slot_rule_name(tslot))
2033               + "\"";
2034           connect(action, SIGNAL(triggered()),
2035                   player_menu_mapper, SLOT(map()));
2036           player_menu_mapper->setMapping(action, str);
2037           submenu_team->addAction(action);
2038         } team_slots_iterate_end;
2039       }
2040 
2041       if (ALLOW_CTRL <= client.conn.access_level && NULL != pplayer) {
2042         str = QString(_("Aitoggle player"));
2043         action = new QAction(str, start_players_tree);
2044         str = "/aitoggle " + sp;
2045         connect(action, SIGNAL(triggered()), player_menu_mapper,
2046                 SLOT(map()));
2047         player_menu_mapper->setMapping(action, str);
2048         menu->addAction(action);
2049       }
2050       connect(player_menu_mapper, SIGNAL(mapped(const QString &)),
2051               this, SLOT(send_fake_chat_message(const QString &)));
2052       menu->popup(global_pos);
2053       return;
2054     }
2055   } players_iterate_end;
2056   delete player_menu_mapper;
2057 }
2058 
2059 /***************************************************************************
2060  Calls dialg selecting nations
2061 ***************************************************************************/
slot_pick_nation()2062 void fc_client::slot_pick_nation()
2063 {
2064   popup_races_dialog(client_player());
2065 }
2066 
2067 /***************************************************************************
2068   Reloads sidebar icons (useful on theme change)
2069 ***************************************************************************/
reload_sidebar_icons()2070 void fc_client::reload_sidebar_icons()
2071 {
2072   sw_map->set_pixmap(fc_icons::instance()->get_pixmap("view"));
2073   sw_cunit->set_pixmap(fc_icons::instance()->get_pixmap("units"));
2074   sw_cities->set_pixmap(fc_icons::instance()->get_pixmap("cities"));
2075   sw_diplo->set_pixmap(fc_icons::instance()->get_pixmap("nations"));
2076   sw_science->set_pixmap(fc_icons::instance()->get_pixmap("research"));
2077   sw_economy->set_pixmap(fc_icons::instance()->get_pixmap("economy"));
2078   sw_endturn->set_pixmap(fc_icons::instance()->get_pixmap("endturn"));
2079   sidebar_wdg->resize_me(game_tab_widget->height(), true);
2080 }
2081 /***************************************************************************
2082   Updates sidebar tooltips
2083 ***************************************************************************/
update_sidebar_tooltips()2084 void fc_client::update_sidebar_tooltips()
2085 {
2086   QString str;
2087   int max;
2088   int entries_used, building_total, unit_total, tax;
2089   char buf[256];
2090 
2091   struct improvement_entry building_entries[B_LAST];
2092   struct unit_entry unit_entries[U_LAST];
2093 
2094   if (current_page() != PAGE_GAME) {
2095     return;
2096   }
2097 
2098   if (NULL != client.conn.playing) {
2099     max = get_player_bonus(client.conn.playing, EFT_MAX_RATES);
2100   } else {
2101     max = 100;
2102   }
2103 
2104   if (!client_is_global_observer()) {
2105     sw_science->set_tooltip(science_dialog_text());
2106     str = QString(nation_plural_for_player(client_player()));
2107     str = str + '\n' + get_info_label_text(false);
2108     sw_map->set_tooltip(str);
2109     str = QString(_("Tax: %1% Science: %2% Luxury: %3%\n"))
2110           .arg(client.conn.playing->economic.tax)
2111           .arg(client.conn.playing->economic.luxury)
2112           .arg(client.conn.playing->economic.science);
2113 
2114     str += QString(_("%1 - max rate: %2%")).
2115            arg(government_name_for_player(client.conn.playing),
2116                QString::number(max));
2117 
2118     get_economy_report_units_data(unit_entries, &entries_used, &unit_total);
2119     get_economy_report_data(building_entries, &entries_used,
2120                             &building_total, &tax);
2121     fc_snprintf(buf, sizeof(buf), _("Income: %d    Total Costs: %d"),
2122                 tax, building_total + unit_total);
2123     sw_economy->set_tooltip(buf);
2124     if (player_capital(client_player())) {
2125       sw_cities->set_tooltip(text_happiness_cities(
2126                                           player_capital(client_player())));
2127     }
2128   } else {
2129     sw_tax->set_tooltip("");
2130     sw_science->set_tooltip("");
2131     sw_map->set_tooltip("");
2132     sw_economy->set_tooltip("");
2133   }
2134   sw_indicators->set_tooltip(QString(get_info_label_text_popup()));
2135 }
2136 
2137 /****************************************************************************
2138   Centers next enemy city on view
2139 ****************************************************************************/
center_next_enemy_city()2140 void center_next_enemy_city()
2141 {
2142   bool center_next = false;
2143   bool first_tile = false;
2144   int first_id;
2145   struct tile *ptile = nullptr;
2146 
2147   players_iterate(pplayer) {
2148     if (pplayer != client_player()) {
2149       city_list_iterate(pplayer->cities, pcity) {
2150         if (!first_tile) {
2151           first_tile = true;
2152           ptile = pcity->tile;
2153           first_id = pcity->id;
2154         }
2155         if ((last_center_enemy_city == 0) || center_next) {
2156           last_center_enemy_city = pcity->id;
2157           center_tile_mapcanvas(pcity->tile);
2158           return;
2159         }
2160         if (pcity->id == last_center_enemy_city) {
2161           center_next = true;
2162         }
2163       } city_list_iterate_end;
2164     }
2165   } players_iterate_end;
2166 
2167   if (ptile != nullptr) {
2168     center_tile_mapcanvas(ptile);
2169     last_center_enemy_city = first_id;
2170   }
2171 }
2172 
2173 /****************************************************************************
2174   Centers next player city on view
2175 ****************************************************************************/
center_next_player_city()2176 void center_next_player_city()
2177 {
2178   bool center_next = false;
2179   bool first_tile = false;
2180   int first_id;
2181   struct tile *ptile = nullptr;
2182 
2183   players_iterate(pplayer) {
2184     if (pplayer == client_player()) {
2185       city_list_iterate(pplayer->cities, pcity) {
2186         if (!first_tile) {
2187           first_tile = true;
2188           ptile = pcity->tile;
2189           first_id = pcity->id;
2190         }
2191         if ((last_center_player_city == 0) || center_next) {
2192           last_center_player_city = pcity->id;
2193           center_tile_mapcanvas(pcity->tile);
2194           return;
2195         }
2196         if (pcity->id == last_center_player_city) {
2197           center_next = true;
2198         }
2199       } city_list_iterate_end;
2200     }
2201   } players_iterate_end;
2202 
2203   if (ptile != nullptr) {
2204     center_tile_mapcanvas(ptile);
2205     last_center_player_city = first_id;
2206   }
2207 }
2208 
2209 /****************************************************************************
2210   Centers next enemy capital
2211 ****************************************************************************/
center_next_player_capital()2212 void center_next_player_capital()
2213 {
2214   struct city *capital;
2215   bool center_next = false;
2216   bool first_tile = false;
2217   int first_id;
2218   struct tile *ptile = nullptr;
2219 
2220   players_iterate(pplayer) {
2221     if (pplayer != client_player()) {
2222       capital = player_capital(pplayer);
2223       if (capital == nullptr) {
2224         continue;
2225       }
2226         if (!first_tile) {
2227           first_tile = true;
2228           ptile = capital->tile;
2229           first_id = capital->id;
2230         }
2231         if ((last_center_player_city == 0) || center_next) {
2232           last_center_player_city = capital->id;
2233           center_tile_mapcanvas(capital->tile);
2234           put_cross_overlay_tile(capital->tile);
2235           return;
2236         }
2237         if (capital->id == last_center_player_city) {
2238           center_next = true;
2239         }
2240     }
2241   } players_iterate_end;
2242 
2243   if (ptile != nullptr) {
2244     center_tile_mapcanvas(ptile);
2245     put_cross_overlay_tile(ptile);
2246     last_center_player_city = first_id;
2247   }
2248 
2249 }
2250 
2251 /***************************************************************************
2252   Update postion
2253 ***************************************************************************/
update_sidebar_position()2254 void fc_client::update_sidebar_position()
2255 {
2256   pages_layout[PAGE_GAME]->removeWidget(gui()->sidebar_wdg);
2257   if (gui_options.gui_qt_sidebar_left) {
2258     pages_layout[PAGE_GAME]->addWidget(sidebar_wdg, 1, 0);
2259   } else {
2260     pages_layout[PAGE_GAME]->addWidget(sidebar_wdg, 1, 2);
2261   }
2262 }
2263 
2264 /***************************************************************************
2265   Center on next enemy unit
2266 ***************************************************************************/
cycle_enemy_units()2267 void cycle_enemy_units()
2268 {
2269   bool center_next = false;
2270   bool first_tile = false;
2271   int first_id;
2272   struct tile *ptile = nullptr;
2273 
2274   players_iterate(pplayer) {
2275     if (pplayer != client_player()) {
2276       unit_list_iterate(pplayer->units, punit) {
2277         if (!first_tile) {
2278           first_tile = true;
2279           ptile = punit->tile;
2280           first_id = punit->id;
2281         }
2282         if ((last_center_enemy == 0) || center_next) {
2283           last_center_enemy = punit->id;
2284           center_tile_mapcanvas(punit->tile);
2285           return;
2286         }
2287         if (punit->id == last_center_enemy) {
2288           center_next = true;
2289         }
2290       } unit_list_iterate_end;
2291     }
2292   } players_iterate_end;
2293 
2294   if (ptile != nullptr) {
2295     center_tile_mapcanvas(ptile);
2296     last_center_enemy = first_id;
2297   }
2298 }
2299