QTfrontend/ui/widget/feedbackdialog.cpp
changeset 8385 9e8924ff9813
child 8386 2aaa2995a32e
equal deleted inserted replaced
8384:a6e7a95f3b2e 8385:9e8924ff9813
       
     1 /*
       
     2  * Hedgewars, a free turn based strategy game
       
     3  * Copyright (c) 2004-2012 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
       
    17  */
       
    18 
       
    19 #include <QHBoxLayout>
       
    20 #include <QLineEdit>
       
    21 #include <QTextBrowser>
       
    22 #include <QLabel>
       
    23 #include <QHttp>
       
    24 #include <QSysInfo>
       
    25 #include <QDebug>
       
    26 #include <QBuffer>
       
    27 #include <QApplication>
       
    28 #include <QDesktopWidget>
       
    29 #include <QNetworkReply>
       
    30 #include <QProcess>
       
    31 #include <QMessageBox>
       
    32 #include <QCheckBox>
       
    33 
       
    34 #include <string>
       
    35 
       
    36 #ifdef Q_WS_WIN
       
    37 #define WINVER 0x0500
       
    38 #include <windows.h>
       
    39 #else
       
    40 #include <unistd.h>
       
    41 #include <sys/types.h>
       
    42 #endif
       
    43 
       
    44 #ifdef Q_WS_MAC
       
    45 #include <sys/sysctl.h>
       
    46 #endif
       
    47 
       
    48 #include <stdint.h>
       
    49 
       
    50 #include "AbstractPage.h"
       
    51 #include "hwconsts.h"
       
    52 #include "feedbackdialog.h"
       
    53 
       
    54 FeedbackDialog::FeedbackDialog(QWidget * parent) : QDialog(parent)
       
    55 {
       
    56 	setModal(true);
       
    57 	setWindowFlags(Qt::Sheet);
       
    58 	setWindowModality(Qt::WindowModal);
       
    59 	setMinimumSize(700, 460);
       
    60 	resize(700, 460);
       
    61 	setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
       
    62 
       
    63 	netManager = NULL;
       
    64 	GenerateSpecs();
       
    65 
       
    66 	/* Top layout */
       
    67 
       
    68 	QVBoxLayout * pageLayout = new QVBoxLayout();
       
    69     QHBoxLayout * summaryLayout = new QHBoxLayout();
       
    70     QHBoxLayout * emailLayout = new QHBoxLayout();
       
    71     QHBoxLayout * descriptionLayout = new QHBoxLayout();
       
    72     QHBoxLayout * combinedTopLayout = new QHBoxLayout();
       
    73     QHBoxLayout * systemLayout = new QHBoxLayout();
       
    74 
       
    75     info = new QLabel();
       
    76     info->setText(
       
    77         "<style type=\"text/css\">"
       
    78         "a { color: #fc0; }"
       
    79         "b { color: #0df; }"
       
    80         "</style>"
       
    81         "<div align=\"center\"><h1>Please give us feedback!</h1>"
       
    82         "<h3>We are always happy about suggestions, ideas, or bug reports.<h3>"
       
    83         "<h4>Your email address is optional, but we may want to contact you.<h4>"
       
    84         "</div>"
       
    85     );
       
    86     pageLayout->addWidget(info);
       
    87 
       
    88     QVBoxLayout * summaryEmailLayout = new QVBoxLayout();
       
    89 
       
    90     const int labelWidth = 90;
       
    91 
       
    92     label_email = new QLabel();
       
    93     label_email->setText(QLabel::tr("Your Email"));
       
    94     label_email->setFixedWidth(labelWidth);
       
    95     emailLayout->addWidget(label_email);
       
    96     email = new QLineEdit();
       
    97     emailLayout->addWidget(email);
       
    98     summaryEmailLayout->addLayout(emailLayout);
       
    99     
       
   100     label_summary = new QLabel();
       
   101     label_summary->setText(QLabel::tr("Summary"));
       
   102     label_summary->setFixedWidth(labelWidth);
       
   103     summaryLayout->addWidget(label_summary);
       
   104     summary = new QLineEdit();
       
   105     summaryLayout->addWidget(summary);
       
   106     summaryEmailLayout->addLayout(summaryLayout);
       
   107     
       
   108     combinedTopLayout->addLayout(summaryEmailLayout);
       
   109 
       
   110     CheckSendSpecs = new QCheckBox();
       
   111     CheckSendSpecs->setText(QLabel::tr("Send system information"));
       
   112     CheckSendSpecs->setChecked(true);
       
   113     systemLayout->addWidget(CheckSendSpecs);
       
   114     BtnViewInfo = new QPushButton(tr("View"));
       
   115     systemLayout->addWidget(BtnViewInfo, 1);
       
   116     BtnViewInfo->setFixedSize(60, 30);
       
   117     connect(BtnViewInfo, SIGNAL(clicked()), this, SLOT(ShowSpecs()));
       
   118     combinedTopLayout->addLayout(systemLayout);
       
   119 
       
   120     combinedTopLayout->setStretch(0, 1);
       
   121     combinedTopLayout->insertSpacing(1, 20);
       
   122 
       
   123     pageLayout->addLayout(combinedTopLayout);
       
   124 
       
   125     label_description = new QLabel();
       
   126     label_description->setText(QLabel::tr("Description"));
       
   127     label_description->setFixedWidth(labelWidth);
       
   128     descriptionLayout->addWidget(label_description, 0, Qt::AlignTop);
       
   129     description = new QTextBrowser();
       
   130     description->setReadOnly(false);
       
   131     descriptionLayout->addWidget(description);
       
   132     pageLayout->addLayout(descriptionLayout);
       
   133 
       
   134     /* Bottom layout */
       
   135 
       
   136     QHBoxLayout * bottomLayout = new QHBoxLayout();
       
   137     QHBoxLayout * captchaLayout = new QHBoxLayout();
       
   138     QVBoxLayout * captchaInputLayout = new QVBoxLayout();
       
   139 
       
   140     QPushButton * BtnCancel = new QPushButton(tr("Cancel"));
       
   141     bottomLayout->addWidget(BtnCancel, 0);
       
   142     BtnCancel->setFixedSize(100, 40);
       
   143     connect(BtnCancel, SIGNAL(clicked()), this, SLOT(reject()));
       
   144 
       
   145     bottomLayout->insertStretch(1);
       
   146 
       
   147     label_captcha = new QLabel();
       
   148     label_captcha->setStyleSheet("border: 3px solid #ffcc00; border-radius: 4px");
       
   149     label_captcha->setText("loading<br>captcha");
       
   150     label_captcha->setFixedSize(200, 50);
       
   151     captchaLayout->addWidget(label_captcha);
       
   152 
       
   153     label_captcha_input = new QLabel();
       
   154     label_captcha_input->setText(QLabel::tr("Type the security code:"));
       
   155     captchaInputLayout->addWidget(label_captcha_input);
       
   156     captchaInputLayout->setAlignment(label_captcha, Qt::AlignBottom);
       
   157     captcha_code = new QLineEdit();
       
   158     captcha_code->setFixedSize(165, 30);
       
   159     captchaInputLayout->addWidget(captcha_code);
       
   160     captchaInputLayout->setAlignment(captcha_code, Qt::AlignTop);
       
   161     captchaLayout->addLayout(captchaInputLayout);
       
   162     captchaLayout->setAlignment(captchaInputLayout, Qt::AlignLeft);
       
   163 
       
   164     bottomLayout->addLayout(captchaLayout);
       
   165     bottomLayout->addSpacing(40);
       
   166     
       
   167     // TODO: Set green arrow icon for send button (:/res/Start.png)
       
   168     BtnSend = new QPushButton(tr("Send Feedback"));
       
   169     bottomLayout->addWidget(BtnSend, 0);
       
   170     BtnSend->setFixedSize(120, 40);
       
   171     connect(BtnSend, SIGNAL(clicked()), this, SLOT(SendFeedback()));
       
   172 
       
   173     bottomLayout->setStretchFactor(captchaLayout, 0);
       
   174     bottomLayout->setStretchFactor(BtnSend, 1);
       
   175 
       
   176     QVBoxLayout * dialogLayout = new QVBoxLayout(this);
       
   177     dialogLayout->addLayout(pageLayout, 1);
       
   178     dialogLayout->addLayout(bottomLayout);
       
   179 
       
   180     LoadCaptchaImage();
       
   181 }
       
   182 
       
   183 void FeedbackDialog::GenerateSpecs()
       
   184 {
       
   185     // Gather some information about the system and embed it into the report
       
   186     QDesktopWidget* screen = QApplication::desktop();
       
   187     QString os_version = "Operating system: ";
       
   188     QString qt_version = QString("Qt version: ") + QT_VERSION_STR + QString("\n");
       
   189     QString total_ram = "Total RAM: ";
       
   190     QString number_of_cores = "Number of cores: ";
       
   191     QString compiler_bits = "Compiler architecture: ";
       
   192     QString compiler_version = "Compiler version: ";
       
   193     QString kernel_line = "Kernel: ";
       
   194     QString screen_size = "Size of the screen(s): " +
       
   195         QString::number(screen->width()) + "x" + QString::number(screen->height()) + "\n";
       
   196     QString number_of_screens = "Number of screens: " + QString::number(screen->screenCount()) + "\n";
       
   197     std::string processor_name = "Processor: ";
       
   198 
       
   199     // platform specific code
       
   200 #ifdef Q_WS_MACX
       
   201     number_of_cores += QString::number(sysconf(_SC_NPROCESSORS_ONLN)) + "\n";
       
   202 
       
   203     uint64_t memsize;
       
   204     size_t len = sizeof(memsize);
       
   205     static int mib_s[2] = { CTL_HW, HW_MEMSIZE };
       
   206     if (sysctl (mib_s, 2, &memsize, &len, NULL, 0) == 0)
       
   207         total_ram += QString::number(memsize/1024/1024) + " MB\n";
       
   208     else
       
   209         total_ram += "Error getting total RAM information\n";
       
   210 
       
   211     int mib[] = {CTL_KERN, KERN_OSRELEASE};
       
   212     sysctl(mib, sizeof mib / sizeof(int), NULL, &len, NULL, 0);
       
   213 
       
   214     char *kernelVersion = (char *)malloc(sizeof(char)*len);
       
   215     sysctl(mib, sizeof mib / sizeof(int), kernelVersion, &len, NULL, 0);
       
   216 
       
   217     QString kernelVersionStr = QString(kernelVersion);
       
   218     free(kernelVersion);
       
   219     int major_version = kernelVersionStr.split(".").first().toUInt() - 4;
       
   220     int minor_version = kernelVersionStr.split(".").at(1).toUInt();
       
   221     os_version += QString("Mac OS X 10.%1.%2").arg(major_version).arg(minor_version) + " ";
       
   222 
       
   223     switch(major_version)
       
   224     {
       
   225         case 4:  os_version += "\"Tiger\"\n"; break;
       
   226         case 5:  os_version += "\"Leopard\"\n"; break;
       
   227         case 6:  os_version += "\"Snow Leopard\"\n"; break;
       
   228         case 7:  os_version += "\"Lion\"\n"; break;
       
   229         case 8:  os_version += "\"Mountain Lion\"\n"; break;
       
   230         default: os_version += "\"Unknown version\"\n"; break;
       
   231     }
       
   232 #endif
       
   233 #ifdef Q_WS_WIN
       
   234     SYSTEM_INFO sysinfo;
       
   235     GetSystemInfo(&sysinfo);
       
   236     number_of_cores += QString::number(sysinfo.dwNumberOfProcessors) + "\n";
       
   237     MEMORYSTATUSEX status;
       
   238     status.dwLength = sizeof(status);
       
   239     GlobalMemoryStatusEx(&status);
       
   240     total_ram += QString::number(status.ullTotalPhys);
       
   241 
       
   242     switch(QSysInfo::WinVersion())
       
   243     {
       
   244         case QSysInfo::WV_2000: os_version += "Windows 2000\n"; break;
       
   245         case QSysInfo::WV_XP: os_version += "Windows XP\n"; break;
       
   246         case QSysInfo::WV_VISTA: os_version += "Windows Vista\n"; break;
       
   247         case QSysInfo::WV_WINDOWS7: os_version += "Windows 7\n"; break;
       
   248         default: os_version += "Windows (Unknown version)\n"; break;
       
   249     }
       
   250     kernel_line += "Windows kernel\n";
       
   251 #endif
       
   252 #ifdef Q_WS_X11
       
   253     number_of_cores += QString::number(sysconf(_SC_NPROCESSORS_ONLN)) + "\n";
       
   254     long pages = sysconf(_SC_PHYS_PAGES),
       
   255 #ifndef Q_OS_FREEBSD
       
   256          available_pages = sysconf(_SC_AVPHYS_PAGES),
       
   257 #else
       
   258          available_pages = 0,
       
   259 #endif
       
   260          page_size = sysconf(_SC_PAGE_SIZE);
       
   261     total_ram += QString::number(pages * page_size) + "\n";
       
   262     os_version += "GNU/Linux or BSD\n";
       
   263 #endif
       
   264 
       
   265     // uname -a
       
   266 #if defined(Q_WS_X11) || defined(Q_WS_MACX)
       
   267     QProcess *process = new QProcess();
       
   268     QStringList arguments = QStringList("-a");
       
   269     process->start("uname", arguments);
       
   270     if (process->waitForFinished())
       
   271         kernel_line += QString(process->readAll());
       
   272     delete process;
       
   273 #endif
       
   274 
       
   275     // cpu info
       
   276     quint32 registers[4];
       
   277     quint32 i;
       
   278 
       
   279     i = 0x80000002;
       
   280     asm volatile
       
   281       ("cpuid" : "=a" (registers[0]), "=b" (registers[1]), "=c" (registers[2]), "=d" (registers[3])
       
   282        : "a" (i), "c" (0));
       
   283     processor_name += std::string((const char *)&registers[0], 4);
       
   284     processor_name += std::string((const char *)&registers[1], 4);
       
   285     processor_name += std::string((const char *)&registers[2], 4);
       
   286     processor_name += std::string((const char *)&registers[3], 4);
       
   287     i = 0x80000003;
       
   288     asm volatile
       
   289       ("cpuid" : "=a" (registers[0]), "=b" (registers[1]), "=c" (registers[2]), "=d" (registers[3])
       
   290        : "a" (i), "c" (0));
       
   291     processor_name += std::string((const char *)&registers[0], 4);
       
   292     processor_name += std::string((const char *)&registers[1], 4);
       
   293     processor_name += std::string((const char *)&registers[2], 4);
       
   294     processor_name += std::string((const char *)&registers[3], 4);
       
   295     i = 0x80000004;
       
   296     asm volatile
       
   297       ("cpuid" : "=a" (registers[0]), "=b" (registers[1]), "=c" (registers[2]), "=d" (registers[3])
       
   298        : "a" (i), "c" (0));
       
   299     processor_name += std::string((const char *)&registers[0], 4);
       
   300     processor_name += std::string((const char *)&registers[1], 4);
       
   301     processor_name += std::string((const char *)&registers[2], 4);
       
   302     processor_name += std::string((const char *)&registers[3], 3);
       
   303 
       
   304     // compiler
       
   305 #ifdef __GNUC__
       
   306     compiler_version += "GCC " + QString(__VERSION__) + "\n";
       
   307 #else
       
   308     compiler_version += "Unknown\n";
       
   309 #endif
       
   310 
       
   311     if(sizeof(void*) == 4)
       
   312         compiler_bits += "i386\n";
       
   313     else if(sizeof(void*) == 8)
       
   314         compiler_bits += "x86_64\n";
       
   315 
       
   316     // concat system info
       
   317     specs = qt_version
       
   318         + os_version
       
   319         + total_ram
       
   320         + screen_size
       
   321         + number_of_screens
       
   322         + QString::fromStdString(processor_name + "\n")
       
   323         + number_of_cores
       
   324         + compiler_version
       
   325         + compiler_bits
       
   326         + kernel_line;
       
   327 }
       
   328 
       
   329 void FeedbackDialog::ShowErrorMessage(const QString & msg)
       
   330 {
       
   331     QMessageBox msgMsg(this);
       
   332     msgMsg.setIcon(QMessageBox::Warning);
       
   333     msgMsg.setWindowTitle(QMessageBox::tr("Hedgewars - Error"));
       
   334     msgMsg.setText(msg);
       
   335     msgMsg.setWindowModality(Qt::WindowModal);
       
   336     msgMsg.exec();
       
   337 }
       
   338 
       
   339 void FeedbackDialog::ShowSpecs()
       
   340 {
       
   341     QMessageBox msgMsg(this);
       
   342     msgMsg.setIcon(QMessageBox::Information);
       
   343     msgMsg.setWindowTitle(QMessageBox::tr("System Information Preview"));
       
   344     msgMsg.setText(specs);
       
   345     msgMsg.setTextFormat(Qt::PlainText);
       
   346     msgMsg.setWindowModality(Qt::WindowModal);
       
   347     msgMsg.setStyleSheet("background: #0A0533;");
       
   348     msgMsg.exec();
       
   349 }
       
   350 
       
   351 void FeedbackDialog::NetReply(QNetworkReply *reply)
       
   352 {
       
   353     if (reply == genCaptchaRequest)
       
   354     {
       
   355         if (reply->error() != QNetworkReply::NoError)
       
   356         {
       
   357             qDebug() << "Error generating captcha image: " << reply->errorString();
       
   358             ShowErrorMessage(QMessageBox::tr("Failed to generate captcha"));
       
   359             return;
       
   360         }
       
   361 
       
   362         bool okay;
       
   363         QByteArray body = reply->readAll();
       
   364         captchaID = QString(body).toInt(&okay);
       
   365 
       
   366         if (!okay)
       
   367         {
       
   368             qDebug() << "Failed to get captcha ID: " << body;
       
   369             ShowErrorMessage(QMessageBox::tr("Failed to generate captcha"));
       
   370             return;
       
   371         }
       
   372 
       
   373         QString url = "http://hedgewars.org/feedback/?captcha&id=";
       
   374         url += QString::number(captchaID);
       
   375         
       
   376         QNetworkAccessManager *netManager = GetNetManager();
       
   377         QUrl captchaURL(url);
       
   378         QNetworkRequest req(captchaURL);
       
   379         captchaImageRequest = netManager->get(req);
       
   380     }
       
   381     else if (reply == captchaImageRequest)
       
   382     {
       
   383         if (reply->error() != QNetworkReply::NoError)
       
   384         {
       
   385             qDebug() << "Error loading captcha image: " << reply->errorString();
       
   386             ShowErrorMessage(QMessageBox::tr("Failed to download captcha"));
       
   387             return;
       
   388         }
       
   389 
       
   390         QByteArray imageData = reply->readAll();
       
   391         QPixmap pixmap;
       
   392         pixmap.loadFromData(imageData);
       
   393         label_captcha->setPixmap(pixmap);
       
   394         captcha_code->setText("");
       
   395     }
       
   396 }
       
   397 
       
   398 QNetworkAccessManager * FeedbackDialog::GetNetManager()
       
   399 {
       
   400     if (netManager) return netManager;
       
   401     netManager = new QNetworkAccessManager(this);
       
   402     connect(netManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(NetReply(QNetworkReply*)));
       
   403     return netManager;
       
   404 }
       
   405 
       
   406 void FeedbackDialog::LoadCaptchaImage()
       
   407 {
       
   408         QNetworkAccessManager *netManager = GetNetManager();
       
   409         QUrl captchaURL("http://hedgewars.org/feedback/?gencaptcha");
       
   410         QNetworkRequest req(captchaURL);
       
   411         genCaptchaRequest = netManager->get(req);
       
   412 }
       
   413 
       
   414 void FeedbackDialog::finishedSlot(QNetworkReply* reply)
       
   415 {
       
   416     if (reply && reply->error() == QNetworkReply::NoError)
       
   417     {
       
   418             QMessageBox infoMsg(this);
       
   419             infoMsg.setIcon(QMessageBox::Information);
       
   420             infoMsg.setWindowTitle(QMessageBox::tr("Hedgewars - Success"));
       
   421             infoMsg.setText(reply->readAll());
       
   422             infoMsg.setWindowModality(Qt::WindowModal);
       
   423             infoMsg.exec();
       
   424 
       
   425             accept();
       
   426             
       
   427             return;
       
   428     }
       
   429     else
       
   430     {
       
   431         ShowErrorMessage(QString("Error: ") + reply->readAll());
       
   432         LoadCaptchaImage();
       
   433     }
       
   434 }
       
   435 
       
   436 void FeedbackDialog::SendFeedback()
       
   437 {
       
   438     // Get form data
       
   439     
       
   440     QString summary = this->summary->text();
       
   441     QString description = this->description->toPlainText();
       
   442     QString email = this->email->text();
       
   443     QString captchaCode = this->captcha_code->text();
       
   444     QString captchaID = QString::number(this->captchaID);
       
   445     QString version = "HedgewarsFoundation-Hedgewars-" + (cVersionString?(*cVersionString):QString(""));
       
   446 
       
   447     if (summary.isEmpty() || description.isEmpty())
       
   448     {
       
   449         ShowErrorMessage(QMessageBox::tr("Please fill out all fields. Email is optional."));
       
   450         return;
       
   451     }
       
   452 
       
   453     // Submit issue to PHP script
       
   454     
       
   455     QByteArray body;
       
   456     body.append("captcha=");
       
   457     body.append(captchaID);
       
   458     body.append("&code=");
       
   459     body.append(captchaCode);
       
   460     body.append("&version=");
       
   461     body.append(QUrl::toPercentEncoding(version));
       
   462     body.append("&title=");
       
   463     body.append(QUrl::toPercentEncoding(summary));
       
   464     body.append("&body=");
       
   465     body.append(QUrl::toPercentEncoding(description));
       
   466     body.append("&email=");
       
   467     body.append(QUrl::toPercentEncoding(email));
       
   468     if (CheckSendSpecs->isChecked())
       
   469     {
       
   470         body.append("&specs=");
       
   471         body.append(QUrl::toPercentEncoding(specs));
       
   472     }
       
   473     
       
   474     nam = new QNetworkAccessManager(this);
       
   475     connect(nam, SIGNAL(finished(QNetworkReply*)),
       
   476             this, SLOT(finishedSlot(QNetworkReply*)));
       
   477             
       
   478     QNetworkRequest header(QUrl("http://hedgewars.org/feedback/?submit"));
       
   479     header.setRawHeader("Content-Length", QString::number(body.size()).toAscii());
       
   480     
       
   481     nam->post(header, body);
       
   482 }