# HG changeset patch # User Chris Cannam # Date 1290446879 0 # Node ID 8a4e26dc31825ff5afb1a2a4eafd3e69510eb2fc # Parent af7cf6f7282cfd1d1d6190e30bd1fb1acd4e6cef * Make all of the "Open" options do something sensible diff -r af7cf6f7282c -r 8a4e26dc3182 common.cpp --- a/common.cpp Mon Nov 22 16:02:13 2010 +0000 +++ b/common.cpp Mon Nov 22 17:27:59 2010 +0000 @@ -184,19 +184,24 @@ FolderStatus getFolderStatus(QString path) { + DEBUG << "getFolderStatus: " << path << endl; QFileInfo fi(path); if (fi.exists()) { + DEBUG << "exists" << endl; QDir dir(path); if (!dir.exists()) { // returns false for files + DEBUG << "not directory" << endl; return FolderIsFile; } if (QDir(dir.filePath(".hg")).exists()) { + DEBUG << "has repo" << endl; return FolderHasRepo; } return FolderExists; } else { QDir parent = fi.dir(); if (parent.exists()) { + DEBUG << "parent exists" << endl; return FolderParentExists; } return FolderUnknown; diff -r af7cf6f7282c -r 8a4e26dc3182 hgrunner.cpp --- a/hgrunner.cpp Mon Nov 22 16:02:13 2010 +0000 +++ b/hgrunner.cpp Mon Nov 22 17:27:59 2010 +0000 @@ -42,53 +42,33 @@ HgRunner::HgRunner(QWidget * parent): QProgressBar(parent) { - proc = new QProcess(this); + m_proc = new QProcess(this); QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); env.insert("LANG", "en_US.utf8"); env.insert("LC_ALL", "en_US.utf8"); - proc->setProcessEnvironment(env); + m_proc->setProcessEnvironment(env); + + m_proc->setProcessChannelMode(QProcess::MergedChannels); setTextVisible(false); setVisible(false); - isRunning = false; + m_isRunning = false; - stdOut.clear(); - stdErr.clear(); + m_output.clear(); - procInput = 0; -#ifndef Q_OS_WIN32 - char name[1024]; - if (openpty(&ptyMasterFd, &ptySlaveFd, name, NULL, NULL)) { - perror("openpty failed"); - } else { - DEBUG << "openpty succeeded: master " << ptyMasterFd - << " slave " << ptySlaveFd << " filename " << name << endl; - procInput = new QFile; - procInput->open(ptyMasterFd, QFile::WriteOnly); - ptySlaveFilename = name; - proc->setStandardInputFile(ptySlaveFilename); - ::close(ptySlaveFd); - } -#endif - connect(proc, SIGNAL(started()), this, SLOT(started())); - connect(proc, SIGNAL(finished(int, QProcess::ExitStatus)), + connect(m_proc, SIGNAL(started()), this, SLOT(started())); + connect(m_proc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus))); - connect(proc, SIGNAL(readyReadStandardOutput()), - this, SLOT(stdOutReady())); - connect(proc, SIGNAL(readyReadStandardError()), - this, SLOT(stdErrReady())); - - reportErrors = false; + connect(m_proc, SIGNAL(readyRead()), this, SLOT(dataReady())); } HgRunner::~HgRunner() { - if (ptySlaveFilename != "") { - ::close(ptyMasterFd); -// ::close(ptySlaveFd); + if (m_ptySlaveFilename != "") { + ::close(m_ptyMasterFd); } - delete proc; + delete m_proc; } QString HgRunner::getHgBinaryName() @@ -117,82 +97,134 @@ */ } -void HgRunner::saveOutput() -{ - stdOut += QString::fromUtf8(proc -> readAllStandardOutput()); - stdErr += QString::fromUtf8(proc -> readAllStandardError()); - - DEBUG << "saveOutput: " << stdOut.split("\n").size() << " line(s) of stdout, " << stdErr.split("\n").size() << " line(s) of stderr" << endl; - -// std::cerr << "stdout was " << stdOut.toStdString() << std::endl; -} - void HgRunner::setProcExitInfo(int procExitCode, QProcess::ExitStatus procExitStatus) { - exitCode = procExitCode; - exitStatus = procExitStatus; + m_exitCode = procExitCode; + m_exitStatus = procExitStatus; } QString HgRunner::getLastCommandLine() { - return QString("Command line: " + lastHgCommand + " " + lastParams); + return QString("Command line: " + m_lastHgCommand + " " + m_lastParams); } -void HgRunner::stdOutReady() +void HgRunner::noteUsername(QString name) { - DEBUG << "stdOutReady" << endl; - QString chunk = QString::fromUtf8(proc->readAllStandardOutput()); - //DEBUG << "stdout was " << chunk << endl; - stdOut += chunk; + m_userName = name; } -void HgRunner::stdErrReady() +void HgRunner::noteRealm(QString realm) { - DEBUG << "stdErrReady" << endl; - QString chunk = QString::fromUtf8(proc->readAllStandardError()); - //DEBUG << "stderr was " << chunk << endl; - stdErr += chunk; - if (procInput) { - if (chunk.toLower().trimmed() == "password:") { - bool ok = false; - QString pwd = QInputDialog::getText - (qobject_cast(parent()), - tr("Enter password"), tr("Password (but for what user name and repository??"), - QLineEdit::Password, QString(), &ok); - if (ok) { - procInput->write(QString("%1\n").arg(pwd).toUtf8()); - procInput->flush(); + m_realm = realm; +} + +void HgRunner::getUsername() +{ + if (m_procInput) { + bool ok = false; + QString prompt = tr("User name:"); + if (m_realm != "") { + prompt = tr("User name for \"%1\":").arg(m_realm); + } + QString pwd = QInputDialog::getText + (qobject_cast(parent()), + tr("Enter user name"), prompt, + QLineEdit::Normal, QString(), &ok); + if (ok) { + m_procInput->write(QString("%1\n").arg(pwd).toUtf8()); + m_procInput->flush(); + return; + } + } + // user cancelled or something went wrong + killCurrentCommand(); +} + +void HgRunner::getPassword() +{ + if (m_procInput) { + bool ok = false; + QString prompt = tr("Password:"); + if (m_userName != "") { + if (m_realm != "") { + prompt = tr("Password for \"%1\" at \"%2\":") + .arg(m_userName).arg(m_realm); } else { - //!!! do what? close the terminal? + prompt = tr("Password for user \"%1\":") + .arg(m_userName); } } + QString pwd = QInputDialog::getText + (qobject_cast(parent()), + tr("Enter password"), prompt, + QLineEdit::Password, QString(), &ok); + if (ok) { + m_procInput->write(QString("%1\n").arg(pwd).toUtf8()); + m_procInput->flush(); + return; + } } + // user cancelled or something went wrong + killCurrentCommand(); +} + +void HgRunner::checkPrompts(QString chunk) +{ + DEBUG << "checkPrompts: " << chunk << endl; + + QString text = chunk.trimmed(); + QString lower = text.toLower(); + if (lower.endsWith("password:")) { + getPassword(); + return; + } + if (lower.endsWith("user:")) { + getUsername(); + return; + } + QRegExp userRe("\\buser:\\s*([^\\s]+)"); + if (userRe.indexIn(text) >= 0) { + noteUsername(userRe.cap(1)); + } + QRegExp realmRe("\\brealmr:\\s*([^\\s]+)"); + if (realmRe.indexIn(text) >= 0) { + noteRealm(realmRe.cap(1)); + } +} + +void HgRunner::dataReady() +{ + DEBUG << "dataReady" << endl; + QString chunk = QString::fromUtf8(m_proc->readAll()); + m_output += chunk; + checkPrompts(chunk); } void HgRunner::finished(int procExitCode, QProcess::ExitStatus procExitStatus) { setProcExitInfo(procExitCode, procExitStatus); - saveOutput(); - isRunning = false; + m_isRunning = false; + + closeProcInput(); if (procExitCode == 0 && procExitStatus == QProcess::NormalExit) { - DEBUG << "HgRunner::finished: Command completed successfully: stderr says: " << stdErr << endl; + DEBUG << "HgRunner::finished: Command completed successfully" << endl; emit commandCompleted(); } else { - DEBUG << "HgRunner::finished: Command failed: stderr says: " << stdErr << endl; + DEBUG << "HgRunner::finished: Command failed" << endl; emit commandFailed(); } } bool HgRunner::isCommandRunning() { - return isRunning; + return m_isRunning; } void HgRunner::killCurrentCommand() { if (isCommandRunning()) { - proc -> kill(); + m_proc -> kill(); } } @@ -203,38 +235,67 @@ void HgRunner::startCommand(QString command, QString workingDir, QStringList params) { - isRunning = true; + m_isRunning = true; setRange(0, 0); setVisible(true); - stdOut.clear(); - stdErr.clear(); - exitCode = 0; - exitStatus = QProcess::NormalExit; + m_output.clear(); + m_exitCode = 0; + m_exitStatus = QProcess::NormalExit; + m_realm = ""; + m_userName = ""; - if (!workingDir.isEmpty()) - { - proc -> setWorkingDirectory(workingDir); + if (!workingDir.isEmpty()) { + m_proc->setWorkingDirectory(workingDir); } - lastHgCommand = command; - lastParams = params.join(" "); + m_procInput = 0; +#ifndef Q_OS_WIN32 + char name[1024]; + if (openpty(&m_ptyMasterFd, &m_ptySlaveFd, name, NULL, NULL)) { + perror("openpty failed"); + } else { + DEBUG << "openpty succeeded: master " << m_ptyMasterFd + << " slave " << m_ptySlaveFd << " filename " << name << endl; + m_procInput = new QFile; + m_procInput->open(m_ptyMasterFd, QFile::WriteOnly); + m_ptySlaveFilename = name; + m_proc->setStandardInputFile(m_ptySlaveFilename); + ::close(m_ptySlaveFd); + } +#endif + + m_lastHgCommand = command; + m_lastParams = params.join(" "); QString cmdline = command; foreach (QString param, params) cmdline += " " + param; DEBUG << "HgRunner: starting: " << cmdline << " with cwd " << workingDir << endl; - proc -> start(command, params); + m_proc->start(command, params); +} + +void HgRunner::closeProcInput() +{ + DEBUG << "closeProcInput" << endl; + + m_proc->closeWriteChannel(); +#ifndef Q_OS_WIN32 + if (m_ptySlaveFilename != "") { + ::close(m_ptyMasterFd); + m_ptySlaveFilename = ""; + } +#endif } int HgRunner::getExitCode() { - return exitCode; + return m_exitCode; } -QString HgRunner::getStdOut() +QString HgRunner::getOutput() { - return stdOut; + return m_output; } void HgRunner::hideProgBar() diff -r af7cf6f7282c -r 8a4e26dc3182 hgrunner.h --- a/hgrunner.h Mon Nov 22 16:02:13 2010 +0000 +++ b/hgrunner.h Mon Nov 22 17:27:59 2010 +0000 @@ -43,39 +43,45 @@ void hideProgBar(); - QString getStdOut(); + QString getOutput(); signals: void commandCompleted(); void commandFailed(); private: - void saveOutput(); void setProcExitInfo(int procExitCode, QProcess::ExitStatus procExitStatus); QString getLastCommandLine(); void presentErrorToUser(); QString getHgBinaryName(); + void closeProcInput(); - int ptyMasterFd; - int ptySlaveFd; - QString ptySlaveFilename; - QFile *procInput; + void noteUsername(QString); + void noteRealm(QString); + void getUsername(); + void getPassword(); + void checkPrompts(QString); + + int m_ptyMasterFd; + int m_ptySlaveFd; + QString m_ptySlaveFilename; + QFile *m_procInput; - bool reportErrors; - bool isRunning; - QProcess *proc; - QString stdOut; - QString stdErr; - int exitCode; - QProcess::ExitStatus exitStatus; - QString lastHgCommand; - QString lastParams; + bool m_isRunning; + QProcess *m_proc; + QString m_output; + int m_exitCode; + QProcess::ExitStatus m_exitStatus; + QString m_lastHgCommand; + QString m_lastParams; + + QString m_userName; + QString m_realm; private slots: void started(); void finished(int procExitCode, QProcess::ExitStatus procExitStatus); - void stdOutReady(); - void stdErrReady(); + void dataReady(); }; #endif // HGRUNNER_H diff -r af7cf6f7282c -r 8a4e26dc3182 mainwindow.cpp --- a/mainwindow.cpp Mon Nov 22 16:02:13 2010 +0000 +++ b/mainwindow.cpp Mon Nov 22 17:27:59 2010 +0000 @@ -603,6 +603,7 @@ QStringList params; params << "init"; + params << workFolderPath; runner -> startHgCommand(workFolderPath, params); runningAction = ACT_INIT; @@ -761,7 +762,7 @@ { QMessageBox::critical (this, tr("File chosen"), - tr("File chosen
The selected path (%1) is a file, not a folder as expected
").arg(xmlEncode(arg))); + tr("Folder required

You asked to open \"%1\".
This is a file; to open a repository, you need to choose a folder.
").arg(xmlEncode(arg))); return false; } @@ -769,15 +770,55 @@ { QMessageBox::critical (this, tr("Folder does not exist"), - tr("Folder does not exist
The selected path (%1) does not exist, and nor does its parent
").arg(xmlEncode(arg))); + tr("Folder does not exist

You asked to open \"%1\".
This folder does not exist, and it cannot be created because its parent does not exist either.
").arg(xmlEncode(arg))); + return false; +} + +bool MainWindow::complainAboutInitInRepo(QString arg) +{ + QMessageBox::critical + (this, tr("Path is in existing repository"), + tr("Path is in an existing repository

You asked to initialise a repository at \"%1\".
This path is already inside an existing repository.
").arg(xmlEncode(arg))); + return false; +} + +bool MainWindow::complainAboutInitFile(QString arg) +{ + QMessageBox::critical + (this, tr("Path is a file"), + tr("Path is a file

You asked to initialise a repository at \"%1\".
This is an existing file; it is only possible to initialise in folders.
").arg(xmlEncode(arg))); + return false; +} + +bool MainWindow::complainAboutCloneToExisting(QString arg) +{ + QMessageBox::critical + (this, tr("Path is in existing repository"), + tr("Local path is in an existing repository

You asked to open a remote repository by cloning it to the local path \"%1\".
This path is already inside an existing repository.
Please provide a new folder name for the local repository.
").arg(xmlEncode(arg))); + return false; +} + +bool MainWindow::complainAboutCloneToFile(QString arg) +{ + QMessageBox::critical + (this, tr("Path is a file"), + tr("Local path is a file

You asked to open a remote repository by cloning it to the local path \"%1\".
This path is an existing file.
Please provide a new folder name for the local repository.
").arg(xmlEncode(arg))); + return false; +} + +bool MainWindow::complainAboutCloneToExistingFolder(QString arg) +{ + QMessageBox::critical + (this, tr("Folder exists"), + tr("Local folder already exists

You asked to open a remote repository by cloning it to the local path \"%1\".
This is the path of an existing folder.
Please provide a new folder name for the local repository.
").arg(xmlEncode(arg))); return false; } bool MainWindow::askToOpenParentRepo(QString arg, QString parent) { return (QMessageBox::question - (this, tr("Open containing repository?"), - tr("Open containing repository?
The selected path (%1) is located inside an existing repository (at %2).
Would you like to open that repository instead?
") + (this, tr("Path is inside a repository"), + tr("Open the repository that contains this path?

You asked to open \"%1\".
There is no repository here — but it is inside a repository, whose root folder is at \"%2\".

Would you like to open that repository instead?
") .arg(xmlEncode(arg)).arg(xmlEncode(parent)), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) @@ -787,8 +828,8 @@ bool MainWindow::askToInitExisting(QString arg) { return (QMessageBox::question - (this, tr("Initialise repository?"), - tr("Initialise repository?
The selected folder (%1) does not contain a Mercurial repository. Would you like to initialise a repository here?
") + (this, tr("Folder has no repository"), + tr("Initialise a repository here?

You asked to open \"%1\".
This folder does not contain a Mercurial repository.

Would you like to initialise a repository here?
") .arg(xmlEncode(arg)), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) @@ -798,8 +839,19 @@ bool MainWindow::askToInitNew(QString arg) { return (QMessageBox::question - (this, tr("Initialise new repository?"), - tr("Initialise new repository?
The selected folder (%1) does not exist, but its parent does. Would you like to initialise a new empty repository in the selected folder?
") + (this, tr("Folder does not exist"), + tr("Initialise a new repository?

You asked to open \"%1\".
This folder does not yet exist.

Would you like to create the folder and initialise a new empty repository in it?
") + .arg(xmlEncode(arg)), + QMessageBox::Ok | QMessageBox::Cancel, + QMessageBox::Ok) + == QMessageBox::Ok); +} + +bool MainWindow::askToOpenInsteadOfInit(QString arg) +{ + return (QMessageBox::question + (this, tr("Repository exists"), + tr("Open existing repository?

You asked to initialise a new repository at \"%1\".
This folder already contains a repository. Would you like to open it?
") .arg(xmlEncode(arg)), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) @@ -822,6 +874,7 @@ case FolderExists: if (containing != "") { if (!askToOpenParentRepo(local, containing)) return false; + local = containing; } else { if (!askToInitExisting(local)) return false; return openInit(local); @@ -831,6 +884,7 @@ case FolderParentExists: if (containing != "") { if (!askToOpenParentRepo(local, containing)) return false; + local = containing; } else { if (!askToInitNew(local)) return false; return openInit(local); @@ -838,7 +892,13 @@ break; case FolderUnknown: - return complainAboutUnknownFolder(local); + if (containing != "") { + if (!askToOpenParentRepo(local, containing)) return false; + local = containing; + } else { + return complainAboutUnknownFolder(local); + } + break; case FolderIsFile: return complainAboutFilePath(local); @@ -852,12 +912,64 @@ bool MainWindow::openRemote(QString remote, QString local) { DEBUG << "clone " << remote << " to " << local << endl; - //!!! check that work folder does not exist, append to it if it does + + FolderStatus status = getFolderStatus(local); + QString containing = getContainingRepoFolder(local); + + DEBUG << "status = " << status << ", containing = " << containing << endl; + + if (status == FolderHasRepo || containing != "") { + return complainAboutCloneToExisting(local); + } + + if (status == FolderIsFile) { + return complainAboutCloneToFile(local); + } + + if (status == FolderUnknown) { + return complainAboutUnknownFolder(local); + } + + if (status == FolderExists) { + //!!! we can do better than this surely? + return complainAboutCloneToExistingFolder(local); + } + + workFolderPath = local; + remoteRepoPath = remote; + hgCloneFromRemote(); + return true; } -bool MainWindow::openInit(QString arg) +bool MainWindow::openInit(QString local) { + DEBUG << "openInit " << local << endl; + + FolderStatus status = getFolderStatus(local); + QString containing = getContainingRepoFolder(local); + + DEBUG << "status = " << status << ", containing = " << containing << endl; + + if (status == FolderHasRepo) { + if (!askToOpenInsteadOfInit(local)) return false; + } + + if (containing != "") { + return complainAboutInitInRepo(local); + } + + if (status == FolderIsFile) { + return complainAboutInitFile(local); + } + + if (status == FolderUnknown) { + return complainAboutUnknownFolder(local); + } + + workFolderPath = local; + remoteRepoPath = ""; + hgInit(); return true; } @@ -1125,7 +1237,7 @@ { case ACT_PATHS: { - QString sout = runner->getStdOut(); + QString sout = runner->getOutput(); DEBUG << "stdout is " << sout << endl; LogParser lp(sout, "="); LogList ll = lp.parse(); @@ -1141,24 +1253,24 @@ } case ACT_STAT: - hgExp -> updateWorkFolderFileList(runner -> getStdOut()); + hgExp -> updateWorkFolderFileList(runner -> getOutput()); break; case ACT_INCOMING: case ACT_ANNOTATE: case ACT_RESOLVE_LIST: case ACT_RESOLVE_MARK: - presentLongStdoutToUser(runner -> getStdOut()); + presentLongStdoutToUser(runner -> getOutput()); shouldHgStat = true; break; case ACT_PULL: - QMessageBox::information(this, "Pull", runner -> getStdOut()); + QMessageBox::information(this, "Pull", runner -> getOutput()); shouldHgStat = true; break; case ACT_PUSH: - QMessageBox::information(this, "Push", runner -> getStdOut()); + QMessageBox::information(this, "Push", runner -> getOutput()); shouldHgStat = true; break; @@ -1173,26 +1285,26 @@ MultiChoiceDialog::addRecentArgument("local", workFolderPath); MultiChoiceDialog::addRecentArgument("remote", remoteRepoPath); MultiChoiceDialog::addRecentArgument("remote", workFolderPath, true); - QMessageBox::information(this, "Clone", runner -> getStdOut()); + QMessageBox::information(this, "Clone", runner -> getOutput()); enableDisableActions(); shouldHgStat = true; break; case ACT_LOG: { - hgExp -> updateLocalRepoHgLogList(runner -> getStdOut()); + hgExp -> updateLocalRepoHgLogList(runner -> getOutput()); } break; case ACT_PARENTS: { - hgExp -> updateLocalRepoParentsList(runner -> getStdOut()); + hgExp -> updateLocalRepoParentsList(runner -> getOutput()); } break; case ACT_HEADS: { - QString stdOut = runner -> getStdOut(); + QString stdOut = runner -> getOutput(); hgExp -> updateLocalRepoHeadsList(stdOut); } break; @@ -1211,12 +1323,12 @@ break; case ACT_UPDATE: - QMessageBox::information(this, tr("Update"), runner -> getStdOut()); + QMessageBox::information(this, tr("Update"), runner -> getOutput()); shouldHgStat = true; break; case ACT_MERGE: - QMessageBox::information(this, tr("Merge"), runner -> getStdOut()); + QMessageBox::information(this, tr("Merge"), runner -> getOutput()); shouldHgStat = true; justMerged = true; break; diff -r af7cf6f7282c -r 8a4e26dc3182 mainwindow.h --- a/mainwindow.h Mon Nov 22 16:02:13 2010 +0000 +++ b/mainwindow.h Mon Nov 22 17:27:59 2010 +0000 @@ -147,9 +147,16 @@ bool complainAboutFilePath(QString); bool complainAboutUnknownFolder(QString); + bool complainAboutInitInRepo(QString); + bool complainAboutInitFile(QString); + bool complainAboutCloneToExisting(QString); + bool complainAboutCloneToFile(QString); + bool complainAboutCloneToExistingFolder(QString); //!!! not sure about this one + bool askToInitExisting(QString); bool askToInitNew(QString); bool askToOpenParentRepo(QString, QString); + bool askToOpenInsteadOfInit(QString); bool firstStart;