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