Chris@57: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ jtkorhonen@0: Chris@57: /* Chris@57: EasyMercurial Chris@57: Chris@98: Based on hgExplorer by Jari Korhonen Chris@57: Copyright (c) 2010 Jari Korhonen Chris@57: Copyright (c) 2010 Chris Cannam Chris@57: Copyright (c) 2010 Queen Mary, University of London Chris@57: Chris@57: This program is free software; you can redistribute it and/or Chris@57: modify it under the terms of the GNU General Public License as Chris@57: published by the Free Software Foundation; either version 2 of the Chris@57: License, or (at your option) any later version. See the file Chris@57: COPYING included with this distribution for more information. Chris@57: */ Chris@50: jtkorhonen@0: #include jtkorhonen@0: #include jtkorhonen@24: #include jtkorhonen@17: #include jtkorhonen@17: #include jtkorhonen@34: #include Chris@50: #include Chris@50: #include Chris@50: #include Chris@50: #include Chris@50: #include Chris@61: #include Chris@50: #include Chris@90: #include Chris@125: #include jtkorhonen@0: Chris@53: #include "mainwindow.h" Chris@69: #include "multichoicedialog.h" Chris@64: #include "startupdialog.h" Chris@53: #include "colourset.h" Chris@62: #include "debug.h" Chris@74: #include "logparser.h" Chris@103: #include "confirmcommentdialog.h" Chris@125: #include "incomingdialog.h" Chris@53: jtkorhonen@0: jtkorhonen@0: MainWindow::MainWindow() jtkorhonen@0: { jtkorhonen@0: QString wndTitle; jtkorhonen@0: Chris@90: fsWatcher = 0; Chris@133: commitsSincePush = 0; Chris@163: shouldHgStat = true; Chris@90: jtkorhonen@0: createActions(); jtkorhonen@0: createMenus(); jtkorhonen@0: createToolBars(); jtkorhonen@0: createStatusBar(); jtkorhonen@0: jtkorhonen@0: runner = new HgRunner(this); Chris@109: connect(runner, SIGNAL(commandCompleted(HgAction, QString)), Chris@109: this, SLOT(commandCompleted(HgAction, QString))); Chris@109: connect(runner, SIGNAL(commandFailed(HgAction, QString)), Chris@109: this, SLOT(commandFailed(HgAction, QString))); jtkorhonen@0: statusBar()->addPermanentWidget(runner); jtkorhonen@0: Chris@61: setWindowTitle(tr("EasyMercurial")); jtkorhonen@0: jtkorhonen@0: remoteRepoPath = ""; jtkorhonen@0: workFolderPath = ""; jtkorhonen@0: jtkorhonen@0: readSettings(); jtkorhonen@0: jtkorhonen@33: justMerged = false; Chris@98: hgTabs = new HgTabWidget((QWidget *) this, remoteRepoPath, workFolderPath); Chris@141: connectTabsSignals(); Chris@98: setCentralWidget(hgTabs); jtkorhonen@0: Chris@98: connect(hgTabs, SIGNAL(selectionChanged()), Chris@95: this, SLOT(enableDisableActions())); Chris@95: jtkorhonen@0: setUnifiedTitleAndToolBarOnMac(true); jtkorhonen@0: connectActions(); Chris@120: clearState(); jtkorhonen@0: enableDisableActions(); jtkorhonen@0: Chris@64: if (firstStart) { Chris@64: startupDialog(); jtkorhonen@0: } jtkorhonen@0: Chris@112: findDiffBinaryName(); Chris@163: findMergeBinaryName(); Chris@112: Chris@64: ColourSet *cs = ColourSet::instance(); Chris@64: cs->clearDefaultNames(); Chris@64: cs->addDefaultName(""); Chris@153: cs->addDefaultName("default"); Chris@64: cs->addDefaultName(getUserInfo()); Chris@62: Chris@72: if (workFolderPath == "") { Chris@72: open(); Chris@72: } Chris@72: Chris@109: hgQueryPaths(); jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: jtkorhonen@0: void MainWindow::closeEvent(QCloseEvent *) jtkorhonen@0: { jtkorhonen@0: writeSettings(); Chris@90: delete fsWatcher; jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: Chris@64: QString MainWindow::getUserInfo() const Chris@64: { Chris@64: QSettings settings; Chris@64: settings.beginGroup("User Information"); Chris@64: QString name = settings.value("name", getUserRealName()).toString(); Chris@64: QString email = settings.value("email", "").toString(); Chris@64: Chris@64: QString identifier; Chris@64: Chris@64: if (email != "") { Chris@64: identifier = QString("%1 <%2>").arg(name).arg(email); Chris@64: } else { Chris@64: identifier = name; Chris@64: } Chris@64: Chris@64: return identifier; Chris@64: } Chris@64: jtkorhonen@0: void MainWindow::about() jtkorhonen@0: { Chris@97: QMessageBox::about(this, tr("About EasyMercurial"), Chris@97: tr("

About EasyMercurial

" Chris@97: "

EasyMercurial is a simple user interface for the " Chris@97: "Mercurial version control system.

" Chris@98: "

EasyMercurial is based on hgExplorer by " Chris@97: "Jari Korhonen, with thanks.
EasyMercurial development carried out by " Chris@97: "Chris Cannam for soundsoftware.ac.uk at the Centre for Digital Music, Queen Mary, University of London." Chris@97: "

  • Copyright © 2010 Jari Korhonen
  • " Chris@97: "
  • Copyright © 2010 Chris Cannam
  • " Chris@97: "
  • Copyright © 2010 Queen Mary, University of London
  • " Chris@97: "
" Chris@97: "

This program is free software; you can redistribute it and/or " Chris@97: "modify it under the terms of the GNU General Public License as " Chris@97: "published by the Free Software Foundation; either version 2 of the " Chris@97: "License, or (at your option) any later version. See the file " Chris@97: "COPYING included with this distribution for more information.")); jtkorhonen@0: } jtkorhonen@0: Chris@94: void MainWindow::clearSelections() Chris@94: { Chris@98: hgTabs->clearSelections(); Chris@94: } jtkorhonen@0: Chris@120: void MainWindow::hgRefresh() Chris@120: { Chris@120: clearState(); Chris@120: hgQueryPaths(); Chris@120: } Chris@120: jtkorhonen@0: void MainWindow::hgStat() jtkorhonen@0: { Chris@109: QStringList params; Chris@109: params << "stat" << "-ardum"; Chris@153: Chris@163: lastStatOutput = ""; Chris@163: Chris@153: // annoyingly, hg stat actually modifies the working directory -- Chris@153: // it creates files called hg-checklink and hg-checkexec to test Chris@153: // properties of the filesystem Chris@153: if (fsWatcher) fsWatcher->blockSignals(true); Chris@153: Chris@109: runner->requestAction(HgAction(ACT_STAT, workFolderPath, params)); jtkorhonen@0: } jtkorhonen@0: Chris@109: void MainWindow::hgQueryPaths() Chris@74: { Chris@109: QStringList params; Chris@109: params << "paths"; Chris@109: runner->requestAction(HgAction(ACT_QUERY_PATHS, workFolderPath, params)); Chris@74: } Chris@74: Chris@109: void MainWindow::hgQueryBranch() Chris@106: { Chris@109: QStringList params; Chris@109: params << "branch"; Chris@109: runner->requestAction(HgAction(ACT_QUERY_BRANCH, workFolderPath, params)); Chris@106: } Chris@106: Chris@109: void MainWindow::hgQueryHeads() jtkorhonen@0: { Chris@109: QStringList params; Chris@137: // On empty repos, "hg heads" will fail -- we don't care about Chris@137: // that. Use --closed option so as to include closed branches; Chris@137: // otherwise we'll be stuck if the user updates into one, and our Chris@137: // incremental log will end up with spurious stuff in it because Chris@137: // we won't be pruning at the ends of closed branches Chris@137: params << "heads" << "--closed"; Chris@109: runner->requestAction(HgAction(ACT_QUERY_HEADS, workFolderPath, params)); jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: void MainWindow::hgLog() jtkorhonen@0: { Chris@109: QStringList params; Chris@109: params << "log"; Chris@109: params << "--template"; Chris@125: params << Changeset::getLogTemplate(); Chris@109: Chris@109: runner->requestAction(HgAction(ACT_LOG, workFolderPath, params)); Chris@109: } Chris@109: Chris@150: void MainWindow::hgLogIncremental(QStringList prune) Chris@120: { Chris@120: QStringList params; Chris@120: params << "log"; Chris@120: Chris@150: foreach (QString p, prune) { Chris@153: params << "--prune" << Changeset::hashOf(p); Chris@120: } Chris@120: Chris@120: params << "--template"; Chris@125: params << Changeset::getLogTemplate(); Chris@120: Chris@120: runner->requestAction(HgAction(ACT_LOG_INCREMENTAL, workFolderPath, params)); Chris@120: } Chris@109: Chris@109: void MainWindow::hgQueryParents() Chris@109: { Chris@109: QStringList params; Chris@109: params << "parents"; Chris@109: runner->requestAction(HgAction(ACT_QUERY_PARENTS, workFolderPath, params)); Chris@109: } Chris@109: Chris@109: void MainWindow::hgAnnotate() Chris@109: { Chris@109: QStringList params; Chris@109: QString currentFile;//!!! = hgTabs -> getCurrentFileListLine(); Chris@109: Chris@109: if (!currentFile.isEmpty()) jtkorhonen@0: { Chris@109: params << "annotate" << "--" << currentFile.mid(2); //Jump over status marker characters (e.g "M ") jtkorhonen@0: Chris@109: runner->requestAction(HgAction(ACT_ANNOTATE, workFolderPath, params)); jtkorhonen@0: } jtkorhonen@0: } jtkorhonen@0: Chris@109: void MainWindow::hgResolveList() Chris@109: { Chris@109: QStringList params; jtkorhonen@0: Chris@109: params << "resolve" << "--list"; Chris@109: runner->requestAction(HgAction(ACT_RESOLVE_LIST, workFolderPath, params)); Chris@109: } Chris@109: Chris@109: void MainWindow::hgAdd() jtkorhonen@0: { Chris@109: QStringList params; jtkorhonen@0: Chris@109: // hgExplorer permitted adding "all" files -- I'm not sure Chris@109: // that one is a good idea, let's require the user to select jtkorhonen@0: Chris@109: QStringList files = hgTabs->getSelectedAddableFiles(); Chris@109: Chris@109: if (!files.empty()) { Chris@109: params << "add" << "--" << files; Chris@109: runner->requestAction(HgAction(ACT_ADD, workFolderPath, params)); jtkorhonen@0: } jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: Chris@98: void MainWindow::hgRemove() Chris@98: { Chris@109: QStringList params; Chris@98: Chris@109: QStringList files = hgTabs->getSelectedRemovableFiles(); Chris@98: Chris@109: //!!! todo: confirmation dialog (with file list in it) (or do we Chris@109: // need that? all it does is move the files to the removed Chris@109: // list... doesn't it?) Chris@98: Chris@109: if (!files.empty()) { Chris@109: params << "remove" << "--after" << "--force" << "--" << files; Chris@109: runner->requestAction(HgAction(ACT_REMOVE, workFolderPath, params)); Chris@109: } jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: void MainWindow::hgCommit() jtkorhonen@0: { Chris@109: QStringList params; Chris@109: QString comment; Chris@94: Chris@157: if (justMerged) { Chris@157: comment = mergeCommitComment; Chris@157: } Chris@157: Chris@109: QStringList files = hgTabs->getSelectedCommittableFiles(); Chris@127: QStringList reportFiles = files; Chris@127: if (reportFiles.empty()) reportFiles = hgTabs->getAllCommittableFiles(); Chris@103: Chris@155: QString cf(tr("Commit files")); Chris@155: Chris@109: if (ConfirmCommentDialog::confirmAndGetLongComment Chris@109: (this, Chris@155: cf, Chris@155: tr("

%1

%2").arg(cf) Chris@155: .arg(tr("You are about to commit the following files:")), Chris@155: tr("

%1

%2").arg(cf) Chris@155: .arg(tr("You are about to commit %n file(s):", "", reportFiles.size())), Chris@127: reportFiles, Chris@109: comment)) { Chris@103: Chris@157: if (!justMerged && !files.empty()) { Chris@157: // User wants to commit selected file(s) (and this is not Chris@157: // merge commit, which would fail if we selected files) Chris@157: params << "commit" << "--message" << comment Chris@157: << "--user" << getUserInfo() << "--" << files; Chris@109: } else { Chris@109: // Commit all changes Chris@157: params << "commit" << "--message" << comment Chris@157: << "--user" << getUserInfo(); jtkorhonen@0: } Chris@109: Chris@109: runner->requestAction(HgAction(ACT_COMMIT, workFolderPath, params)); jtkorhonen@0: } jtkorhonen@0: } jtkorhonen@0: jtkorhonen@34: QString MainWindow::filterTag(QString tag) jtkorhonen@34: { jtkorhonen@34: for(int i = 0; i < tag.size(); i++) jtkorhonen@34: { jtkorhonen@34: if (tag[i].isLower() || tag[i].isUpper() || tag[i].isDigit() || (tag[i] == QChar('.'))) jtkorhonen@34: { jtkorhonen@34: //ok jtkorhonen@34: } jtkorhonen@34: else jtkorhonen@34: { jtkorhonen@34: tag[i] = QChar('_'); jtkorhonen@34: } jtkorhonen@34: } jtkorhonen@34: return tag; jtkorhonen@34: } jtkorhonen@34: jtkorhonen@34: Chris@164: void MainWindow::hgTag(QString id) jtkorhonen@34: { Chris@109: QStringList params; Chris@109: QString tag; jtkorhonen@34: Chris@109: if (ConfirmCommentDialog::confirmAndGetShortComment Chris@109: (this, Chris@109: tr("Tag"), Chris@109: tr("Enter tag:"), Chris@109: tag)) { Chris@164: if (!tag.isEmpty()) {//!!! do something better if it is empty Chris@164: Chris@164: params << "tag" << "--user" << getUserInfo(); Chris@164: params << "--rev" << Changeset::hashOf(id) << filterTag(tag); Chris@109: Chris@109: runner->requestAction(HgAction(ACT_TAG, workFolderPath, params)); jtkorhonen@34: } jtkorhonen@34: } jtkorhonen@34: } jtkorhonen@34: jtkorhonen@34: jtkorhonen@34: void MainWindow::hgIgnore() jtkorhonen@34: { Chris@109: QString hgIgnorePath; Chris@109: QStringList params; Chris@109: QString editorName; Chris@109: Chris@109: hgIgnorePath = workFolderPath; Chris@109: hgIgnorePath += ".hgignore"; Chris@109: Chris@109: params << hgIgnorePath; Chris@112: Chris@112: //!!! Chris@112: #ifdef Q_OS_LINUX Chris@112: Chris@109: editorName = "gedit"; Chris@112: Chris@112: #else Chris@112: Chris@109: editorName = """C:\\Program Files\\Windows NT\\Accessories\\wordpad.exe"""; Chris@112: Chris@112: #endif jtkorhonen@34: Chris@109: HgAction action(ACT_HG_IGNORE, workFolderPath, params); Chris@109: action.executable = editorName; jtkorhonen@34: Chris@109: runner->requestAction(action); jtkorhonen@34: } jtkorhonen@34: Chris@112: void MainWindow::findDiffBinaryName() Chris@112: { Chris@112: QSettings settings; Chris@112: QString diff = settings.value("extdiffbinary", "").toString(); Chris@112: if (diff == "") { Chris@112: QStringList bases; Chris@112: bases << "opendiff" << "kompare" << "kdiff3" << "meld"; Chris@112: bool found = false; Chris@112: foreach (QString base, bases) { Chris@112: diff = findExecutable(base); Chris@112: if (diff != base) { Chris@112: found = true; Chris@112: break; Chris@112: } Chris@112: } Chris@112: if (found) { Chris@112: settings.setValue("extdiffbinary", diff); Chris@112: } else { Chris@112: diff = ""; Chris@112: } Chris@112: } Chris@112: diffBinaryName = diff; Chris@112: } jtkorhonen@34: Chris@163: void MainWindow::findMergeBinaryName() Chris@163: { Chris@163: QSettings settings; Chris@163: QString merge = settings.value("mergebinary", "").toString(); Chris@163: if (merge == "") { Chris@163: QStringList bases; Chris@164: bases << "fmdiff3" << "meld" << "diffuse" << "kdiff3"; Chris@163: bool found = false; Chris@163: foreach (QString base, bases) { Chris@163: merge = findExecutable(base); Chris@163: if (merge != base) { Chris@163: found = true; Chris@163: break; Chris@163: } Chris@163: } Chris@163: if (found) { Chris@163: settings.setValue("mergebinary", merge); Chris@163: } else { Chris@163: merge = ""; Chris@163: } Chris@163: } Chris@163: mergeBinaryName = merge; Chris@163: } Chris@163: jtkorhonen@0: void MainWindow::hgFolderDiff() jtkorhonen@0: { Chris@112: if (diffBinaryName == "") return; Chris@112: Chris@109: QStringList params; jtkorhonen@0: Chris@112: // Diff parent against working folder (folder diff) Chris@112: Chris@148: params << "--config" << "extensions.extdiff=" << "extdiff"; Chris@148: params << "--program" << diffBinaryName; Chris@109: Chris@153: params << hgTabs->getSelectedCommittableFiles(); // may be none: whole dir Chris@153: Chris@109: runner->requestAction(HgAction(ACT_FOLDERDIFF, workFolderPath, params)); jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: Chris@148: void MainWindow::hgDiffToCurrent(QString id) jtkorhonen@0: { Chris@163: if (diffBinaryName == "") return; Chris@163: Chris@148: QStringList params; jtkorhonen@0: Chris@148: // Diff given revision against working folder jtkorhonen@0: Chris@148: params << "--config" << "extensions.extdiff=" << "extdiff"; Chris@148: params << "--program" << diffBinaryName; Chris@153: params << "--rev" << Changeset::hashOf(id); Chris@148: Chris@148: runner->requestAction(HgAction(ACT_FOLDERDIFF, workFolderPath, params)); jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: Chris@148: void MainWindow::hgDiffToParent(QString child, QString parent) Chris@148: { Chris@163: if (diffBinaryName == "") return; Chris@163: Chris@148: QStringList params; Chris@148: Chris@148: // Diff given revision against working folder Chris@148: Chris@148: params << "--config" << "extensions.extdiff=" << "extdiff"; Chris@148: params << "--program" << diffBinaryName; Chris@153: params << "--rev" << Changeset::hashOf(parent) Chris@153: << "--rev" << Changeset::hashOf(child); Chris@148: Chris@148: runner->requestAction(HgAction(ACT_CHGSETDIFF, workFolderPath, params)); Chris@148: } Chris@148: jtkorhonen@0: jtkorhonen@0: void MainWindow::hgUpdate() jtkorhonen@0: { Chris@109: QStringList params; jtkorhonen@0: Chris@109: params << "update"; Chris@109: Chris@109: runner->requestAction(HgAction(ACT_UPDATE, workFolderPath, params)); jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: Chris@148: void MainWindow::hgUpdateToRev(QString id) jtkorhonen@0: { Chris@148: QStringList params; jtkorhonen@0: Chris@153: params << "update" << "--rev" << Changeset::hashOf(id) << "--check"; jtkorhonen@0: Chris@148: runner->requestAction(HgAction(ACT_UPDATE, workFolderPath, params)); jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: jtkorhonen@0: void MainWindow::hgRevert() jtkorhonen@0: { Chris@109: QStringList params; Chris@109: QString comment; Chris@98: Chris@109: QStringList files = hgTabs->getSelectedRevertableFiles(); Chris@109: if (files.empty()) files = hgTabs->getAllRevertableFiles(); Chris@155: Chris@155: QString rf(tr("Revert files")); Chris@109: Chris@109: if (ConfirmCommentDialog::confirmDangerousFilesAction Chris@109: (this, Chris@155: rf, Chris@155: tr("

%1

%2").arg(rf) Chris@155: .arg(tr("You are about to revert the following files to their previous committed state.

This will throw away any changes that you have made to these files but have not committed:")), Chris@155: tr("

%1

%2").arg(rf) Chris@155: .arg(tr("You are about to revert %n file(s).

This will throw away any changes that you have made to these files but have not committed.", "", files.size())), Chris@109: files)) { Chris@163: Chris@163: lastRevertedFiles = files; Chris@109: Chris@98: if (files.empty()) { Chris@98: params << "revert" << "--no-backup"; Chris@98: } else { Chris@164: params << "revert" << "--" << files; Chris@98: } Chris@163: Chris@163: //!!! This is problematic. If you've got an uncommitted Chris@163: //!!! merge, you can't revert it without declaring which Chris@163: //!!! parent of the merge you want to revert to (reasonably Chris@163: //!!! enough). We're OK if we just did the merge in easyhg a Chris@163: //!!! moment ago, because we have a record of which parent was Chris@163: //!!! the target -- but if you exit and restart, we've lost Chris@163: //!!! that record and it doesn't appear to be possible to get Chris@163: //!!! it back from Hg. Even if you just switched from one Chris@163: //!!! repo to another, the record is lost. What to do? Chris@163: Chris@163: if (justMerged && mergeTargetRevision != "") { Chris@163: params << "--rev" << mergeTargetRevision; Chris@163: } Chris@109: Chris@109: runner->requestAction(HgAction(ACT_REVERT, workFolderPath, params)); jtkorhonen@0: } jtkorhonen@0: } jtkorhonen@0: Chris@163: Chris@163: void MainWindow::hgMarkResolved(QStringList files) Chris@163: { Chris@163: QStringList params; Chris@163: Chris@163: params << "resolve" << "--mark"; Chris@163: Chris@163: if (files.empty()) { Chris@163: params << "--all"; Chris@163: } else { Chris@164: params << "--" << files; Chris@163: } Chris@163: Chris@163: runner->requestAction(HgAction(ACT_RESOLVE_MARK, workFolderPath, params)); Chris@163: } Chris@163: Chris@163: jtkorhonen@33: void MainWindow::hgRetryMerge() jtkorhonen@33: { Chris@109: QStringList params; jtkorhonen@33: Chris@163: params << "resolve"; Chris@163: Chris@163: if (mergeBinaryName != "") { Chris@163: params << "--tool" << mergeBinaryName; Chris@163: } Chris@163: Chris@163: QStringList files = hgTabs->getSelectedUnresolvedFiles(); Chris@163: if (files.empty()) { Chris@163: params << "--all"; Chris@163: } else { Chris@164: params << "--" << files; Chris@163: } Chris@163: Chris@109: runner->requestAction(HgAction(ACT_RETRY_MERGE, workFolderPath, params)); Chris@163: Chris@163: mergeCommitComment = tr("Merge"); jtkorhonen@33: } jtkorhonen@33: jtkorhonen@33: jtkorhonen@0: void MainWindow::hgMerge() jtkorhonen@0: { Chris@163: if (hgTabs->canResolve()) { Chris@163: hgRetryMerge(); Chris@163: return; Chris@163: } Chris@163: Chris@109: QStringList params; jtkorhonen@0: Chris@109: params << "merge"; Chris@163: Chris@163: if (mergeBinaryName != "") { Chris@163: params << "--tool" << mergeBinaryName; Chris@163: } Chris@163: Chris@163: if (currentParents.size() == 1) { Chris@163: mergeTargetRevision = currentParents[0]->id(); Chris@163: } Chris@163: Chris@109: runner->requestAction(HgAction(ACT_MERGE, workFolderPath, params)); Chris@157: Chris@157: mergeCommitComment = tr("Merge"); jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: Chris@148: void MainWindow::hgMergeFrom(QString id) Chris@148: { Chris@148: QStringList params; Chris@148: Chris@148: params << "merge"; Chris@153: params << "--rev" << Changeset::hashOf(id); Chris@163: Chris@163: if (mergeBinaryName != "") { Chris@163: params << "--tool" << mergeBinaryName; Chris@163: } Chris@148: Chris@148: runner->requestAction(HgAction(ACT_MERGE, workFolderPath, params)); Chris@157: Chris@157: mergeCommitComment = ""; Chris@157: Chris@157: foreach (Changeset *cs, currentHeads) { Chris@157: if (cs->id() == id && !cs->isOnBranch(currentBranch)) { Chris@157: if (cs->branch() == "" || cs->branch() == "default") { Chris@157: mergeCommitComment = tr("Merge from the default branch"); Chris@157: } else { Chris@157: mergeCommitComment = tr("Merge from branch \"%1\"").arg(cs->branch()); Chris@157: } Chris@157: } Chris@157: } Chris@157: Chris@157: if (mergeCommitComment == "") { Chris@157: mergeCommitComment = tr("Merge from %1").arg(id); Chris@157: } Chris@148: } Chris@148: Chris@148: jtkorhonen@0: void MainWindow::hgCloneFromRemote() jtkorhonen@0: { Chris@109: QStringList params; jtkorhonen@0: Chris@109: if (!QDir(workFolderPath).exists()) { Chris@109: if (!QDir().mkpath(workFolderPath)) { Chris@109: DEBUG << "hgCloneFromRemote: Failed to create target path " Chris@109: << workFolderPath << endl; Chris@109: //!!! report error Chris@109: return; Chris@104: } Chris@109: } Chris@104: Chris@109: params << "clone" << remoteRepoPath << workFolderPath; Chris@109: Chris@109: hgTabs->setWorkFolderAndRepoNames(workFolderPath, remoteRepoPath); Chris@113: hgTabs->updateWorkFolderFileList(""); jtkorhonen@0: Chris@109: runner->requestAction(HgAction(ACT_CLONEFROMREMOTE, workFolderPath, params)); jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: void MainWindow::hgInit() jtkorhonen@0: { Chris@109: QStringList params; jtkorhonen@0: Chris@109: params << "init"; Chris@109: params << workFolderPath; jtkorhonen@0: Chris@109: runner->requestAction(HgAction(ACT_INIT, workFolderPath, params)); jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: void MainWindow::hgIncoming() jtkorhonen@0: { Chris@109: QStringList params; jtkorhonen@0: Chris@109: params << "incoming" << "--newest-first" << remoteRepoPath; Chris@125: params << "--template" << Changeset::getLogTemplate(); jtkorhonen@0: Chris@109: runner->requestAction(HgAction(ACT_INCOMING, workFolderPath, params)); jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: void MainWindow::hgPull() jtkorhonen@0: { Chris@126: if (QMessageBox::question Chris@126: (this, tr("Confirm pull"), Chris@126: format3(tr("Confirm pull from remote repository"), Chris@126: tr("You are about to pull from the following remote repository:"), Chris@126: remoteRepoPath), Chris@126: QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) { jtkorhonen@0: Chris@126: QStringList params; Chris@126: params << "pull" << remoteRepoPath; Chris@126: runner->requestAction(HgAction(ACT_PULL, workFolderPath, params)); Chris@126: } jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: void MainWindow::hgPush() jtkorhonen@0: { Chris@126: if (QMessageBox::question Chris@126: (this, tr("Confirm push"), Chris@126: format3(tr("Confirm push to remote repository"), Chris@126: tr("You are about to push to the following remote repository:"), Chris@126: remoteRepoPath), Chris@126: QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) { jtkorhonen@0: Chris@126: QStringList params; Chris@154: params << "push" << "--new-branch" << remoteRepoPath; Chris@126: runner->requestAction(HgAction(ACT_PUSH, workFolderPath, params)); Chris@126: } jtkorhonen@0: } jtkorhonen@0: jtkorhonen@28: QString MainWindow::listAllUpIpV4Addresses() jtkorhonen@26: { jtkorhonen@28: QString ret; jtkorhonen@26: QList ifaces = QNetworkInterface::allInterfaces(); jtkorhonen@26: jtkorhonen@26: for (int i = 0; i < ifaces.count(); i++) jtkorhonen@26: { jtkorhonen@26: QNetworkInterface iface = ifaces.at(i); jtkorhonen@26: jtkorhonen@26: if (iface.flags().testFlag(QNetworkInterface::IsUp) && !iface.flags().testFlag(QNetworkInterface::IsLoopBack)) jtkorhonen@26: { jtkorhonen@26: for (int j=0; jrequestAction(HgAction(ACT_SERVE, workFolderPath, params)); Chris@109: Chris@109: QMessageBox::information(this, "Serve", msg, QMessageBox::Close); Chris@109: //!!! runner -> killCurrentCommand(); jtkorhonen@11: } jtkorhonen@11: Chris@64: void MainWindow::startupDialog() Chris@64: { Chris@64: StartupDialog *dlg = new StartupDialog(this); Chris@65: if (dlg->exec()) firstStart = false; Chris@64: } jtkorhonen@11: Chris@69: void MainWindow::open() Chris@69: { Chris@86: bool done = false; Chris@69: Chris@86: while (!done) { Chris@69: Chris@86: MultiChoiceDialog *d = new MultiChoiceDialog Chris@86: (tr("Open Repository"), Chris@86: tr("What would you like to open?"), Chris@86: this); Chris@69: Chris@86: d->addChoice("remote", Chris@86: tr("


Remote repository
"), Chris@86: tr("Open a remote Mercurial repository, by cloning from its URL into a local folder."), Chris@86: MultiChoiceDialog::UrlToDirectoryArg); Chris@69: Chris@86: d->addChoice("local", Chris@86: tr("

Local repository
"), Chris@86: tr("Open an existing local Mercurial repository."), Chris@86: MultiChoiceDialog::DirectoryArg); Chris@69: Chris@86: d->addChoice("init", Chris@86: tr("

File folder
"), Chris@86: tr("Open a local folder, by creating a Mercurial repository in it."), Chris@86: MultiChoiceDialog::DirectoryArg); Chris@79: Chris@86: d->setCurrentChoice("local"); Chris@86: Chris@86: if (d->exec() == QDialog::Accepted) { Chris@86: Chris@86: QString choice = d->getCurrentChoice(); Chris@86: QString arg = d->getArgument().trimmed(); Chris@86: Chris@86: bool result = false; Chris@86: Chris@86: if (choice == "local") { Chris@86: result = openLocal(arg); Chris@86: } else if (choice == "remote") { Chris@86: result = openRemote(arg, d->getAdditionalArgument().trimmed()); Chris@86: } else if (choice == "init") { Chris@86: result = openInit(arg); Chris@86: } Chris@86: Chris@86: if (result) { Chris@86: enableDisableActions(); Chris@120: clearState(); Chris@109: hgQueryPaths(); Chris@91: done = true; Chris@91: } Chris@86: Chris@86: } else { Chris@86: Chris@86: // cancelled Chris@86: done = true; Chris@69: } Chris@79: Chris@86: delete d; Chris@69: } Chris@69: } Chris@69: Chris@145: void MainWindow::open(QString local) Chris@145: { Chris@145: if (openLocal(local)) { Chris@145: enableDisableActions(); Chris@145: clearState(); Chris@145: hgQueryPaths(); Chris@145: } Chris@145: } Chris@145: Chris@79: bool MainWindow::complainAboutFilePath(QString arg) Chris@79: { Chris@79: QMessageBox::critical Chris@79: (this, tr("File chosen"), Chris@84: 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))); Chris@79: return false; Chris@79: } Chris@79: Chris@79: bool MainWindow::complainAboutUnknownFolder(QString arg) Chris@79: { Chris@79: QMessageBox::critical Chris@79: (this, tr("Folder does not exist"), Chris@84: 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))); Chris@84: return false; Chris@84: } Chris@84: Chris@84: bool MainWindow::complainAboutInitInRepo(QString arg) Chris@84: { Chris@84: QMessageBox::critical Chris@84: (this, tr("Path is in existing repository"), Chris@84: 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))); Chris@84: return false; Chris@84: } Chris@84: Chris@84: bool MainWindow::complainAboutInitFile(QString arg) Chris@84: { Chris@84: QMessageBox::critical Chris@84: (this, tr("Path is a file"), Chris@84: 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))); Chris@84: return false; Chris@84: } Chris@84: Chris@84: bool MainWindow::complainAboutCloneToExisting(QString arg) Chris@84: { Chris@84: QMessageBox::critical Chris@84: (this, tr("Path is in existing repository"), Chris@84: 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))); Chris@84: return false; Chris@84: } Chris@84: Chris@84: bool MainWindow::complainAboutCloneToFile(QString arg) Chris@84: { Chris@84: QMessageBox::critical Chris@84: (this, tr("Path is a file"), Chris@84: 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))); Chris@84: return false; Chris@84: } Chris@84: Chris@84: bool MainWindow::complainAboutCloneToExistingFolder(QString arg) Chris@84: { Chris@84: QMessageBox::critical Chris@84: (this, tr("Folder exists"), Chris@84: 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))); Chris@79: return false; Chris@79: } Chris@79: Chris@79: bool MainWindow::askToOpenParentRepo(QString arg, QString parent) Chris@79: { Chris@79: return (QMessageBox::question Chris@84: (this, tr("Path is inside a repository"), Chris@86: tr("Open the repository that contains this path?

You asked to open \"%1\".
This is not the root folder of a repository.
But it is inside a repository, whose root is at \"%2\".

Would you like to open that repository instead?
") Chris@79: .arg(xmlEncode(arg)).arg(xmlEncode(parent)), Chris@79: QMessageBox::Ok | QMessageBox::Cancel, Chris@79: QMessageBox::Ok) Chris@79: == QMessageBox::Ok); Chris@79: } Chris@79: Chris@79: bool MainWindow::askToInitExisting(QString arg) Chris@79: { Chris@79: return (QMessageBox::question Chris@84: (this, tr("Folder has no repository"), Chris@84: 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?
") Chris@79: .arg(xmlEncode(arg)), Chris@79: QMessageBox::Ok | QMessageBox::Cancel, Chris@79: QMessageBox::Ok) Chris@79: == QMessageBox::Ok); Chris@79: } Chris@79: Chris@79: bool MainWindow::askToInitNew(QString arg) Chris@79: { Chris@79: return (QMessageBox::question Chris@84: (this, tr("Folder does not exist"), Chris@84: 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?
") Chris@84: .arg(xmlEncode(arg)), Chris@84: QMessageBox::Ok | QMessageBox::Cancel, Chris@84: QMessageBox::Ok) Chris@84: == QMessageBox::Ok); Chris@84: } Chris@84: Chris@84: bool MainWindow::askToOpenInsteadOfInit(QString arg) Chris@84: { Chris@84: return (QMessageBox::question Chris@84: (this, tr("Repository exists"), Chris@84: 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?
") Chris@79: .arg(xmlEncode(arg)), Chris@79: QMessageBox::Ok | QMessageBox::Cancel, Chris@79: QMessageBox::Ok) Chris@79: == QMessageBox::Ok); Chris@79: } Chris@79: Chris@79: bool MainWindow::openLocal(QString local) Chris@79: { Chris@79: DEBUG << "open " << local << endl; Chris@79: Chris@79: FolderStatus status = getFolderStatus(local); Chris@79: QString containing = getContainingRepoFolder(local); Chris@79: Chris@79: switch (status) { Chris@79: Chris@79: case FolderHasRepo: Chris@79: // fine Chris@79: break; Chris@79: Chris@79: case FolderExists: Chris@79: if (containing != "") { Chris@79: if (!askToOpenParentRepo(local, containing)) return false; Chris@84: local = containing; Chris@79: } else { Chris@86: //!!! No -- this is likely to happen far more by accident Chris@86: // than because the user actually wanted to init something. Chris@86: // Don't ask, just politely reject. Chris@79: if (!askToInitExisting(local)) return false; Chris@79: return openInit(local); Chris@79: } Chris@79: break; Chris@79: Chris@79: case FolderParentExists: Chris@79: if (containing != "") { Chris@79: if (!askToOpenParentRepo(local, containing)) return false; Chris@84: local = containing; Chris@79: } else { Chris@79: if (!askToInitNew(local)) return false; Chris@79: return openInit(local); Chris@79: } Chris@79: break; Chris@79: Chris@79: case FolderUnknown: Chris@84: if (containing != "") { Chris@84: if (!askToOpenParentRepo(local, containing)) return false; Chris@84: local = containing; Chris@84: } else { Chris@84: return complainAboutUnknownFolder(local); Chris@84: } Chris@84: break; Chris@79: Chris@79: case FolderIsFile: Chris@79: return complainAboutFilePath(local); Chris@79: } Chris@79: Chris@79: workFolderPath = local; Chris@79: remoteRepoPath = ""; Chris@79: return true; Chris@79: } Chris@79: Chris@79: bool MainWindow::openRemote(QString remote, QString local) Chris@79: { Chris@79: DEBUG << "clone " << remote << " to " << local << endl; Chris@84: Chris@84: FolderStatus status = getFolderStatus(local); Chris@84: QString containing = getContainingRepoFolder(local); Chris@84: Chris@84: DEBUG << "status = " << status << ", containing = " << containing << endl; Chris@84: Chris@84: if (status == FolderHasRepo || containing != "") { Chris@84: return complainAboutCloneToExisting(local); Chris@84: } Chris@84: Chris@84: if (status == FolderIsFile) { Chris@84: return complainAboutCloneToFile(local); Chris@84: } Chris@84: Chris@84: if (status == FolderUnknown) { Chris@84: return complainAboutUnknownFolder(local); Chris@84: } Chris@84: Chris@84: if (status == FolderExists) { Chris@84: //!!! we can do better than this surely? Chris@84: return complainAboutCloneToExistingFolder(local); Chris@84: } Chris@84: Chris@84: workFolderPath = local; Chris@84: remoteRepoPath = remote; Chris@84: hgCloneFromRemote(); Chris@84: Chris@79: return true; Chris@79: } Chris@79: Chris@84: bool MainWindow::openInit(QString local) Chris@79: { Chris@84: DEBUG << "openInit " << local << endl; Chris@84: Chris@84: FolderStatus status = getFolderStatus(local); Chris@84: QString containing = getContainingRepoFolder(local); Chris@84: Chris@84: DEBUG << "status = " << status << ", containing = " << containing << endl; Chris@84: Chris@84: if (status == FolderHasRepo) { Chris@84: if (!askToOpenInsteadOfInit(local)) return false; Chris@84: } Chris@84: Chris@84: if (containing != "") { Chris@84: return complainAboutInitInRepo(local); Chris@84: } Chris@84: Chris@84: if (status == FolderIsFile) { Chris@84: return complainAboutInitFile(local); Chris@84: } Chris@84: Chris@84: if (status == FolderUnknown) { Chris@84: return complainAboutUnknownFolder(local); Chris@84: } Chris@84: Chris@84: workFolderPath = local; Chris@84: remoteRepoPath = ""; Chris@84: hgInit(); Chris@79: return true; Chris@79: } Chris@79: jtkorhonen@0: void MainWindow::settings() jtkorhonen@0: { Chris@69: /*!!! jtkorhonen@0: SettingsDialog *settingsDlg = new SettingsDialog(this); jtkorhonen@0: settingsDlg->setModal(true); jtkorhonen@0: settingsDlg->exec(); Chris@98: hgTabs -> clearLists(); jtkorhonen@0: enableDisableActions(); jtkorhonen@0: hgStat(); Chris@69: */ jtkorhonen@0: } jtkorhonen@0: jtkorhonen@2: #define STDOUT_NEEDS_BIG_WINDOW 512 jtkorhonen@2: #define SMALL_WND_W 500 jtkorhonen@2: #define SMALL_WND_H 300 jtkorhonen@2: jtkorhonen@2: #define BIG_WND_W 1024 jtkorhonen@2: #define BIG_WND_H 768 jtkorhonen@2: jtkorhonen@2: jtkorhonen@2: void MainWindow::presentLongStdoutToUser(QString stdo) jtkorhonen@0: { jtkorhonen@2: if (!stdo.isEmpty()) jtkorhonen@2: { jtkorhonen@2: QDialog dlg; jtkorhonen@0: jtkorhonen@2: if (stdo.length() > STDOUT_NEEDS_BIG_WINDOW) jtkorhonen@2: { jtkorhonen@2: dlg.setMinimumWidth(BIG_WND_W); jtkorhonen@2: dlg.setMinimumHeight(BIG_WND_H); jtkorhonen@2: } jtkorhonen@2: else jtkorhonen@2: { jtkorhonen@2: dlg.setMinimumWidth(SMALL_WND_W); jtkorhonen@2: dlg.setMinimumHeight(SMALL_WND_H); jtkorhonen@2: } jtkorhonen@0: jtkorhonen@2: QVBoxLayout *box = new QVBoxLayout; jtkorhonen@2: QListWidget *list = new QListWidget; jtkorhonen@2: list-> addItems(stdo.split("\n")); jtkorhonen@2: QPushButton *btn = new QPushButton(tr("Ok")); jtkorhonen@2: connect(btn, SIGNAL(clicked()), &dlg, SLOT(accept())); jtkorhonen@0: jtkorhonen@2: box -> addWidget(list); jtkorhonen@2: box -> addWidget(btn); jtkorhonen@2: dlg.setLayout(box); jtkorhonen@2: jtkorhonen@2: dlg.exec(); jtkorhonen@2: } jtkorhonen@2: else jtkorhonen@2: { Chris@98: QMessageBox::information(this, tr("EasyMercurial"), tr("Mercurial command did not return any output.")); jtkorhonen@2: } jtkorhonen@0: } jtkorhonen@0: Chris@90: void MainWindow::updateFileSystemWatcher() Chris@90: { Chris@90: delete fsWatcher; Chris@90: fsWatcher = new QFileSystemWatcher(); Chris@90: std::deque pending; Chris@90: pending.push_back(workFolderPath); Chris@90: while (!pending.empty()) { Chris@90: QString path = pending.front(); Chris@90: pending.pop_front(); Chris@90: fsWatcher->addPath(path); Chris@90: DEBUG << "Added to file system watcher: " << path << endl; Chris@90: QDir d(path); Chris@90: if (d.exists()) { Chris@115: d.setFilter(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable); Chris@90: foreach (QString entry, d.entryList()) { Chris@90: if (entry == ".hg") continue; Chris@90: QString entryPath = d.absoluteFilePath(entry); Chris@90: pending.push_back(entryPath); Chris@90: } Chris@90: } Chris@90: } Chris@90: connect(fsWatcher, SIGNAL(directoryChanged(QString)), Chris@90: this, SLOT(fsDirectoryChanged(QString))); Chris@90: connect(fsWatcher, SIGNAL(fileChanged(QString)), Chris@90: this, SLOT(fsFileChanged(QString))); Chris@90: } Chris@90: Chris@122: void MainWindow::fsDirectoryChanged(QString d) Chris@90: { Chris@122: DEBUG << "MainWindow::fsDirectoryChanged " << d << endl; Chris@90: hgStat(); Chris@90: } Chris@90: Chris@122: void MainWindow::fsFileChanged(QString f) Chris@90: { Chris@122: DEBUG << "MainWindow::fsFileChanged " << f << endl; Chris@90: hgStat(); Chris@90: } Chris@90: Chris@125: QString MainWindow::format3(QString head, QString intro, QString code) Chris@125: { Chris@125: if (intro == "") { Chris@125: return QString("

%1

%2") Chris@125: .arg(head).arg(xmlEncode(code).replace("\n", "
")); Chris@126: } else if (code == "") { Chris@126: return QString("

%1

%2

") Chris@126: .arg(head).arg(intro); Chris@125: } else { Chris@125: return QString("

%1

%2

%3") Chris@125: .arg(head).arg(intro).arg(xmlEncode(code).replace("\n", "
")); Chris@125: } Chris@125: } Chris@125: Chris@120: void MainWindow::showIncoming(QString output) Chris@120: { Chris@120: runner->hide(); Chris@125: IncomingDialog *d = new IncomingDialog(this, output); Chris@125: d->exec(); Chris@125: delete d; Chris@125: } Chris@125: Chris@125: int MainWindow::extractChangeCount(QString text) Chris@125: { Chris@125: QRegExp re("added (\\d+) ch\\w+ with (\\d+) ch\\w+ to (\\d+) f\\w+"); Chris@125: if (re.indexIn(text) >= 0) { Chris@125: return re.cap(1).toInt(); Chris@125: } else if (text.contains("no changes")) { Chris@125: return 0; Chris@125: } else { Chris@125: return -1; // unknown Chris@125: } Chris@120: } Chris@120: Chris@120: void MainWindow::showPushResult(QString output) Chris@120: { Chris@125: QString report; Chris@125: int n = extractChangeCount(output); Chris@125: if (n > 0) { Chris@125: report = tr("Pushed %n changeset(s)", "", n); Chris@125: } else if (n == 0) { Chris@125: report = tr("No changes to push"); Chris@125: } else { Chris@125: report = tr("Push complete"); Chris@125: } Chris@125: report = format3(report, tr("The push command output was:"), output); Chris@120: runner->hide(); Chris@125: QMessageBox::information(this, "Push complete", report); Chris@120: } Chris@120: Chris@120: void MainWindow::showPullResult(QString output) Chris@120: { Chris@125: QString report; Chris@125: int n = extractChangeCount(output); Chris@125: if (n > 0) { Chris@125: report = tr("Pulled %n changeset(s)", "", n); Chris@125: } else if (n == 0) { Chris@125: report = tr("No changes to pull"); Chris@125: } else { Chris@125: report = tr("Pull complete"); Chris@125: } Chris@125: report = format3(report, tr("The pull command output was:"), output); Chris@120: runner->hide(); Chris@125: Chris@125: //!!! and something about updating Chris@125: Chris@125: QMessageBox::information(this, "Pull complete", report); Chris@120: } Chris@120: Chris@143: void MainWindow::commandFailed(HgAction action, QString output) Chris@62: { Chris@62: DEBUG << "MainWindow::commandFailed" << endl; Chris@74: Chris@113: // Some commands we just have to ignore bad return values from: Chris@113: Chris@113: switch(action.action) { Chris@113: case ACT_NONE: Chris@113: // uh huh Chris@113: return; Chris@113: case ACT_INCOMING: Chris@113: // returns non-zero code if the check was successful but there Chris@113: // are no changes pending Chris@143: if (output.trimmed() == "") showIncoming(""); Chris@113: return; Chris@162: case ACT_QUERY_HEADS: Chris@162: // fails if repo is empty; we don't care (if there's a genuine Chris@162: // problem, something else will fail too). Need to do this, Chris@162: // otherwise empty repo state will not be reflected properly Chris@162: // (since heads/log procedure never completes for empty repo) Chris@162: enableDisableActions(); Chris@162: return; Chris@113: case ACT_FOLDERDIFF: Chris@113: case ACT_CHGSETDIFF: Chris@113: // external program, unlikely to be anything useful in stderr Chris@113: // and some return with failure codes when something as basic Chris@113: // as the user closing the window via the wm happens Chris@113: return; Chris@113: Chris@113: default: Chris@114: break; Chris@113: } Chris@113: Chris@113: QString command = action.executable; Chris@113: if (command == "") command = "hg"; Chris@113: foreach (QString arg, action.params) { Chris@113: command += " " + arg; Chris@113: } Chris@113: Chris@113: QString message = tr("

Command failed

" Chris@113: "

The following command failed:

" Chris@113: "%1" Chris@113: "%2
") Chris@113: .arg(command) Chris@143: .arg((output.trimmed() != "") ? Chris@113: tr("

Its output said:

%1") Chris@143: .arg(xmlEncode(output.left(800)) Chris@113: .replace("\n", "
")) Chris@113: : ""); Chris@113: Chris@113: QMessageBox::warning(this, tr("Command failed"), message); Chris@62: } Chris@62: Chris@109: void MainWindow::commandCompleted(HgAction completedAction, QString output) jtkorhonen@0: { Chris@109: HGACTIONS action = completedAction.action; Chris@109: Chris@109: if (action == ACT_NONE) return; Chris@109: Chris@150: bool headsChanged = false; Chris@150: QStringList oldHeadIds; Chris@150: Chris@150: switch (action) { Chris@109: Chris@109: case ACT_QUERY_PATHS: jtkorhonen@0: { Chris@109: DEBUG << "stdout is " << output << endl; Chris@109: LogParser lp(output, "="); Chris@109: LogList ll = lp.parse(); Chris@109: DEBUG << ll.size() << " results" << endl; Chris@109: if (!ll.empty()) { Chris@109: remoteRepoPath = lp.parse()[0]["default"].trimmed(); Chris@109: DEBUG << "Set remote path to " << remoteRepoPath << endl; Chris@109: } Chris@109: MultiChoiceDialog::addRecentArgument("local", workFolderPath); Chris@109: MultiChoiceDialog::addRecentArgument("remote", remoteRepoPath); Chris@109: hgTabs->setWorkFolderAndRepoNames(workFolderPath, remoteRepoPath); Chris@109: break; Chris@109: } jtkorhonen@0: Chris@109: case ACT_QUERY_BRANCH: Chris@109: currentBranch = output.trimmed(); Chris@109: break; jtkorhonen@0: Chris@109: case ACT_STAT: Chris@153: if (fsWatcher) fsWatcher->blockSignals(false); Chris@163: lastStatOutput = output; Chris@109: updateFileSystemWatcher(); Chris@109: break; Chris@163: Chris@163: case ACT_RESOLVE_LIST: Chris@163: if (output != "") { Chris@163: // Remove lines beginning with R (they are resolved, Chris@163: // and the file stat parser treats R as removed) Chris@163: QStringList outList = output.split('\n'); Chris@163: QStringList winnowed; Chris@163: foreach (QString line, outList) { Chris@163: if (!line.startsWith("R ")) winnowed.push_back(line); Chris@163: } Chris@163: output = winnowed.join("\n"); Chris@163: } Chris@163: DEBUG << "lastStatOutput = " << lastStatOutput << endl; Chris@163: DEBUG << "output = " << output << endl; Chris@163: hgTabs->updateWorkFolderFileList(lastStatOutput + output); Chris@163: break; Chris@163: Chris@163: case ACT_RESOLVE_MARK: Chris@163: shouldHgStat = true; Chris@163: break; Chris@109: Chris@109: case ACT_INCOMING: Chris@120: showIncoming(output); Chris@120: break; Chris@120: Chris@109: case ACT_ANNOTATE: Chris@109: presentLongStdoutToUser(output); Chris@109: shouldHgStat = true; Chris@109: break; Chris@109: Chris@109: case ACT_PULL: Chris@120: showPullResult(output); Chris@109: shouldHgStat = true; Chris@109: break; Chris@109: Chris@109: case ACT_PUSH: Chris@120: showPushResult(output); Chris@109: break; Chris@109: Chris@109: case ACT_INIT: Chris@109: MultiChoiceDialog::addRecentArgument("init", workFolderPath); Chris@109: MultiChoiceDialog::addRecentArgument("local", workFolderPath); Chris@109: enableDisableActions(); Chris@109: shouldHgStat = true; Chris@109: break; Chris@109: Chris@109: case ACT_CLONEFROMREMOTE: Chris@109: MultiChoiceDialog::addRecentArgument("local", workFolderPath); Chris@109: MultiChoiceDialog::addRecentArgument("remote", remoteRepoPath); Chris@109: MultiChoiceDialog::addRecentArgument("remote", workFolderPath, true); Chris@109: QMessageBox::information(this, "Clone", output); Chris@109: enableDisableActions(); Chris@109: shouldHgStat = true; Chris@109: break; Chris@109: Chris@109: case ACT_LOG: Chris@120: hgTabs->setNewLog(output); Chris@120: needNewLog = false; Chris@120: break; Chris@120: Chris@120: case ACT_LOG_INCREMENTAL: Chris@120: hgTabs->addIncrementalLog(output); Chris@109: break; Chris@109: Chris@109: case ACT_QUERY_PARENTS: Chris@152: { Chris@109: foreach (Changeset *cs, currentParents) delete cs; Chris@109: currentParents = Changeset::parseChangesets(output); Chris@152: QStringList parentIds = Changeset::getIds(currentParents); Chris@153: hgTabs->setCurrent(parentIds, currentBranch); Chris@152: } Chris@109: break; Chris@109: Chris@109: case ACT_QUERY_HEADS: Chris@150: { Chris@150: oldHeadIds = Changeset::getIds(currentHeads); Chris@150: Changesets newHeads = Changeset::parseChangesets(output); Chris@150: QStringList newHeadIds = Changeset::getIds(newHeads); Chris@150: if (oldHeadIds != newHeadIds) { Chris@150: DEBUG << "Heads changed, will prompt an incremental log if appropriate" << endl; Chris@150: headsChanged = true; Chris@150: foreach (Changeset *cs, currentHeads) delete cs; Chris@150: currentHeads = newHeads; Chris@150: } Chris@150: } Chris@109: break; Chris@130: Chris@130: case ACT_COMMIT: Chris@130: hgTabs->clearSelections(); Chris@163: justMerged = false; Chris@130: shouldHgStat = true; Chris@130: break; Chris@163: Chris@163: case ACT_REVERT: Chris@163: hgMarkResolved(lastRevertedFiles); Chris@163: justMerged = false; Chris@163: break; Chris@109: Chris@109: case ACT_REMOVE: Chris@109: case ACT_ADD: Chris@116: hgTabs->clearSelections(); Chris@116: shouldHgStat = true; Chris@116: break; Chris@116: Chris@164: case ACT_TAG: Chris@164: needNewLog = true; Chris@164: shouldHgStat = true; Chris@164: break; Chris@164: Chris@109: case ACT_FOLDERDIFF: Chris@109: case ACT_CHGSETDIFF: Chris@109: case ACT_SERVE: Chris@109: case ACT_HG_IGNORE: Chris@109: shouldHgStat = true; Chris@109: break; Chris@109: Chris@109: case ACT_UPDATE: Chris@162: QMessageBox::information(this, tr("Update"), tr("

Update successful

%1

").arg(xmlEncode(output))); Chris@109: shouldHgStat = true; Chris@109: break; Chris@109: Chris@109: case ACT_MERGE: Chris@162: QMessageBox::information(this, tr("Update"), tr("

Merge successful

%1

").arg(xmlEncode(output))); Chris@109: shouldHgStat = true; Chris@109: justMerged = true; Chris@109: break; Chris@109: Chris@109: case ACT_RETRY_MERGE: Chris@163: QMessageBox::information(this, tr("Resolved"), Chris@163: tr("

Merge resolved

Merge resolved successfully.

")); Chris@109: shouldHgStat = true; Chris@109: justMerged = true; Chris@109: break; Chris@109: Chris@109: default: Chris@109: break; Chris@109: } Chris@108: Chris@121: // Sequence when no full log required: Chris@163: // paths -> branch -> stat -> resolve-list -> heads -> Chris@150: // incremental-log (only if heads changed) -> parents Chris@150: // Chris@121: // Sequence when full log required: Chris@163: // paths -> branch -> stat -> resolve-list -> heads -> parents -> log Chris@150: // Chris@150: // Note we want to call enableDisableActions only once, at the end Chris@150: // of whichever sequence is in use. Chris@150: Chris@156: bool noMore = false; Chris@156: Chris@150: switch (action) { Chris@150: Chris@150: case ACT_QUERY_PATHS: Chris@109: hgQueryBranch(); Chris@150: break; Chris@150: Chris@150: case ACT_QUERY_BRANCH: Chris@109: hgStat(); Chris@150: break; Chris@150: Chris@150: case ACT_STAT: Chris@163: hgResolveList(); Chris@163: break; Chris@163: Chris@163: case ACT_RESOLVE_LIST: Chris@150: hgQueryHeads(); Chris@150: break; Chris@150: Chris@150: case ACT_QUERY_HEADS: Chris@150: if (headsChanged && !needNewLog) { Chris@150: hgLogIncremental(oldHeadIds); Chris@121: } else { Chris@150: hgQueryParents(); Chris@121: } Chris@150: break; Chris@150: Chris@150: case ACT_LOG_INCREMENTAL: Chris@109: hgQueryParents(); Chris@150: break; Chris@150: Chris@150: case ACT_QUERY_PARENTS: Chris@120: if (needNewLog) { Chris@120: hgLog(); Chris@150: } else { Chris@150: // we're done Chris@156: noMore = true; Chris@120: } Chris@150: break; Chris@150: Chris@150: case ACT_LOG: Chris@150: // we're done Chris@156: noMore = true; Chris@150: Chris@150: default: Chris@109: if (shouldHgStat) { Chris@163: shouldHgStat = false; Chris@109: hgQueryPaths(); Chris@150: } else { Chris@156: noMore = true; jtkorhonen@0: } Chris@150: break; Chris@150: } Chris@156: Chris@156: if (noMore) { Chris@156: enableDisableActions(); Chris@156: hgTabs->updateHistory(); Chris@156: } jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: void MainWindow::connectActions() jtkorhonen@0: { jtkorhonen@0: connect(exitAct, SIGNAL(triggered()), this, SLOT(close())); jtkorhonen@0: connect(aboutAct, SIGNAL(triggered()), this, SLOT(about())); jtkorhonen@0: connect(aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt())); jtkorhonen@0: Chris@120: connect(hgRefreshAct, SIGNAL(triggered()), this, SLOT(hgRefresh())); jtkorhonen@0: connect(hgRemoveAct, SIGNAL(triggered()), this, SLOT(hgRemove())); jtkorhonen@0: connect(hgAddAct, SIGNAL(triggered()), this, SLOT(hgAdd())); jtkorhonen@0: connect(hgCommitAct, SIGNAL(triggered()), this, SLOT(hgCommit())); jtkorhonen@0: connect(hgFolderDiffAct, SIGNAL(triggered()), this, SLOT(hgFolderDiff())); Chris@149: // connect(hgChgSetDiffAct, SIGNAL(triggered()), this, SLOT(hgChgSetDiff())); jtkorhonen@0: connect(hgUpdateAct, SIGNAL(triggered()), this, SLOT(hgUpdate())); jtkorhonen@0: connect(hgRevertAct, SIGNAL(triggered()), this, SLOT(hgRevert())); jtkorhonen@0: connect(hgMergeAct, SIGNAL(triggered()), this, SLOT(hgMerge())); Chris@164: // connect(hgTagAct, SIGNAL(triggered()), this, SLOT(hgTag())); jtkorhonen@34: connect(hgIgnoreAct, SIGNAL(triggered()), this, SLOT(hgIgnore())); jtkorhonen@0: jtkorhonen@0: connect(settingsAct, SIGNAL(triggered()), this, SLOT(settings())); Chris@69: connect(openAct, SIGNAL(triggered()), this, SLOT(open())); jtkorhonen@0: jtkorhonen@0: connect(hgInitAct, SIGNAL(triggered()), this, SLOT(hgInit())); jtkorhonen@0: connect(hgCloneFromRemoteAct, SIGNAL(triggered()), this, SLOT(hgCloneFromRemote())); jtkorhonen@0: connect(hgIncomingAct, SIGNAL(triggered()), this, SLOT(hgIncoming())); jtkorhonen@0: connect(hgPullAct, SIGNAL(triggered()), this, SLOT(hgPull())); jtkorhonen@0: connect(hgPushAct, SIGNAL(triggered()), this, SLOT(hgPush())); jtkorhonen@0: Chris@109: // connect(hgTabs, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int))); jtkorhonen@0: Chris@148: // connect(hgUpdateToRevAct, SIGNAL(triggered()), this, SLOT(hgUpdateToRev())); jtkorhonen@0: connect(hgAnnotateAct, SIGNAL(triggered()), this, SLOT(hgAnnotate())); jtkorhonen@11: connect(hgServeAct, SIGNAL(triggered()), this, SLOT(hgServe())); Chris@94: connect(clearSelectionsAct, SIGNAL(triggered()), this, SLOT(clearSelections())); jtkorhonen@0: } Chris@141: Chris@141: void MainWindow::connectTabsSignals() Chris@141: { Chris@141: connect(hgTabs, SIGNAL(commit()), Chris@141: this, SLOT(hgCommit())); Chris@141: Chris@141: connect(hgTabs, SIGNAL(revert()), Chris@141: this, SLOT(hgRevert())); Chris@141: Chris@141: connect(hgTabs, SIGNAL(diffWorkingFolder()), Chris@141: this, SLOT(hgFolderDiff())); Chris@148: Chris@141: connect(hgTabs, SIGNAL(updateTo(QString)), Chris@148: this, SLOT(hgUpdateToRev(QString))); Chris@141: Chris@141: connect(hgTabs, SIGNAL(diffToCurrent(QString)), Chris@148: this, SLOT(hgDiffToCurrent(QString))); Chris@141: Chris@148: connect(hgTabs, SIGNAL(diffToParent(QString, QString)), Chris@148: this, SLOT(hgDiffToParent(QString, QString))); Chris@141: Chris@141: connect(hgTabs, SIGNAL(mergeFrom(QString)), Chris@148: this, SLOT(hgMergeFrom(QString))); Chris@164: Chris@141: connect(hgTabs, SIGNAL(tag(QString)), Chris@148: this, SLOT(hgTag(QString))); Chris@141: } Chris@141: Chris@109: /*!!! jtkorhonen@0: void MainWindow::tabChanged(int currTab) jtkorhonen@0: { jtkorhonen@0: tabPage = currTab; jtkorhonen@32: jtkorhonen@0: } Chris@109: */ jtkorhonen@0: void MainWindow::enableDisableActions() jtkorhonen@0: { Chris@90: DEBUG << "MainWindow::enableDisableActions" << endl; Chris@90: Chris@115: //!!! should also do things like set the status texts for the Chris@115: //!!! actions appropriately by context Chris@115: jtkorhonen@0: QDir localRepoDir; jtkorhonen@0: QDir workFolderDir; Chris@145: bool workFolderExist = true; Chris@145: bool localRepoExist = true; jtkorhonen@0: jtkorhonen@0: remoteRepoActionsEnabled = true; Chris@90: if (remoteRepoPath.isEmpty()) { jtkorhonen@0: remoteRepoActionsEnabled = false; jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: localRepoActionsEnabled = true; Chris@90: if (workFolderPath.isEmpty()) { jtkorhonen@0: localRepoActionsEnabled = false; jtkorhonen@0: workFolderExist = false; jtkorhonen@0: } jtkorhonen@0: Chris@90: if (!workFolderDir.exists(workFolderPath)) { jtkorhonen@0: localRepoActionsEnabled = false; jtkorhonen@0: workFolderExist = false; Chris@90: } else { jtkorhonen@0: workFolderExist = true; jtkorhonen@0: } jtkorhonen@0: Chris@112: if (!localRepoDir.exists(workFolderPath + "/.hg")) { jtkorhonen@0: localRepoActionsEnabled = false; jtkorhonen@0: localRepoExist = false; jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: hgCloneFromRemoteAct -> setEnabled(remoteRepoActionsEnabled); jtkorhonen@0: hgIncomingAct -> setEnabled(remoteRepoActionsEnabled && remoteRepoActionsEnabled); jtkorhonen@0: hgPullAct -> setEnabled(remoteRepoActionsEnabled && remoteRepoActionsEnabled); jtkorhonen@0: hgPushAct -> setEnabled(remoteRepoActionsEnabled && remoteRepoActionsEnabled); Chris@73: /* jtkorhonen@0: if (tabPage != WORKTAB) jtkorhonen@0: { jtkorhonen@0: localRepoActionsEnabled = false; jtkorhonen@0: } Chris@73: */ Chris@112: bool haveDiff = (diffBinaryName != ""); Chris@112: jtkorhonen@0: hgInitAct -> setEnabled((localRepoExist == false) && (workFolderExist==true)); Chris@120: hgRefreshAct -> setEnabled(localRepoActionsEnabled); Chris@112: hgFolderDiffAct -> setEnabled(localRepoActionsEnabled && haveDiff); jtkorhonen@0: hgRevertAct -> setEnabled(localRepoActionsEnabled); jtkorhonen@0: hgAddAct -> setEnabled(localRepoActionsEnabled); jtkorhonen@0: hgRemoveAct -> setEnabled(localRepoActionsEnabled); jtkorhonen@0: hgUpdateAct -> setEnabled(localRepoActionsEnabled); jtkorhonen@0: hgCommitAct -> setEnabled(localRepoActionsEnabled); jtkorhonen@0: hgMergeAct -> setEnabled(localRepoActionsEnabled); jtkorhonen@2: hgAnnotateAct -> setEnabled(localRepoActionsEnabled); jtkorhonen@11: hgServeAct -> setEnabled(localRepoActionsEnabled); jtkorhonen@34: hgTagAct -> setEnabled(localRepoActionsEnabled); jtkorhonen@34: hgIgnoreAct -> setEnabled(localRepoActionsEnabled); jtkorhonen@0: Chris@98: //!!!hgTabs -> enableDisableOtherTabs(tabPage); jtkorhonen@0: Chris@90: DEBUG << "localRepoActionsEnabled = " << localRepoActionsEnabled << endl; Chris@98: DEBUG << "canCommit = " << hgTabs->canCommit() << endl; Chris@90: Chris@90: //!!! new stuff: Chris@98: hgAddAct->setEnabled(localRepoActionsEnabled && hgTabs->canAdd()); Chris@98: hgRemoveAct->setEnabled(localRepoActionsEnabled && hgTabs->canRemove()); Chris@98: hgCommitAct->setEnabled(localRepoActionsEnabled && hgTabs->canCommit()); Chris@163: hgRevertAct->setEnabled(localRepoActionsEnabled && hgTabs->canRevert()); Chris@163: hgFolderDiffAct->setEnabled(localRepoActionsEnabled && hgTabs->canDiff()); Chris@90: Chris@108: // A default merge makes sense if: Chris@108: // * there is only one parent (if there are two, we have an uncommitted merge) and Chris@108: // * there are exactly two heads that have the same branch as the current branch and Chris@108: // * our parent is one of those heads Chris@108: // Chris@108: // A default update makes sense if: Chris@108: // * there is only one parent and Chris@108: // * the parent is not one of the current heads Chris@156: Chris@108: bool canMerge = false; Chris@108: bool canUpdate = false; Chris@156: bool haveMerge = false; Chris@162: bool emptyRepo = false; Chris@156: int currentBranchHeads = 0; Chris@156: Chris@108: if (currentParents.size() == 1) { Chris@156: bool parentIsHead = false; Chris@108: Changeset *parent = currentParents[0]; Chris@108: foreach (Changeset *head, currentHeads) { Chris@108: DEBUG << "head branch " << head->branch() << ", current branch " << currentBranch << endl; Chris@108: if (head->isOnBranch(currentBranch)) { Chris@108: ++currentBranchHeads; Chris@108: if (parent->id() == head->id()) { Chris@108: parentIsHead = true; Chris@108: } Chris@108: } Chris@108: } Chris@108: if (currentBranchHeads == 2 && parentIsHead) { Chris@108: canMerge = true; Chris@108: } Chris@108: if (!parentIsHead) { Chris@108: canUpdate = true; Chris@108: DEBUG << "parent id = " << parent->id() << endl; Chris@108: DEBUG << " head ids "<setState(tr("Nothing committed to this repository yet")); Chris@162: } else if (canMerge) { Chris@156: hgTabs->setState(tr("Awaiting merge on %1").arg(branchText)); Chris@163: } else if (!hgTabs->getAllUnresolvedFiles().empty()) { Chris@163: hgTabs->setState(tr("Have unresolved files following merge on %1").arg(branchText)); Chris@156: } else if (haveMerge) { Chris@157: hgTabs->setState(tr("Have merged but not yet committed on %1").arg(branchText)); Chris@156: } else if (canUpdate) { Chris@163: if (hgTabs->canRevert()) { Chris@163: // have uncommitted changes Chris@163: hgTabs->setState(tr("On %1. Not at the head of the branch").arg(branchText)); Chris@163: } else { Chris@163: // no uncommitted changes Chris@163: hgTabs->setState(tr("On %1. Not at the head of the branch: consider updating").arg(branchText)); Chris@163: } Chris@156: } else if (currentBranchHeads > 1) { Chris@156: hgTabs->setState(tr("At one of %n heads of %1", "", currentBranchHeads).arg(branchText)); Chris@115: } else { Chris@115: hgTabs->setState(tr("At the head of %1").arg(branchText)); Chris@115: } jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: void MainWindow::createActions() jtkorhonen@0: { jtkorhonen@0: //File actions jtkorhonen@0: hgInitAct = new QAction(tr("Init local repository"), this); jtkorhonen@0: hgInitAct->setStatusTip(tr("Create an empty local repository in selected folder")); jtkorhonen@0: jtkorhonen@0: hgCloneFromRemoteAct = new QAction(tr("Clone from remote"), this); jtkorhonen@0: hgCloneFromRemoteAct->setStatusTip(tr("Clone from remote repository into local repository in selected folder")); jtkorhonen@0: Chris@69: openAct = new QAction(QIcon(":/images/fileopen.png"), tr("Open..."), this); Chris@69: openAct -> setStatusTip(tr("Open repository")); Chris@69: openAct -> setIconVisibleInMenu(true); Chris@69: jtkorhonen@0: settingsAct = new QAction(QIcon(":/images/settings.png"), tr("Settings..."), this); jtkorhonen@0: settingsAct -> setStatusTip(tr("View and change application settings")); jtkorhonen@0: settingsAct -> setIconVisibleInMenu(true); jtkorhonen@0: jtkorhonen@0: exitAct = new QAction(QIcon(":/images/exit.png"), tr("Exit"), this); jtkorhonen@0: exitAct->setShortcuts(QKeySequence::Quit); jtkorhonen@0: exitAct->setStatusTip(tr("Exit application")); jtkorhonen@0: exitAct -> setIconVisibleInMenu(true); jtkorhonen@0: jtkorhonen@0: //Repository actions Chris@120: hgRefreshAct = new QAction(QIcon(":/images/status.png"), tr("Refresh"), this); Chris@120: hgRefreshAct->setStatusTip(tr("Refresh (info of) status of workfolder files")); Chris@61: Chris@61: hgIncomingAct = new QAction(QIcon(":/images/incoming.png"), tr("Preview"), this); jtkorhonen@0: hgIncomingAct -> setStatusTip(tr("View info of changesets incoming to us from remote repository (on pull operation)")); jtkorhonen@0: Chris@61: hgPullAct = new QAction(QIcon(":/images/pull.png"), tr("Pull"), this); jtkorhonen@0: hgPullAct -> setStatusTip(tr("Pull changesets from remote repository to local repository")); jtkorhonen@0: Chris@61: hgPushAct = new QAction(QIcon(":/images/push.png"), tr("Push"), this); jtkorhonen@0: hgPushAct->setStatusTip(tr("Push local changesets to remote repository")); jtkorhonen@0: jtkorhonen@0: //Workfolder actions Chris@99: hgFolderDiffAct = new QAction(QIcon(":/images/folderdiff.png"), tr("Diff"), this); jtkorhonen@0: hgFolderDiffAct->setStatusTip(tr("Folderdiff: View all differences between working folder files and local repository files")); jtkorhonen@0: jtkorhonen@0: hgChgSetDiffAct = new QAction(QIcon(":/images/chgsetdiff.png"), tr("View changesetdiff"), this); jtkorhonen@0: hgChgSetDiffAct->setStatusTip(tr("Change set diff: View differences between all files of 2 repository changesets")); jtkorhonen@0: Chris@61: hgRevertAct = new QAction(QIcon(":/images/undo.png"), tr("Revert"), this); jtkorhonen@0: hgRevertAct->setStatusTip(tr("Undo selected working folder file changes (return to local repository version)")); jtkorhonen@0: Chris@61: hgAddAct = new QAction(QIcon(":/images/add.png"), tr("Add"), this); jtkorhonen@17: hgAddAct -> setStatusTip(tr("Add working folder file(s) (selected or all yet untracked) to local repository (on next commit)")); jtkorhonen@0: Chris@61: hgRemoveAct = new QAction(QIcon(":/images/remove.png"), tr("Remove"), this); jtkorhonen@0: hgRemoveAct -> setStatusTip(tr("Remove selected working folder file from local repository (on next commit)")); jtkorhonen@0: Chris@61: hgUpdateAct = new QAction(QIcon(":/images/update.png"), tr("Update"), this); jtkorhonen@0: hgUpdateAct->setStatusTip(tr("Update working folder from local repository")); jtkorhonen@0: Chris@61: hgCommitAct = new QAction(QIcon(":/images/commit.png"), tr("Commit"), this); jtkorhonen@20: hgCommitAct->setStatusTip(tr("Save selected file(s) or all changed files in working folder (and all subfolders) to local repository")); jtkorhonen@0: jtkorhonen@0: hgMergeAct = new QAction(QIcon(":/images/merge.png"), tr("Merge"), this); jtkorhonen@0: hgMergeAct->setStatusTip(tr("Merge two local repository changesets to working folder")); jtkorhonen@0: jtkorhonen@0: //Advanced actions Chris@148: /* hgUpdateToRevAct = new QAction(tr("Update to revision"), this); jtkorhonen@0: hgUpdateToRevAct -> setStatusTip(tr("Update working folder to version selected from history list")); Chris@148: */ jtkorhonen@0: hgAnnotateAct = new QAction(tr("Annotate"), this); jtkorhonen@0: hgAnnotateAct -> setStatusTip(tr("Show line-by-line version information for selected file")); jtkorhonen@0: jtkorhonen@34: hgTagAct = new QAction(tr("Tag revision"), this); jtkorhonen@34: hgTagAct -> setStatusTip(tr("Give decsriptive name (tag) to current workfolder parent revision.")); jtkorhonen@34: jtkorhonen@34: hgIgnoreAct = new QAction(tr("Edit .hgignore"), this); jtkorhonen@34: hgIgnoreAct -> setStatusTip(tr("Edit .hgignore file (file contains names of files that should be ignored by mercurial)")); jtkorhonen@34: jtkorhonen@11: hgServeAct = new QAction(tr("Serve (via http)"), this); jtkorhonen@11: hgServeAct -> setStatusTip(tr("Serve local repository via http for workgroup access")); jtkorhonen@11: jtkorhonen@0: //Help actions jtkorhonen@0: aboutAct = new QAction(tr("About"), this); jtkorhonen@0: aboutAct->setStatusTip(tr("Show the application's About box")); jtkorhonen@0: jtkorhonen@0: aboutQtAct = new QAction(tr("About Qt"), this); jtkorhonen@0: aboutQtAct->setStatusTip(tr("Show the Qt library's About box")); Chris@94: Chris@94: // Miscellaneous Chris@94: clearSelectionsAct = new QAction(tr("Clear selections"), this); Chris@94: clearSelectionsAct->setShortcut(Qt::Key_Escape); jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: void MainWindow::createMenus() jtkorhonen@0: { jtkorhonen@0: fileMenu = menuBar()->addMenu(tr("File")); Chris@131: /* fileMenu -> addAction(hgInitAct); jtkorhonen@0: fileMenu -> addAction(hgCloneFromRemoteAct); Chris@94: fileMenu->addAction(clearSelectionsAct); //!!! can't live here! jtkorhonen@0: fileMenu -> addSeparator(); Chris@131: */ Chris@69: fileMenu -> addAction(openAct); jtkorhonen@0: fileMenu -> addAction(settingsAct); jtkorhonen@0: fileMenu -> addSeparator(); jtkorhonen@0: fileMenu -> addAction(exitAct); jtkorhonen@0: jtkorhonen@0: advancedMenu = menuBar()->addMenu(tr("Advanced")); Chris@131: /* jtkorhonen@0: advancedMenu -> addAction(hgUpdateToRevAct); jtkorhonen@0: advancedMenu -> addSeparator(); jtkorhonen@0: advancedMenu -> addAction(hgAnnotateAct); jtkorhonen@0: advancedMenu -> addSeparator(); jtkorhonen@33: advancedMenu -> addAction(hgRetryMergeAct); jtkorhonen@0: advancedMenu -> addAction(hgResolveListAct); jtkorhonen@0: advancedMenu -> addAction(hgResolveMarkAct); jtkorhonen@11: advancedMenu -> addSeparator(); jtkorhonen@34: advancedMenu -> addAction(hgTagAct); jtkorhonen@34: advancedMenu -> addSeparator(); Chris@131: */ jtkorhonen@34: advancedMenu -> addAction(hgIgnoreAct); jtkorhonen@34: advancedMenu -> addSeparator(); jtkorhonen@11: advancedMenu -> addAction(hgServeAct); jtkorhonen@0: jtkorhonen@0: helpMenu = menuBar()->addMenu(tr("Help")); jtkorhonen@0: helpMenu->addAction(aboutAct); Chris@128: //!!! helpMenu->addAction(aboutQtAct); jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: void MainWindow::createToolBars() jtkorhonen@0: { jtkorhonen@0: fileToolBar = addToolBar(tr("File")); jtkorhonen@0: fileToolBar -> setIconSize(QSize(MY_ICON_SIZE, MY_ICON_SIZE)); Chris@69: fileToolBar -> addAction(openAct); Chris@120: fileToolBar -> addAction(hgRefreshAct); jtkorhonen@0: fileToolBar -> addSeparator(); Chris@61: // fileToolBar -> addAction(hgChgSetDiffAct); jtkorhonen@0: fileToolBar -> setMovable(false); jtkorhonen@0: jtkorhonen@0: repoToolBar = addToolBar(tr(REPOMENU_TITLE)); jtkorhonen@0: repoToolBar -> setIconSize(QSize(MY_ICON_SIZE, MY_ICON_SIZE)); jtkorhonen@0: repoToolBar->addAction(hgIncomingAct); jtkorhonen@0: repoToolBar->addAction(hgPullAct); jtkorhonen@0: repoToolBar->addAction(hgPushAct); jtkorhonen@0: repoToolBar -> setMovable(false); jtkorhonen@0: jtkorhonen@0: workFolderToolBar = addToolBar(tr(WORKFOLDERMENU_TITLE)); jtkorhonen@0: addToolBar(Qt::LeftToolBarArea, workFolderToolBar); jtkorhonen@0: workFolderToolBar -> setIconSize(QSize(MY_ICON_SIZE, MY_ICON_SIZE)); Chris@95: workFolderToolBar->addAction(hgFolderDiffAct); jtkorhonen@0: workFolderToolBar->addSeparator(); jtkorhonen@0: workFolderToolBar->addAction(hgRevertAct); jtkorhonen@0: workFolderToolBar->addAction(hgUpdateAct); jtkorhonen@0: workFolderToolBar->addAction(hgCommitAct); jtkorhonen@0: workFolderToolBar->addAction(hgMergeAct); jtkorhonen@0: workFolderToolBar->addSeparator(); jtkorhonen@0: workFolderToolBar->addAction(hgAddAct); jtkorhonen@0: workFolderToolBar->addAction(hgRemoveAct); jtkorhonen@0: workFolderToolBar -> setMovable(false); Chris@61: Chris@61: foreach (QToolButton *tb, findChildren()) { Chris@61: tb->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); Chris@61: } jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: jtkorhonen@0: void MainWindow::createStatusBar() jtkorhonen@0: { jtkorhonen@0: statusBar()->showMessage(tr("Ready")); jtkorhonen@0: } jtkorhonen@0: Chris@69: Chris@69: //!!! review these: Chris@69: jtkorhonen@0: void MainWindow::readSettings() jtkorhonen@0: { jtkorhonen@0: QDir workFolder; jtkorhonen@0: Chris@61: QSettings settings; jtkorhonen@0: jtkorhonen@30: remoteRepoPath = settings.value("remoterepopath", "").toString(); jtkorhonen@0: workFolderPath = settings.value("workfolderpath", "").toString(); jtkorhonen@0: if (!workFolder.exists(workFolderPath)) jtkorhonen@0: { jtkorhonen@0: workFolderPath = ""; jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint(); jtkorhonen@0: QSize size = settings.value("size", QSize(400, 400)).toSize(); jtkorhonen@0: firstStart = settings.value("firststart", QVariant(true)).toBool(); jtkorhonen@0: Chris@109: //!!! initialFileTypesBits = (unsigned char) settings.value("viewFileTypes", QVariant(DEFAULT_HG_STAT_BITS)).toInt(); jtkorhonen@0: resize(size); jtkorhonen@0: move(pos); jtkorhonen@0: } jtkorhonen@0: jtkorhonen@17: jtkorhonen@0: void MainWindow::writeSettings() jtkorhonen@0: { Chris@61: QSettings settings; jtkorhonen@0: settings.setValue("pos", pos()); jtkorhonen@0: settings.setValue("size", size()); jtkorhonen@0: settings.setValue("remoterepopath", remoteRepoPath); jtkorhonen@0: settings.setValue("workfolderpath", workFolderPath); jtkorhonen@0: settings.setValue("firststart", firstStart); Chris@98: //!!!settings.setValue("viewFileTypes", hgTabs -> getFileTypesBits()); jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: jtkorhonen@0: jtkorhonen@0: