QTfrontend/ui/widget/feedbackdialog.cpp
changeset 8385 9e8924ff9813
child 8386 2aaa2995a32e
--- /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