Convert feedback page to dialog (+some fixes from unC0Rr)
authordag10
Mon, 14 Jan 2013 21:17:42 +0400
changeset 8385 9e8924ff9813
parent 8384 a6e7a95f3b2e
child 8386 2aaa2995a32e
Convert feedback page to dialog (+some fixes from unC0Rr)
QTfrontend/gameuiconfig.cpp
QTfrontend/hwform.cpp
QTfrontend/hwform.h
QTfrontend/main.cpp
QTfrontend/model/HatModel.cpp
QTfrontend/model/MapModel.cpp
QTfrontend/ui/page/AbstractPage.cpp
QTfrontend/ui/page/pageeditteam.cpp
QTfrontend/ui/page/pagefeedback.cpp
QTfrontend/ui/page/pagefeedback.h
QTfrontend/ui/widget/feedbackdialog.cpp
QTfrontend/ui/widget/feedbackdialog.h
QTfrontend/ui/widget/flowlayout.h
QTfrontend/ui/widget/hatprompt.h
QTfrontend/ui/widget/lineeditcursor.cpp
QTfrontend/ui/widget/lineeditcursor.h
QTfrontend/ui/widget/themeprompt.cpp
QTfrontend/ui_hwform.cpp
QTfrontend/ui_hwform.h
QTfrontend/util/MessageDialog.h
project_files/hedgewars.pro
--- a/QTfrontend/gameuiconfig.cpp	Mon Jan 14 13:28:03 2013 +0100
+++ b/QTfrontend/gameuiconfig.cpp	Mon Jan 14 21:17:42 2013 +0400
@@ -160,7 +160,6 @@
     }
 
     { // load binds
-        QStandardItemModel * binds = DataManager::instance().bindsModel();
         for(int i = 0; i < BINDS_NUMBER; i++)
         {
             m_binds[i].strbind = value(QString("Binds/%1").arg(m_binds[i].action), cbinds[i].strbind).toString();
--- a/QTfrontend/hwform.cpp	Mon Jan 14 13:28:03 2013 +0100
+++ b/QTfrontend/hwform.cpp	Mon Jan 14 21:17:42 2013 +0400
@@ -72,7 +72,6 @@
 #include "pagemultiplayer.h"
 #include "pagenet.h"
 #include "pagemain.h"
-#include "pagefeedback.h"
 #include "pagenetserver.h"
 #include "pagedrawmap.h"
 #include "pagegamestats.h"
@@ -95,6 +94,7 @@
 #include "roomslistmodel.h"
 #include "recorder.h"
 #include "playerslistmodel.h"
+#include "feedbackdialog.h"
 
 #include "MessageDialog.h"
 #include "DataManager.h"
@@ -214,8 +214,7 @@
     connect(ui.pageMain->BtnSetup, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
     pageSwitchMapper->setMapping(ui.pageMain->BtnSetup, ID_PAGE_SETUP);
 
-    connect(ui.pageMain->BtnFeedback, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
-    pageSwitchMapper->setMapping(ui.pageMain->BtnFeedback, ID_PAGE_FEEDBACK);
+    connect(ui.pageMain->BtnFeedback, SIGNAL(clicked()), this, SLOT(showFeedbackDialog()));
 
     connect(ui.pageMain->BtnInfo, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
     pageSwitchMapper->setMapping(ui.pageMain->BtnInfo, ID_PAGE_INFO);
@@ -232,8 +231,6 @@
     //connect(ui.pageMain->BtnExit, SIGNAL(pressed()), this, SLOT(btnExitPressed()));
     //connect(ui.pageMain->BtnExit, SIGNAL(clicked()), this, SLOT(btnExitClicked()));
 
-    connect(ui.pageFeedback->BtnSend, SIGNAL(clicked()), this, SLOT(SendFeedback()));
-
     connect(ui.pageEditTeam, SIGNAL(goBack()), this, SLOT(AfterTeamEdit()));
 
     connect(ui.pageMultiplayer->BtnStartMPGame, SIGNAL(clicked()), this, SLOT(StartMPGame()));
@@ -582,7 +579,6 @@
       case ID_PAGE_CAMPAIGN :     pageName = "PAGE_CAMPAIGN"; break;
       case ID_PAGE_DRAWMAP :      pageName = "PAGE_DRAWMAP"; break;
       case ID_PAGE_DATADOWNLOAD : pageName = "PAGE_DATADOWNLOAD"; break;
-      case ID_PAGE_FEEDBACK :     pageName = "PAGE_FEEDBACK"; break;
       case ID_PAGE_VIDEOS :       pageName = "PAGE_VIDEOS"; break;
       case MAX_PAGE :             pageName = "MAX_PAGE"; break;
       default :                   pageName = "UNKNOWN_PAGE"; break;
@@ -678,11 +674,6 @@
     {
         ui.pageOptions->setTeamOptionsEnabled(true);
     }
-
-    if (id == ID_PAGE_FEEDBACK)
-    {
-        ui.pageFeedback->LoadCaptchaImage();
-    }
 }
 
 void HWForm::GoToPage(int id)
@@ -1019,6 +1010,7 @@
 
 void HWForm::NetPassword(const QString & nick)
 {
+    Q_UNUSED(nick);
     //Get hashes
     QString hash =  config->passwordHash();
     QString temphash =  config->tempHash();
@@ -1075,6 +1067,8 @@
 
 void HWForm::NetNickNotRegistered(const QString & nick)
 {
+    Q_UNUSED(nick);
+
     QMessageBox noRegMsg(this);
     noRegMsg.setIcon(QMessageBox::Information);
     noRegMsg.setWindowTitle(QMessageBox::tr("Hedgewars - Nick not registered"));
@@ -1185,6 +1179,8 @@
 
 void HWForm::_NetConnect(const QString & hostName, quint16 port, QString nick)
 {
+    Q_UNUSED(nick);
+
     if(hwnet)
     {
         hwnet->Disconnect();
@@ -1956,80 +1952,14 @@
     }
 }
 
-void HWForm::SendFeedback()
-{
-    // Get form data
-    
-    QString summary = ui.pageFeedback->summary->text();
-    QString description = ui.pageFeedback->description->toPlainText();
-    QString email = ui.pageFeedback->email->text();
-    QString captchaCode = ui.pageFeedback->captcha_code->text();
-    QString captchaID = QString::number(ui.pageFeedback->captchaID);
-    QString version = "HedgewarsFoundation-Hedgewars-" + (cVersionString?(*cVersionString):QString(""));
-
-    if (summary.isEmpty() || description.isEmpty())
-    {
-        MessageDialog::ShowErrorMessage(QMessageBox::tr("Please fill out all fields. Email is optional."), this);
-        return;
-    }
-
-    // Submit issue to PHP script
-    
-    QByteArray body;
-    body.append("captcha=");
-    body.append(captchaID);
-    body.append("&code=");
-    body.append(captchaCode);
-    body.append("&version=");
-    body.append(QUrl::toPercentEncoding(version));
-    body.append("&title=");
-    body.append(QUrl::toPercentEncoding(summary));
-    body.append("&body=");
-    body.append(QUrl::toPercentEncoding(description));
-    body.append("&email=");
-    body.append(QUrl::toPercentEncoding(email));
-    if (ui.pageFeedback->CheckSendSpecs->isChecked())
-    {
-        body.append("&specs=");
-        body.append(QUrl::toPercentEncoding(ui.pageFeedback->specs));
-    }
-    
-    nam = new QNetworkAccessManager(this);
-    connect(nam, SIGNAL(finished(QNetworkReply*)),
-            this, SLOT(finishedSlot(QNetworkReply*)));
-            
-    QNetworkRequest header(QUrl("http://hedgewars.org/feedback/?submit"));
-    header.setRawHeader("Content-Length", QString::number(body.size()).toAscii());
-    
-    nam->post(header, body);
-}
-
-void HWForm::finishedSlot(QNetworkReply* reply)
-{
-    if (reply && reply->error() == QNetworkReply::NoError)
-    {
-            QMessageBox infoMsg(this);
-            infoMsg.setIcon(QMessageBox::Information);
-            infoMsg.setWindowTitle(QMessageBox::tr("Hedgewars - Success"));
-            infoMsg.setText(reply->readAll());
-            infoMsg.setWindowModality(Qt::WindowModal);
-            infoMsg.exec();
-
-            ui.pageFeedback->summary->clear();
-            ui.pageFeedback->email->clear();
-            ui.pageFeedback->description->clear();
-            ui.pageFeedback->LoadCaptchaImage();
-            
-            return;
-    }
-    else
-    {
-        MessageDialog::ShowErrorMessage(QString("Error: ") + reply->readAll(), this);
-        ui.pageFeedback->LoadCaptchaImage();
-    }
-}
 
 void HWForm::ShowErrorMessage(const QString & msg)
 {
     MessageDialog::ShowErrorMessage(msg, this);
 }
+
+void HWForm::showFeedbackDialog()
+{
+    FeedbackDialog dialog(this);
+    dialog.exec();
+}
--- a/QTfrontend/hwform.h	Mon Jan 14 13:28:03 2013 +0100
+++ b/QTfrontend/hwform.h	Mon Jan 14 21:17:42 2013 +0400
@@ -126,10 +126,7 @@
         void UpdateCampaignPage(int index);
         void UpdateCampaignPageProgress(int index);
         void InitCampaignPage();
-        //Starts the transmission process for the feedback
-        void SendFeedback();
-        //Called after submitting new feedback
-        void finishedSlot(QNetworkReply* reply);
+        void showFeedbackDialog();
 
         void NetGameChangeStatus(bool isMaster);
         void NetGameMaster();
@@ -175,7 +172,6 @@
             ID_PAGE_CAMPAIGN       ,
             ID_PAGE_DRAWMAP        ,
             ID_PAGE_DATADOWNLOAD   ,
-            ID_PAGE_FEEDBACK	   ,
             ID_PAGE_VIDEOS	   ,
             MAX_PAGE
         };
@@ -191,7 +187,6 @@
         BGWidget * wBackground;
         QSignalMapper * pageSwitchMapper;
         QByteArray m_lastDemo;
-        QNetworkAccessManager * nam;
 
         QPropertyAnimation *animationNewSlide;
         QPropertyAnimation *animationOldSlide;
--- a/QTfrontend/main.cpp	Mon Jan 14 13:28:03 2013 +0100
+++ b/QTfrontend/main.cpp	Mon Jan 14 21:17:42 2013 +0400
@@ -92,6 +92,8 @@
 #ifndef _WIN32
 void terminateFrontend(int signal)
 {
+    Q_UNUSED(signal);
+
     QCoreApplication::exit(0);
 }
 #endif
--- a/QTfrontend/model/HatModel.cpp	Mon Jan 14 13:28:03 2013 +0100
+++ b/QTfrontend/model/HatModel.cpp	Mon Jan 14 21:17:42 2013 +0400
@@ -103,4 +103,4 @@
 
     QStandardItemModel::appendColumn(hats);
     QStandardItemModel::endResetModel();
-}
\ No newline at end of file
+}
--- a/QTfrontend/model/MapModel.cpp	Mon Jan 14 13:28:03 2013 +0100
+++ b/QTfrontend/model/MapModel.cpp	Mon Jan 14 21:17:42 2013 +0400
@@ -21,10 +21,10 @@
  * @brief MapModel class implementation
  */
 
+#include <QSettings>
+
 #include "MapModel.h"
-#include "hwapplication.h"
-
-#include <QSettings>
+#include "HWApplication.h"
 
 MapModel::MapInfo MapModel::MapInfoRandom = {MapModel::GeneratedMap, "+rnd+", "", 0, "", "", ""};
 MapModel::MapInfo MapModel::MapInfoMaze = {MapModel::GeneratedMaze, "+maze+", "", 0, "", "", ""};
--- a/QTfrontend/ui/page/AbstractPage.cpp	Mon Jan 14 13:28:03 2013 +0100
+++ b/QTfrontend/ui/page/AbstractPage.cpp	Mon Jan 14 21:17:42 2013 +0400
@@ -25,7 +25,7 @@
 #include <QLabel>
 #include <QSize>
 #include <QFontMetricsF>
-#include <qDebug>
+#include <QDebug>
 
 #include "qpushbuttonwithsound.h"
 
--- a/QTfrontend/ui/page/pageeditteam.cpp	Mon Jan 14 13:28:03 2013 +0100
+++ b/QTfrontend/ui/page/pageeditteam.cpp	Mon Jan 14 21:17:42 2013 +0400
@@ -62,7 +62,6 @@
     GBoxHedgehogs->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
     QGridLayout * GBHLayout = new QGridLayout(GBoxHedgehogs);
 
-    HatModel * hatModel = DataManager::instance().hatModel();
 
     GBHLayout->addWidget(new QLabel(tr("Hat")), 0, 0);
     GBHLayout->addWidget(new QLabel(tr("Name")), 0, 1);
--- a/QTfrontend/ui/page/pagefeedback.cpp	Mon Jan 14 13:28:03 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,391 +0,0 @@
-/*
- * Hedgewars, a free turn based strategy game
- * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-#include <QHBoxLayout>
-#include <QLineEdit>
-#include <QTextBrowser>
-#include <QLabel>
-#include <QHttp>
-#include <QSysInfo>
-#include <QDebug>
-#include <QBuffer>
-#include <QApplication>
-#include <QDesktopWidget>
-#include <QNetworkReply>
-#include <QProcess>
-#include <QMessageBox>
-#include <QCheckBox>
-
-#include <string>
-
-#ifdef Q_WS_WIN
-#define WINVER 0x0500
-#include <windows.h>
-#else
-#include <unistd.h>
-#include <sys/types.h>
-#endif
-
-#ifdef Q_WS_MAC
-#include <sys/sysctl.h>
-#endif
-
-#include <stdint.h>
-
-#include "pagefeedback.h"
-#include "MessageDialog.h"
-#include "hwconsts.h"
-
-QLayout * PageFeedback::bodyLayoutDefinition()
-{
-    QVBoxLayout * pageLayout = new QVBoxLayout();
-    QHBoxLayout * summaryLayout = new QHBoxLayout();
-    QHBoxLayout * emailLayout = new QHBoxLayout();
-    QHBoxLayout * descriptionLayout = new QHBoxLayout();
-    QHBoxLayout * combinedTopLayout = new QHBoxLayout();
-    QHBoxLayout * systemLayout = new QHBoxLayout();
-
-    info = new QLabel();
-    info->setText(
-        "<style type=\"text/css\">"
-        "a { color: #fc0; }"
-        "b { color: #0df; }"
-        "</style>"
-        "<div align=\"center\"><h1>Please give us feedback!</h1>"
-        "<h3>We are always happy about suggestions, ideas, or bug reports.<h3>"
-        "<h4>Your email address is optional, but we may want to contact you.<h4>"
-        "</div>"
-    );
-    pageLayout->addWidget(info);
-
-    QVBoxLayout * summaryEmailLayout = new QVBoxLayout();
-
-    const int labelWidth = 90;
-
-    label_email = new QLabel();
-    label_email->setText(QLabel::tr("Your Email"));
-    label_email->setFixedWidth(labelWidth);
-    emailLayout->addWidget(label_email);
-    email = new QLineEdit();
-    emailLayout->addWidget(email);
-    summaryEmailLayout->addLayout(emailLayout);
-    
-    label_summary = new QLabel();
-    label_summary->setText(QLabel::tr("Summary"));
-    label_summary->setFixedWidth(labelWidth);
-    summaryLayout->addWidget(label_summary);
-    summary = new QLineEdit();
-    summaryLayout->addWidget(summary);
-    summaryEmailLayout->addLayout(summaryLayout);
-    
-    combinedTopLayout->addLayout(summaryEmailLayout);
-
-
-    CheckSendSpecs = new QCheckBox();
-    CheckSendSpecs->setText(QLabel::tr("Send system information"));
-    CheckSendSpecs->setChecked(true);
-    systemLayout->addWidget(CheckSendSpecs);
-    BtnViewInfo = addButton("View", systemLayout, 1, false);
-    BtnViewInfo->setFixedSize(60, 30);
-    connect(BtnViewInfo, SIGNAL(clicked()), this, SLOT(ShowSpecs()));
-    combinedTopLayout->addLayout(systemLayout);
-
-    combinedTopLayout->setStretch(0, 1);
-    combinedTopLayout->insertSpacing(1, 20);
-
-    pageLayout->addLayout(combinedTopLayout);
-
-    label_description = new QLabel();
-    label_description->setText(QLabel::tr("Description"));
-    label_description->setFixedWidth(labelWidth);
-    descriptionLayout->addWidget(label_description, 0, Qt::AlignTop);
-    description = new QTextBrowser();
-    description->setReadOnly(false);
-    descriptionLayout->addWidget(description);
-    pageLayout->addLayout(descriptionLayout);
-
-    return pageLayout;
-}
-
-QNetworkAccessManager * PageFeedback::GetNetManager()
-{
-    if (netManager) return netManager;
-    netManager = new QNetworkAccessManager(this);
-    connect(netManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(NetReply(QNetworkReply*)));
-    return netManager;
-}
-
-void PageFeedback::LoadCaptchaImage()
-{
-        QNetworkAccessManager *netManager = GetNetManager();
-        QUrl captchaURL("http://hedgewars.org/feedback/?gencaptcha");
-        QNetworkRequest req(captchaURL);
-        genCaptchaRequest = netManager->get(req);
-}
-
-void PageFeedback::NetReply(QNetworkReply *reply)
-{
-    if (reply == genCaptchaRequest)
-    {
-        if (reply->error() != QNetworkReply::NoError)
-        {
-            qDebug() << "Error generating captcha image: " << reply->errorString();
-            MessageDialog::ShowErrorMessage(QMessageBox::tr("Failed to generate captcha"), this);
-            return;
-        }
-
-        bool okay;
-        QByteArray body = reply->readAll();
-        captchaID = QString(body).toInt(&okay);
-
-        if (!okay)
-        {
-            qDebug() << "Failed to get captcha ID: " << body;
-            MessageDialog::ShowErrorMessage(QMessageBox::tr("Failed to generate captcha"), this);
-            return;
-        }
-
-        QString url = "http://hedgewars.org/feedback/?captcha&id=";
-        url += QString::number(captchaID);
-        
-        QNetworkAccessManager *netManager = GetNetManager();
-        QUrl captchaURL(url);
-        QNetworkRequest req(captchaURL);
-        captchaImageRequest = netManager->get(req);
-    }
-    else if (reply == captchaImageRequest)
-    {
-        if (reply->error() != QNetworkReply::NoError)
-        {
-            qDebug() << "Error loading captcha image: " << reply->errorString();
-            MessageDialog::ShowErrorMessage(QMessageBox::tr("Failed to download captcha"), this);
-            return;
-        }
-
-        QByteArray imageData = reply->readAll();
-        QPixmap pixmap;
-        pixmap.loadFromData(imageData);
-        label_captcha->setPixmap(pixmap);
-        captcha_code->setText("");
-    }
-}
-
-QLayout * PageFeedback::footerLayoutDefinition()
-{
-    QHBoxLayout * bottomLayout = new QHBoxLayout();
-    QHBoxLayout * captchaLayout = new QHBoxLayout();
-    QVBoxLayout * captchaInputLayout = new QVBoxLayout();
-
-    label_captcha = new QLabel();
-    label_captcha->setStyleSheet("border: 3px solid #ffcc00; border-radius: 4px");
-    label_captcha->setText("<div style='width: 200px; height: 100px;'>loading<br>captcha</div>");
-    captchaLayout->addWidget(label_captcha);
-
-    label_captcha_input = new QLabel();
-    label_captcha_input->setText(QLabel::tr("Type the security code:"));
-    captchaInputLayout->addWidget(label_captcha_input);
-    captchaInputLayout->setAlignment(label_captcha, Qt::AlignBottom);
-    captcha_code = new QLineEdit();
-    captcha_code->setFixedSize(165, 30);
-    captchaInputLayout->addWidget(captcha_code);
-    captchaInputLayout->setAlignment(captcha_code, Qt::AlignTop);
-    captchaLayout->addLayout(captchaInputLayout);
-    captchaLayout->setAlignment(captchaInputLayout, Qt::AlignLeft);
-
-    captchaLayout->insertSpacing(-1, 40);
-    bottomLayout->addLayout(captchaLayout);
-    
-    //TODO: create logo for send button
-    BtnSend = addButton("Send Feedback", bottomLayout, 0, false);
-    BtnSend->setFixedSize(120, 40);
-
-    bottomLayout->setStretchFactor(captchaLayout, 0);
-    bottomLayout->setStretchFactor(BtnSend, 1);
-
-    return bottomLayout;
-}
-
-void PageFeedback::GenerateSpecs()
-{
-    // Gather some information about the system and embed it into the report
-    QDesktopWidget* screen = QApplication::desktop();
-    QString os_version = "Operating system: ";
-    QString qt_version = QString("Qt version: ") + QT_VERSION_STR + QString("\n");
-    QString total_ram = "Total RAM: ";
-    QString number_of_cores = "Number of cores: ";
-    QString compiler_bits = "Compiler architecture: ";
-    QString compiler_version = "Compiler version: ";
-    QString kernel_line = "Kernel: ";
-    QString screen_size = "Size of the screen(s): " +
-        QString::number(screen->width()) + "x" + QString::number(screen->height()) + "\n";
-    QString number_of_screens = "Number of screens: " + QString::number(screen->screenCount()) + "\n";
-    std::string processor_name = "Processor: ";
-
-    // platform specific code
-#ifdef Q_WS_MACX
-    number_of_cores += QString::number(sysconf(_SC_NPROCESSORS_ONLN)) + "\n";
-
-    uint64_t memsize;
-    size_t len = sizeof(memsize);
-    static int mib_s[2] = { CTL_HW, HW_MEMSIZE };
-    if (sysctl (mib_s, 2, &memsize, &len, NULL, 0) == 0)
-        total_ram += QString::number(memsize/1024/1024) + " MB\n";
-    else
-        total_ram += "Error getting total RAM information\n";
-
-    int mib[] = {CTL_KERN, KERN_OSRELEASE};
-    sysctl(mib, sizeof mib / sizeof(int), NULL, &len, NULL, 0);
-
-    char *kernelVersion = (char *)malloc(sizeof(char)*len);
-    sysctl(mib, sizeof mib / sizeof(int), kernelVersion, &len, NULL, 0);
-
-    QString kernelVersionStr = QString(kernelVersion);
-    free(kernelVersion);
-    int major_version = kernelVersionStr.split(".").first().toUInt() - 4;
-    int minor_version = kernelVersionStr.split(".").at(1).toUInt();
-    os_version += QString("Mac OS X 10.%1.%2").arg(major_version).arg(minor_version) + " ";
-
-    switch(major_version)
-    {
-        case 4:  os_version += "\"Tiger\"\n"; break;
-        case 5:  os_version += "\"Leopard\"\n"; break;
-        case 6:  os_version += "\"Snow Leopard\"\n"; break;
-        case 7:  os_version += "\"Lion\"\n"; break;
-        case 8:  os_version += "\"Mountain Lion\"\n"; break;
-        default: os_version += "\"Unknown version\"\n"; break;
-    }
-#endif
-#ifdef Q_WS_WIN
-    SYSTEM_INFO sysinfo;
-    GetSystemInfo(&sysinfo);
-    number_of_cores += QString::number(sysinfo.dwNumberOfProcessors) + "\n";
-    MEMORYSTATUSEX status;
-    status.dwLength = sizeof(status);
-    GlobalMemoryStatusEx(&status);
-    total_ram += QString::number(status.ullTotalPhys);
-
-    switch(QSysInfo::WinVersion())
-    {
-        case QSysInfo::WV_2000: os_version += "Windows 2000\n"; break;
-        case QSysInfo::WV_XP: os_version += "Windows XP\n"; break;
-        case QSysInfo::WV_VISTA: os_version += "Windows Vista\n"; break;
-        case QSysInfo::WV_WINDOWS7: os_version += "Windows 7\n"; break;
-        default: os_version += "Windows (Unknown version)\n"; break;
-    }
-    kernel_line += "Windows kernel\n";
-#endif
-#ifdef Q_WS_X11
-    number_of_cores += QString::number(sysconf(_SC_NPROCESSORS_ONLN)) + "\n";
-    long pages = sysconf(_SC_PHYS_PAGES),
-#ifndef Q_OS_FREEBSD
-         available_pages = sysconf(_SC_AVPHYS_PAGES),
-#else
-         available_pages = 0,
-#endif
-         page_size = sysconf(_SC_PAGE_SIZE);
-    total_ram += QString::number(pages * page_size) + "\n";
-    os_version += "GNU/Linux or BSD\n";
-#endif
-
-    // uname -a
-#if defined(Q_WS_X11) || defined(Q_WS_MACX)
-    QProcess *process = new QProcess();
-    QStringList arguments = QStringList("-a");
-    process->start("uname", arguments);
-    if (process->waitForFinished())
-        kernel_line += QString(process->readAll());
-    delete process;
-#endif
-
-    // cpu info
-    quint32 registers[4];
-    quint32 i;
-
-    i = 0x80000002;
-    asm volatile
-      ("cpuid" : "=a" (registers[0]), "=b" (registers[1]), "=c" (registers[2]), "=d" (registers[3])
-       : "a" (i), "c" (0));
-    processor_name += std::string((const char *)&registers[0], 4);
-    processor_name += std::string((const char *)&registers[1], 4);
-    processor_name += std::string((const char *)&registers[2], 4);
-    processor_name += std::string((const char *)&registers[3], 4);
-    i = 0x80000003;
-    asm volatile
-      ("cpuid" : "=a" (registers[0]), "=b" (registers[1]), "=c" (registers[2]), "=d" (registers[3])
-       : "a" (i), "c" (0));
-    processor_name += std::string((const char *)&registers[0], 4);
-    processor_name += std::string((const char *)&registers[1], 4);
-    processor_name += std::string((const char *)&registers[2], 4);
-    processor_name += std::string((const char *)&registers[3], 4);
-    i = 0x80000004;
-    asm volatile
-      ("cpuid" : "=a" (registers[0]), "=b" (registers[1]), "=c" (registers[2]), "=d" (registers[3])
-       : "a" (i), "c" (0));
-    processor_name += std::string((const char *)&registers[0], 4);
-    processor_name += std::string((const char *)&registers[1], 4);
-    processor_name += std::string((const char *)&registers[2], 4);
-    processor_name += std::string((const char *)&registers[3], 3);
-
-    // compiler
-#ifdef __GNUC__
-    compiler_version += "GCC " + QString(__VERSION__) + "\n";
-#else
-    compiler_version += "Unknown\n";
-#endif
-
-    if(sizeof(void*) == 4)
-        compiler_bits += "i386\n";
-    else if(sizeof(void*) == 8)
-        compiler_bits += "x86_64\n";
-
-    // concat system info
-    specs = qt_version
-        + os_version
-        + total_ram
-        + screen_size
-        + number_of_screens
-        + QString::fromStdString(processor_name + "\n")
-        + number_of_cores
-        + compiler_version
-        + compiler_bits
-        + kernel_line;
-}
-
-void PageFeedback::connectSignals()
-{
-    //TODO
-}
-
-void PageFeedback::ShowSpecs()
-{
-    QMessageBox msgMsg(this);
-    msgMsg.setIcon(QMessageBox::Information);
-    msgMsg.setWindowTitle(QMessageBox::tr("System Information Preview"));
-    msgMsg.setText(specs);
-    msgMsg.setTextFormat(Qt::PlainText);
-    msgMsg.setWindowModality(Qt::WindowModal);
-    msgMsg.setStyleSheet("background: #0A0533;");
-    msgMsg.exec();
-}
-
-PageFeedback::PageFeedback(QWidget* parent) : AbstractPage(parent)
-{
-    initPage();
-    netManager = NULL;
-    GenerateSpecs();
-}
--- a/QTfrontend/ui/page/pagefeedback.h	Mon Jan 14 13:28:03 2013 +0100
+++ b/QTfrontend/ui/page/pagefeedback.h	Mon Jan 14 21:17:42 2013 +0400
@@ -1,70 +0,0 @@
-/*
- * Hedgewars, a free turn based strategy game
- * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-#ifndef PAGE_FEEDBACK_H
-#define PAGE_FEEDBACK_H
-
-#include "AbstractPage.h"
-
-class QNetworkReply;
-class QNetworkAccessManager;
-
-class PageFeedback : public AbstractPage
-{
-        Q_OBJECT
-
-    public:
-        PageFeedback(QWidget * parent = 0);
-        void EmbedSystemInfo();
-        void LoadCaptchaImage();
-
-        QPushButton * BtnSend;
-        QPushButton * BtnViewInfo;
-        QCheckBox * CheckSendSpecs;
-        QLineEdit * summary;
-        QTextBrowser * description;
-        QLabel * info;
-        QLabel * label_summary;
-        QLabel * label_description;
-        QLabel * label_captcha;
-        QLabel * label_email;
-        QLabel * label_captcha_input;
-        QLineEdit * captcha_code;
-        QLineEdit * email;
-        int captchaID;
-        QString specs;
-        
-    private slots:
-    
-        virtual void NetReply(QNetworkReply*);
-        virtual void ShowSpecs();
-
-    private:
-        void GenerateSpecs();
-        QLayout * bodyLayoutDefinition();
-        QLayout * footerLayoutDefinition();
-        QNetworkAccessManager * GetNetManager();
-        void ShowErrorMessage(const QString & msg);
-        void connectSignals();
-
-        QNetworkAccessManager * netManager;
-        QNetworkReply * captchaImageRequest;
-        QNetworkReply * genCaptchaRequest;
-};
-
-#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/widget/feedbackdialog.cpp	Mon Jan 14 21:17:42 2013 +0400
@@ -0,0 +1,482 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <QHBoxLayout>
+#include <QLineEdit>
+#include <QTextBrowser>
+#include <QLabel>
+#include <QHttp>
+#include <QSysInfo>
+#include <QDebug>
+#include <QBuffer>
+#include <QApplication>
+#include <QDesktopWidget>
+#include <QNetworkReply>
+#include <QProcess>
+#include <QMessageBox>
+#include <QCheckBox>
+
+#include <string>
+
+#ifdef Q_WS_WIN
+#define WINVER 0x0500
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <sys/types.h>
+#endif
+
+#ifdef Q_WS_MAC
+#include <sys/sysctl.h>
+#endif
+
+#include <stdint.h>
+
+#include "AbstractPage.h"
+#include "hwconsts.h"
+#include "feedbackdialog.h"
+
+FeedbackDialog::FeedbackDialog(QWidget * parent) : QDialog(parent)
+{
+	setModal(true);
+	setWindowFlags(Qt::Sheet);
+	setWindowModality(Qt::WindowModal);
+	setMinimumSize(700, 460);
+	resize(700, 460);
+	setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+
+	netManager = NULL;
+	GenerateSpecs();
+
+	/* Top layout */
+
+	QVBoxLayout * pageLayout = new QVBoxLayout();
+    QHBoxLayout * summaryLayout = new QHBoxLayout();
+    QHBoxLayout * emailLayout = new QHBoxLayout();
+    QHBoxLayout * descriptionLayout = new QHBoxLayout();
+    QHBoxLayout * combinedTopLayout = new QHBoxLayout();
+    QHBoxLayout * systemLayout = new QHBoxLayout();
+
+    info = new QLabel();
+    info->setText(
+        "<style type=\"text/css\">"
+        "a { color: #fc0; }"
+        "b { color: #0df; }"
+        "</style>"
+        "<div align=\"center\"><h1>Please give us feedback!</h1>"
+        "<h3>We are always happy about suggestions, ideas, or bug reports.<h3>"
+        "<h4>Your email address is optional, but we may want to contact you.<h4>"
+        "</div>"
+    );
+    pageLayout->addWidget(info);
+
+    QVBoxLayout * summaryEmailLayout = new QVBoxLayout();
+
+    const int labelWidth = 90;
+
+    label_email = new QLabel();
+    label_email->setText(QLabel::tr("Your Email"));
+    label_email->setFixedWidth(labelWidth);
+    emailLayout->addWidget(label_email);
+    email = new QLineEdit();
+    emailLayout->addWidget(email);
+    summaryEmailLayout->addLayout(emailLayout);
+    
+    label_summary = new QLabel();
+    label_summary->setText(QLabel::tr("Summary"));
+    label_summary->setFixedWidth(labelWidth);
+    summaryLayout->addWidget(label_summary);
+    summary = new QLineEdit();
+    summaryLayout->addWidget(summary);
+    summaryEmailLayout->addLayout(summaryLayout);
+    
+    combinedTopLayout->addLayout(summaryEmailLayout);
+
+    CheckSendSpecs = new QCheckBox();
+    CheckSendSpecs->setText(QLabel::tr("Send system information"));
+    CheckSendSpecs->setChecked(true);
+    systemLayout->addWidget(CheckSendSpecs);
+    BtnViewInfo = new QPushButton(tr("View"));
+    systemLayout->addWidget(BtnViewInfo, 1);
+    BtnViewInfo->setFixedSize(60, 30);
+    connect(BtnViewInfo, SIGNAL(clicked()), this, SLOT(ShowSpecs()));
+    combinedTopLayout->addLayout(systemLayout);
+
+    combinedTopLayout->setStretch(0, 1);
+    combinedTopLayout->insertSpacing(1, 20);
+
+    pageLayout->addLayout(combinedTopLayout);
+
+    label_description = new QLabel();
+    label_description->setText(QLabel::tr("Description"));
+    label_description->setFixedWidth(labelWidth);
+    descriptionLayout->addWidget(label_description, 0, Qt::AlignTop);
+    description = new QTextBrowser();
+    description->setReadOnly(false);
+    descriptionLayout->addWidget(description);
+    pageLayout->addLayout(descriptionLayout);
+
+    /* Bottom layout */
+
+    QHBoxLayout * bottomLayout = new QHBoxLayout();
+    QHBoxLayout * captchaLayout = new QHBoxLayout();
+    QVBoxLayout * captchaInputLayout = new QVBoxLayout();
+
+    QPushButton * BtnCancel = new QPushButton(tr("Cancel"));
+    bottomLayout->addWidget(BtnCancel, 0);
+    BtnCancel->setFixedSize(100, 40);
+    connect(BtnCancel, SIGNAL(clicked()), this, SLOT(reject()));
+
+    bottomLayout->insertStretch(1);
+
+    label_captcha = new QLabel();
+    label_captcha->setStyleSheet("border: 3px solid #ffcc00; border-radius: 4px");
+    label_captcha->setText("loading<br>captcha");
+    label_captcha->setFixedSize(200, 50);
+    captchaLayout->addWidget(label_captcha);
+
+    label_captcha_input = new QLabel();
+    label_captcha_input->setText(QLabel::tr("Type the security code:"));
+    captchaInputLayout->addWidget(label_captcha_input);
+    captchaInputLayout->setAlignment(label_captcha, Qt::AlignBottom);
+    captcha_code = new QLineEdit();
+    captcha_code->setFixedSize(165, 30);
+    captchaInputLayout->addWidget(captcha_code);
+    captchaInputLayout->setAlignment(captcha_code, Qt::AlignTop);
+    captchaLayout->addLayout(captchaInputLayout);
+    captchaLayout->setAlignment(captchaInputLayout, Qt::AlignLeft);
+
+    bottomLayout->addLayout(captchaLayout);
+    bottomLayout->addSpacing(40);
+    
+    // TODO: Set green arrow icon for send button (:/res/Start.png)
+    BtnSend = new QPushButton(tr("Send Feedback"));
+    bottomLayout->addWidget(BtnSend, 0);
+    BtnSend->setFixedSize(120, 40);
+    connect(BtnSend, SIGNAL(clicked()), this, SLOT(SendFeedback()));
+
+    bottomLayout->setStretchFactor(captchaLayout, 0);
+    bottomLayout->setStretchFactor(BtnSend, 1);
+
+    QVBoxLayout * dialogLayout = new QVBoxLayout(this);
+    dialogLayout->addLayout(pageLayout, 1);
+    dialogLayout->addLayout(bottomLayout);
+
+    LoadCaptchaImage();
+}
+
+void FeedbackDialog::GenerateSpecs()
+{
+    // Gather some information about the system and embed it into the report
+    QDesktopWidget* screen = QApplication::desktop();
+    QString os_version = "Operating system: ";
+    QString qt_version = QString("Qt version: ") + QT_VERSION_STR + QString("\n");
+    QString total_ram = "Total RAM: ";
+    QString number_of_cores = "Number of cores: ";
+    QString compiler_bits = "Compiler architecture: ";
+    QString compiler_version = "Compiler version: ";
+    QString kernel_line = "Kernel: ";
+    QString screen_size = "Size of the screen(s): " +
+        QString::number(screen->width()) + "x" + QString::number(screen->height()) + "\n";
+    QString number_of_screens = "Number of screens: " + QString::number(screen->screenCount()) + "\n";
+    std::string processor_name = "Processor: ";
+
+    // platform specific code
+#ifdef Q_WS_MACX
+    number_of_cores += QString::number(sysconf(_SC_NPROCESSORS_ONLN)) + "\n";
+
+    uint64_t memsize;
+    size_t len = sizeof(memsize);
+    static int mib_s[2] = { CTL_HW, HW_MEMSIZE };
+    if (sysctl (mib_s, 2, &memsize, &len, NULL, 0) == 0)
+        total_ram += QString::number(memsize/1024/1024) + " MB\n";
+    else
+        total_ram += "Error getting total RAM information\n";
+
+    int mib[] = {CTL_KERN, KERN_OSRELEASE};
+    sysctl(mib, sizeof mib / sizeof(int), NULL, &len, NULL, 0);
+
+    char *kernelVersion = (char *)malloc(sizeof(char)*len);
+    sysctl(mib, sizeof mib / sizeof(int), kernelVersion, &len, NULL, 0);
+
+    QString kernelVersionStr = QString(kernelVersion);
+    free(kernelVersion);
+    int major_version = kernelVersionStr.split(".").first().toUInt() - 4;
+    int minor_version = kernelVersionStr.split(".").at(1).toUInt();
+    os_version += QString("Mac OS X 10.%1.%2").arg(major_version).arg(minor_version) + " ";
+
+    switch(major_version)
+    {
+        case 4:  os_version += "\"Tiger\"\n"; break;
+        case 5:  os_version += "\"Leopard\"\n"; break;
+        case 6:  os_version += "\"Snow Leopard\"\n"; break;
+        case 7:  os_version += "\"Lion\"\n"; break;
+        case 8:  os_version += "\"Mountain Lion\"\n"; break;
+        default: os_version += "\"Unknown version\"\n"; break;
+    }
+#endif
+#ifdef Q_WS_WIN
+    SYSTEM_INFO sysinfo;
+    GetSystemInfo(&sysinfo);
+    number_of_cores += QString::number(sysinfo.dwNumberOfProcessors) + "\n";
+    MEMORYSTATUSEX status;
+    status.dwLength = sizeof(status);
+    GlobalMemoryStatusEx(&status);
+    total_ram += QString::number(status.ullTotalPhys);
+
+    switch(QSysInfo::WinVersion())
+    {
+        case QSysInfo::WV_2000: os_version += "Windows 2000\n"; break;
+        case QSysInfo::WV_XP: os_version += "Windows XP\n"; break;
+        case QSysInfo::WV_VISTA: os_version += "Windows Vista\n"; break;
+        case QSysInfo::WV_WINDOWS7: os_version += "Windows 7\n"; break;
+        default: os_version += "Windows (Unknown version)\n"; break;
+    }
+    kernel_line += "Windows kernel\n";
+#endif
+#ifdef Q_WS_X11
+    number_of_cores += QString::number(sysconf(_SC_NPROCESSORS_ONLN)) + "\n";
+    long pages = sysconf(_SC_PHYS_PAGES),
+#ifndef Q_OS_FREEBSD
+         available_pages = sysconf(_SC_AVPHYS_PAGES),
+#else
+         available_pages = 0,
+#endif
+         page_size = sysconf(_SC_PAGE_SIZE);
+    total_ram += QString::number(pages * page_size) + "\n";
+    os_version += "GNU/Linux or BSD\n";
+#endif
+
+    // uname -a
+#if defined(Q_WS_X11) || defined(Q_WS_MACX)
+    QProcess *process = new QProcess();
+    QStringList arguments = QStringList("-a");
+    process->start("uname", arguments);
+    if (process->waitForFinished())
+        kernel_line += QString(process->readAll());
+    delete process;
+#endif
+
+    // cpu info
+    quint32 registers[4];
+    quint32 i;
+
+    i = 0x80000002;
+    asm volatile
+      ("cpuid" : "=a" (registers[0]), "=b" (registers[1]), "=c" (registers[2]), "=d" (registers[3])
+       : "a" (i), "c" (0));
+    processor_name += std::string((const char *)&registers[0], 4);
+    processor_name += std::string((const char *)&registers[1], 4);
+    processor_name += std::string((const char *)&registers[2], 4);
+    processor_name += std::string((const char *)&registers[3], 4);
+    i = 0x80000003;
+    asm volatile
+      ("cpuid" : "=a" (registers[0]), "=b" (registers[1]), "=c" (registers[2]), "=d" (registers[3])
+       : "a" (i), "c" (0));
+    processor_name += std::string((const char *)&registers[0], 4);
+    processor_name += std::string((const char *)&registers[1], 4);
+    processor_name += std::string((const char *)&registers[2], 4);
+    processor_name += std::string((const char *)&registers[3], 4);
+    i = 0x80000004;
+    asm volatile
+      ("cpuid" : "=a" (registers[0]), "=b" (registers[1]), "=c" (registers[2]), "=d" (registers[3])
+       : "a" (i), "c" (0));
+    processor_name += std::string((const char *)&registers[0], 4);
+    processor_name += std::string((const char *)&registers[1], 4);
+    processor_name += std::string((const char *)&registers[2], 4);
+    processor_name += std::string((const char *)&registers[3], 3);
+
+    // compiler
+#ifdef __GNUC__
+    compiler_version += "GCC " + QString(__VERSION__) + "\n";
+#else
+    compiler_version += "Unknown\n";
+#endif
+
+    if(sizeof(void*) == 4)
+        compiler_bits += "i386\n";
+    else if(sizeof(void*) == 8)
+        compiler_bits += "x86_64\n";
+
+    // concat system info
+    specs = qt_version
+        + os_version
+        + total_ram
+        + screen_size
+        + number_of_screens
+        + QString::fromStdString(processor_name + "\n")
+        + number_of_cores
+        + compiler_version
+        + compiler_bits
+        + kernel_line;
+}
+
+void FeedbackDialog::ShowErrorMessage(const QString & msg)
+{
+    QMessageBox msgMsg(this);
+    msgMsg.setIcon(QMessageBox::Warning);
+    msgMsg.setWindowTitle(QMessageBox::tr("Hedgewars - Error"));
+    msgMsg.setText(msg);
+    msgMsg.setWindowModality(Qt::WindowModal);
+    msgMsg.exec();
+}
+
+void FeedbackDialog::ShowSpecs()
+{
+    QMessageBox msgMsg(this);
+    msgMsg.setIcon(QMessageBox::Information);
+    msgMsg.setWindowTitle(QMessageBox::tr("System Information Preview"));
+    msgMsg.setText(specs);
+    msgMsg.setTextFormat(Qt::PlainText);
+    msgMsg.setWindowModality(Qt::WindowModal);
+    msgMsg.setStyleSheet("background: #0A0533;");
+    msgMsg.exec();
+}
+
+void FeedbackDialog::NetReply(QNetworkReply *reply)
+{
+    if (reply == genCaptchaRequest)
+    {
+        if (reply->error() != QNetworkReply::NoError)
+        {
+            qDebug() << "Error generating captcha image: " << reply->errorString();
+            ShowErrorMessage(QMessageBox::tr("Failed to generate captcha"));
+            return;
+        }
+
+        bool okay;
+        QByteArray body = reply->readAll();
+        captchaID = QString(body).toInt(&okay);
+
+        if (!okay)
+        {
+            qDebug() << "Failed to get captcha ID: " << body;
+            ShowErrorMessage(QMessageBox::tr("Failed to generate captcha"));
+            return;
+        }
+
+        QString url = "http://hedgewars.org/feedback/?captcha&id=";
+        url += QString::number(captchaID);
+        
+        QNetworkAccessManager *netManager = GetNetManager();
+        QUrl captchaURL(url);
+        QNetworkRequest req(captchaURL);
+        captchaImageRequest = netManager->get(req);
+    }
+    else if (reply == captchaImageRequest)
+    {
+        if (reply->error() != QNetworkReply::NoError)
+        {
+            qDebug() << "Error loading captcha image: " << reply->errorString();
+            ShowErrorMessage(QMessageBox::tr("Failed to download captcha"));
+            return;
+        }
+
+        QByteArray imageData = reply->readAll();
+        QPixmap pixmap;
+        pixmap.loadFromData(imageData);
+        label_captcha->setPixmap(pixmap);
+        captcha_code->setText("");
+    }
+}
+
+QNetworkAccessManager * FeedbackDialog::GetNetManager()
+{
+    if (netManager) return netManager;
+    netManager = new QNetworkAccessManager(this);
+    connect(netManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(NetReply(QNetworkReply*)));
+    return netManager;
+}
+
+void FeedbackDialog::LoadCaptchaImage()
+{
+        QNetworkAccessManager *netManager = GetNetManager();
+        QUrl captchaURL("http://hedgewars.org/feedback/?gencaptcha");
+        QNetworkRequest req(captchaURL);
+        genCaptchaRequest = netManager->get(req);
+}
+
+void FeedbackDialog::finishedSlot(QNetworkReply* reply)
+{
+    if (reply && reply->error() == QNetworkReply::NoError)
+    {
+            QMessageBox infoMsg(this);
+            infoMsg.setIcon(QMessageBox::Information);
+            infoMsg.setWindowTitle(QMessageBox::tr("Hedgewars - Success"));
+            infoMsg.setText(reply->readAll());
+            infoMsg.setWindowModality(Qt::WindowModal);
+            infoMsg.exec();
+
+            accept();
+            
+            return;
+    }
+    else
+    {
+        ShowErrorMessage(QString("Error: ") + reply->readAll());
+        LoadCaptchaImage();
+    }
+}
+
+void FeedbackDialog::SendFeedback()
+{
+    // Get form data
+    
+    QString summary = this->summary->text();
+    QString description = this->description->toPlainText();
+    QString email = this->email->text();
+    QString captchaCode = this->captcha_code->text();
+    QString captchaID = QString::number(this->captchaID);
+    QString version = "HedgewarsFoundation-Hedgewars-" + (cVersionString?(*cVersionString):QString(""));
+
+    if (summary.isEmpty() || description.isEmpty())
+    {
+        ShowErrorMessage(QMessageBox::tr("Please fill out all fields. Email is optional."));
+        return;
+    }
+
+    // Submit issue to PHP script
+    
+    QByteArray body;
+    body.append("captcha=");
+    body.append(captchaID);
+    body.append("&code=");
+    body.append(captchaCode);
+    body.append("&version=");
+    body.append(QUrl::toPercentEncoding(version));
+    body.append("&title=");
+    body.append(QUrl::toPercentEncoding(summary));
+    body.append("&body=");
+    body.append(QUrl::toPercentEncoding(description));
+    body.append("&email=");
+    body.append(QUrl::toPercentEncoding(email));
+    if (CheckSendSpecs->isChecked())
+    {
+        body.append("&specs=");
+        body.append(QUrl::toPercentEncoding(specs));
+    }
+    
+    nam = new QNetworkAccessManager(this);
+    connect(nam, SIGNAL(finished(QNetworkReply*)),
+            this, SLOT(finishedSlot(QNetworkReply*)));
+            
+    QNetworkRequest header(QUrl("http://hedgewars.org/feedback/?submit"));
+    header.setRawHeader("Content-Length", QString::number(body.size()).toAscii());
+    
+    nam->post(header, body);
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/widget/feedbackdialog.h	Mon Jan 14 21:17:42 2013 +0400
@@ -0,0 +1,75 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef FEEDBACKDIALOG_H
+#define FEEDBACKDIALOG_H
+
+#include <QDialog>
+
+class QNetworkReply;
+class QNetworkAccessManager;
+class QCheckBox;
+class QLineEdit;
+class QTextBrowser;
+class QLabel;
+
+class FeedbackDialog : public QDialog
+{
+        Q_OBJECT
+
+    public:
+        FeedbackDialog(QWidget * parent = 0);
+        void EmbedSystemInfo();
+        void LoadCaptchaImage();
+
+        QPushButton * BtnSend;
+        QPushButton * BtnViewInfo;
+        QCheckBox * CheckSendSpecs;
+        QLineEdit * summary;
+        QTextBrowser * description;
+        QLabel * info;
+        QLabel * label_summary;
+        QLabel * label_description;
+        QLabel * label_captcha;
+        QLabel * label_email;
+        QLabel * label_captcha_input;
+        QLineEdit * captcha_code;
+        QLineEdit * email;
+        int captchaID;
+        QString specs;
+
+    private:
+        void GenerateSpecs();
+        QLayout * bodyLayoutDefinition();
+        QLayout * footerLayoutDefinition();
+        QNetworkAccessManager * GetNetManager();
+        void ShowErrorMessage(const QString & msg);
+
+        QNetworkAccessManager * netManager;
+        QNetworkReply * captchaImageRequest;
+        QNetworkReply * genCaptchaRequest;
+        QNetworkAccessManager * nam;
+
+    private slots:
+        virtual void NetReply(QNetworkReply*);
+        virtual void ShowSpecs();
+        void SendFeedback();
+        void finishedSlot(QNetworkReply* reply);
+};
+
+#endif // FEEDBACKDIALOG_H
--- a/QTfrontend/ui/widget/flowlayout.h	Mon Jan 14 13:28:03 2013 +0100
+++ b/QTfrontend/ui/widget/flowlayout.h	Mon Jan 14 21:17:42 2013 +0400
@@ -74,4 +74,4 @@
      int m_vSpace;
  };
 
- #endif
\ No newline at end of file
+ #endif
--- a/QTfrontend/ui/widget/hatprompt.h	Mon Jan 14 13:28:03 2013 +0100
+++ b/QTfrontend/ui/widget/hatprompt.h	Mon Jan 14 21:17:42 2013 +0400
@@ -33,7 +33,7 @@
 	friend class HatPrompt;
 
 	public:
-		HatListView(QWidget* parent = 0) {};
+        HatListView(QWidget* parent = 0) : QListView(parent){}
 };
 
 class HatPrompt : public QDialog
--- a/QTfrontend/ui/widget/lineeditcursor.cpp	Mon Jan 14 13:28:03 2013 +0100
+++ b/QTfrontend/ui/widget/lineeditcursor.cpp	Mon Jan 14 21:17:42 2013 +0400
@@ -32,4 +32,4 @@
 		emit moveRight();
 	else
 		QLineEdit::keyPressEvent(event);
-}
\ No newline at end of file
+}
--- a/QTfrontend/ui/widget/lineeditcursor.h	Mon Jan 14 13:28:03 2013 +0100
+++ b/QTfrontend/ui/widget/lineeditcursor.h	Mon Jan 14 21:17:42 2013 +0400
@@ -26,7 +26,7 @@
         Q_OBJECT
 
     public:
-        LineEditCursor(QWidget* parent = 0) : QLineEdit(parent) {};
+        LineEditCursor(QWidget* parent = 0) : QLineEdit(parent) {}
 
     signals:
         void moveUp();
@@ -38,4 +38,4 @@
         void keyPressEvent(QKeyEvent * event);
 };
 
-#endif // LINEEDITCURSOR_H
\ No newline at end of file
+#endif // LINEEDITCURSOR_H
--- a/QTfrontend/ui/widget/themeprompt.cpp	Mon Jan 14 13:28:03 2013 +0100
+++ b/QTfrontend/ui/widget/themeprompt.cpp	Mon Jan 14 21:17:42 2013 +0400
@@ -26,8 +26,8 @@
 #include <QLabel>
 
 #include "flowlayout.h"
-#include "datamanager.h"
-#include "thememodel.h"
+#include "DataManager.h"
+#include "ThemeModel.h"
 #include "themeprompt.h"
 
 ThemePrompt::ThemePrompt(QWidget* parent) : QDialog(parent)
@@ -97,4 +97,4 @@
 {
 	QWidget * btn = (QWidget*)sender();
 	done(btn->property("themeID").toInt() + 1); // Since returning 0 means canceled
-}
\ No newline at end of file
+}
--- a/QTfrontend/ui_hwform.cpp	Mon Jan 14 13:28:03 2013 +0100
+++ b/QTfrontend/ui_hwform.cpp	Mon Jan 14 21:17:42 2013 +0400
@@ -27,7 +27,6 @@
 #include "pagetraining.h"
 #include "pagenetserver.h"
 #include "pageoptions.h"
-#include "pagefeedback.h"
 #include "pageingame.h"
 #include "pagescheme.h"
 #include "pageroomslist.h"
@@ -140,9 +139,6 @@
     pageDataDownload = new PageDataDownload();
     Pages->addWidget(pageDataDownload);
 
-    pageFeedback = new PageFeedback();
-    Pages->addWidget(pageFeedback);
-
     pageVideos = new PageVideos();
     Pages->addWidget(pageVideos);
 }
--- a/QTfrontend/ui_hwform.h	Mon Jan 14 13:28:03 2013 +0100
+++ b/QTfrontend/ui_hwform.h	Mon Jan 14 21:17:42 2013 +0400
@@ -24,7 +24,6 @@
 class PageMultiplayer;
 class PagePlayDemo;
 class PageOptions;
-class PageFeedback;
 class PageNet;
 class PageNetServer;
 class PageNetChat;
@@ -59,7 +58,6 @@
         PageMultiplayer *pageMultiplayer;
         PagePlayDemo *pagePlayDemo;
         PageOptions *pageOptions;
-        PageFeedback *pageFeedback;
         PageNet *pageNet;
         PageNetServer * pageNetServer;
         PageNetChat *pageNetChat;
--- a/QTfrontend/util/MessageDialog.h	Mon Jan 14 13:28:03 2013 +0100
+++ b/QTfrontend/util/MessageDialog.h	Mon Jan 14 21:17:42 2013 +0400
@@ -32,4 +32,4 @@
 		static int ShowMessage(const QString & msg, const QString & title = QString(), QMessageBox::Icon icon = QMessageBox::NoIcon, QWidget * parent = 0);
 };
 
-#endif
\ No newline at end of file
+#endif
--- a/project_files/hedgewars.pro	Mon Jan 14 13:28:03 2013 +0100
+++ b/project_files/hedgewars.pro	Mon Jan 14 21:17:42 2013 +0400
@@ -109,7 +109,15 @@
     ../QTfrontend/util/LibavInteraction.h \
     ../QTfrontend/util/FileEngine.h \
     ../QTfrontend/ui/dialog/bandialog.h \
-    ../QTfrontend/ui/widget/keybinder.h
+    ../QTfrontend/ui/widget/keybinder.h \
+    ../QTfrontend/ui/widget/seedprompt.h \
+    ../QTfrontend/ui/widget/themeprompt.h \
+    ../QTfrontend/ui/widget/hatbutton.h \
+    ../QTfrontend/util/MessageDialog.h \
+    ../QTfrontend/ui/widget/hatprompt.h \
+    ../QTfrontend/ui/widget/feedbackdialog.h \
+    ../QTfrontend/ui/widget/flowlayout.h \
+    ../QTfrontend/ui/widget/lineeditcursor.h
 
 
 SOURCES += ../QTfrontend/model/ammoSchemeModel.cpp \
@@ -186,7 +194,6 @@
     ../QTfrontend/hwconsts.cpp \
     ../QTfrontend/ui/mouseoverfilter.cpp \
     ../QTfrontend/ui/widget/qpushbuttonwithsound.cpp \
-    ../QTfrontend/ui/page/pagefeedback.cpp \
     ../QTfrontend/model/roomslistmodel.cpp \
     ../QTfrontend/ui/dialog/input_password.cpp \
     ../QTfrontend/ui/widget/colorwidget.cpp \
@@ -203,7 +210,13 @@
     ../QTfrontend/util/LibavInteraction.cpp \
     ../QTfrontend/util/FileEngine.cpp \
     ../QTfrontend/ui/dialog/bandialog.cpp \
-    ../QTfrontend/ui/widget/keybinder.cpp
+    ../QTfrontend/ui/widget/keybinder.cpp \
+    ../QTfrontend/ui/widget/seedprompt.cpp \
+    ../QTfrontend/ui/widget/themeprompt.cpp \
+    ../QTfrontend/util/MessageDialog.cpp \
+    ../QTfrontend/ui/widget/feedbackdialog.cpp \
+    ../QTfrontend/ui/widget/flowlayout.cpp \
+    ../QTfrontend/ui/widget/lineeditcursor.cpp
 
 
 TRANSLATIONS += ../share/hedgewars/Data/Locale/hedgewars_ar.ts \
@@ -269,5 +282,3 @@
         INCLUDEPATH += /usr/local/include/SDL /usr/include/SDL
     }
 }
-
-FORMS +=