Mercurial > hg > easyhg
changeset 113:5fc7b4fc77a8
* Better error handling/reporting; some futile changes to termios handling; avoid weirdly stretching panned view in panner
author | Chris Cannam |
---|---|
date | Fri, 26 Nov 2010 21:04:40 +0000 |
parents | 4bd17f36d059 |
children | bb2d2eecdd60 |
files | hgaction.h hgrunner.cpp hgrunner.h mainwindow.cpp panner.cpp panner.h |
diffstat | 6 files changed, 160 insertions(+), 52 deletions(-) [+] |
line wrap: on
line diff
--- a/hgaction.h Fri Nov 26 17:02:55 2010 +0000 +++ b/hgaction.h Fri Nov 26 21:04:40 2010 +0000 @@ -58,8 +58,7 @@ HGACTIONS action; QString workingDir; QStringList params; - - QString executable; // empty for normal Hg + QString executable; // empty for normal Hg, but gets filled in by hgrunner HgAction() : action(ACT_NONE) { }
--- a/hgrunner.cpp Fri Nov 26 17:02:55 2010 +0000 +++ b/hgrunner.cpp Fri Nov 26 21:04:40 2010 +0000 @@ -34,30 +34,17 @@ #ifndef Q_OS_WIN32 #include <unistd.h> +#include <termios.h> #include <fcntl.h> #endif HgRunner::HgRunner(QWidget * parent): QProgressBar(parent) { - m_proc = new QProcess(this); - - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - env.insert("LANG", "en_US.utf8"); - env.insert("LC_ALL", "en_US.utf8"); - env.insert("HGPLAIN", "1"); - m_proc->setProcessEnvironment(env); + m_proc = 0; setTextVisible(false); setVisible(false); m_isRunning = false; - - connect(m_proc, SIGNAL(started()), this, SLOT(started())); - connect(m_proc, SIGNAL(finished(int, QProcess::ExitStatus)), - this, SLOT(finished(int, QProcess::ExitStatus))); - connect(m_proc, SIGNAL(readyReadStandardOutput()), - this, SLOT(dataReadyStdout())); - connect(m_proc, SIGNAL(readyReadStandardError()), - this, SLOT(dataReadyStderr())); } HgRunner::~HgRunner() @@ -124,7 +111,7 @@ void HgRunner::getUsername() { - if (m_procInput) { + if (m_ptyFile) { bool ok = false; QString prompt = tr("User name:"); if (m_realm != "") { @@ -135,8 +122,8 @@ tr("Enter user name"), prompt, QLineEdit::Normal, QString(), &ok); if (ok) { - m_procInput->write(QString("%1\n").arg(pwd).toUtf8()); - m_procInput->flush(); + m_ptyFile->write(QString("%1\n").arg(pwd).toUtf8()); + m_ptyFile->flush(); return; } else { DEBUG << "HgRunner::getUsername: user cancelled" << endl; @@ -151,7 +138,7 @@ void HgRunner::getPassword() { - if (m_procInput) { + if (m_ptyFile) { bool ok = false; QString prompt = tr("Password:"); if (m_userName != "") { @@ -168,8 +155,8 @@ tr("Enter password"), prompt, QLineEdit::Password, QString(), &ok); if (ok) { - m_procInput->write(QString("%1\n").arg(pwd).toUtf8()); - m_procInput->flush(); + m_ptyFile->write(QString("%1\n").arg(pwd).toUtf8()); + m_ptyFile->flush(); return; } else { DEBUG << "HgRunner::getPassword: user cancelled" << endl; @@ -182,7 +169,7 @@ killCurrentCommand(); } -void HgRunner::checkPrompts(QString chunk) +bool HgRunner::checkPrompts(QString chunk) { //DEBUG << "checkPrompts: " << chunk << endl; @@ -190,11 +177,11 @@ QString lower = text.toLower(); if (lower.endsWith("password:")) { getPassword(); - return; + return true; } if (lower.endsWith("user:")) { getUsername(); - return; + return true; } QRegExp userRe("\\buser:\\s*([^\\s]+)"); if (userRe.indexIn(text) >= 0) { @@ -204,22 +191,36 @@ if (realmRe.indexIn(text) >= 0) { noteRealm(realmRe.cap(1)); } + return false; } void HgRunner::dataReadyStdout() { DEBUG << "dataReadyStdout" << endl; QString chunk = QString::fromUtf8(m_proc->readAllStandardOutput()); - m_stdout += chunk; - checkPrompts(chunk); + if (!checkPrompts(chunk)) { + m_stdout += chunk; + } } void HgRunner::dataReadyStderr() { DEBUG << "dataReadyStderr" << endl; QString chunk = QString::fromUtf8(m_proc->readAllStandardError()); - m_stderr += chunk; - checkPrompts(chunk); + DEBUG << chunk; + if (!checkPrompts(chunk)) { + m_stderr += chunk; + } +} + +void HgRunner::dataReadyPty() +{ + DEBUG << "dataReadyPty" << endl; + QString chunk = QString::fromUtf8(m_ptyFile->readAll()); + DEBUG << "chunk of " << chunk.length() << " chars" << endl; + if (!checkPrompts(chunk)) { + m_stdout += chunk; + } } void HgRunner::finished(int procExitCode, QProcess::ExitStatus procExitStatus) @@ -235,18 +236,23 @@ m_currentAction = HgAction(); closeProcInput(); + delete m_proc; + m_proc = 0; if (completedAction.action == ACT_NONE) { DEBUG << "HgRunner::finished: WARNING: completed action is ACT_NONE" << endl; - } - - if (procExitCode == 0 && procExitStatus == QProcess::NormalExit) { - DEBUG << "HgRunner::finished: Command completed successfully" << endl; - emit commandCompleted(completedAction, m_stdout); } else { - DEBUG << "HgRunner::finished: Command failed, stderr follows" << endl; - DEBUG << m_stderr << endl; - emit commandFailed(completedAction, m_stderr); + if (procExitCode == 0 && procExitStatus == QProcess::NormalExit) { + DEBUG << "HgRunner::finished: Command completed successfully" + << endl; + emit commandCompleted(completedAction, m_stdout); + } else { + DEBUG << "HgRunner::finished: Command failed, exit code " + << procExitCode << ", exit status " << procExitStatus + << ", stderr follows" << endl; + DEBUG << m_stderr << endl; + emit commandFailed(completedAction, m_stderr); + } } checkQueue(); @@ -255,7 +261,8 @@ void HgRunner::killCurrentCommand() { if (m_isRunning) { - m_proc -> kill(); + m_currentAction.action = ACT_NONE; // so that we don't bother to notify + m_proc->kill(); } } @@ -306,6 +313,22 @@ m_realm = ""; m_userName = ""; + m_proc = new QProcess; + + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert("LANG", "en_US.utf8"); + env.insert("LC_ALL", "en_US.utf8"); + env.insert("HGPLAIN", "1"); + m_proc->setProcessEnvironment(env); + + connect(m_proc, SIGNAL(started()), this, SLOT(started())); + connect(m_proc, SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(finished(int, QProcess::ExitStatus))); + connect(m_proc, SIGNAL(readyReadStandardOutput()), + this, SLOT(dataReadyStdout())); + connect(m_proc, SIGNAL(readyReadStandardError()), + this, SLOT(dataReadyStderr())); + if (!action.workingDir.isEmpty()) { m_proc->setWorkingDirectory(action.workingDir); } @@ -313,7 +336,10 @@ if (interactive) { openTerminal(); if (m_ptySlaveFilename != "") { + DEBUG << "HgRunner: connecting to pseudoterminal" << endl; m_proc->setStandardInputFile(m_ptySlaveFilename); + m_proc->setStandardOutputFile(m_ptySlaveFilename); +// m_proc->setStandardErrorFile(m_ptySlaveFilename); } } @@ -324,6 +350,10 @@ m_currentAction = action; + // fill these out with what we actually ran + m_currentAction.executable = executable; + m_currentAction.params = params; + DEBUG << "set current action to " << m_currentAction.action << endl; m_proc->start(executable, params); @@ -347,6 +377,16 @@ perror("openpt failed"); return; } + struct termios t; + if (tcgetattr(master, &t)) { + DEBUG << "tcgetattr failed" << endl; + perror("tcgetattr failed"); + } + cfmakeraw(&t); + if (tcsetattr(master, TCSANOW, &t)) { + DEBUG << "tcsetattr failed" << endl; + perror("tcsetattr failed"); + } if (grantpt(master)) { perror("grantpt failed"); } @@ -360,8 +400,11 @@ return; } m_ptyMasterFd = master; - m_procInput = new QFile(); - m_procInput->open(m_ptyMasterFd, QFile::WriteOnly); + m_ptyFile = new QFile(); + connect(m_ptyFile, SIGNAL(readyRead()), this, SLOT(dataReadyPty())); + if (!m_ptyFile->open(m_ptyMasterFd, QFile::ReadWrite)) { + DEBUG << "HgRunner::openTerminal: Failed to open QFile on master fd" << endl; + } m_ptySlaveFilename = slave; DEBUG << "HgRunner::openTerminal: succeeded, slave is " << m_ptySlaveFilename << endl; @@ -372,8 +415,8 @@ { #ifndef Q_OS_WIN32 if (m_ptySlaveFilename != "") { - delete m_procInput; - m_procInput = 0; + delete m_ptyFile; + m_ptyFile = 0; ::close(m_ptyMasterFd); m_ptySlaveFilename = ""; }
--- a/hgrunner.h Fri Nov 26 17:02:55 2010 +0000 +++ b/hgrunner.h Fri Nov 26 21:04:40 2010 +0000 @@ -52,6 +52,7 @@ void finished(int procExitCode, QProcess::ExitStatus procExitStatus); void dataReadyStdout(); void dataReadyStderr(); + void dataReadyPty(); private: void checkQueue(); @@ -64,7 +65,7 @@ void noteRealm(QString); void getUsername(); void getPassword(); - void checkPrompts(QString); + bool checkPrompts(QString); void openTerminal(); void closeTerminal(); @@ -72,7 +73,7 @@ int m_ptyMasterFd; int m_ptySlaveFd; QString m_ptySlaveFilename; - QFile *m_procInput; + QFile *m_ptyFile; bool m_isRunning; QProcess *m_proc;
--- a/mainwindow.cpp Fri Nov 26 17:02:55 2010 +0000 +++ b/mainwindow.cpp Fri Nov 26 21:04:40 2010 +0000 @@ -540,6 +540,7 @@ params << "clone" << remoteRepoPath << workFolderPath; hgTabs->setWorkFolderAndRepoNames(workFolderPath, remoteRepoPath); + hgTabs->updateWorkFolderFileList(""); runner->requestAction(HgAction(ACT_CLONEFROMREMOTE, workFolderPath, params)); } @@ -1010,9 +1011,46 @@ void MainWindow::commandFailed(HgAction action, QString stderr) { DEBUG << "MainWindow::commandFailed" << endl; -// runner -> hideProgBar(); - //!!! N.B hg incoming returns 1 even if successful, if there were no changes + // Some commands we just have to ignore bad return values from: + + switch(action.action) { + case ACT_NONE: + // uh huh + return; + case ACT_INCOMING: + // returns non-zero code if the check was successful but there + // are no changes pending + return; + case ACT_FOLDERDIFF: + case ACT_FILEDIFF: + case ACT_CHGSETDIFF: + // external program, unlikely to be anything useful in stderr + // and some return with failure codes when something as basic + // as the user closing the window via the wm happens + return; + + default: + } + + QString command = action.executable; + if (command == "") command = "hg"; + foreach (QString arg, action.params) { + command += " " + arg; + } + + QString message = tr("<qt><h3>Command failed</h3>" + "<p>The following command failed:</p>" + "<code>%1</code>" + "%2</qt>") + .arg(command) + .arg((stderr.trimmed() != "") ? + tr("<p>Its output said:</p><code>%1</code>") + .arg(xmlEncode(stderr.left(800)) + .replace("\n", "<br>")) + : ""); + + QMessageBox::warning(this, tr("Command failed"), message); } void MainWindow::commandCompleted(HgAction completedAction, QString output) @@ -1048,7 +1086,7 @@ break; case ACT_STAT: - hgTabs -> updateWorkFolderFileList(output); + hgTabs->updateWorkFolderFileList(output); updateFileSystemWatcher(); break;
--- a/panner.cpp Fri Nov 26 17:02:55 2010 +0000 +++ b/panner.cpp Fri Nov 26 21:04:40 2010 +0000 @@ -17,6 +17,7 @@ #include "panner.h" #include "panned.h" +#include "debug.h" #include <QPolygon> #include <QMouseEvent> @@ -42,6 +43,26 @@ } void +Panner::fit(QRectF r) +{ + Qt::AspectRatioMode m = Qt::IgnoreAspectRatio; + if (height() > width()) { + // Our panner is vertical; if the source is not tall, + // don't stretch it to fit in the height, it'd look weird + if (r.height() < height() * 2) { + m = Qt::KeepAspectRatio; + } + } else { + // Similarly, but horizontal + if (r.width() < width() * 2) { + m = Qt::KeepAspectRatio; + } + } + DEBUG << "Panner: fit mode " << m << endl; + fitInView(r, m); +} + +void Panner::setScene(QGraphicsScene *s) { if (scene()) { @@ -49,7 +70,11 @@ this, SLOT(slotSceneRectChanged(const QRectF &))); } QGraphicsView::setScene(s); - if (scene()) fitInView(sceneRect(), Qt::IgnoreAspectRatio); + if (scene()) { + QRectF r = sceneRect(); + DEBUG << "scene rect: " << r << ", my rect " << rect() << endl; + fit(r); + } m_cache = QPixmap(); connect(scene(), SIGNAL(sceneRectChanged(const QRectF &)), this, SLOT(slotSceneRectChanged(const QRectF &))); @@ -81,7 +106,7 @@ void Panner::resizeEvent(QResizeEvent *) { - if (scene()) fitInView(sceneRect(), Qt::IgnoreAspectRatio); + if (scene()) fit(sceneRect()); m_cache = QPixmap(); } @@ -89,7 +114,7 @@ Panner::slotSceneRectChanged(const QRectF &newRect) { if (!scene()) return; // spurious - fitInView(newRect, Qt::IgnoreAspectRatio); + fit(newRect); m_cache = QPixmap(); viewport()->update(); } @@ -137,7 +162,7 @@ { if (m_cache.size() != viewport()->size()) { - std::cerr << "Panner: recreating cache" << std::endl; + DEBUG << "Panner: recreating cache" << endl; QGraphicsScene *s = scene(); if (!s) return;