1 // -*- c++ -*-
2 // $Id: processwait.cpp,v 1.3 2009-08-28 17:08:55 robertl Exp $
3 //------------------------------------------------------------------------
4 //
5 //  Copyright (C) 2009  S. Khai Mong <khai@mangrai.com>.
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License as
9 //  published by the Free Software Foundation; either version 2 of the
10 //  License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 //  General Public License for more details.
16 //
17 //  You should have received a copy of the GNU General Public License
18 //  along with this program; if not, write to the Free Software
19 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
20 //  USA.
21 //
22 //------------------------------------------------------------------------
23 #include <QProcess>
24 #include <QStringList>
25 #include <QPlainTextEdit>
26 #include <QDialogButtonBox>
27 #include <QVBoxLayout>
28 #include <QDialog>
29 #include <QProgressBar>
30 #include <QPushButton>
31 #include <QTimer>
32 #include "processwait.h"
33 #include "appname.h"
34 
35 
36 //------------------------------------------------------------------------
37 
processErrorString(QProcess::ProcessError err)38 QString ProcessWaitDialog::processErrorString(QProcess::ProcessError err)
39 {
40   switch (err) {
41   case QProcess::FailedToStart:
42     return QString(tr("Process failed to start"));
43     break;
44   case QProcess::Crashed:
45     return QString(tr("Process crashed"));
46     break;
47   case QProcess::Timedout:
48     return QString(tr("Process timedout"));
49     break;
50   case QProcess::WriteError:
51     return QString(tr("Error while trying to write to process"));
52     break;
53   case QProcess::ReadError:
54     return QString(tr("Error while trying to read from process"));
55     break;
56   case QProcess::UnknownError:
57   default:
58     return QString(tr("Unknown process error"));
59   }
60   return QString("");
61 }
62 //------------------------------------------------------------------------
ProcessWaitDialog(QWidget * parent,QProcess * process)63 ProcessWaitDialog::ProcessWaitDialog(QWidget* parent, QProcess* process):
64   QDialog(parent), process_(process)
65 {
66   this->resize(400, 220);
67   this->setWindowTitle(QString(appName) + tr(" ... Process GPSBabel"));
68   auto* layout = new QVBoxLayout(this);
69 
70   textEdit_ = new QPlainTextEdit(this);
71   textEdit_->setReadOnly(true);
72   layout->addWidget(textEdit_);
73 
74   progressBar_ = new QProgressBar(this);
75   progressBar_->setTextVisible(false);
76   layout->addWidget(progressBar_);
77 
78   buttonBox_ = new QDialogButtonBox(this);
79   buttonBox_->setOrientation(Qt::Horizontal);
80   buttonBox_->setStandardButtons(QDialogButtonBox::Abort);
81   QPushButton* btn = buttonBox_->button(QDialogButtonBox::Abort);
82   btn->setText(tr("Stop Process"));
83   layout->addWidget(buttonBox_);
84 
85   connect(process, SIGNAL(error(QProcess::ProcessError)),
86           this,    SLOT(errorX(QProcess::ProcessError)));
87   connect(process, SIGNAL(finished(int,QProcess::ExitStatus)),
88           this,    SLOT(finishedX(int,QProcess::ExitStatus)));
89   connect(process, SIGNAL(readyReadStandardError()),
90           this,    SLOT(readyReadStandardErrorX()));
91   connect(process, SIGNAL(readyReadStandardOutput()),
92           this,    SLOT(readyReadStandardOutputX()));
93   connect(btn,     SIGNAL(clicked()),
94           this,    SLOT(stopClickedX()));
95   exitStatus_ = QProcess::CrashExit;  // Assume all errors are crashes for now.
96 
97   bufferedOut_ = "";
98 
99   //
100   for (int i=0; i<=100; i+=2) {
101     progressVals_.push_back(i);
102   }
103   for (int i=98; i>0; i-=2) {
104     progressVals_.push_back(i);
105   }
106   progressIndex_ = progressVals_.size()/2;
107 
108   timer_ = new QTimer(this);
109   timer_->setInterval(100);
110   timer_->setSingleShot(false);
111   connect(timer_, SIGNAL(timeout()), this, SLOT(timeoutX()));
112   stopCount_ = -1;
113   ecode_ = 0;
114   timer_->start();
115   errorString_ = "";
116 
117 }
118 
119 //------------------------------------------------------------------------
120 ProcessWaitDialog::~ProcessWaitDialog()
121 = default;
122 //------------------------------------------------------------------------
getExitedNormally()123 bool ProcessWaitDialog::getExitedNormally()
124 {
125   return (errorString_.length() == 0);
126 }
127 
128 //------------------------------------------------------------------------
getErrorString()129 QString ProcessWaitDialog::getErrorString()
130 {
131   return errorString_;
132 }
133 
134 //------------------------------------------------------------------------
getExitCode()135 int ProcessWaitDialog::getExitCode()
136 {
137   return ecode_;
138 }
139 
140 //------------------------------------------------------------------------
stopClickedX()141 void ProcessWaitDialog::stopClickedX()
142 {
143   process_->terminate();
144 }
145 
146 //------------------------------------------------------------------------
timeoutX()147 void ProcessWaitDialog::timeoutX()
148 {
149   progressIndex_++;
150   int idx = progressIndex_ % progressVals_.size();
151   progressBar_->setValue(progressVals_[idx]);
152   if (stopCount_ >=0) {
153     stopCount_++;
154   }
155   if (stopCount_ > 150) {
156     process_->kill();
157     errorString_ = QString(tr("Process did not terminate successfully"));
158     timer_->stop();
159     accept();
160   }
161 }
162 
163 //------------------------------------------------------------------------
errorX(QProcess::ProcessError err)164 void ProcessWaitDialog::errorX(QProcess::ProcessError err)
165 {
166   errorString_ = processErrorString(err);
167   timer_->stop();
168   accept();
169 }
170 
171 //------------------------------------------------------------------------
finishedX(int exitCode,QProcess::ExitStatus es)172 void ProcessWaitDialog::finishedX(int exitCode, QProcess::ExitStatus es)
173 {
174   ecode_ = exitCode;
175   if (es == QProcess::CrashExit) {
176     errorString_ = QString(tr("Process crashed while running"));
177   }
178   timer_->stop();
179   accept();
180 }
181 
182 
183 //------------------------------------------------------------------------
184 // appendPlainText automatically puts in a new line with every call.  That's
185 // why you have to buffer it, and only append when we get a real newline.
186 //
appendToText(const char * ptr)187 void ProcessWaitDialog::appendToText(const char* ptr)
188 {
189   outputString_ += QString(ptr);
190   for (const char* cptr = ptr; *cptr != 0; cptr++) {
191     if (*cptr == '\r') {
192       continue;
193     }
194     if (*cptr == '\n') {
195       textEdit_->appendPlainText(QString::fromStdString(bufferedOut_));
196       bufferedOut_ = "";
197       continue;
198     }
199     bufferedOut_ += *cptr;
200   }
201 }
202 
203 
204 //------------------------------------------------------------------------
readyReadStandardErrorX()205 void ProcessWaitDialog::readyReadStandardErrorX()
206 {
207   QByteArray d = process_->readAllStandardError();
208   appendToText(d.data());
209 }
210 
211 //------------------------------------------------------------------------
readyReadStandardOutputX()212 void ProcessWaitDialog::readyReadStandardOutputX()
213 {
214   QByteArray d = process_->readAllStandardOutput();
215   appendToText(d.data());
216 }
217 
closeEvent(QCloseEvent * event)218 void ProcessWaitDialog::closeEvent(QCloseEvent* event)
219 {
220   event->ignore();
221 }
222