1 /*
2  * Hedgewars, a free turn based strategy game
3  * Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; version 2 of the License
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18 
19 #include <QDir>
20 #include <QFile>
21 #include <QTextStream>
22 #include <QMessageBox>
23 #include <QPushButton>
24 #include <QSpinBox>
25 #include <QListWidget>
26 #include <QStackedLayout>
27 #include <QLineEdit>
28 #include <QLabel>
29 #include <QRadioButton>
30 #include <QSpinBox>
31 #include <QCloseEvent>
32 #include <QCheckBox>
33 #include <QTextBrowser>
34 #include <QAction>
35 #include <QTimer>
36 #include <QScrollBar>
37 #include <QDataWidgetMapper>
38 #include <QTableView>
39 #include <QCryptographicHash>
40 #include <QSignalMapper>
41 #include <QShortcut>
42 #include <QDesktopServices>
43 #include <QDesktopWidget>
44 #include <QApplication>
45 #include <QInputDialog>
46 #include <QPropertyAnimation>
47 #include <QSettings>
48 #include <QSortFilterProxyModel>
49 #include <QIcon>
50 #include <QImage>
51 
52 #if (QT_VERSION >= 0x040600)
53 #include <QGraphicsEffect>
54 #include <QParallelAnimationGroup>
55 #endif
56 
57 #include "hwform.h"
58 #include "game.h"
59 #include "team.h"
60 #include "mission.h"
61 #include "campaign.h"
62 #include "teamselect.h"
63 #include "selectWeapon.h"
64 #include "gameuiconfig.h"
65 #include "pageinfo.h"
66 #include "pagetraining.h"
67 #include "pagesingleplayer.h"
68 #include "pageselectweapon.h"
69 #include "pageadmin.h"
70 #include "pagecampaign.h"
71 #include "pagescheme.h"
72 #include "pagenetgame.h"
73 #include "pageroomslist.h"
74 #include "pageconnecting.h"
75 #include "pageoptions.h"
76 #include "pageeditteam.h"
77 #include "pagemultiplayer.h"
78 #include "pagenet.h"
79 #include "pagemain.h"
80 #include "pagenetserver.h"
81 #include "pagedrawmap.h"
82 #include "pagegamestats.h"
83 #include "pageplayrecord.h"
84 #include "pagedata.h"
85 #include "pagevideos.h"
86 #include "hwconsts.h"
87 #include "newnetclient.h"
88 #include "gamecfgwidget.h"
89 #include "netserverslist.h"
90 #include "netudpserver.h"
91 #include "chatwidget.h"
92 #include "input_ip.h"
93 #include "input_password.h"
94 #include "gameSchemeModel.h"
95 #include "bgwidget.h"
96 #include "drawmapwidget.h"
97 #include "mouseoverfilter.h"
98 #include "roomslistmodel.h"
99 #include "recorder.h"
100 #include "playerslistmodel.h"
101 #include "feedbackdialog.h"
102 
103 #include "MessageDialog.h"
104 #include "DataManager.h"
105 #include "AutoUpdater.h"
106 
107 #ifdef Q_OS_WIN
108 #ifndef WINVER
109 #define WINVER 0x0500
110 #endif
111 #include <windows.h>
112 #else
113 #include <unistd.h>
114 #include <sys/types.h>
115 #endif
116 
117 #ifdef Q_OS_MAC
118 #include <sys/sysctl.h>
119 #endif
120 
121 #ifdef __APPLE__
122 #include "M3Panel.h"
123 #ifdef SPARKLE_ENABLED
124 #include "SparkleAutoUpdater.h"
125 #endif
126 #endif
127 
128 
129 // I started handing this down to each place it touches, but it was getting ridiculous
130 // and this one flag does not warrant a static class
131 bool frontendEffects = true;
132 bool demoIsPresent = true;
133 QString playerHash;
134 
135 QIcon finishedIcon;
136 QIcon notFinishedIcon;
137 GameUIConfig* HWForm::config = NULL;
138 
HWForm(QWidget * parent,QString styleSheet)139 HWForm::HWForm(QWidget *parent, QString styleSheet)
140     : QMainWindow(parent)
141     , game(0)
142     , pnetserver(0)
143     , pRegisterServer(0)
144     , editedTeam(0)
145     , hwnet(0)
146 {
147     // set music track
148     SDLInteraction::instance().setMusicTrack("/Music/main_theme.ogg");
149 
150     this->setStyleSheet(styleSheet);
151 
152 
153     QIcon * hwIcon = new QIcon();
154     hwIcon->addFile(":/res/hh_small.png");
155     //hwIcon->addFile(":/res/hh25x25.png");
156     // crop-workaround for the fact that hh25x25.png is actually 25x35
157     QPixmap pm(":/res/hh25x25.png");
158     hwIcon->addPixmap(pm.copy(0,(pm.height()-25)/2,25,25));
159     hwIcon->addFile(":/res/teamicon.png");
160     hwIcon->addFile(":/res/teamicon2.png");
161 
162     this->setWindowIcon(*hwIcon);
163     ui.setupUi(this);
164     setMinimumSize(760, 580);
165     //setFocusPolicy(Qt::StrongFocus);
166     CustomizePalettes();
167 
168     ui.pageOptions->CBResolution->addItems(SDLInteraction::instance().getResolutions());
169 
170     config = new GameUIConfig(this, DataManager::instance().settingsFileName());
171     frontendEffects = config->value("frontend/effects", true).toBool();
172     bool frontendSounds = config->value("frontend/sound", true).toBool();
173     onFrontendSoundsToggled(frontendSounds);
174 
175     playerHash = QString(QCryptographicHash::hash(config->value("net/nick", config->getRandomNick()).toString().toUtf8(), QCryptographicHash::Md5).toHex());
176 
177     // Icons for finished missions
178     finishedIcon.addFile(":/res/missionFinished.png", QSize(), QIcon::Normal, QIcon::On);
179     finishedIcon.addFile(":/res/missionFinishedSelected.png", QSize(), QIcon::Selected, QIcon::On);
180 
181     // A transparent icon, used to nicely align the unfinished missions with the finished ones
182     QPixmap emptySpace = QPixmap(15, 15);
183     emptySpace.fill(QColor(0, 0, 0, 0));
184     notFinishedIcon = QIcon(emptySpace);
185 
186     ui.pageRoomsList->setSettings(config);
187     ui.pageNetGame->setSettings(config);
188     ui.pageNetGame->chatWidget->setSettings(config);
189     ui.pageRoomsList->chatWidget->setSettings(config);
190     ui.pageOptions->setConfig(config);
191 #ifdef VIDEOREC
192     ui.pageVideos->init(config);
193 #endif
194 
195 #if defined(__APPLE__) && defined(SPARKLE_ENABLED)
196     if (config->isAutoUpdateEnabled())
197     {
198         AutoUpdater* updater = NULL;
199 
200         updater = new SparkleAutoUpdater();
201         if (updater)
202         {
203             updater->checkForUpdates();
204             delete updater;
205         }
206     }
207 #endif
208 
209 #ifdef __APPLE__
210     panel = new M3Panel;
211 
212     QShortcut *hideFrontend = new QShortcut(QKeySequence("Ctrl+M"), this);
213     connect (hideFrontend, SIGNAL(activated()), this, SLOT(showMinimized()));
214 #else
215     // ctrl+q closes frontend for consistency
216     QShortcut * closeFrontend = new QShortcut(QKeySequence("Ctrl+Q"), this);
217     connect (closeFrontend, SIGNAL(activated()), this, SLOT(close()));
218     //QShortcut * updateData = new QShortcut(QKeySequence("F5"), this);
219     //connect (updateData, SIGNAL(activated()), &DataManager::instance(), SLOT(reload()));
220 #endif
221 
222     previousCampaignName = "";
223     previousTeamName = "";
224     UpdateTeamsLists();
225     InitCampaignPage();
226     RestoreSingleplayerTeamSelection();
227     UpdateCampaignPage(0);
228     UpdateCampaignPageTeam(0);
229     UpdateCampaignPageMission(0);
230     UpdateWeapons();
231 
232     // connect all goBack signals
233     int nPages = ui.Pages->count();
234 
235     for (int i = 0; i < nPages; i++)
236         connect(ui.Pages->widget(i), SIGNAL(goBack()), this, SLOT(GoBack()));
237 
238     pageSwitchMapper = new QSignalMapper(this);
239     connect(pageSwitchMapper, SIGNAL(mapped(int)), this, SLOT(GoToPage(int)));
240 
241     connect(ui.pageMain->BtnSinglePlayer, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
242     pageSwitchMapper->setMapping(ui.pageMain->BtnSinglePlayer, ID_PAGE_SINGLEPLAYER);
243 
244     connect(ui.pageMain->BtnSetup, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
245     pageSwitchMapper->setMapping(ui.pageMain->BtnSetup, ID_PAGE_SETUP);
246 
247     connect(ui.pageMain->BtnFeedback, SIGNAL(clicked()), this, SLOT(showFeedbackDialog()));
248 
249     connect(ui.pageMain->BtnTitle, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
250     pageSwitchMapper->setMapping(ui.pageMain->BtnTitle, ID_PAGE_INFO);
251 
252     connect(ui.pageMain->BtnInfo, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
253     pageSwitchMapper->setMapping(ui.pageMain->BtnInfo, ID_PAGE_INFO);
254 
255     connect(ui.pageMain->BtnDataDownload, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
256     pageSwitchMapper->setMapping(ui.pageMain->BtnDataDownload, ID_PAGE_DATADOWNLOAD);
257 
258     connect(ui.pageMain->BtnHelp, SIGNAL(clicked()), this, SLOT(GoToHelp()));
259 
260 #ifdef VIDEOREC
261     connect(ui.pageMain->BtnVideos, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
262     pageSwitchMapper->setMapping(ui.pageMain->BtnVideos, ID_PAGE_VIDEOS);
263 #endif
264 
265     //connect(ui.pageMain->BtnExit, SIGNAL(pressed()), this, SLOT(btnExitPressed()));
266     //connect(ui.pageMain->BtnExit, SIGNAL(clicked()), this, SLOT(btnExitClicked()));
267 
268     connect(ui.pageEditTeam, SIGNAL(goBack()), this, SLOT(AfterTeamEdit()));
269 
270     connect(ui.pageMultiplayer->BtnStartMPGame, SIGNAL(clicked()), this, SLOT(StartMPGame()));
271     connect(ui.pageMultiplayer->teamsSelect, SIGNAL(setEnabledGameStart(bool)),
272             ui.pageMultiplayer->BtnStartMPGame, SLOT(setEnabled(bool)));
273     connect(ui.pageMultiplayer, SIGNAL(SetupClicked()), this, SLOT(IntermediateSetup()));
274     connect(ui.pageMultiplayer->gameCFG, SIGNAL(goToSchemes(int)), this, SLOT(GoToScheme(int)));
275     connect(ui.pageMultiplayer->gameCFG, SIGNAL(goToWeapons(int)), this, SLOT(GoToWeapons(int)));
276     connect(ui.pageMultiplayer->gameCFG, SIGNAL(goToDrawMap()), pageSwitchMapper, SLOT(map()));
277     pageSwitchMapper->setMapping(ui.pageMultiplayer->gameCFG, ID_PAGE_DRAWMAP);
278 
279 
280     connect(ui.pagePlayDemo->BtnPlayDemo, SIGNAL(clicked()), this, SLOT(PlayDemo()));
281     connect(ui.pagePlayDemo->DemosList, SIGNAL(doubleClicked (const QModelIndex &)), this, SLOT(PlayDemo()));
282 
283     connect(ui.pageOptions, SIGNAL(newTeamRequested()), this, SLOT(NewTeam()));
284     connect(ui.pageOptions, SIGNAL(editTeamRequested(const QString&)), this, SLOT(EditTeam(const QString&)));
285     connect(ui.pageOptions, SIGNAL(deleteTeamRequested(const QString&)), this, SLOT(DeleteTeam(const QString&)));
286     connect(ui.pageOptions, SIGNAL(goBack()), config, SLOT(SaveOptions()));
287     connect(ui.pageOptions->BtnAssociateFiles, SIGNAL(clicked()), this, SLOT(AssociateFiles()));
288 
289     connect(ui.pageOptions->WeaponEdit, SIGNAL(clicked()), this, SLOT(GoToEditWeapons()));
290     connect(ui.pageOptions->WeaponNew, SIGNAL(clicked()), this, SLOT(GoToNewWeapons()));
291     connect(ui.pageOptions->WeaponDelete, SIGNAL(clicked()), this, SLOT(DeleteWeaponSet()));
292     connect(ui.pageOptions->SchemeEdit, SIGNAL(clicked()), this, SLOT(GoToEditScheme()));
293     connect(ui.pageOptions->SchemeNew, SIGNAL(clicked()), this, SLOT(GoToNewScheme()));
294     connect(ui.pageOptions->SchemeDelete, SIGNAL(clicked()), this, SLOT(DeleteScheme()));
295     connect(ui.pageOptions->CBFrontendEffects, SIGNAL(toggled(bool)), this, SLOT(onFrontendEffects(bool)) );
296     connect(ui.pageOptions->CBFrontendSound, SIGNAL(toggled(bool)), this, SLOT(onFrontendSoundsToggled(bool)));
297 
298     connect(ui.pageNet->BtnSpecifyServer, SIGNAL(clicked()), this, SLOT(NetConnect()));
299     connect(ui.pageNet->BtnNetSvrStart, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
300     pageSwitchMapper->setMapping(ui.pageNet->BtnNetSvrStart, ID_PAGE_NETSERVER);
301 
302     connect(ui.pageNet, SIGNAL(connectClicked(const QString &, quint16, bool)), this, SLOT(NetConnectServer(const QString &, quint16, bool)));
303 
304     connect(ui.pageNetServer->BtnStart, SIGNAL(clicked()), this, SLOT(NetStartServer()));
305 
306     connect(ui.pageNetGame->pNetTeamsWidget, SIGNAL(setEnabledGameStart(bool)),
307             ui.pageNetGame->BtnStart, SLOT(setEnabled(bool)));
308     connect(ui.pageNetGame, SIGNAL(SetupClicked()), this, SLOT(IntermediateSetup()));
309     connect(ui.pageNetGame->pGameCFG, SIGNAL(goToSchemes(int)), this, SLOT(GoToScheme(int)));
310     connect(ui.pageNetGame->pGameCFG, SIGNAL(goToWeapons(int)), this, SLOT(GoToWeapons(int)));
311     connect(ui.pageNetGame->pGameCFG, SIGNAL(goToDrawMap()), pageSwitchMapper, SLOT(map()));
312     pageSwitchMapper->setMapping(ui.pageNetGame->pGameCFG, ID_PAGE_DRAWMAP);
313 
314     connect(ui.pageRoomsList->BtnAdmin, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
315     pageSwitchMapper->setMapping(ui.pageRoomsList->BtnAdmin, ID_PAGE_ADMIN);
316 
317     connect(ui.pageInfo->BtnSnapshots, SIGNAL(clicked()), this, SLOT(OpenSnapshotFolder()));
318 
319     connect(ui.pageGameStats, SIGNAL(saveDemoRequested()), this, SLOT(saveDemoWithCustomName()));
320     connect(ui.pageGameStats, SIGNAL(restartGameRequested()), this, SLOT(restartGame()));
321 
322     connect(ui.pageSinglePlayer->BtnSimpleGamePage, SIGNAL(clicked()), this, SLOT(SimpleGame()));
323     connect(ui.pageSinglePlayer->BtnTrainPage, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
324     pageSwitchMapper->setMapping(ui.pageSinglePlayer->BtnTrainPage, ID_PAGE_TRAINING);
325 
326     connect(ui.pageSinglePlayer->BtnCampaignPage, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
327     pageSwitchMapper->setMapping(ui.pageSinglePlayer->BtnCampaignPage, ID_PAGE_CAMPAIGN);
328 
329     connect(ui.pageSinglePlayer->BtnMultiplayer, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
330     pageSwitchMapper->setMapping(ui.pageSinglePlayer->BtnMultiplayer, ID_PAGE_MULTIPLAYER);
331 
332     connect(ui.pageSinglePlayer->BtnLoad, SIGNAL(clicked()), this, SLOT(GoToSaves()));
333     connect(ui.pageSinglePlayer->BtnDemos, SIGNAL(clicked()), this, SLOT(GoToDemos()));
334 
335     connect(ui.pageTraining, SIGNAL(startMission(const QString&, const QString&)), this, SLOT(startTraining(const QString&, const QString&)));
336 
337     connect(ui.pageCampaign->BtnStartCampaign, SIGNAL(clicked()), this, SLOT(StartCampaign()));
338     connect(ui.pageCampaign->btnPreview, SIGNAL(clicked()), this, SLOT(StartCampaign()));
339     connect(ui.pageCampaign->CBTeam, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateCampaignPage(int)));
340     connect(ui.pageCampaign->CBTeam, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateCampaignPageTeam(int)));
341     connect(ui.pageCampaign->CBCampaign, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateCampaignPage(int)));
342     connect(ui.pageCampaign->CBMission, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateCampaignPageMission(int)));
343     connect(ui.pageTraining->CBTeam, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateTrainingPageTeam(int)));
344     connect(ui.pageCampaign->CBTeam, SIGNAL(currentIndexChanged(int)), ui.pageTraining->CBTeam, SLOT(setCurrentIndex(int)));
345     connect(ui.pageTraining->CBTeam, SIGNAL(currentIndexChanged(int)), ui.pageCampaign->CBTeam, SLOT(setCurrentIndex(int)));
346 
347     connect(ui.pageSelectWeapon->pWeapons, SIGNAL(weaponsDeleted(QString)),
348              this, SLOT(DeleteWeapons(QString)));
349     connect(ui.pageSelectWeapon->pWeapons, SIGNAL(weaponsAdded(QString, QString)),
350              this, SLOT(AddWeapons(QString, QString)));
351     connect(ui.pageSelectWeapon->pWeapons, SIGNAL(weaponsEdited(QString, QString, QString)),
352              this, SLOT(EditWeapons(QString, QString, QString)));
353     connect(ui.pageSelectWeapon->pWeapons, SIGNAL(weaponsEdited(QString, QString, QString)),
354              ui.pageNetGame->pGameCFG, SLOT(resendAmmoData()));
355 
356     connect(ui.pageMain->BtnNetLocal, SIGNAL(clicked()), this, SLOT(GoToNet()));
357     connect(ui.pageMain->BtnNetOfficial, SIGNAL(clicked()), this, SLOT(NetConnectOfficialServer()));
358 
359     connect(ui.pageVideos, SIGNAL(goBack()), config, SLOT(SaveVideosOptions()));
360 
361     gameSchemeModel = new GameSchemeModel(this, cfgdir->absolutePath() + "/Schemes/Game");
362     ui.pageScheme->setModel(gameSchemeModel);
363     ui.pageMultiplayer->gameCFG->GameSchemes->setModel(gameSchemeModel);
364     ui.pageOptions->SchemesName->setModel(gameSchemeModel);
365 
366     wBackground = new BGWidget(this);
367     wBackground->setFixedSize(this->width(), this->height());
368     wBackground->lower();
369     wBackground->init();
370     wBackground->enabled = config->isFrontendEffects();
371     wBackground->startAnimation();
372 
373     //Install all eventFilters :
374 
375     MouseOverFilter *filter = new MouseOverFilter();
376     filter->setUi(&ui);
377 
378     QList<QWidget *> widgets;
379 
380     for (int i=0; i < ui.Pages->count(); i++)
381     {
382         widgets = ui.Pages->widget(i)->findChildren<QWidget *>();
383 
384         for (int i=0; i < widgets.size(); i++)
385         {
386             widgets.at(i)->installEventFilter(filter);
387         }
388     }
389 
390     ui.Pages->setCurrentIndex(ID_PAGE_INFO);
391     PagesStack.push(ID_PAGE_MAIN);
392     ((AbstractPage*)ui.Pages->widget(ID_PAGE_MAIN))->triggerPageEnter();
393     GoBack();
394 
395     connect(config, SIGNAL(frontendFullscreen(bool)), this, SLOT(onFrontendFullscreen(bool)));
396     onFrontendFullscreen(config->isFrontendFullscreen());
397 }
398 
onFrontendFullscreen(bool value)399 void HWForm::onFrontendFullscreen(bool value)
400 {
401     if (value)
402         setWindowState(windowState() | Qt::WindowFullScreen);
403     else
404     {
405         setWindowState(windowState() & ~Qt::WindowFullScreen);
406     }
407 }
408 
onFrontendEffects(bool value)409 void HWForm::onFrontendEffects(bool value)
410 {
411     wBackground->enabled = value;
412     if (value)
413         wBackground->startAnimation();
414     else
415         wBackground->stopAnimation();
416 }
417 
onFrontendSoundsToggled(bool value)418 void HWForm::onFrontendSoundsToggled(bool value)
419 {
420     ui.pageEditTeam->frontendSoundsToggled(value);
421 }
422 
423 /*
424 void HWForm::keyReleaseEvent(QKeyEvent *event)
425 {
426   if (event->key() == Qt::Key_Escape)
427     this->GoBack();
428 }
429 */
430 
CustomizePalettes()431 void HWForm::CustomizePalettes()
432 {
433     // Scroll bar widget palette
434     QList<QScrollBar *> allSBars = findChildren<QScrollBar *>();
435     QPalette pal = palette();
436     pal.setColor(QPalette::WindowText, QColor(0xff, 0xcc, 0x00));
437     pal.setColor(QPalette::Button, QColor(0x00, 0x35, 0x1d));
438     pal.setColor(QPalette::Base, QColor(0x00, 0x35, 0x1d));
439     pal.setColor(QPalette::Window, QColor(0x00, 0x00, 0x00));
440 
441     for (int i = 0; i < allSBars.size(); ++i)
442         allSBars.at(i)->setPalette(pal);
443 
444     // Set default hyperlink color
445     QPalette appPal = qApp->palette();
446     appPal.setColor(QPalette::Link, QColor(0xff, 0xff, 0x6e));
447     qApp->setPalette(appPal);
448 }
449 
UpdateWeapons()450 void HWForm::UpdateWeapons()
451 {
452     QVector<QComboBox*> combos;
453     combos.push_back(ui.pageOptions->WeaponsName);
454     combos.push_back(ui.pageMultiplayer->gameCFG->WeaponsName);
455     combos.push_back(ui.pageNetGame->pGameCFG->WeaponsName);
456     combos.push_back(ui.pageSelectWeapon->selectWeaponSet);
457 
458     QStringList names = ui.pageSelectWeapon->pWeapons->getWeaponNames();
459 
460     for(QVector<QComboBox*>::iterator it = combos.begin(); it != combos.end(); ++it)
461     {
462         (*it)->clear();
463 
464         for(int i = 0; i < names.size(); ++i)
465             (*it)->addItem(names[i], ui.pageSelectWeapon->pWeapons->getWeaponsString(names[i]));
466 
467         int pos = (*it)->findText("Default");
468         if (pos != -1)
469         {
470             (*it)->setCurrentIndex(pos);
471         }
472     }
473 }
474 
AddWeapons(QString weaponsName,QString ammo)475 void HWForm::AddWeapons(QString weaponsName, QString ammo)
476 {
477     QVector<QComboBox*> combos;
478     combos.push_back(ui.pageOptions->WeaponsName);
479     combos.push_back(ui.pageMultiplayer->gameCFG->WeaponsName);
480     combos.push_back(ui.pageNetGame->pGameCFG->WeaponsName);
481     combos.push_back(ui.pageSelectWeapon->selectWeaponSet);
482 
483     QStringList names = ui.pageSelectWeapon->pWeapons->getWeaponNames();
484 
485     for(QVector<QComboBox*>::iterator it = combos.begin(); it != combos.end(); ++it)
486     {
487         (*it)->addItem(weaponsName, QVariant(ammo));
488     }
489     ui.pageSelectWeapon->selectWeaponSet->setCurrentIndex(ui.pageSelectWeapon->selectWeaponSet->count()-1);
490 }
491 
DeleteWeapons(QString weaponsName)492 void HWForm::DeleteWeapons(QString weaponsName)
493 {
494     QVector<QComboBox*> combos;
495     combos.push_back(ui.pageOptions->WeaponsName);
496     combos.push_back(ui.pageMultiplayer->gameCFG->WeaponsName);
497     combos.push_back(ui.pageNetGame->pGameCFG->WeaponsName);
498     combos.push_back(ui.pageSelectWeapon->selectWeaponSet);
499 
500     QStringList names = ui.pageSelectWeapon->pWeapons->getWeaponNames();
501 
502     for(QVector<QComboBox*>::iterator it = combos.begin(); it != combos.end(); ++it)
503     {
504         int pos = (*it)->findText(weaponsName);
505         if (pos != -1)
506         {
507             (*it)->removeItem(pos);
508         }
509     }
510     ui.pageSelectWeapon->pWeapons->deletionDone();
511 }
512 
EditWeapons(QString oldWeaponsName,QString newWeaponsName,QString ammo)513 void HWForm::EditWeapons(QString oldWeaponsName, QString newWeaponsName, QString ammo)
514 {
515     QVector<QComboBox*> combos;
516     combos.push_back(ui.pageOptions->WeaponsName);
517     combos.push_back(ui.pageMultiplayer->gameCFG->WeaponsName);
518     combos.push_back(ui.pageNetGame->pGameCFG->WeaponsName);
519     combos.push_back(ui.pageSelectWeapon->selectWeaponSet);
520 
521     QStringList names = ui.pageSelectWeapon->pWeapons->getWeaponNames();
522 
523     for(QVector<QComboBox*>::iterator it = combos.begin(); it != combos.end(); ++it)
524     {
525         int pos = (*it)->findText(oldWeaponsName);
526         (*it)->setItemText(pos, newWeaponsName);
527         (*it)->setItemData(pos, ammo);
528     }
529 }
530 
UpdateTeamsLists()531 void HWForm::UpdateTeamsLists()
532 {
533     QStringList teamslist = config->GetTeamsList();
534 
535     if(teamslist.empty())
536     {
537         QString currentNickName = config->value("net/nick", config->getRandomNick()).toString();
538         QString teamName;
539         int firstHumanTeam = 1;
540         int lastHumanTeam = 2;
541 
542         // Default team
543         if (currentNickName.isEmpty())
544         {
545             teamName = tr("Team 1");
546             firstHumanTeam++;
547         }
548         else
549         {
550             teamName = tr("%1's Team").arg(currentNickName);
551             lastHumanTeam--;
552         }
553 
554         HWTeam defaultTeam(teamName);
555         // Randomize fort and grave for greater variety by default.
556         // But we exclude DLC graves and forts to not have desyncing teams by default
557         // TODO: Remove DLC filtering when it isn't neccessary anymore
558         HWNamegen::teamRandomGrave(defaultTeam, false);
559         HWNamegen::teamRandomFort(defaultTeam, false);
560         HWNamegen::teamLocalizedDefaultVoice(defaultTeam);
561         defaultTeam.saveToFile();
562         teamslist.push_back(teamName);
563 
564         // Add additional default teams
565 
566         // More human teams to allow local multiplayer instantly
567         for(int i=firstHumanTeam; i<=lastHumanTeam; i++)
568         {
569             //: Default team name
570             teamName = tr("Team %1").arg(i);
571             HWTeam numberTeam(teamName);
572             HWNamegen::teamRandomGrave(numberTeam, false);
573             HWNamegen::teamRandomFort(numberTeam, false);
574             HWNamegen::teamLocalizedDefaultVoice(numberTeam);
575             numberTeam.saveToFile();
576             teamslist.push_back(teamName);
577         }
578         // Add 2 default CPU teams
579         for(int i=1; i<=5; i=i+2)
580         {
581             //: Default computer team name
582             teamName = tr("Computer %1").arg(i);
583             HWTeam numberTeam(teamName);
584             HWNamegen::teamRandomGrave(numberTeam, false);
585             HWNamegen::teamRandomFort(numberTeam, false);
586             HWNamegen::teamLocalizedDefaultVoice(numberTeam);
587             numberTeam.setDifficulty(6-i);
588             numberTeam.saveToFile();
589             teamslist.push_back(teamName);
590         }
591     }
592 
593     ui.pageOptions->CBTeamName->clear();
594     ui.pageOptions->CBTeamName->addItems(teamslist);
595     ui.pageCampaign->CBTeam->clear();
596     ui.pageTraining->CBTeam->clear();
597     /* Only show human teams in campaign/training page */
598     bool playable = false;
599     for(int i=0; i<teamslist.length(); i++)
600     {
601         HWTeam testTeam = HWTeam(teamslist[i]);
602         testTeam.loadFromFile();
603         if(testTeam.difficulty() == 0)
604         {
605             ui.pageCampaign->CBTeam->addItem(teamslist[i]);
606             ui.pageTraining->CBTeam->addItem(teamslist[i]);
607             playable = true;
608         }
609     }
610     ui.pageCampaign->BtnStartCampaign->setEnabled(playable);
611     ui.pageCampaign->btnPreview->setEnabled(playable);
612     ui.pageTraining->btnStart->setEnabled(playable);
613     ui.pageTraining->btnPreview->setEnabled(playable);
614     UpdateTrainingPageTeam(0);
615 }
616 
GoToNewWeapons()617 void HWForm::GoToNewWeapons()
618 {
619     ui.pageSelectWeapon->pWeapons->newWeaponsName();
620     GoToPage(ID_PAGE_SELECTWEAPON);
621 }
622 
GoToEditWeapons()623 void HWForm::GoToEditWeapons()
624 {
625     ui.pageSelectWeapon->selectWeaponSet->setCurrentIndex(ui.pageOptions->WeaponsName->currentIndex());
626     GoToPage(ID_PAGE_SELECTWEAPON);
627 }
628 
GoToWeapons(int index)629 void HWForm::GoToWeapons(int index)
630 {
631     ui.pageSelectWeapon->selectWeaponSet->setCurrentIndex(index);
632     GoToPage(ID_PAGE_SELECTWEAPON);
633 }
634 
635 
GoToSaves()636 void HWForm::GoToSaves()
637 {
638     ui.pagePlayDemo->FillFromDir(PagePlayDemo::RT_Save);
639 
640     GoToPage(ID_PAGE_DEMOS);
641 }
642 
GoToDemos()643 void HWForm::GoToDemos()
644 {
645     ui.pagePlayDemo->FillFromDir(PagePlayDemo::RT_Demo);
646 
647     GoToPage(ID_PAGE_DEMOS);
648 }
649 
GoToNet()650 void HWForm::GoToNet()
651 {
652     ui.pageNet->updateServersList();
653 
654     GoToPage(ID_PAGE_NET);
655 }
656 
GoToScheme(int index)657 void HWForm::GoToScheme(int index)
658 {
659     ui.pageScheme->selectScheme->setCurrentIndex(index);
660     GoToPage(ID_PAGE_SCHEME);
661 }
662 
GoToNewScheme()663 void HWForm::GoToNewScheme()
664 {
665     ui.pageScheme->newRow();
666     GoToPage(ID_PAGE_SCHEME);
667 }
668 
GoToEditScheme()669 void HWForm::GoToEditScheme()
670 {
671     ui.pageScheme->selectScheme->setCurrentIndex(ui.pageOptions->SchemesName->currentIndex());
672     GoToPage(ID_PAGE_SCHEME);
673 }
674 
GoToHelp()675 void HWForm::GoToHelp()
676 {
677     // For now just opens the Hedgewars Wiki in external browser.
678     // TODO: Replace this with an offline help someday (bug 660).
679     QDesktopServices::openUrl(QUrl("https://hedgewars.org/wiki"));
680 }
681 
GoToVideos()682 void HWForm::GoToVideos()
683 {
684     GoToPage(ID_PAGE_VIDEOS);
685 }
686 
GoToTraining()687 void HWForm::GoToTraining()
688 {
689     GoToPage(ID_PAGE_TRAINING);
690 }
691 
692 //TODO: maybe find a better place for this?
stringifyPageId(quint32 id)693 QString HWForm::stringifyPageId(quint32 id)
694 {
695     QString pageName;
696     switch (id)
697     {
698       case ID_PAGE_SETUP_TEAM :   pageName = "PAGE_SETUP_TEAM"; break;
699       case ID_PAGE_SETUP :        pageName = "PAGE_SETUP"; break;
700       case ID_PAGE_MULTIPLAYER :  pageName = "PAGE_MULTIPLAYER"; break;
701       case ID_PAGE_DEMOS :        pageName = "PAGE_DEMOS"; break;
702       case ID_PAGE_NET :          pageName = "PAGE_NET"; break;
703       case ID_PAGE_NETGAME :      pageName = "PAGE_NETGAME"; break;
704       case ID_PAGE_INFO :         pageName = "PAGE_INFO"; break;
705       case ID_PAGE_MAIN :         pageName = "PAGE_MAIN"; break;
706       case ID_PAGE_GAMESTATS :    pageName = "PAGE_GAMESTATS"; break;
707       case ID_PAGE_SINGLEPLAYER : pageName = "PAGE_SINGLEPLAYER"; break;
708       case ID_PAGE_TRAINING :     pageName = "PAGE_TRAINING"; break;
709       case ID_PAGE_SELECTWEAPON : pageName = "PAGE_SELECTWEAPON"; break;
710       case ID_PAGE_NETSERVER :    pageName = "PAGE_NETSERVER"; break;
711       case ID_PAGE_INGAME :       pageName = "PAGE_INGAME"; break;
712       case ID_PAGE_ROOMSLIST :    pageName = "PAGE_ROOMSLIST"; break;
713       case ID_PAGE_CONNECTING :   pageName = "PAGE_CONNECTING"; break;
714       case ID_PAGE_SCHEME :       pageName = "PAGE_SCHEME"; break;
715       case ID_PAGE_ADMIN :        pageName = "PAGE_ADMIN"; break;
716       case ID_PAGE_CAMPAIGN :     pageName = "PAGE_CAMPAIGN"; break;
717       case ID_PAGE_DRAWMAP :      pageName = "PAGE_DRAWMAP"; break;
718       case ID_PAGE_DATADOWNLOAD : pageName = "PAGE_DATADOWNLOAD"; break;
719       case ID_PAGE_VIDEOS :       pageName = "PAGE_VIDEOS"; break;
720       case MAX_PAGE :             pageName = "MAX_PAGE"; break;
721       default :                   pageName = "UNKNOWN_PAGE"; break;
722     }
723     return pageName;
724 }
725 
OnPageShown(quint8 id,quint8 lastid)726 void HWForm::OnPageShown(quint8 id, quint8 lastid)
727 {
728 #ifdef QT_DEBUG
729     qDebug("Leaving %s, entering %s", qPrintable(stringifyPageId(lastid)), qPrintable(stringifyPageId(id)));
730 #endif
731     if (lastid == ID_PAGE_MAIN)
732     {
733         ui.pageMain->resetNetworkChoice();
734     }
735 
736     // pageEnter and pageLeave events
737     ((AbstractPage*)ui.Pages->widget(lastid))->triggerPageLeave();
738     ((AbstractPage*)ui.Pages->widget(id))->triggerPageEnter();
739 
740     if (id == ID_PAGE_DATADOWNLOAD)
741     {
742         ui.pageDataDownload->fetchList();
743     }
744     if (id == ID_PAGE_DRAWMAP)
745     {
746         DrawMapScene * scene;
747         if(lastid == ID_PAGE_MULTIPLAYER)
748             scene = ui.pageMultiplayer->gameCFG->pMapContainer->getDrawMapScene();
749         else
750             scene = ui.pageNetGame->pGameCFG->pMapContainer->getDrawMapScene();
751 
752         ui.pageDrawMap->drawMapWidget->setScene(scene);
753     }
754 
755     if (lastid == ID_PAGE_DRAWMAP)
756     {
757         if (id == ID_PAGE_MULTIPLAYER)
758             ui.pageMultiplayer->gameCFG->pMapContainer->mapDrawingFinished();
759         else
760             ui.pageNetGame->pGameCFG->pMapContainer->mapDrawingFinished();
761     }
762 
763     if (id == ID_PAGE_ROOMSLIST)
764     {
765         if (hwnet && game && game->gameState == gsStarted)   // abnormal exit - kick or room destruction - send kills.
766         {
767             game->netSuspend = true;
768             ui.pageRoomsList->displayWarning(tr("Game aborted"));
769             game->abort();
770         }
771     }
772 
773     if (id == ID_PAGE_MULTIPLAYER || id == ID_PAGE_NETGAME)
774     {
775         QStringList tmNames = config->GetTeamsList();
776         TeamSelWidget* curTeamSelWidget;
777         ui.pageOptions->setTeamOptionsEnabled(false);
778 
779         if (id == ID_PAGE_MULTIPLAYER)
780         {
781             curTeamSelWidget = ui.pageMultiplayer->teamsSelect;
782         }
783         else
784         {
785             curTeamSelWidget = ui.pageNetGame->pNetTeamsWidget;
786         }
787 
788         QList<HWTeam> teamsList;
789         for (QStringList::iterator it = tmNames.begin(); it != tmNames.end(); ++it)
790         {
791             HWTeam team(*it);
792             team.loadFromFile();
793             teamsList.push_back(team);
794         }
795 
796         if (lastid == ID_PAGE_SETUP || lastid == ID_PAGE_DRAWMAP)   // _TEAM
797         {
798             if (editedTeam)
799             {
800                 curTeamSelWidget->addTeam(*editedTeam);
801             }
802         }
803         else if (lastid != ID_PAGE_GAMESTATS
804                  && lastid != ID_PAGE_INGAME
805                  && lastid != ID_PAGE_SCHEME
806                  && lastid != ID_PAGE_SELECTWEAPON)
807         {
808             curTeamSelWidget->resetPlayingTeams(teamsList);
809         }
810     }
811 
812     if (id == ID_PAGE_GAMESTATS)
813     {
814         switch(lastGameType) {
815         case gtLocal:
816         case gtQLocal:
817         case gtTraining:
818         case gtCampaign:
819         case gtDemo:
820         case gtSave:
821             ui.pageGameStats->restartBtnVisible(true);
822             break;
823         default:
824             ui.pageGameStats->restartBtnVisible(false);
825             break;
826         }
827         ui.pageGameStats->saveDemoBtnEnabled(demoIsPresent);
828     }
829 
830     if (id == ID_PAGE_MAIN)
831     {
832         ui.pageOptions->setTeamOptionsEnabled(true);
833     }
834 }
835 
GoToPage(int id)836 void HWForm::GoToPage(int id)
837 {
838     //bool stopAnim = false;
839 
840     int lastid = ui.Pages->currentIndex();
841     PagesStack.push(ui.Pages->currentIndex());
842 
843     OnPageShown(id, lastid);
844     ui.Pages->setCurrentIndex(id);
845 
846 
847    /* if (id == ID_PAGE_DRAWMAP || id == ID_PAGE_GAMESTATS)
848         stopAnim = true;
849     This were disabled due to broken flake animations.  I believe the more general problems w/ opacity that forced its disable makes blocking these
850     unnecessary.
851    */
852 
853 
854 #if (QT_VERSION >= 0x040600)
855     //if (!stopAnim)
856     {
857         /**Start animation :**/
858         int coeff = 1;
859 #ifdef false
860         coeff = 2;
861         QGraphicsOpacityEffect *effectNew = new QGraphicsOpacityEffect(ui.Pages->widget(id));
862         ui.Pages->widget(id)->setGraphicsEffect(effectNew);
863 
864         QGraphicsOpacityEffect *effectLast = new QGraphicsOpacityEffect(ui.Pages->widget(lastid));
865         ui.Pages->widget(lastid)->setGraphicsEffect(effectLast);
866 #endif
867         // no effects, means 0 effect duration :D
868         int duration = config->isFrontendEffects() ? 500 : 0;
869 
870         //New page animation
871         animationNewSlide = new QPropertyAnimation(ui.Pages->widget(id), "pos");
872         animationNewSlide->setDuration(duration);
873         animationNewSlide->setStartValue(QPoint(width()/coeff, 0));
874         animationNewSlide->setEndValue(QPoint(0, 0));
875         animationNewSlide->setEasingCurve(QEasingCurve::OutExpo);
876 
877 #ifdef false
878         animationNewOpacity = new QPropertyAnimation(effectNew, "opacity");
879         animationNewOpacity->setDuration(duration);
880         animationNewOpacity->setStartValue(0.01);
881         animationNewOpacity->setEndValue(1);
882         animationNewOpacity->setEasingCurve(QEasingCurve::OutExpo);
883 #endif
884 
885         //Last page animation
886         ui.Pages->widget(lastid)->setHidden(false);
887 
888         animationOldSlide = new QPropertyAnimation(ui.Pages->widget(lastid), "pos");
889         animationOldSlide->setDuration(duration);
890         animationOldSlide->setStartValue(QPoint(0, 0));
891         animationOldSlide->setEndValue(QPoint(-width()/coeff, 0));
892         animationOldSlide->setEasingCurve(QEasingCurve::OutExpo);
893 
894 #ifdef false
895         animationOldOpacity = new QPropertyAnimation(effectLast, "opacity");
896         animationOldOpacity->setDuration(duration);
897         animationOldOpacity->setStartValue(1);
898         animationOldOpacity->setEndValue(0.01);
899         animationOldOpacity->setEasingCurve(QEasingCurve::OutExpo);
900 #endif
901 
902         // let's hide the old slide after its animation has finished
903         connect(animationOldSlide, SIGNAL(finished()), ui.Pages->widget(lastid), SLOT(hide()));
904 
905         // start animations
906         animationOldSlide->start(QAbstractAnimation::DeleteWhenStopped);
907         animationNewSlide->start(QAbstractAnimation::DeleteWhenStopped);
908 
909         /* this is for the situation when the animation below is interrupted by a new animation.  For some reason, finished is not being fired */
910         for(int i=0;i<MAX_PAGE;i++) if (i!=id && i!=lastid) ui.Pages->widget(i)->hide();
911     }
912 #endif
913 }
914 
GoBack()915 void HWForm::GoBack()
916 {
917     bool stopAnim = false;
918     int curid = ui.Pages->currentIndex();
919     if (curid == ID_PAGE_MAIN)
920     {
921         ((AbstractPage*)ui.Pages->widget(ID_PAGE_MAIN))->triggerPageLeave();
922         if (!ui.pageVideos->tryQuit(this))
923             return;
924         stopAnim = true;
925         exit();
926     }
927 
928     int id = PagesStack.isEmpty() ? ID_PAGE_MAIN : PagesStack.pop();
929     ui.Pages->setCurrentIndex(id);
930     OnPageShown(id, curid);
931 
932     if (id == ID_PAGE_CONNECTING)
933     {
934         stopAnim = true;
935         GoBack();
936     }
937     if (id == ID_PAGE_NETSERVER)
938     {
939         stopAnim = true;
940         GoBack();
941     }
942     if ((!hwnet) && (id == ID_PAGE_ROOMSLIST))
943     {
944         stopAnim = true;
945         GoBack();
946     }
947     /*if (curid == ID_PAGE_DRAWMAP)
948         stopAnim = true; */
949 
950     if ((!hwnet) || (!hwnet->isInRoom()))
951         if (id == ID_PAGE_NETGAME)
952         {
953             stopAnim = true;
954             GoBack();
955         }
956 
957     if (curid == ID_PAGE_CAMPAIGN)
958         config->setValue("frontend/lastSingleplayerTeam", ui.pageCampaign->CBTeam->currentText());
959     if (curid == ID_PAGE_TRAINING)
960         config->setValue("frontend/lastSingleplayerTeam", ui.pageTraining->CBTeam->currentText());
961 
962     if (curid == ID_PAGE_ROOMSLIST || curid == ID_PAGE_CONNECTING) NetDisconnect();
963     if (curid == ID_PAGE_NETGAME && hwnet && hwnet->isInRoom()) hwnet->partRoom();
964     // need to work on this, can cause invalid state for admin quit trying to prevent bad state message on kick
965     //if (curid == ID_PAGE_NETGAME && (!game || game->gameState != gsStarted)) hwnet->partRoom();
966 
967     if (curid == ID_PAGE_SCHEME)
968         gameSchemeModel->Save();
969 
970 #if (QT_VERSION >= 0x040600)
971     /**Start animation :**/
972     if (curid != 0 && !stopAnim)
973     {
974         int coeff = 1;
975 #ifdef false
976         coeff = 2;
977         QGraphicsOpacityEffect *effectNew = new QGraphicsOpacityEffect(ui.Pages->widget(id));
978         effectNew->setOpacity(1);
979         ui.Pages->widget(id)->setGraphicsEffect(effectNew);
980 
981         QGraphicsOpacityEffect *effectLast = new QGraphicsOpacityEffect(ui.Pages->widget(curid));
982         ui.Pages->widget(curid)->setGraphicsEffect(effectLast);
983 #endif
984         // no effects, means 0 effect duration :D
985         int duration = config->isFrontendEffects() ? 500 : 0;
986 
987         //Last page animation
988         animationOldSlide = new QPropertyAnimation(ui.Pages->widget(id), "pos");
989         animationOldSlide->setDuration(duration);
990         animationOldSlide->setStartValue(QPoint(-width()/coeff, 0));
991         animationOldSlide->setEndValue(QPoint(0, 0));
992         animationOldSlide->setEasingCurve(QEasingCurve::OutExpo);
993 
994 #ifdef false
995         animationOldOpacity = new QPropertyAnimation(effectLast, "opacity");
996         animationOldOpacity->setDuration(duration);
997         animationOldOpacity->setStartValue(1);
998         animationOldOpacity->setEndValue(0.01);
999         animationOldOpacity->setEasingCurve(QEasingCurve::OutExpo);
1000 #endif
1001         //New page animation
1002         ui.Pages->widget(curid)->setHidden(false);
1003 
1004         animationNewSlide = new QPropertyAnimation(ui.Pages->widget(curid), "pos");
1005         animationNewSlide->setDuration(duration);
1006         animationNewSlide->setStartValue(QPoint(0, 0));
1007         animationNewSlide->setEndValue(QPoint(width()/coeff, 0));
1008         animationNewSlide->setEasingCurve(QEasingCurve::OutExpo);
1009 
1010 #ifdef false
1011         animationNewOpacity = new QPropertyAnimation(effectNew, "opacity");
1012         animationNewOpacity->setDuration(duration);
1013         animationNewOpacity->setStartValue(0.01);
1014         animationNewOpacity->setEndValue(1);
1015         animationNewOpacity->setEasingCurve(QEasingCurve::OutExpo);
1016 #endif
1017 
1018         // let's hide the old slide after its animation has finished
1019         connect(animationNewSlide, SIGNAL(finished()), ui.Pages->widget(curid), SLOT(hide()));
1020 
1021         // start animations
1022         animationOldSlide->start(QAbstractAnimation::DeleteWhenStopped);
1023         animationNewSlide->start(QAbstractAnimation::DeleteWhenStopped);
1024     }
1025 #endif
1026 
1027     if (stopAnim)
1028         ui.Pages->widget(curid)->hide();
1029 
1030 // TODO the whole pages shown and effects stuff should be moved
1031 // out of hwform.cpp and into a subclass of QStackedLayout
1032 
1033 }
1034 
OpenSnapshotFolder()1035 void HWForm::OpenSnapshotFolder()
1036 {
1037     QString path = QDir::toNativeSeparators(cfgdir->absolutePath() + "/Screenshots");
1038     QDesktopServices::openUrl(QUrl("file:///" + path));
1039 }
1040 
btnExitPressed()1041 void HWForm::btnExitPressed()
1042 {
1043     eggTimer.start();
1044 }
1045 
exit()1046 void HWForm::exit()
1047 {
1048 //   if (eggTimer.elapsed() < 3000){
1049 #ifdef __APPLE__
1050     panel->showInstallController();
1051 #endif
1052     close();
1053 // TODO reactivate egg
1054     /*    }
1055         else
1056         {
1057             QPushButton * btn = findChild<QPushButton *>("imageButt");
1058             if (btn)
1059             {
1060                 btn->setIcon(QIcon(":/res/bonus.png"));
1061             }
1062         } */
1063 }
1064 
IntermediateSetup()1065 void HWForm::IntermediateSetup()
1066 {
1067     quint8 id=ui.Pages->currentIndex();
1068     TeamSelWidget* curTeamSelWidget;
1069 
1070     if(id == ID_PAGE_MULTIPLAYER)
1071     {
1072         curTeamSelWidget = ui.pageMultiplayer->teamsSelect;
1073     }
1074     else
1075     {
1076         curTeamSelWidget = ui.pageNetGame->pNetTeamsWidget;
1077     }
1078 
1079     QStringList tmnames;
1080 
1081     foreach(HWTeam team, curTeamSelWidget->getNotPlayingTeams())
1082     tmnames += team.name();
1083 
1084     //UpdateTeamsLists(&tmnames); // FIXME: still need more work if teamname is updated while configuring
1085     UpdateTeamsLists();
1086 
1087     GoToPage(ID_PAGE_SETUP);
1088 }
1089 
NewTeam()1090 void HWForm::NewTeam()
1091 {
1092     QString teamName = QLineEdit::tr("unnamed");
1093     QStringList teamslist = config->GetTeamsList();
1094     if(teamslist.contains(teamName))
1095     {
1096         //name already used -> look for an appropriate name:
1097         int i=2;
1098         while(teamslist.contains(teamName = QLineEdit::tr("unnamed (%1)").arg(i++)));
1099     }
1100 
1101     ui.pageEditTeam->createTeam(teamName, playerHash);
1102     UpdateTeamsLists();
1103     GoToPage(ID_PAGE_SETUP_TEAM);
1104 }
1105 
EditTeam(const QString & teamName)1106 void HWForm::EditTeam(const QString & teamName)
1107 {
1108     ui.pageEditTeam->editTeam(teamName, playerHash);
1109     GoToPage(ID_PAGE_SETUP_TEAM);
1110 }
1111 
AfterTeamEdit()1112 void HWForm::AfterTeamEdit()
1113 {
1114     UpdateTeamsLists();
1115     //GoBack();
1116 }
1117 
1118 
DeleteTeam(const QString & teamName)1119 void HWForm::DeleteTeam(const QString & teamName)
1120 {
1121     ui.pageEditTeam->deleteTeam(teamName);
1122     UpdateTeamsLists();
1123 }
1124 
DeleteScheme()1125 void HWForm::DeleteScheme()
1126 {
1127     ui.pageScheme->selectScheme->setCurrentIndex(ui.pageOptions->SchemesName->currentIndex());
1128     if (ui.pageOptions->SchemesName->currentIndex() < gameSchemeModel->numberOfDefaultSchemes)
1129     {
1130         MessageDialog::ShowErrorMessage(QMessageBox::tr("Cannot delete default scheme '%1'!").arg(ui.pageOptions->SchemesName->currentText()), this);
1131     }
1132     else
1133     {
1134         ui.pageScheme->deleteRow();
1135         gameSchemeModel->Save();
1136     }
1137 }
1138 
DeleteWeaponSet()1139 void HWForm::DeleteWeaponSet()
1140 {
1141     ui.pageSelectWeapon->selectWeaponSet->setCurrentIndex(ui.pageOptions->WeaponsName->currentIndex());
1142     ui.pageSelectWeapon->pWeapons->deleteWeaponsName();
1143 }
1144 
SimpleGame()1145 void HWForm::SimpleGame()
1146 {
1147     CreateGame(0, 0, *cDefaultAmmoStore);
1148     game->StartQuick();
1149 }
1150 
PlayDemo()1151 void HWForm::PlayDemo()
1152 {
1153     QListWidgetItem * curritem = ui.pagePlayDemo->DemosList->currentItem();
1154     if (!curritem)
1155     {
1156         MessageDialog::ShowErrorMessage(QMessageBox::tr("Please select a record from the list"), this);
1157         return;
1158     }
1159     CreateGame(0, 0, 0);
1160     game->PlayDemo(curritem->data(Qt::UserRole).toString(), ui.pagePlayDemo->isSave());
1161 }
1162 
PlayDemoQuick(const QString & demofilename)1163 void HWForm::PlayDemoQuick(const QString & demofilename)
1164 {
1165     GoToPage(ID_PAGE_MAIN);
1166     //GoBack() <- don't or you'll close the socket
1167     CreateGame(0, 0, 0);
1168     game->PlayDemo(demofilename, false);
1169 }
1170 
NetConnectQuick(const QString & host,quint16 port)1171 void HWForm::NetConnectQuick(const QString & host, quint16 port)
1172 {
1173     GoToPage(ID_PAGE_MAIN);
1174     NetConnectServer(host, port, false);
1175 }
1176 
NetConnectServer(const QString & host,quint16 port,bool useTls)1177 void HWForm::NetConnectServer(const QString & host, quint16 port, bool useTls)
1178 {
1179     qDebug("connecting to %s:%d", qPrintable(host), port);
1180     _NetConnect(host, port, useTls, ui.pageOptions->editNetNick->text().trimmed());
1181 }
1182 
NetConnectOfficialServer()1183 void HWForm::NetConnectOfficialServer()
1184 {
1185     NetConnectServer(NETGAME_DEFAULT_SERVER, NETGAME_DEFAULT_PORT, false);
1186 }
1187 
NetPassword(const QString & nick)1188 void HWForm::NetPassword(const QString & nick)
1189 {
1190     Q_UNUSED(nick);
1191     //Get hashes
1192     QString hash =  config->passwordHash();
1193     QString temphash =  config->tempHash();
1194 
1195     //Check them
1196 
1197     if (temphash.isEmpty() && hash.isEmpty()) { //If the user enters a registered nick with no password, sends a bogus hash
1198         hwnet->SendPasswordHash("THISISNOHASH");
1199     }
1200     else if (temphash.isEmpty()) { //Send saved hash as default
1201         hwnet->SendPasswordHash(hash);
1202     }
1203     else { //Send the hash
1204         hwnet->SendPasswordHash(temphash);
1205     }
1206 
1207     //Remove temporary hash from config
1208     config->clearTempHash();
1209 }
1210 
NetNickRegistered(const QString & nick)1211 void HWForm::NetNickRegistered(const QString & nick)
1212 {
1213     //Get hashes
1214     QString hash =  config->passwordHash();
1215     QString temphash =  config->tempHash();
1216 
1217     if (hash.isEmpty()) {
1218         if (temphash.isEmpty()) { //If the user enters a registered nick with no password
1219             QString suppliedpass;
1220             while (suppliedpass.isEmpty()) {
1221                 QInputDialog nickRegedDialog(this);
1222                 nickRegedDialog.setWindowModality(Qt::WindowModal);
1223                 nickRegedDialog.setInputMode(QInputDialog::TextInput);
1224                 nickRegedDialog.setWindowTitle(tr("Hedgewars - Nick registered"));
1225                 nickRegedDialog.setLabelText(tr("This nick is registered, and you haven't specified a password.\n\nIf this nick isn't yours, please register your own nick at www.hedgewars.org\n\nPassword:"));
1226                 nickRegedDialog.setTextEchoMode(QLineEdit::Password);
1227                 nickRegedDialog.exec();
1228 
1229                 suppliedpass = nickRegedDialog.textValue();
1230 
1231                 if (nickRegedDialog.result() == QDialog::Rejected) {
1232                     config->clearPasswordHash();
1233                     config->clearTempHash();
1234                     GoBack();
1235                     return;
1236                 }
1237                 temphash = QCryptographicHash::hash(suppliedpass.toUtf8(), QCryptographicHash::Md5).toHex();
1238                 config->setTempHash(temphash);
1239             }
1240         }
1241     }
1242     NetPassword(nick);
1243 }
1244 
NetNickNotRegistered(const QString & nick)1245 void HWForm::NetNickNotRegistered(const QString & nick)
1246 {
1247     Q_UNUSED(nick);
1248 
1249     QMessageBox noRegMsg(this);
1250     noRegMsg.setIcon(QMessageBox::Information);
1251     noRegMsg.setWindowTitle(QMessageBox::tr("Hedgewars - Nick not registered"));
1252     noRegMsg.setWindowModality(Qt::WindowModal);
1253     noRegMsg.setText(tr("Your nickname is not registered.\nTo prevent someone else from using it,\nplease register it at www.hedgewars.org"));
1254 
1255     if (!config->passwordHash().isEmpty())
1256     {
1257         config->clearPasswordHash();
1258         noRegMsg.setText(noRegMsg.text()+tr("\n\nYour password wasn't saved either."));
1259     }
1260     if (!config->tempHash().isEmpty())
1261     {
1262         config->clearTempHash();
1263     }
1264     noRegMsg.exec();
1265 }
1266 
NetNickTaken(const QString & nick)1267 void HWForm::NetNickTaken(const QString & nick)
1268 {
1269     bool ok = false;
1270     QString newNick = QInputDialog::getText(this, tr("Nickname"), tr("Someone already uses your nickname %1 on the server.\nPlease pick another nickname:").arg(nick), QLineEdit::Normal, nick, &ok);
1271 
1272     if (!ok || newNick.isEmpty())
1273     {
1274         //ForcedDisconnect(tr("No nickname supplied."));
1275         bool retry = RetryDialog(tr("Hedgewars - Empty nickname"), tr("No nickname supplied."));
1276         GoBack();
1277         if (retry && hwnet) {
1278             if (hwnet->m_private_game) {
1279                 QStringList list = hwnet->getHost().split(":");
1280                 NetConnectServer(list.at(0), list.at(1).toShort(), false);
1281             } else
1282                 NetConnectOfficialServer();
1283         }
1284         return;
1285     }
1286 
1287     if(hwnet)
1288         hwnet->NewNick(newNick);
1289     config->setValue("net/nick", newNick);
1290     config->updNetNick();
1291 
1292     ui.pageRoomsList->setUser(nick);
1293     ui.pageNetGame->setUser(nick);
1294 }
1295 
NetAuthFailed()1296 void HWForm::NetAuthFailed()
1297 {
1298     // Set the password blank if case the user tries to join and enter his password again
1299     config->clearTempHash();
1300 
1301     //Try to login again
1302     bool retry = RetryDialog(tr("Hedgewars - Wrong password"), tr("You entered a wrong password."));
1303     GoBack();
1304 
1305     config->clearPasswordHash();
1306     config->clearTempHash();
1307     if (retry) {
1308        NetConnectOfficialServer();
1309     }
1310 }
1311 
askRoomPassword()1312 void HWForm::askRoomPassword()
1313 {
1314     QString password = QInputDialog::getText(this, tr("Room password"), tr("The room is protected with password.\nPlease, enter the password:"));
1315     if(hwnet && !password.isEmpty())
1316         hwnet->roomPasswordEntered(password);
1317 }
1318 
RetryDialog(const QString & title,const QString & label)1319 bool HWForm::RetryDialog(const QString & title, const QString & label)
1320 {
1321     QMessageBox retryMsg(this);
1322     retryMsg.setIcon(QMessageBox::Warning);
1323     retryMsg.setWindowTitle(title);
1324     retryMsg.setText(label);
1325     retryMsg.setWindowModality(Qt::WindowModal);
1326 
1327     retryMsg.addButton(QMessageBox::Cancel);
1328 
1329     QPushButton *retryButton = retryMsg.addButton(QMessageBox::Ok);
1330     retryButton->setText(tr("Try Again"));
1331     retryButton->setFocus();
1332 
1333     retryMsg.exec();
1334 
1335     if (retryMsg.clickedButton() == retryButton) {
1336        return true;
1337     }
1338     return false;
1339 }
1340 
NetTeamAccepted(const QString & team)1341 void HWForm::NetTeamAccepted(const QString & team)
1342 {
1343     ui.pageNetGame->pNetTeamsWidget->changeTeamStatus(team);
1344 }
1345 
NetError(const QString & errmsg)1346 void HWForm::NetError(const QString & errmsg)
1347 {
1348     switch (ui.Pages->currentIndex())
1349     {
1350         case ID_PAGE_INGAME:
1351             MessageDialog::ShowErrorMessage(errmsg, this);
1352             /* fallthrough */
1353         case ID_PAGE_NETGAME:
1354             ui.pageNetGame->displayError(errmsg);
1355             break;
1356         default:
1357             ui.pageRoomsList->displayError(errmsg);
1358     }
1359 }
1360 
NetWarning(const QString & wrnmsg)1361 void HWForm::NetWarning(const QString & wrnmsg)
1362 {
1363     if (ui.Pages->currentIndex() == ID_PAGE_NETGAME || ui.Pages->currentIndex() == ID_PAGE_INGAME)
1364         ui.pageNetGame->displayWarning(wrnmsg);
1365     else
1366         ui.pageRoomsList->displayWarning(wrnmsg);
1367 }
1368 
_NetConnect(const QString & hostName,quint16 port,bool useTls,QString nick)1369 void HWForm::_NetConnect(const QString & hostName, quint16 port, bool useTls, QString nick)
1370 {
1371     Q_UNUSED(nick);
1372 
1373     if (hwnet) {
1374         // destroy old connection
1375         hwnet->Disconnect();
1376         delete hwnet;
1377         hwnet = NULL;
1378     }
1379 
1380     hwnet = new HWNewNet();
1381 
1382     GoToPage(ID_PAGE_CONNECTING);
1383 
1384     connect(hwnet, SIGNAL(AskForRunGame()), this, SLOT(CreateNetGame()), Qt::QueuedConnection);
1385     connect(hwnet, SIGNAL(AskForOfficialServerDemo()), this, SLOT(PlayOfficialServerDemo()), Qt::QueuedConnection);
1386     connect(hwnet, SIGNAL(redirected(quint16)), this, SLOT(NetRedirected(quint16)), Qt::QueuedConnection);
1387     connect(hwnet, SIGNAL(connected()), this, SLOT(NetConnected()), Qt::QueuedConnection);
1388     connect(hwnet, SIGNAL(Error(const QString&)), this, SLOT(NetError(const QString&)), Qt::QueuedConnection);
1389     connect(hwnet, SIGNAL(Warning(const QString&)), this, SLOT(NetWarning(const QString&)), Qt::QueuedConnection);
1390     connect(hwnet, SIGNAL(EnteredGame()), this, SLOT(NetGameEnter()), Qt::QueuedConnection);
1391     connect(hwnet, SIGNAL(LeftRoom(const QString&)), this, SLOT(NetLeftRoom(const QString&)), Qt::QueuedConnection);
1392     connect(hwnet, SIGNAL(AddNetTeam(const HWTeam&)), this, SLOT(AddNetTeam(const HWTeam&)), Qt::QueuedConnection);
1393     connect(hwnet, SIGNAL(RemoveNetTeam(const HWTeam&)), this, SLOT(RemoveNetTeam(const HWTeam&)), Qt::QueuedConnection);
1394     connect(hwnet, SIGNAL(TeamAccepted(const QString&)), this, SLOT(NetTeamAccepted(const QString&)), Qt::QueuedConnection);
1395     connect(hwnet, SIGNAL(NickRegistered(const QString&)), this, SLOT(NetNickRegistered(const QString&)), Qt::QueuedConnection);
1396     connect(hwnet, SIGNAL(NickNotRegistered(const QString&)), this, SLOT(NetNickNotRegistered(const QString&)), Qt::QueuedConnection);
1397     connect(hwnet, SIGNAL(NickTaken(const QString&)), this, SLOT(NetNickTaken(const QString&)), Qt::QueuedConnection);
1398     connect(hwnet, SIGNAL(AuthFailed()), this, SLOT(NetAuthFailed()), Qt::QueuedConnection);
1399     //connect(ui.pageNetGame->BtnBack, SIGNAL(clicked()), hwnet, SLOT(partRoom()));
1400     connect(hwnet, SIGNAL(askForRoomPassword()), this, SLOT(askRoomPassword()), Qt::QueuedConnection);
1401 
1402     ui.pageRoomsList->chatWidget->setUsersModel(hwnet->lobbyPlayersModel());
1403     ui.pageNetGame->chatWidget->setUsersModel(hwnet->roomPlayersModel());
1404 
1405 // rooms list page stuff
1406     ui.pageRoomsList->setModel(hwnet->roomsListModel());
1407     connect(hwnet, SIGNAL(adminAccess(bool)),
1408             ui.pageRoomsList, SLOT(setAdmin(bool)), Qt::QueuedConnection);
1409     connect(hwnet, SIGNAL(adminAccess(bool)),
1410             ui.pageRoomsList->chatWidget, SLOT(adminAccess(bool)), Qt::QueuedConnection);
1411 
1412     connect(hwnet, SIGNAL(serverMessage(const QString&)),
1413             ui.pageRoomsList->chatWidget, SLOT(onServerMessage(const QString&)), Qt::QueuedConnection);
1414 
1415     connect(ui.pageRoomsList, SIGNAL(askForCreateRoom(const QString &, const QString &)),
1416             hwnet, SLOT(CreateRoom(const QString&, const QString &)));
1417     connect(ui.pageRoomsList, SIGNAL(askForJoinRoom(const QString &, const QString &)),
1418             hwnet, SLOT(JoinRoom(const QString&, const QString &)));
1419 //  connect(ui.pageRoomsList, SIGNAL(askForCreateRoom(const QString &)),
1420 //      this, SLOT(NetGameMaster()));
1421 //  connect(ui.pageRoomsList, SIGNAL(askForJoinRoom(const QString &)),
1422 //      this, SLOT(NetGameSlave()));
1423     connect(ui.pageRoomsList, SIGNAL(askForRoomList()),
1424             hwnet, SLOT(askRoomsList()));
1425 
1426 // room status stuff
1427     connect(hwnet, SIGNAL(roomMaster(bool)),
1428             this, SLOT(NetGameChangeStatus(bool)));
1429 
1430 // net page stuff
1431     connect(hwnet, SIGNAL(roomNameUpdated(const QString &)),
1432             ui.pageNetGame, SLOT(setRoomName(const QString &)), Qt::QueuedConnection);
1433     connect(hwnet, SIGNAL(roomChatAction(const QString&, const QString&)),
1434             ui.pageNetGame->chatWidget, SLOT(onChatAction(const QString&, const QString&)), Qt::QueuedConnection);
1435     connect(hwnet, SIGNAL(roomChatMessage(const QString&, const QString&)),
1436             ui.pageNetGame->chatWidget, SLOT(onChatMessage(const QString&, const QString&)), Qt::QueuedConnection);
1437 
1438     connect(hwnet, SIGNAL(roomMaster(bool)),
1439             ui.pageNetGame->chatWidget, SLOT(adminAccess(bool)), Qt::QueuedConnection);
1440     connect(ui.pageNetGame->chatWidget, SIGNAL(chatLine(const QString&)),
1441             hwnet, SLOT(chatLineToNetWithEcho(const QString&)));
1442     connect(ui.pageNetGame->BtnGo, SIGNAL(clicked()), hwnet, SLOT(ToggleReady()));
1443     connect(hwnet, SIGNAL(setMyReadyStatus(bool)),
1444             ui.pageNetGame, SLOT(setReadyStatus(bool)), Qt::QueuedConnection);
1445 
1446 // chat widget actions
1447     connect(ui.pageNetGame->chatWidget, SIGNAL(kick(const QString&)),
1448             hwnet, SLOT(kickPlayer(const QString&)));
1449     connect(ui.pageNetGame->chatWidget, SIGNAL(delegate(const QString&)),
1450             hwnet, SLOT(delegateToPlayer(const QString&)));
1451     connect(ui.pageNetGame->chatWidget, SIGNAL(ban(const QString&)),
1452             hwnet, SLOT(banPlayer(const QString&)));
1453     connect(ui.pageNetGame->chatWidget, SIGNAL(info(const QString&)),
1454             hwnet, SLOT(infoPlayer(const QString&)));
1455     connect(ui.pageNetGame->chatWidget, SIGNAL(follow(const QString&)),
1456             hwnet, SLOT(followPlayer(const QString&)));
1457     connect(ui.pageNetGame->chatWidget, SIGNAL(consoleCommand(const QString&)),
1458             hwnet, SLOT(consoleCommand(const QString&)));
1459     connect(ui.pageRoomsList->chatWidget, SIGNAL(kick(const QString&)),
1460             hwnet, SLOT(kickPlayer(const QString&)));
1461     connect(ui.pageRoomsList->chatWidget, SIGNAL(ban(const QString&)),
1462             hwnet, SLOT(banPlayer(const QString&)));
1463     connect(ui.pageRoomsList->chatWidget, SIGNAL(info(const QString&)),
1464             hwnet, SLOT(infoPlayer(const QString&)));
1465     connect(ui.pageRoomsList->chatWidget, SIGNAL(follow(const QString&)),
1466             hwnet, SLOT(followPlayer(const QString&)));
1467     connect(ui.pageRoomsList->chatWidget, SIGNAL(consoleCommand(const QString&)),
1468             hwnet, SLOT(consoleCommand(const QString&)));
1469 
1470 // player info
1471     connect(hwnet, SIGNAL(playerInfo(const QString&, const QString&, const QString&, const QString&)),
1472             ui.pageRoomsList->chatWidget, SLOT(onPlayerInfo(const QString&, const QString&, const QString&, const QString&)), Qt::QueuedConnection);
1473     connect(hwnet, SIGNAL(playerInfo(const QString&, const QString&, const QString&, const QString&)),
1474             ui.pageNetGame->chatWidget, SLOT(onPlayerInfo(const QString&, const QString&, const QString&, const QString&)), Qt::QueuedConnection);
1475 
1476 // chatting
1477     connect(ui.pageRoomsList->chatWidget, SIGNAL(chatLine(const QString&)),
1478             hwnet, SLOT(chatLineToLobby(const QString&)));
1479     connect(hwnet, SIGNAL(lobbyChatAction(const QString&,const QString&)),
1480             ui.pageRoomsList->chatWidget, SLOT(onChatAction(const QString&,const QString&)), Qt::QueuedConnection);
1481     connect(hwnet, SIGNAL(lobbyChatMessage(const QString&, const QString&)),
1482             ui.pageRoomsList->chatWidget, SLOT(onChatMessage(const QString&, const QString&)), Qt::QueuedConnection);
1483 
1484 // nick list stuff
1485     {
1486         QSortFilterProxyModel * playersSortFilterModel = qobject_cast<QSortFilterProxyModel *>(hwnet->lobbyPlayersModel());
1487         if(playersSortFilterModel)
1488         {
1489             PlayersListModel * players = qobject_cast<PlayersListModel *>(playersSortFilterModel->sourceModel());
1490             connect(players, SIGNAL(nickAdded(const QString&, bool)),
1491                     ui.pageNetGame->chatWidget, SLOT(nickAdded(const QString&, bool)));
1492             connect(players, SIGNAL(nickRemoved(const QString&)),
1493                     ui.pageNetGame->chatWidget, SLOT(nickRemoved(const QString&)));
1494             connect(players, SIGNAL(nickAddedLobby(const QString&, bool)),
1495                     ui.pageRoomsList->chatWidget, SLOT(nickAdded(const QString&, bool)));
1496             connect(players, SIGNAL(nickRemovedLobby(const QString&)),
1497                     ui.pageRoomsList->chatWidget, SLOT(nickRemoved(const QString&)));
1498             connect(players, SIGNAL(nickRemovedLobby(const QString&, const QString&)),
1499                     ui.pageRoomsList->chatWidget, SLOT(nickRemoved(const QString&, const QString&)));
1500         }
1501     }
1502 
1503 // teams selecting stuff
1504     connect(ui.pageNetGame->pNetTeamsWidget, SIGNAL(hhogsNumChanged(const HWTeam&)),
1505             hwnet, SLOT(onHedgehogsNumChanged(const HWTeam&)));
1506     connect(ui.pageNetGame->pNetTeamsWidget, SIGNAL(teamColorChanged(const HWTeam&)),
1507             hwnet, SLOT(onTeamColorChanged(const HWTeam&)));
1508     connect(ui.pageNetGame->pNetTeamsWidget, SIGNAL(teamWillPlay(HWTeam)), hwnet, SLOT(AddTeam(HWTeam)));
1509     connect(ui.pageNetGame->pNetTeamsWidget, SIGNAL(acceptRequested(HWTeam)), hwnet, SLOT(AddTeam(HWTeam)));
1510     connect(ui.pageNetGame->pNetTeamsWidget, SIGNAL(teamNotPlaying(const HWTeam&)), hwnet, SLOT(RemoveTeam(const HWTeam&)));
1511     connect(hwnet, SIGNAL(hhnumChanged(const HWTeam&)),
1512             ui.pageNetGame->pNetTeamsWidget, SLOT(changeHHNum(const HWTeam&)), Qt::QueuedConnection);
1513     connect(hwnet, SIGNAL(teamColorChanged(const HWTeam&)),
1514             ui.pageNetGame->pNetTeamsWidget, SLOT(changeTeamColor(const HWTeam&)), Qt::QueuedConnection);
1515 
1516 // admin stuff
1517     connect(hwnet, SIGNAL(serverMessageNew(const QString&)), ui.pageAdmin, SLOT(serverMessageNew(const QString &)));
1518     connect(hwnet, SIGNAL(serverMessageOld(const QString&)), ui.pageAdmin, SLOT(serverMessageOld(const QString &)));
1519     connect(hwnet, SIGNAL(latestProtocolVar(int)), ui.pageAdmin, SLOT(protocol(int)));
1520     connect(hwnet, SIGNAL(bansList(const QStringList &)), ui.pageAdmin, SLOT(setBansList(const QStringList &)));
1521     connect(ui.pageAdmin, SIGNAL(setServerMessageNew(const QString&)), hwnet, SLOT(setServerMessageNew(const QString &)));
1522     connect(ui.pageAdmin, SIGNAL(setServerMessageOld(const QString&)), hwnet, SLOT(setServerMessageOld(const QString &)));
1523     connect(ui.pageAdmin, SIGNAL(setProtocol(int)), hwnet, SLOT(setLatestProtocolVar(int)));
1524     connect(ui.pageAdmin, SIGNAL(askServerVars()), hwnet, SLOT(askServerVars()));
1525     connect(ui.pageAdmin, SIGNAL(clearAccountsCache()), hwnet, SLOT(clearAccountsCache()));
1526     connect(ui.pageAdmin, SIGNAL(bansListRequest()), hwnet, SLOT(getBanList()));
1527     connect(ui.pageAdmin, SIGNAL(removeBan(QString)), hwnet, SLOT(removeBan(QString)));
1528     connect(ui.pageAdmin, SIGNAL(banIP(QString,QString,int)), hwnet, SLOT(banIP(QString,QString,int)));
1529     connect(ui.pageAdmin, SIGNAL(banNick(QString,QString,int)), hwnet, SLOT(banNick(QString,QString,int)));
1530 
1531 // disconnect
1532     connect(hwnet, SIGNAL(disconnected(const QString&)), this, SLOT(ForcedDisconnect(const QString&)), Qt::QueuedConnection);
1533 
1534 // config stuff
1535     connect(hwnet, SIGNAL(paramChanged(const QString &, const QStringList &)), ui.pageNetGame->pGameCFG, SLOT(setParam(const QString &, const QStringList &)));
1536     connect(ui.pageNetGame->pGameCFG, SIGNAL(paramChanged(const QString &, const QStringList &)), hwnet, SLOT(onParamChanged(const QString &, const QStringList &)));
1537     connect(hwnet, SIGNAL(configAsked()), ui.pageNetGame->pGameCFG, SLOT(fullNetConfig()));
1538 
1539     // using proxy slot to prevent loss of game messages when they're sent to not yet connected slot of game object
1540     connect(hwnet, SIGNAL(FromNet(const QByteArray &)), this, SLOT(FromNetProxySlot(const QByteArray &)), Qt::QueuedConnection);
1541 
1542     //nick and pass stuff
1543     hwnet->m_private_game = !(hostName == NETGAME_DEFAULT_SERVER && port == NETGAME_DEFAULT_PORT);
1544     if (hwnet->m_private_game == false && AskForNickAndPwd() != 0)
1545         return;
1546 
1547     QString nickname = config->value("net/nick", config->getRandomNick()).toString();
1548     ui.pageRoomsList->setUser(nickname);
1549     ui.pageNetGame->setUser(nickname);
1550 
1551     hwnet->Connect(hostName, port, useTls, nickname);
1552 }
1553 
AskForNickAndPwd(void)1554 int HWForm::AskForNickAndPwd(void)
1555 {
1556     //remove temppasswordhash just in case
1557     config->clearTempHash();
1558 
1559     //initialize
1560     QString hash;
1561     QString temphash;
1562     QString nickname;
1563     QString password;
1564 
1565     do {
1566         nickname = config->value("net/nick", config->getRandomNick()).toString();
1567         hash = config->passwordHash();
1568         temphash = config->tempHash();
1569 
1570         //if something from login is missing, start dialog loop
1571         if (nickname.isEmpty() || hash.isEmpty()) {
1572             //open dialog
1573             HWPasswordDialog * pwDialog = new HWPasswordDialog(this);
1574             // make the "new account" button dialog open a browser with the registration page
1575             connect(pwDialog->pbNewAccount, SIGNAL(clicked()), this, SLOT(openRegistrationPage()));
1576             pwDialog->cbSave->setChecked(config->value("net/savepassword", true).toBool());
1577 
1578             //if nickname is present, put it into the field
1579             if (!nickname.isEmpty()) {
1580                 pwDialog->leNickname->setText(nickname);
1581                 pwDialog->lePassword->setFocus();
1582             }
1583 
1584             //if dialog aborted, return failure
1585             if (pwDialog->exec() != QDialog::Accepted) {
1586                 delete pwDialog;
1587                 GoBack();
1588                 return 1;
1589             }
1590 
1591             //set nick and pass from the dialog
1592             nickname = pwDialog->leNickname->text();
1593             password = pwDialog->lePassword->text();
1594             bool save = pwDialog->cbSave->isChecked();
1595             //clean up
1596             delete pwDialog;
1597 
1598             //check the nickname variable
1599             if (nickname.isEmpty()) {
1600                 int retry = RetryDialog(tr("Hedgewars - Empty nickname"), tr("No nickname supplied."));
1601                 GoBack();
1602                 if (retry) {
1603                     if (hwnet->m_private_game) {
1604                         QStringList list = hwnet->getHost().split(":");
1605                         NetConnectServer(list.at(0), list.at(1).toShort(), false);
1606                     } else
1607                         NetConnectOfficialServer();
1608                 }
1609                 break; //loop restart
1610             } else {
1611                 //update nickname if it's fine
1612                 config->setValue("net/nick", nickname);
1613                 config->updNetNick();
1614             }
1615 
1616             //check the password variable
1617             if (password.isEmpty()) {
1618                 config->clearPasswordHash();
1619                 break;
1620             }  else {
1621                 //calculate temphash and set it into config
1622                 temphash = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Md5).toHex();
1623                 config->setTempHash(temphash);
1624 
1625                 //if user wants to save password
1626                 config->setValue("net/savepassword", save);
1627                 if (save) {
1628                     // user wants to save password
1629                     ui.pageOptions->CBSavePassword->setChecked(true);
1630                     config->setPasswordHash(temphash);
1631                 }
1632             }
1633         }
1634     } while (nickname.isEmpty() || (hash.isEmpty() && temphash.isEmpty())); //while a nickname, or both hashes are missing
1635 
1636     return 0;
1637 }
1638 
NetConnect()1639 void HWForm::NetConnect()
1640 {
1641     HWHostPortDialog * hpd = new HWHostPortDialog(this);
1642     hpd->leHost->setText(*netHost);
1643     hpd->sbPort->setValue(netPort);
1644 
1645     if (hpd->exec() == QDialog::Accepted)
1646     {
1647         config->SaveOptions();
1648         delete netHost;
1649         netHost = new QString(hpd->leHost->text());
1650         netPort = hpd->sbPort->value();
1651         NetConnectServer(*netHost, netPort, false);
1652     }
1653     delete hpd;
1654 }
1655 
NetStartServer()1656 void HWForm::NetStartServer()
1657 {
1658     config->SaveOptions();
1659 
1660     pnetserver = new HWNetServer;
1661     if (!pnetserver->StartServer(ui.pageNetServer->sbPort->value()))
1662     {
1663         MessageDialog::ShowErrorMessage(QMessageBox::tr("Unable to start server"), this);
1664 
1665         delete pnetserver;
1666         pnetserver = 0;
1667         return;
1668     }
1669 
1670     QTimer::singleShot(250, this, SLOT(AsyncNetServerStart()));
1671 
1672     pRegisterServer = new HWNetUdpServer(0,
1673                                          ui.pageNetServer->leServerDescr->text(),
1674                                          ui.pageNetServer->sbPort->value());
1675 }
1676 
AsyncNetServerStart()1677 void HWForm::AsyncNetServerStart()
1678 {
1679     NetConnectServer("localhost", pnetserver->getRunningPort(), false);
1680 }
1681 
NetDisconnect()1682 void HWForm::NetDisconnect()
1683 {
1684     if(pnetserver)
1685     {
1686         if (pRegisterServer)
1687         {
1688             pRegisterServer->unregister();
1689             pRegisterServer = 0;
1690         }
1691 
1692         pnetserver->StopServer();
1693         delete pnetserver;
1694         pnetserver = 0;
1695     }
1696 
1697     if(hwnet)
1698         hwnet->Disconnect();
1699 }
1700 
ForcedDisconnect(const QString & reason)1701 void HWForm::ForcedDisconnect(const QString & reason)
1702 {
1703     if (reason == "Reconnected too fast") { //TODO: this is a hack, which should be remade
1704         bool retry = RetryDialog(tr("Hedgewars - Connection error"), tr("You reconnected too fast.\nPlease wait a few seconds and try again."));
1705         if (retry) {
1706             if (hwnet->m_private_game) {
1707                 QStringList list = hwnet->getHost().split(":");
1708                 NetConnectServer(list.at(0), list.at(1).toUInt(), false);
1709             } else
1710                 NetConnectOfficialServer();
1711         }
1712         else {
1713             while (ui.Pages->currentIndex() != ID_PAGE_NET
1714                 && ui.Pages->currentIndex() != ID_PAGE_MAIN)
1715             {
1716                 GoBack();
1717             }
1718         }
1719         return;
1720     }
1721     if (pnetserver)
1722         return; // we have server - let it care of all things
1723     if (hwnet && (reason != "bye"))
1724     {
1725         QString errorStr = QMessageBox::tr("The connection to the server is lost.") + (reason.isEmpty()?"":("\n\n" + HWNewNet::tr("Reason:") + "\n" + reason));
1726         MessageDialog::ShowErrorMessage(errorStr, this);
1727     }
1728 
1729     while (ui.Pages->currentIndex() != ID_PAGE_NET
1730         && ui.Pages->currentIndex() != ID_PAGE_MAIN)
1731     {
1732         GoBack();
1733     }
1734 }
1735 
NetRedirected(quint16 port)1736 void HWForm::NetRedirected(quint16 port)
1737 {
1738     QMessageBox questionMsg(this);
1739     questionMsg.setIcon(QMessageBox::Question);
1740     questionMsg.setWindowTitle(QMessageBox::tr("Server redirection"));
1741     questionMsg.setText(QMessageBox::tr("This server supports secure connections on port %1.\nWould you like to reconnect securely?").arg(port));
1742     questionMsg.setTextFormat(Qt::PlainText);
1743     questionMsg.setWindowModality(Qt::WindowModal);
1744     questionMsg.addButton(QMessageBox::Yes);
1745     questionMsg.addButton(QMessageBox::No);
1746 
1747     if (questionMsg.exec() == QMessageBox::Yes)
1748     {
1749         QString host = hwnet->getHost().split(":").at(0);
1750         NetConnectServer(host, port, true);
1751     }
1752     else if (hwnet)
1753     {
1754         hwnet->ContinueConnection();
1755     }
1756 }
1757 
NetConnected()1758 void HWForm::NetConnected()
1759 {
1760     GoToPage(ID_PAGE_ROOMSLIST);
1761 }
1762 
NetGameEnter()1763 void HWForm::NetGameEnter()
1764 {
1765     ui.pageNetGame->chatWidget->clear();
1766     GoToPage(ID_PAGE_NETGAME);
1767 }
1768 
AddNetTeam(const HWTeam & team)1769 void HWForm::AddNetTeam(const HWTeam& team)
1770 {
1771     ui.pageNetGame->pNetTeamsWidget->addTeam(team);
1772 }
1773 
RemoveNetTeam(const HWTeam & team)1774 void HWForm::RemoveNetTeam(const HWTeam& team)
1775 {
1776     ui.pageNetGame->pNetTeamsWidget->removeNetTeam(team);
1777 }
1778 
StartMPGame()1779 void HWForm::StartMPGame()
1780 {
1781     QString ammo;
1782     ammo = ui.pageMultiplayer->gameCFG->WeaponsName->itemData(
1783                ui.pageMultiplayer->gameCFG->WeaponsName->currentIndex()
1784            ).toString();
1785 
1786     CreateGame(ui.pageMultiplayer->gameCFG, ui.pageMultiplayer->teamsSelect, ammo);
1787 
1788     game->StartLocal();
1789 }
1790 
GameStateChanged(GameState gameState)1791 void HWForm::GameStateChanged(GameState gameState)
1792 {
1793     quint8 id = ui.Pages->currentIndex();
1794     switch(gameState)
1795     {
1796         case gsStarted:
1797         {
1798             demoIsPresent = true;
1799             Music(false);
1800             if (wBackground) wBackground->stopAnimation();
1801             if (!hwnet || (!hwnet->isRoomChief() || !hwnet->isInRoom())) GoToPage(ID_PAGE_INGAME);
1802             ui.pageGameStats->clear();
1803             if (pRegisterServer)
1804             {
1805                 pRegisterServer->unregister();
1806                 pRegisterServer = 0;
1807             }
1808             //setVisible(false);
1809             setFocusPolicy(Qt::NoFocus);
1810             break;
1811         }
1812         case gsFinished:
1813         {
1814             //setVisible(true);
1815             setFocusPolicy(Qt::StrongFocus);
1816             if (id == ID_PAGE_INGAME) GoBack();
1817             Music(ui.pageOptions->CBFrontendMusic->isChecked());
1818             if (wBackground) wBackground->startAnimation();
1819             GoToPage(ID_PAGE_GAMESTATS);
1820             if (hwnet)
1821             {
1822                 if (!game || !game->netSuspend)
1823                     hwnet->gameFinished(true);
1824                 // After a game, the local player might have pseudo-teams left
1825                 // when rejoining a previously left game. This makes sure the
1826                 // teams list is in a consistent state.
1827                 ui.pageNetGame->cleanupFakeNetTeams();
1828             }
1829             if (game) game->netSuspend = false;
1830             break;
1831         }
1832         default:
1833         {
1834             //setVisible(true);
1835             setFocusPolicy(Qt::StrongFocus);
1836             quint8 id = ui.Pages->currentIndex();
1837             if (id == ID_PAGE_INGAME ||
1838 // was room chief and the game was aborted
1839                     (hwnet && hwnet->isRoomChief() && hwnet->isInRoom() &&
1840                      (gameState == gsInterrupted || gameState == gsStopped || gameState == gsDestroyed || gameState == gsHalted)))
1841             {
1842                 if (id == ID_PAGE_INGAME) GoBack();
1843                 Music(ui.pageOptions->CBFrontendMusic->isChecked());
1844                 if (wBackground) wBackground->startAnimation();
1845                 if (hwnet) hwnet->gameFinished(false);
1846             }
1847             if (gameState == gsHalted) close();
1848         };
1849     }
1850 }
1851 
DemoPresenceChanged(bool hasDemo)1852 void HWForm::DemoPresenceChanged(bool hasDemo)
1853 {
1854     demoIsPresent = hasDemo;
1855 }
1856 
CreateGame(GameCFGWidget * gamecfg,TeamSelWidget * pTeamSelWidget,QString ammo)1857 void HWForm::CreateGame(GameCFGWidget * gamecfg, TeamSelWidget* pTeamSelWidget, QString ammo)
1858 {
1859     game = new HWGame(config, gamecfg, ammo, pTeamSelWidget);
1860     connect(game, SIGNAL(CampStateChanged(int)), this, SLOT(UpdateCampaignPageProgress(int)));
1861     connect(game, SIGNAL(TrainingStateChanged(int)), this, SLOT(UpdateTrainingPageTeam(int)));
1862     connect(game, SIGNAL(GameStateChanged(GameState)), this, SLOT(GameStateChanged(GameState)));
1863     connect(game, SIGNAL(DemoPresenceChanged(bool)), this, SLOT(DemoPresenceChanged(bool)));
1864     connect(game, SIGNAL(GameStats(char, const QString &)), ui.pageGameStats, SLOT(GameStats(char, const QString &)));
1865     connect(game, SIGNAL(ErrorMessage(const QString &)), this, SLOT(ShowFatalErrorMessage(const QString &)), Qt::QueuedConnection);
1866     connect(game, SIGNAL(HaveRecord(RecordType, const QByteArray &)), this, SLOT(GetRecord(RecordType, const QByteArray &)));
1867     m_lastDemo = QByteArray();
1868 }
1869 
GetRecord(RecordType type,const QByteArray & record)1870 void HWForm::GetRecord(RecordType type, const QByteArray & record)
1871 {
1872     if (type != rtNeither)
1873     {
1874         QString filename;
1875         QByteArray demo = record;
1876         QString recordFileName =
1877             config->appendDateTimeToRecordName() ?
1878             QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm") :
1879             "LastRound";
1880 
1881         recordFileName += "_" + *cRevisionString + "-" + *cHashString;
1882 
1883         if (type == rtDemo)
1884         {
1885             demo.replace(QByteArray("\x02TL"), QByteArray("\x02TD"));
1886             demo.replace(QByteArray("\x02TN"), QByteArray("\x02TD"));
1887             demo.replace(QByteArray("\x02TS"), QByteArray("\x02TD"));
1888             filename = cfgdir->absolutePath() + "/Demos/" + recordFileName + "." + *cProtoVer + ".hwd";
1889             m_lastDemo = demo;
1890         }
1891         else
1892         {
1893             demo.replace(QByteArray("\x02TL"), QByteArray("\x02TS"));
1894             demo.replace(QByteArray("\x02TN"), QByteArray("\x02TS"));
1895             filename = cfgdir->absolutePath() + "/Saves/" + recordFileName + "." + *cProtoVer + ".hws";
1896         }
1897 
1898         QFile demofile(filename);
1899         if (!demofile.open(QIODevice::WriteOnly))
1900             MessageDialog::ShowErrorMessage(tr("Cannot save record to file %1").arg(filename), this);
1901         else
1902         {
1903             demofile.write(demo);
1904             demofile.close();
1905         }
1906     }
1907 
1908     ui.pageVideos->startEncoding(record);
1909 }
1910 
startTraining(const QString & scriptName,const QString & subFolder)1911 void HWForm::startTraining(const QString & scriptName, const QString & subFolder)
1912 {
1913     CreateGame(0, 0, 0);
1914 
1915     QString trainTeam = ui.pageTraining->CBTeam->currentText();
1916     game->StartTraining(scriptName, subFolder, trainTeam);
1917 }
1918 
StartCampaign()1919 void HWForm::StartCampaign()
1920 {
1921     CreateGame(0, 0, 0);
1922     QString camp = ui.pageCampaign->CBCampaign->itemData(ui.pageCampaign->CBCampaign->currentIndex()).toString();
1923     QString miss = campaignMissionInfo[ui.pageCampaign->CBMission->currentIndex()].script;
1924     QString campTeam = ui.pageCampaign->CBTeam->currentText();
1925     game->StartCampaign(camp, miss, campTeam);
1926 }
1927 
CreateNetGame()1928 void HWForm::CreateNetGame()
1929 {
1930     // go back in pages to prevent user from being stuck on certain pages
1931     if(ui.Pages->currentIndex() == ID_PAGE_GAMESTATS ||
1932        ui.Pages->currentIndex() == ID_PAGE_INGAME)
1933         GoBack();
1934 
1935     QString ammo;
1936     ammo = ui.pageNetGame->pGameCFG->WeaponsName->itemData(
1937                ui.pageNetGame->pGameCFG->WeaponsName->currentIndex()
1938            ).toString();
1939 
1940     CreateGame(ui.pageNetGame->pGameCFG, ui.pageNetGame->pNetTeamsWidget, ammo);
1941 
1942     connect(game, SIGNAL(SendNet(const QByteArray &)), hwnet, SLOT(SendNet(const QByteArray &)));
1943     connect(game, SIGNAL(SendChat(const QString &)), hwnet, SLOT(chatLineToNet(const QString &)));
1944     connect(game, SIGNAL(SendConsoleCommand(const QString&)), hwnet, SLOT(consoleCommand(const QString&)));
1945     connect(game, SIGNAL(SendTeamMessage(const QString &)), hwnet, SLOT(SendTeamMessage(const QString &)));
1946     connect(hwnet, SIGNAL(chatStringFromNet(const QString &)), game, SLOT(FromNetChat(const QString &)), Qt::QueuedConnection);
1947     connect(hwnet, SIGNAL(Warning(const QString&)), game, SLOT(FromNetWarning(const QString&)), Qt::QueuedConnection);
1948     connect(hwnet, SIGNAL(Error(const QString&)), game, SLOT(FromNetError(const QString&)), Qt::QueuedConnection);
1949 
1950     game->StartNet();
1951 }
1952 
PlayOfficialServerDemo()1953 void HWForm::PlayOfficialServerDemo()
1954 {
1955     // go back in pages to prevent user from being stuck on certain pages
1956     if(ui.Pages->currentIndex() == ID_PAGE_GAMESTATS ||
1957        ui.Pages->currentIndex() == ID_PAGE_INGAME)
1958         GoBack();
1959 
1960     QString ammo;
1961     ammo = ui.pageNetGame->pGameCFG->WeaponsName->itemData(
1962                ui.pageNetGame->pGameCFG->WeaponsName->currentIndex()
1963            ).toString();
1964 
1965     CreateGame(ui.pageNetGame->pGameCFG, ui.pageNetGame->pNetTeamsWidget, ammo);
1966 
1967     game->PlayOfficialServerDemo();
1968 }
1969 
closeEvent(QCloseEvent * event)1970 void HWForm::closeEvent(QCloseEvent *event)
1971 {
1972     config->SaveOptions();
1973 #ifdef VIDEOREC
1974     config->SaveVideosOptions();
1975 #endif
1976     event->accept();
1977 }
1978 
Music(bool checked)1979 void HWForm::Music(bool checked)
1980 {
1981     if (checked)
1982         SDLInteraction::instance().startMusic();
1983     else
1984         SDLInteraction::instance().stopMusic();
1985 }
1986 
NetGameChangeStatus(bool isMaster)1987 void HWForm::NetGameChangeStatus(bool isMaster)
1988 {
1989     if (isMaster)
1990         NetGameMaster();
1991     else
1992         NetGameSlave();
1993 }
1994 
NetGameMaster()1995 void HWForm::NetGameMaster()
1996 {
1997     ui.pageNetGame->setMasterMode(true);
1998     ui.pageNetGame->restrictJoins->setChecked(false);
1999     ui.pageNetGame->restrictTeamAdds->setChecked(false);
2000     ui.pageNetGame->restrictUnregistered->setChecked(false);
2001     ui.pageNetGame->pGameCFG->GameSchemes->setModel(gameSchemeModel);
2002     ui.pageNetGame->pGameCFG->setMaster(true);
2003     ui.pageNetGame->pNetTeamsWidget->setInteractivity(true);
2004     ui.pageNetGame->pGameCFG->resetSchemeStates();
2005 
2006     if (hwnet)
2007     {
2008         // disconnect connections first to ensure their inexistance and not to connect twice
2009         ui.pageNetGame->BtnStart->disconnect(this);
2010         ui.pageNetGame->BtnUpdate->disconnect(hwnet);
2011         ui.pageNetGame->leRoomName->disconnect(hwnet);
2012         ui.pageNetGame->restrictJoins->disconnect(hwnet);
2013         ui.pageNetGame->restrictTeamAdds->disconnect(hwnet);
2014         ui.pageNetGame->restrictUnregistered->disconnect(hwnet);
2015         ui.pageNetGame->disconnect(hwnet, SLOT(updateRoomName(const QString&)));
2016 
2017         ui.pageNetGame->setRoomName(hwnet->getRoom());
2018 
2019         connect(ui.pageNetGame->BtnStart, SIGNAL(clicked()), this, SLOT(startGame()));
2020         connect(ui.pageNetGame, SIGNAL(askForUpdateRoomName(const QString &)), hwnet, SLOT(updateRoomName(const QString &)));
2021         connect(ui.pageNetGame->restrictJoins, SIGNAL(triggered()), hwnet, SLOT(toggleRestrictJoins()));
2022         connect(ui.pageNetGame->restrictTeamAdds, SIGNAL(triggered()), hwnet, SLOT(toggleRestrictTeamAdds()));
2023         connect(ui.pageNetGame->restrictUnregistered, SIGNAL(triggered()), hwnet, SLOT(toggleRegisteredOnly()));
2024         connect(ui.pageNetGame->pGameCFG->GameSchemes->model(),
2025                 SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
2026                 ui.pageNetGame->pGameCFG,
2027                 SLOT(resendSchemeData())
2028                );
2029     }
2030 }
2031 
NetGameSlave()2032 void HWForm::NetGameSlave()
2033 {
2034     ui.pageNetGame->pGameCFG->setMaster(false);
2035     ui.pageNetGame->pNetTeamsWidget->setInteractivity(false);
2036 
2037     if (hwnet)
2038     {
2039         NetGameSchemeModel * netAmmo = new NetGameSchemeModel(hwnet);
2040         connect(hwnet, SIGNAL(netSchemeConfig(QStringList)), netAmmo, SLOT(setNetSchemeConfig(QStringList)));
2041 
2042         ui.pageNetGame->pGameCFG->GameSchemes->setModel(netAmmo);
2043 
2044         ui.pageNetGame->setRoomName(hwnet->getRoom());
2045 
2046         ui.pageNetGame->pGameCFG->GameSchemes->view()->disconnect(hwnet);
2047         connect(hwnet, SIGNAL(netSchemeConfig(QStringList)),
2048                 this, SLOT(selectFirstNetScheme()));
2049     }
2050 
2051     ui.pageNetGame->setMasterMode(false);
2052 }
2053 
FromNetProxySlot(const QByteArray & msg)2054 void HWForm::FromNetProxySlot(const QByteArray & msg)
2055 {
2056     if(game)
2057         game->FromNet(msg);
2058 
2059 }
2060 
selectFirstNetScheme()2061 void HWForm::selectFirstNetScheme()
2062 {
2063     ui.pageNetGame->pGameCFG->GameSchemes->setCurrentIndex(0);
2064 }
2065 
NetLeftRoom(const QString & reason)2066 void HWForm::NetLeftRoom(const QString & reason)
2067 {
2068     if (ui.Pages->currentIndex() == ID_PAGE_NETGAME || ui.Pages->currentIndex() == ID_PAGE_INGAME)
2069     {
2070         GoBack();
2071         if (!reason.isEmpty())
2072             ui.pageRoomsList->displayNotice(reason);
2073     }
2074     else
2075         qWarning("Left room while not in room");
2076 }
2077 
resizeEvent(QResizeEvent * event)2078 void HWForm::resizeEvent(QResizeEvent * event)
2079 {
2080     int w = event->size().width();
2081     int h = event->size().height();
2082     if (wBackground)
2083     {
2084         wBackground->setFixedSize(w, h);
2085         wBackground->move(0, 0);
2086     }
2087 }
2088 
UpdateTrainingPageTeam(int index)2089 void HWForm::UpdateTrainingPageTeam(int index)
2090 {
2091     Q_UNUSED(index);
2092     HWTeam team(ui.pageTraining->CBTeam->currentText());
2093     QString tName = team.name();
2094 
2095     QListWidget* listWidget;
2096     for(int w = 0; w < 3; w++)
2097     {
2098         switch(w) {
2099             case 0: listWidget = ui.pageTraining->lstTrainings; break;
2100             case 1: listWidget = ui.pageTraining->lstChallenges; break;
2101             case 2: listWidget = ui.pageTraining->lstScenarios; break;
2102             default: listWidget = ui.pageTraining->lstTrainings; break;
2103         }
2104         unsigned int n = listWidget->count();
2105 
2106         for(unsigned int i = 0; i < n; i++)
2107         {
2108             QListWidgetItem* item = listWidget->item(i);
2109             QString missionName = QString(item->data(Qt::UserRole).toString()).replace(QString(" "),QString("_"));
2110             if(isMissionWon(missionName, tName))
2111                 item->setIcon(finishedIcon);
2112             else
2113                 item->setIcon(notFinishedIcon);
2114         }
2115     }
2116     ui.pageTraining->updateInfo();
2117 }
2118 
InitCampaignPage()2119 void HWForm::InitCampaignPage()
2120 {
2121     ui.pageCampaign->CBCampaign->clear();
2122     HWTeam team(ui.pageCampaign->CBTeam->currentText());
2123 
2124     QStringList entries = DataManager::instance().entryList(
2125                                   "Missions/Campaign",
2126                                   QDir::Dirs,
2127                                   QStringList("[^\\.]*")
2128                               );
2129 
2130     unsigned int n = entries.count();
2131 
2132     for(unsigned int i = 0; i < n; i++)
2133     {
2134         const QString & campaignName = entries[i];
2135         QString tName = team.name();
2136         ui.pageCampaign->CBCampaign->addItem(getRealCampName(campaignName), campaignName);
2137     }
2138 
2139 }
2140 
RestoreSingleplayerTeamSelection()2141 void HWForm::RestoreSingleplayerTeamSelection()
2142 {
2143     QString lastTeam = config->value("frontend/lastSingleplayerTeam", QString()).toString();
2144     if (!lastTeam.isNull() && !lastTeam.isEmpty())
2145     {
2146         int index = ui.pageCampaign->CBTeam->findData(lastTeam, Qt::DisplayRole);
2147         if(index != -1)
2148         {
2149             ui.pageCampaign->CBTeam->setCurrentIndex(index);
2150             UpdateCampaignPageTeam(index);
2151         }
2152         index = ui.pageTraining->CBTeam->findData(lastTeam, Qt::DisplayRole);
2153         if(index != -1)
2154         {
2155             ui.pageTraining->CBTeam->setCurrentIndex(index);
2156             UpdateTrainingPageTeam(index);
2157         }
2158     }
2159 }
2160 
UpdateCampaignPage(int index)2161 void HWForm::UpdateCampaignPage(int index)
2162 {
2163     Q_UNUSED(index);
2164     HWTeam team(ui.pageCampaign->CBTeam->currentText());
2165     QString campaignName = ui.pageCampaign->CBCampaign->currentData().toString();
2166     QString tName = team.name();
2167 
2168     campaignMissionInfo = getCampMissionList(campaignName,tName);
2169     ui.pageCampaign->CBMission->clear();
2170 
2171     // Populate mission list
2172     for(int i=0;i<campaignMissionInfo.size();i++)
2173     {
2174         ui.pageCampaign->CBMission->addItem(QString(campaignMissionInfo[i].realName), QString(campaignMissionInfo[i].name));
2175         if(isCampMissionWon(campaignName, i, tName))
2176             ui.pageCampaign->CBMission->setItemIcon(i, finishedIcon);
2177         else
2178             ui.pageCampaign->CBMission->setItemIcon(i, notFinishedIcon);
2179     }
2180 
2181     // Select first open mission
2182     int missionIndex = ui.pageCampaign->CBMission->currentIndex();
2183     if(isCampMissionWon(campaignName, missionIndex, tName))
2184     {
2185         for(int m = 0; m < ui.pageCampaign->CBMission->count(); m++)
2186         {
2187             if(!isCampMissionWon(campaignName, m, tName))
2188             {
2189                 ui.pageCampaign->CBMission->setCurrentIndex(m);
2190                 break;
2191             }
2192         }
2193     }
2194 }
2195 
UpdateCampaignPageTeam(int index)2196 void HWForm::UpdateCampaignPageTeam(int index)
2197 {
2198     Q_UNUSED(index);
2199     HWTeam team(ui.pageCampaign->CBTeam->currentText());
2200     QString tName = team.name();
2201 
2202     QStringList entries = DataManager::instance().entryList(
2203                                   "Missions/Campaign",
2204                                   QDir::Dirs,
2205                                   QStringList("[^\\.]*")
2206                               );
2207 
2208     unsigned int n = entries.count();
2209 
2210     // Update campaign status
2211     for(unsigned int i = 0; i < n; i++)
2212     {
2213         QString campaignName = QString(entries[i]).replace(QString(" "),QString("_"));
2214         if(isCampWon(campaignName, tName))
2215             ui.pageCampaign->CBCampaign->setItemIcon(i, finishedIcon);
2216         else
2217             ui.pageCampaign->CBCampaign->setItemIcon(i, notFinishedIcon);
2218     }
2219 }
2220 
UpdateCampaignPageMission(int index)2221 void HWForm::UpdateCampaignPageMission(int index)
2222 {
2223     // update thumbnail and description
2224     QString campaignName = ui.pageCampaign->CBCampaign->currentData().toString();
2225     // when campaign changes the UpdateCampaignPageMission is triggered with wrong values
2226     // this will cause segfault. This check prevents illegal memory reads
2227     if(index > -1 && index < campaignMissionInfo.count()) {
2228         ui.pageCampaign->lbltitle->setText("<h2>"+ui.pageCampaign->CBMission->currentText()+"</h2>");
2229         ui.pageCampaign->lbldescription->setText(campaignMissionInfo[index].description);
2230         ui.pageCampaign->btnPreview->setIcon(QIcon(campaignMissionInfo[index].image));
2231     }
2232 }
2233 
UpdateCampaignPageProgress(int index)2234 void HWForm::UpdateCampaignPageProgress(int index)
2235 {
2236     QString missionTitle = ui.pageCampaign->CBMission->currentData().toString();
2237     UpdateCampaignPage(0);
2238     int missionIndex = 0;
2239     // Restore selected mission (because UpdateCampaignPage repopulated the list)
2240     for(int i=0;i<ui.pageCampaign->CBMission->count();i++)
2241     {
2242         if (ui.pageCampaign->CBMission->itemData(i).toString() == missionTitle)
2243         {
2244             missionIndex = i;
2245             break;
2246         }
2247     }
2248 
2249     // Get metadata
2250     int c = ui.pageCampaign->CBCampaign->currentIndex();
2251     QString campaignName = ui.pageCampaign->CBCampaign->itemData(c).toString();
2252     HWTeam team(ui.pageCampaign->CBTeam->currentText());
2253     QString tName = team.name();
2254 
2255     if(index == gsFinished)
2256     {
2257         // Select new mission when current mission went from
2258         // unfinished to finished.
2259         if(ui.pageCampaign->currentMissionWon == false &&
2260            isCampMissionWon(campaignName, missionIndex, tName))
2261         {
2262             // Traverse all missions and pick first mission that
2263             // has not been won.
2264             bool selected = false;
2265             // start from mission that comes after the selected one
2266             for(int m = missionIndex-1; m >= 0;m--)
2267             {
2268                 if(!isCampMissionWon(campaignName, m, tName))
2269                 {
2270                     missionIndex = m;
2271                     selected = true;
2272                     break;
2273                 }
2274             }
2275             // No mission selected? Let's try again from the end of the list
2276             if(!selected)
2277             {
2278                 for(int m = ui.pageCampaign->CBMission->count()-1; m > missionIndex-1; m--)
2279                 {
2280                     if(!isCampMissionWon(campaignName, m, tName))
2281                     {
2282                         missionIndex = m;
2283                         break;
2284                     }
2285                 }
2286             }
2287             // If no mission was selected, the old selection remains unchanged.
2288         }
2289     }
2290     else if(index == gsStarted)
2291     {
2292         // Remember the "won" state of current mission before we start it.
2293         // We'll need it when the game has finished.
2294         ui.pageCampaign->currentMissionWon = isCampMissionWon(campaignName, missionIndex, tName);
2295     }
2296 
2297     ui.pageCampaign->CBMission->setCurrentIndex(missionIndex);
2298 
2299     // Update campaign victory status
2300     if(isCampWon(campaignName, tName))
2301         ui.pageCampaign->CBCampaign->setItemIcon(c, finishedIcon);
2302     else
2303         ui.pageCampaign->CBCampaign->setItemIcon(c, notFinishedIcon);
2304 }
2305 
getDemoArguments()2306 QString HWForm::getDemoArguments()
2307 {
2308 
2309     QString prefix = "\"" + datadir->absolutePath() + "\"";
2310     QString userPrefix = "\"" + cfgdir->absolutePath() + "\"";
2311 #ifdef Q_OS_WIN
2312     prefix = prefix.replace("/","\\");
2313     userPrefix = userPrefix.replace("/","\\");
2314 #endif
2315 
2316     std::pair<QRect, QRect> resolutions = config->vid_ResolutionPair();
2317     return QString("--prefix " + prefix
2318                    + " --user-prefix " + userPrefix
2319                    + " --fullscreen-width " + QString::number(resolutions.first.width())
2320                    + " --fullscreen-height " + QString::number(resolutions.first.height())
2321                    + " --width " + QString::number(resolutions.second.width())
2322                    + " --height " + QString::number(resolutions.second.height())
2323                    + (config->vid_Maximized() ? " --maximized" : "")
2324                    + (config->zoom() == 100 ? "" : " --zoom " + QString::number(config->zoom()))
2325                    + " --volume " + QString::number(config->volume())
2326                    + (config->isMusicEnabled() ? "" : " --nomusic")
2327                    + (config->isSoundEnabled() ? "" : " --nosound")
2328                    + (config->isAudioDampenEnabled() ? "" : " --nodampen")
2329                    + " --locale " + config->language() + ".txt"
2330                    + (config->vid_Fullscreen() ? " --fullscreen" : "")
2331                    + (config->isShowFPSEnabled() ? " --showfps" : "")
2332                    + (config->isAltDamageEnabled() ? " --altdmg" : "")
2333                    + " --frame-interval " + QString::number(config->timerInterval())
2334                    + " --raw-quality " + QString::number(config->translateQuality()))
2335                    + (!config->Form->ui.pageOptions->CBTeamTag->isChecked() ? " --no-teamtag" : "")
2336                    + (!config->Form->ui.pageOptions->CBHogTag->isChecked() ? " --no-hogtag" : "")
2337                    + (!config->Form->ui.pageOptions->CBHealthTag->isChecked() ? " --no-healthtag" : "")
2338                    + (config->Form->ui.pageOptions->CBTagOpacity->isChecked() ? " --translucent-tags" : "")
2339                    + (!config->isHolidaySillinessEnabled() ? " --no-holiday-silliness" : "");
2340 }
2341 
AssociateFiles()2342 void HWForm::AssociateFiles()
2343 {
2344     bool success = true;
2345     QString arguments = getDemoArguments();
2346 #ifdef _WIN32
2347     QSettings registry_hkcr("HKEY_CLASSES_ROOT", QSettings::NativeFormat);
2348 
2349     // file extension(s)
2350     registry_hkcr.setValue(".hwd/Default", "Hedgewars.Demo");
2351     registry_hkcr.setValue(".hws/Default", "Hedgewars.Save");
2352     registry_hkcr.setValue("Hedgewars.Demo/Default", tr("Hedgewars Demo File", "File Types"));
2353     registry_hkcr.setValue("Hedgewars.Save/Default", tr("Hedgewars Save File", "File Types"));
2354     registry_hkcr.setValue("Hedgewars.Demo/DefaultIcon/Default", "\"" + bindir->absolutePath().replace("/", "\\") + "\\hwdfile.ico\",0");
2355     registry_hkcr.setValue("Hedgewars.Save/DefaultIcon/Default", "\"" + bindir->absolutePath().replace("/", "\\") + "\\hwsfile.ico\",0");
2356     registry_hkcr.setValue("Hedgewars.Demo/Shell/Open/Command/Default", "\"" + bindir->absolutePath().replace("/", "\\") + "\\hwengine.exe\" " + arguments + " %1");
2357     registry_hkcr.setValue("Hedgewars.Save/Shell/Open/Command/Default", "\"" + bindir->absolutePath().replace("/", "\\") + "\\hwengine.exe\" " + arguments + " %1");
2358 
2359     // custom url scheme(s)
2360     registry_hkcr.setValue("hwplay/Default", "\"URL:Hedgewars ServerAccess Scheme\"");
2361     registry_hkcr.setValue("hwplay/URL Protocol", "");
2362     registry_hkcr.setValue("hwplay/DefaultIcon/Default", "\"" + bindir->absolutePath().replace("/", "\\") + "\\hedgewars.exe\",0");
2363     registry_hkcr.setValue("hwplay/Shell/Open/Command/Default", "\"" + bindir->absolutePath().replace("/", "\\") + "\\hedgewars.exe\"  %1");
2364 #elif defined __APPLE__
2365     // only useful when other apps have taken precedence over our file extensions and you want to reset it
2366     system("defaults write com.apple.LaunchServices LSHandlers -array-add '<dict><key>LSHandlerContentTag</key><string>hwd</string><key>LSHandlerContentTagClass</key><string>public.filename-extension</string><key>LSHandlerRoleAll</key><string>org.hedgewars.desktop</string></dict>'");
2367     system("defaults write com.apple.LaunchServices LSHandlers -array-add '<dict><key>LSHandlerContentTag</key><string>hws</string><key>LSHandlerContentTagClass</key><string>public.filename-extension</string><key>LSHandlerRoleAll</key><string>org.hedgewars.desktop</string></dict>'");
2368     system("/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister -kill -domain local -domain system -domain user");
2369 #else
2370     // this is a little silly due to all the system commands below anyway - just use mkdir -p ?  Does have the advantage of the alert I guess
2371     if (success) success = checkForDir(QDir::home().absolutePath() + "/.local");
2372     if (success) success = checkForDir(QDir::home().absolutePath() + "/.local/share");
2373     if (success) success = checkForDir(QDir::home().absolutePath() + "/.local/share/mime");
2374     if (success) success = checkForDir(QDir::home().absolutePath() + "/.local/share/mime/packages");
2375     if (success) success = checkForDir(QDir::home().absolutePath() + "/.local");
2376     if (success) success = checkForDir(QDir::home().absolutePath() + "/.local/share");
2377     if (success) success = checkForDir(QDir::home().absolutePath() + "/.local/share/applications");
2378     if (success) success = system(("cp "+datadir->absolutePath()+"/misc/hedgewars-mimeinfo.xml "+QDir::home().absolutePath()+"/.local/share/mime/packages").toLocal8Bit().constData())==0;
2379     if (success) success = system(("cp "+datadir->absolutePath()+"/misc/hedgewars.desktop "+QDir::home().absolutePath()+"/.local/share/applications").toLocal8Bit().constData())==0;
2380     if (success) success = system(("cp "+datadir->absolutePath()+"/misc/hwengine.desktop "+QDir::home().absolutePath()+"/.local/share/applications").toLocal8Bit().constData())==0;
2381     if (success) success = system(("update-mime-database "+QDir::home().absolutePath()+"/.local/share/mime").toLocal8Bit().constData())==0;
2382     if (success) success = system("xdg-mime default hedgewars.desktop x-scheme-handler/hwplay")==0;
2383     if (success) success = system("xdg-mime default hwengine.desktop application/x-hedgewars-demo")==0;
2384     if (success) success = system("xdg-mime default hwengine.desktop application/x-hedgewars-save")==0;
2385     // hack to add user's settings to hwengine. might be better at this point to read in the file, append it, and write it out to its new home.  This assumes no spaces in the data dir path
2386     if (success) success = system(("sed -i 's|^\\(Exec=.*\\) \\(%f\\)|\\1 \\2 "+arguments+"|' "+QDir::home().absolutePath()+"/.local/share/applications/hwengine.desktop").toLocal8Bit().constData())==0;
2387 #endif
2388     if (success)
2389     {
2390         QMessageBox infoMsg(this);
2391         infoMsg.setIcon(QMessageBox::Information);
2392         infoMsg.setWindowTitle(QMessageBox::tr("Hedgewars - Success"));
2393         infoMsg.setText(QMessageBox::tr("All file associations have been set"));
2394         infoMsg.setTextFormat(Qt::PlainText);
2395         infoMsg.setWindowModality(Qt::WindowModal);
2396         infoMsg.exec();
2397     }
2398     else
2399         MessageDialog::ShowErrorMessage(QMessageBox::tr("File association failed."), this);
2400 }
2401 
openRegistrationPage()2402 void HWForm::openRegistrationPage()
2403 {
2404     QDesktopServices::openUrl(QUrl("https://www.hedgewars.org/user/register"));
2405 }
2406 
saveDemoWithCustomName()2407 void HWForm::saveDemoWithCustomName()
2408 {
2409     if(!m_lastDemo.isEmpty())
2410     {
2411         QString fileName;
2412         bool ok = false;
2413         do
2414         {
2415             fileName = QInputDialog::getText(this, tr("Demo name"), tr("Demo name:"));
2416 
2417             if(!fileName.isEmpty())
2418             {
2419                 QString filePath = cfgdir->absolutePath() + "/Demos/" + fileName + "." + *cProtoVer + ".hwd";
2420                 QFile demofile(filePath);
2421                 ok = demofile.open(QIODevice::WriteOnly);
2422                 if (!ok)
2423                     MessageDialog::ShowErrorMessage(tr("Cannot save record to file %1").arg(filePath), this);
2424                 else
2425                 {
2426                     ok = -1 != demofile.write(m_lastDemo);
2427                     demofile.close();
2428                 }
2429             }
2430         }
2431         while(!fileName.isEmpty() && !ok);
2432     }
2433 }
2434 
restartGame()2435 void HWForm::restartGame()
2436 {
2437     // get rid off old game stats page
2438     if(ui.Pages->currentIndex() == ID_PAGE_GAMESTATS)
2439         GoBack();
2440 
2441     CreateGame(lastGameCfg, lastGameTeamSel, lastGameAmmo);
2442 
2443     switch(lastGameType) {
2444     case gtTraining:
2445         game->StartTraining(lastGameStartArgs.at(0).toString(), lastGameStartArgs.at(1).toString(), lastGameStartArgs.at(2).toString());
2446         break;
2447     case gtQLocal:
2448         game->StartQuick();
2449         break;
2450     case gtCampaign:
2451         game->StartCampaign(lastGameStartArgs.at(0).toString(), lastGameStartArgs.at(1).toString(), lastGameStartArgs.at(2).toString());
2452         break;
2453     case gtLocal:
2454         game->StartLocal();
2455         break;
2456     case gtDemo:
2457     case gtSave:
2458         PlayDemo();
2459         break;
2460     default:
2461         break;
2462     }
2463 }
2464 
ShowFatalErrorMessage(const QString & msg)2465 void HWForm::ShowFatalErrorMessage(const QString & msg)
2466 {
2467     MessageDialog::ShowFatalMessage(msg, this);
2468 }
2469 
showFeedbackDialog()2470 void HWForm::showFeedbackDialog()
2471 {
2472     QNetworkRequest newRequest(QUrl("https://www.hedgewars.org"));
2473 
2474     QNetworkAccessManager *manager = new QNetworkAccessManager(this);
2475     QNetworkReply *reply = manager->get(newRequest);
2476     connect(reply, SIGNAL(finished()), this, SLOT(showFeedbackDialogNetChecked()));
2477 }
2478 
showFeedbackDialogNetChecked()2479 void HWForm::showFeedbackDialogNetChecked()
2480 {
2481     QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
2482 
2483     if (reply) {
2484         switch (reply->error()) {
2485             case QNetworkReply::NoError:
2486                 {
2487                     FeedbackDialog dialog(this);
2488                     dialog.exec();
2489                 }
2490                 break;
2491             case QNetworkReply::UnknownNetworkError:
2492                 MessageDialog::ShowFatalMessage(
2493                     tr("Unknown network error (possibly missing SSL library)."), this);
2494                 break;
2495             default:
2496                 MessageDialog::ShowFatalMessage(
2497                     QString(tr("This feature requires an Internet connection, but you don't appear to be online (error code: %1).")).arg(reply->error()), this);
2498                 break;
2499         }
2500     }
2501     else {
2502         MessageDialog::ShowFatalMessage(tr("Internal error: Reply object is invalid."), this);
2503     }
2504 }
2505 
startGame()2506 void HWForm::startGame()
2507 {
2508     QMessageBox questionMsg(this);
2509     questionMsg.setIcon(QMessageBox::Question);
2510     questionMsg.setWindowTitle(QMessageBox::tr("Not all players are ready"));
2511     questionMsg.setText(QMessageBox::tr("Are you sure you want to start this game?\nNot all players are ready."));
2512     questionMsg.setTextFormat(Qt::PlainText);
2513     questionMsg.setWindowModality(Qt::WindowModal);
2514     questionMsg.addButton(QMessageBox::Yes);
2515     questionMsg.addButton(QMessageBox::Cancel);
2516 
2517     if (hwnet->allPlayersReady() || questionMsg.exec() == QMessageBox::Yes)
2518         hwnet->startGame();
2519 }
2520