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@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@112: Chris@64: ColourSet *cs = ColourSet::instance(); Chris@64: cs->clearDefaultNames(); Chris@64: cs->addDefaultName(""); 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@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@150: params << "--prune" << 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::hgResolveMark() Chris@109: { Chris@109: QStringList params; Chris@109: QString currentFile;//!!! = hgTabs -> getCurrentFileListLine(); jtkorhonen@0: Chris@109: if (!currentFile.isEmpty()) jtkorhonen@0: { Chris@109: params << "resolve" << "--mark" << "--" << currentFile.mid(2); //Jump over status marker characters (e.g "M ") jtkorhonen@0: Chris@109: runner->requestAction(HgAction(ACT_RESOLVE_MARK, 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: } Chris@98: Chris@91: /*!!! Chris@98: QString currentFile;//!!! = hgTabs -> getCurrentFileListLine(); Chris@98: Chris@98: if (!currentFile.isEmpty()) jtkorhonen@5: { Chris@98: if (QMessageBox::Ok == QMessageBox::warning(this, "Remove file", "Really remove file " + currentFile.mid(2) + "?", Chris@98: QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel)) Chris@98: { Chris@98: params << "remove" << "--after" << "--force" << "--" << currentFile.mid(2); //Jump over status marker characters (e.g "M ") jtkorhonen@17: Chris@98: runner -> startHgCommand(workFolderPath, params); Chris@98: runningAction = ACT_REMOVE; jtkorhonen@17: } jtkorhonen@5: } Chris@98: */ jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: void MainWindow::hgCommit() jtkorhonen@0: { Chris@109: QStringList params; Chris@109: QString comment; Chris@94: Chris@109: QStringList files = hgTabs->getSelectedCommittableFiles(); Chris@127: QStringList reportFiles = files; Chris@127: if (reportFiles.empty()) reportFiles = hgTabs->getAllCommittableFiles(); Chris@103: Chris@109: if (ConfirmCommentDialog::confirmAndGetLongComment Chris@109: (this, Chris@109: tr("Commit files"), Chris@112: tr("

Commit files

You are about to commit the following files:"), Chris@112: tr("

Commit files

You are about to commit %1 files."), Chris@127: reportFiles, Chris@109: comment)) { Chris@103: Chris@109: if ((justMerged == false) && //!!! review usage of justMerged, and how it interacts with asynchronous request queue Chris@109: !files.empty()) { Chris@109: // User wants to commit selected file(s) (and this is not merge commit, which would fail if we selected files) Chris@109: params << "commit" << "--message" << comment << "--user" << getUserInfo() << "--" << files; Chris@109: } else { Chris@109: // Commit all changes Chris@109: params << "commit" << "--message" << comment << "--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: jtkorhonen@34: void MainWindow::hgTag() 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@109: if (!tag.isEmpty()) //!!! do something better if it is empty Chris@109: { Chris@109: params << "tag" << "--user" << getUserInfo() << 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: jtkorhonen@0: void MainWindow::hgFileDiff() jtkorhonen@0: { jtkorhonen@0: QStringList params; Chris@91: /*!!! Chris@98: QString currentFile = hgTabs -> getCurrentFileListLine(); jtkorhonen@0: jtkorhonen@0: if (!currentFile.isEmpty()) jtkorhonen@0: { jtkorhonen@0: //Diff parent file against working folder file mg@41: params << "kdiff3" << "--" << currentFile.mid(2); Chris@62: runner -> startHgCommand(workFolderPath, params); jtkorhonen@0: runningAction = ACT_FILEDIFF; jtkorhonen@0: } Chris@91: */ jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: 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@109: runner->requestAction(HgAction(ACT_FOLDERDIFF, workFolderPath, params)); jtkorhonen@0: } jtkorhonen@0: jtkorhonen@0: Chris@148: void MainWindow::hgDiffToCurrent(QString id) jtkorhonen@0: { 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@148: params << "--rev" << 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@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@148: params << "--rev" << parent << "--rev" << 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@148: params << "update" << "--rev" << 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@109: Chris@109: if (ConfirmCommentDialog::confirmDangerousFilesAction Chris@109: (this, Chris@109: tr("Revert files"), Chris@127: tr("

Revert files

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@112: tr("

Revert files

You are about to revert %1 files.

This will throw away any changes that you have made to these files but have not committed."), Chris@109: files)) { Chris@109: Chris@98: if (files.empty()) { Chris@98: params << "revert" << "--no-backup"; Chris@98: } else { Chris@98: params << "revert" << "--no-backup" << "--" << files; Chris@98: } Chris@109: Chris@109: runner->requestAction(HgAction(ACT_REVERT, workFolderPath, params)); jtkorhonen@0: } jtkorhonen@0: } jtkorhonen@0: jtkorhonen@33: void MainWindow::hgRetryMerge() jtkorhonen@33: { Chris@109: QStringList params; jtkorhonen@33: Chris@109: params << "resolve" << "--all"; Chris@109: runner->requestAction(HgAction(ACT_RETRY_MERGE, workFolderPath, params)); jtkorhonen@33: } jtkorhonen@33: jtkorhonen@33: jtkorhonen@0: void MainWindow::hgMerge() jtkorhonen@0: { Chris@109: QStringList params; jtkorhonen@0: Chris@109: params << "merge"; Chris@109: Chris@109: runner->requestAction(HgAction(ACT_MERGE, workFolderPath, params)); 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@148: params << "--rev" << id; Chris@148: Chris@148: runner->requestAction(HgAction(ACT_MERGE, workFolderPath, params)); 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@126: params << "push" << 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@113: case ACT_FOLDERDIFF: Chris@113: case ACT_FILEDIFF: 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@150: Chris@150: /* todo: Chris@150: if ((runningAction == ACT_MERGE) && (exitCode != 0)) Chris@150: { Chris@150: // If we had a failed merge, offer to retry Chris@150: if (QMessageBox::Ok == QMessageBox::information(this, tr("Retry merge ?"), tr("Merge attempt failed. retry ?"), QMessageBox::Ok | QMessageBox::Cancel)) Chris@150: { Chris@150: runningAction = ACT_NONE; Chris@150: hgRetryMerge(); Chris@150: } Chris@150: else Chris@150: { Chris@150: runningAction = ACT_NONE; Chris@150: hgStat(); Chris@150: } Chris@150: } Chris@150: else Chris@150: { Chris@150: */ 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 shouldHgStat = false; 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@113: hgTabs->updateWorkFolderFileList(output); Chris@109: updateFileSystemWatcher(); Chris@109: break; Chris@109: Chris@109: case ACT_INCOMING: Chris@120: showIncoming(output); Chris@120: break; Chris@120: Chris@109: case ACT_ANNOTATE: Chris@109: case ACT_RESOLVE_LIST: Chris@109: case ACT_RESOLVE_MARK: 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@109: foreach (Changeset *cs, currentParents) delete cs; Chris@109: currentParents = Changeset::parseChangesets(output); 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@130: shouldHgStat = true; Chris@130: break; Chris@109: Chris@109: case ACT_REMOVE: Chris@109: case ACT_ADD: Chris@116: case ACT_REVERT: Chris@116: hgTabs->clearSelections(); Chris@116: shouldHgStat = true; Chris@116: break; Chris@116: Chris@109: case ACT_FILEDIFF: Chris@109: case ACT_FOLDERDIFF: Chris@109: case ACT_CHGSETDIFF: Chris@109: case ACT_SERVE: Chris@109: case ACT_TAG: Chris@109: case ACT_HG_IGNORE: Chris@109: shouldHgStat = true; Chris@109: break; Chris@109: Chris@109: case ACT_UPDATE: Chris@109: QMessageBox::information(this, tr("Update"), output); Chris@109: shouldHgStat = true; Chris@109: break; Chris@109: Chris@109: case ACT_MERGE: Chris@109: QMessageBox::information(this, tr("Merge"), output); Chris@109: shouldHgStat = true; Chris@109: justMerged = true; Chris@109: break; Chris@109: Chris@109: case ACT_RETRY_MERGE: Chris@150: QMessageBox::information(this, tr("Merge retry"), Chris@150: tr("Merge retry successful.")); 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@150: // paths -> branch -> stat -> heads -> Chris@150: // incremental-log (only if heads changed) -> parents Chris@150: // Chris@121: // Sequence when full log required: Chris@121: // paths -> branch -> stat -> 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@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@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@150: enableDisableActions(); Chris@120: } Chris@150: break; Chris@150: Chris@150: case ACT_LOG: Chris@150: // we're done Chris@150: enableDisableActions(); Chris@150: Chris@150: default: Chris@109: if (shouldHgStat) { Chris@109: hgQueryPaths(); Chris@150: } else { Chris@150: enableDisableActions(); jtkorhonen@0: } Chris@150: break; Chris@150: } 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(hgFileDiffAct, SIGNAL(triggered()), this, SLOT(hgFileDiff())); 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())); jtkorhonen@33: connect(hgRetryMergeAct, SIGNAL(triggered()), this, SLOT(hgRetryMerge())); jtkorhonen@34: 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@0: connect(hgResolveListAct, SIGNAL(triggered()), this, SLOT(hgResolveList())); jtkorhonen@0: connect(hgResolveMarkAct, SIGNAL(triggered()), this, SLOT(hgResolveMark())); 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@148: /* Chris@141: connect(hgTabs, SIGNAL(tag(QString)), Chris@148: this, SLOT(hgTag(QString))); Chris@141: */ 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: hgFileDiffAct -> setEnabled(localRepoActionsEnabled && haveDiff); 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@33: hgRetryMergeAct -> setEnabled(localRepoActionsEnabled); jtkorhonen@2: hgResolveListAct -> setEnabled(localRepoActionsEnabled); jtkorhonen@2: hgResolveMarkAct -> 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@98: hgRevertAct->setEnabled(localRepoActionsEnabled && hgTabs->canCommit()); Chris@98: hgFolderDiffAct->setEnabled(localRepoActionsEnabled && hgTabs->canDoDiff()); 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@108: //!!! test this Chris@108: bool canMerge = false; Chris@108: bool canUpdate = false; Chris@108: if (currentParents.size() == 1) { Chris@108: Changeset *parent = currentParents[0]; Chris@108: int currentBranchHeads = 0; Chris@108: bool parentIsHead = false; 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("On %1. Not at the head of the branch: consider updating").arg(branchText)); Chris@115: } else if (canMerge) { Chris@115: hgTabs->setState(tr("Awaiting merge on %1").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@61: hgFileDiffAct = new QAction(QIcon(":/images/diff.png"), tr("Diff"), this); jtkorhonen@0: hgFileDiffAct->setStatusTip(tr("Filediff: View differences between selected working folder file and local repository file")); jtkorhonen@0: 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@0: hgResolveListAct = new QAction(tr("Resolve (list)"), this); jtkorhonen@0: hgResolveListAct -> setStatusTip(tr("Resolve (list): Show list of files needing merge")); jtkorhonen@0: jtkorhonen@0: hgResolveMarkAct = new QAction(tr("Resolve (mark)"), this); jtkorhonen@0: hgResolveMarkAct -> setStatusTip(tr("Resolve (mark): Mark selected file status as resolved")); jtkorhonen@0: jtkorhonen@33: hgRetryMergeAct = new QAction(tr("Retry merge"), this); jtkorhonen@33: hgRetryMergeAct -> setStatusTip(tr("Retry merge after failed merge attempt.")); jtkorhonen@33: 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@61: // workFolderToolBar->addSeparator(); Chris@95: // workFolderToolBar->addAction(hgFileDiffAct); 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: