annotate mainwindow.cpp @ 200:8c8c04bdf0fa

* Separate out the hg test action into two tests, one for plain hg and one with the extension (so can report separately)
author Chris Cannam
date Tue, 04 Jan 2011 12:42:28 +0000
parents f16fe0db11f3
children 3d4291d4226c
rev   line source
Chris@57 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
jtkorhonen@0 2
Chris@57 3 /*
Chris@57 4 EasyMercurial
Chris@57 5
Chris@98 6 Based on hgExplorer by Jari Korhonen
Chris@57 7 Copyright (c) 2010 Jari Korhonen
Chris@57 8 Copyright (c) 2010 Chris Cannam
Chris@57 9 Copyright (c) 2010 Queen Mary, University of London
Chris@57 10
Chris@57 11 This program is free software; you can redistribute it and/or
Chris@57 12 modify it under the terms of the GNU General Public License as
Chris@57 13 published by the Free Software Foundation; either version 2 of the
Chris@57 14 License, or (at your option) any later version. See the file
Chris@57 15 COPYING included with this distribution for more information.
Chris@57 16 */
Chris@50 17
jtkorhonen@0 18 #include <QStringList>
jtkorhonen@0 19 #include <QDir>
jtkorhonen@24 20 #include <QNetworkInterface>
jtkorhonen@17 21 #include <QHostAddress>
jtkorhonen@17 22 #include <QHostInfo>
jtkorhonen@34 23 #include <QDesktopServices>
Chris@50 24 #include <QStatusBar>
Chris@50 25 #include <QMessageBox>
Chris@50 26 #include <QMenuBar>
Chris@50 27 #include <QApplication>
Chris@50 28 #include <QToolBar>
Chris@61 29 #include <QToolButton>
Chris@50 30 #include <QSettings>
Chris@90 31 #include <QInputDialog>
Chris@125 32 #include <QRegExp>
Chris@199 33 #include <QShortcut>
jtkorhonen@0 34
Chris@53 35 #include "mainwindow.h"
Chris@69 36 #include "multichoicedialog.h"
Chris@64 37 #include "startupdialog.h"
Chris@53 38 #include "colourset.h"
Chris@62 39 #include "debug.h"
Chris@74 40 #include "logparser.h"
Chris@103 41 #include "confirmcommentdialog.h"
Chris@125 42 #include "incomingdialog.h"
Chris@175 43 #include "settingsdialog.h"
Chris@53 44
jtkorhonen@0 45
Chris@172 46 MainWindow::MainWindow(QString myDirPath) :
Chris@172 47 m_myDirPath(myDirPath)
jtkorhonen@0 48 {
Chris@197 49 setWindowIcon(QIcon(":images/easyhg-icon.png"));
Chris@197 50
jtkorhonen@0 51 QString wndTitle;
jtkorhonen@0 52
Chris@199 53 showAllFiles = false;
Chris@199 54
Chris@90 55 fsWatcher = 0;
Chris@133 56 commitsSincePush = 0;
Chris@163 57 shouldHgStat = true;
Chris@90 58
jtkorhonen@0 59 createActions();
jtkorhonen@0 60 createMenus();
jtkorhonen@0 61 createToolBars();
jtkorhonen@0 62 createStatusBar();
jtkorhonen@0 63
Chris@172 64 runner = new HgRunner(myDirPath, this);
Chris@109 65 connect(runner, SIGNAL(commandCompleted(HgAction, QString)),
Chris@109 66 this, SLOT(commandCompleted(HgAction, QString)));
Chris@109 67 connect(runner, SIGNAL(commandFailed(HgAction, QString)),
Chris@109 68 this, SLOT(commandFailed(HgAction, QString)));
jtkorhonen@0 69 statusBar()->addPermanentWidget(runner);
jtkorhonen@0 70
Chris@61 71 setWindowTitle(tr("EasyMercurial"));
jtkorhonen@0 72
jtkorhonen@0 73 remoteRepoPath = "";
jtkorhonen@0 74 workFolderPath = "";
jtkorhonen@0 75
jtkorhonen@0 76 readSettings();
jtkorhonen@0 77
jtkorhonen@33 78 justMerged = false;
Chris@98 79 hgTabs = new HgTabWidget((QWidget *) this, remoteRepoPath, workFolderPath);
Chris@141 80 connectTabsSignals();
Chris@98 81 setCentralWidget(hgTabs);
jtkorhonen@0 82
Chris@98 83 connect(hgTabs, SIGNAL(selectionChanged()),
Chris@95 84 this, SLOT(enableDisableActions()));
Chris@199 85 connect(hgTabs, SIGNAL(showAllChanged(bool)),
Chris@199 86 this, SLOT(showAllChanged(bool)));
Chris@95 87
jtkorhonen@0 88 setUnifiedTitleAndToolBarOnMac(true);
jtkorhonen@0 89 connectActions();
Chris@120 90 clearState();
jtkorhonen@0 91 enableDisableActions();
jtkorhonen@0 92
Chris@64 93 if (firstStart) {
Chris@64 94 startupDialog();
jtkorhonen@0 95 }
jtkorhonen@0 96
Chris@179 97 (void)findDiffBinaryName();
Chris@179 98 (void)findMergeBinaryName();
Chris@179 99 (void)findEditorBinaryName();
Chris@112 100
Chris@64 101 ColourSet *cs = ColourSet::instance();
Chris@64 102 cs->clearDefaultNames();
Chris@64 103 cs->addDefaultName("");
Chris@153 104 cs->addDefaultName("default");
Chris@64 105 cs->addDefaultName(getUserInfo());
Chris@62 106
Chris@72 107 if (workFolderPath == "") {
Chris@72 108 open();
Chris@72 109 }
Chris@72 110
Chris@175 111 hgTest();
jtkorhonen@0 112 }
jtkorhonen@0 113
jtkorhonen@0 114
jtkorhonen@0 115 void MainWindow::closeEvent(QCloseEvent *)
jtkorhonen@0 116 {
jtkorhonen@0 117 writeSettings();
Chris@90 118 delete fsWatcher;
jtkorhonen@0 119 }
jtkorhonen@0 120
jtkorhonen@0 121
Chris@64 122 QString MainWindow::getUserInfo() const
Chris@64 123 {
Chris@64 124 QSettings settings;
Chris@64 125 settings.beginGroup("User Information");
Chris@64 126 QString name = settings.value("name", getUserRealName()).toString();
Chris@64 127 QString email = settings.value("email", "").toString();
Chris@64 128
Chris@64 129 QString identifier;
Chris@64 130
Chris@64 131 if (email != "") {
Chris@64 132 identifier = QString("%1 <%2>").arg(name).arg(email);
Chris@64 133 } else {
Chris@64 134 identifier = name;
Chris@64 135 }
Chris@64 136
Chris@64 137 return identifier;
Chris@64 138 }
Chris@64 139
jtkorhonen@0 140 void MainWindow::about()
jtkorhonen@0 141 {
Chris@97 142 QMessageBox::about(this, tr("About EasyMercurial"),
Chris@97 143 tr("<qt><h2>About EasyMercurial</h2>"
Chris@97 144 "<p>EasyMercurial is a simple user interface for the "
Chris@186 145 "Mercurial</a> version control system.</p>"
Chris@186 146 "<h4>Credits and Copyright</h4>"
Chris@186 147 "<p>Development carried out by Chris Cannam for "
Chris@186 148 "SoundSoftware.ac.uk at the Centre for Digital Music, "
Chris@186 149 "Queen Mary, University of London.</p>"
Chris@186 150 "<p>EasyMercurial is based on HgExplorer by "
Chris@186 151 "Jari Korhonen, with thanks.</p>"
Chris@186 152 "<p style=\"margin-left: 2em;\">"
Chris@186 153 "Copyright &copy; 2010 Queen Mary, University of London.<br>"
Chris@186 154 "Copyright &copy; 2010 Jari Korhonen.<br>"
Chris@186 155 "Copyright &copy; 2010 Chris Cannam."
Chris@186 156 "</p>"
Chris@186 157 "<p style=\"margin-left: 2em;\">"
Chris@186 158 "This program requires Mercurial, by Matt Mackall and others.<br>"
Chris@186 159 "This program uses Qt by Nokia.<br>"
Chris@186 160 "This program uses Nuvola icons by David Vignoni.<br>"
Chris@186 161 "This program may use KDiff3 by Joachim Eibl.<br>"
Chris@186 162 "This program may use PyQt by River Bank Computing.<br>"
Chris@186 163 "Packaging for Mercurial and other dependencies on Windows is derived from TortoiseHg by Steve Borho and others."
Chris@186 164 "</p>"
Chris@186 165 "<h4>License</h4>"
Chris@186 166 "<p>This program is free software; you can redistribute it and/or "
Chris@97 167 "modify it under the terms of the GNU General Public License as "
Chris@97 168 "published by the Free Software Foundation; either version 2 of the "
Chris@97 169 "License, or (at your option) any later version. See the file "
Chris@186 170 "COPYING included with this distribution for more information.</p>"));
jtkorhonen@0 171 }
jtkorhonen@0 172
Chris@94 173 void MainWindow::clearSelections()
Chris@94 174 {
Chris@98 175 hgTabs->clearSelections();
Chris@94 176 }
jtkorhonen@0 177
Chris@199 178 void MainWindow::showAllChanged(bool s)
Chris@199 179 {
Chris@199 180 showAllFiles = s;
Chris@199 181 hgQueryPaths();
Chris@199 182 }
Chris@199 183
Chris@120 184 void MainWindow::hgRefresh()
Chris@120 185 {
Chris@120 186 clearState();
Chris@120 187 hgQueryPaths();
Chris@120 188 }
Chris@120 189
Chris@175 190 void MainWindow::hgTest()
Chris@175 191 {
Chris@175 192 QStringList params;
Chris@175 193 params << "--version";
Chris@175 194 runner->requestAction(HgAction(ACT_TEST_HG, m_myDirPath, params));
Chris@175 195 }
Chris@175 196
Chris@200 197 void MainWindow::hgTestExtension()
Chris@200 198 {
Chris@200 199 QStringList params;
Chris@200 200 params << "--version";
Chris@200 201 runner->requestAction(HgAction(ACT_TEST_HG_EXT, m_myDirPath, params));
Chris@200 202 }
Chris@200 203
jtkorhonen@0 204 void MainWindow::hgStat()
jtkorhonen@0 205 {
Chris@109 206 QStringList params;
Chris@199 207
Chris@199 208 if (showAllFiles) {
Chris@199 209 params << "stat" << "-A";
Chris@199 210 } else {
Chris@199 211 params << "stat" << "-ardum";
Chris@199 212 }
Chris@153 213
Chris@163 214 lastStatOutput = "";
Chris@163 215
Chris@153 216 // annoyingly, hg stat actually modifies the working directory --
Chris@153 217 // it creates files called hg-checklink and hg-checkexec to test
Chris@153 218 // properties of the filesystem
Chris@153 219 if (fsWatcher) fsWatcher->blockSignals(true);
Chris@153 220
Chris@109 221 runner->requestAction(HgAction(ACT_STAT, workFolderPath, params));
jtkorhonen@0 222 }
jtkorhonen@0 223
Chris@109 224 void MainWindow::hgQueryPaths()
Chris@74 225 {
Chris@198 226 // Quickest is to just read the file -- but fall back on hg
Chris@198 227 // command if we get confused
Chris@198 228
Chris@198 229 QFileInfo hgrc(workFolderPath + "/.hg/hgrc");
Chris@198 230
Chris@198 231 if (hgrc.exists()) {
Chris@198 232
Chris@198 233 QSettings s(hgrc.canonicalFilePath(), QSettings::IniFormat);
Chris@198 234 s.beginGroup("paths");
Chris@198 235 remoteRepoPath = s.value("default").toString();
Chris@198 236
Chris@198 237 // We have to do this here, because commandCompleted won't be called
Chris@198 238 MultiChoiceDialog::addRecentArgument("local", workFolderPath);
Chris@198 239 MultiChoiceDialog::addRecentArgument("remote", remoteRepoPath);
Chris@198 240 hgTabs->setWorkFolderAndRepoNames(workFolderPath, remoteRepoPath);
Chris@198 241
Chris@198 242 hgQueryBranch();
Chris@198 243 return;
Chris@198 244 }
Chris@198 245
Chris@109 246 QStringList params;
Chris@109 247 params << "paths";
Chris@109 248 runner->requestAction(HgAction(ACT_QUERY_PATHS, workFolderPath, params));
Chris@74 249 }
Chris@74 250
Chris@109 251 void MainWindow::hgQueryBranch()
Chris@106 252 {
Chris@198 253 // Quickest is to just read the file -- but fall back on hg
Chris@198 254 // command if we get confused
Chris@198 255
Chris@198 256 QFile hgbr(workFolderPath + "/.hg/branch");
Chris@198 257
Chris@198 258 if (hgbr.exists() && hgbr.open(QFile::ReadOnly)) {
Chris@198 259
Chris@198 260 QByteArray ba = hgbr.readLine();
Chris@198 261 currentBranch = QString::fromUtf8(ba).trimmed();
Chris@198 262
Chris@198 263 // We have to do this here, because commandCompleted won't be called
Chris@198 264 hgStat();
Chris@198 265 return;
Chris@198 266 }
Chris@198 267
Chris@109 268 QStringList params;
Chris@109 269 params << "branch";
Chris@109 270 runner->requestAction(HgAction(ACT_QUERY_BRANCH, workFolderPath, params));
Chris@106 271 }
Chris@106 272
Chris@109 273 void MainWindow::hgQueryHeads()
jtkorhonen@0 274 {
Chris@109 275 QStringList params;
Chris@137 276 // On empty repos, "hg heads" will fail -- we don't care about
Chris@137 277 // that. Use --closed option so as to include closed branches;
Chris@137 278 // otherwise we'll be stuck if the user updates into one, and our
Chris@137 279 // incremental log will end up with spurious stuff in it because
Chris@137 280 // we won't be pruning at the ends of closed branches
Chris@137 281 params << "heads" << "--closed";
Chris@109 282 runner->requestAction(HgAction(ACT_QUERY_HEADS, workFolderPath, params));
jtkorhonen@0 283 }
jtkorhonen@0 284
jtkorhonen@0 285 void MainWindow::hgLog()
jtkorhonen@0 286 {
Chris@109 287 QStringList params;
Chris@109 288 params << "log";
Chris@109 289 params << "--template";
Chris@125 290 params << Changeset::getLogTemplate();
Chris@109 291
Chris@109 292 runner->requestAction(HgAction(ACT_LOG, workFolderPath, params));
Chris@109 293 }
Chris@109 294
Chris@150 295 void MainWindow::hgLogIncremental(QStringList prune)
Chris@120 296 {
Chris@120 297 QStringList params;
Chris@120 298 params << "log";
Chris@120 299
Chris@150 300 foreach (QString p, prune) {
Chris@153 301 params << "--prune" << Changeset::hashOf(p);
Chris@120 302 }
Chris@120 303
Chris@120 304 params << "--template";
Chris@125 305 params << Changeset::getLogTemplate();
Chris@120 306
Chris@120 307 runner->requestAction(HgAction(ACT_LOG_INCREMENTAL, workFolderPath, params));
Chris@120 308 }
Chris@109 309
Chris@109 310 void MainWindow::hgQueryParents()
Chris@109 311 {
Chris@109 312 QStringList params;
Chris@109 313 params << "parents";
Chris@109 314 runner->requestAction(HgAction(ACT_QUERY_PARENTS, workFolderPath, params));
Chris@109 315 }
Chris@109 316
Chris@109 317 void MainWindow::hgAnnotate()
Chris@109 318 {
Chris@109 319 QStringList params;
Chris@109 320 QString currentFile;//!!! = hgTabs -> getCurrentFileListLine();
Chris@109 321
Chris@109 322 if (!currentFile.isEmpty())
jtkorhonen@0 323 {
Chris@109 324 params << "annotate" << "--" << currentFile.mid(2); //Jump over status marker characters (e.g "M ")
jtkorhonen@0 325
Chris@109 326 runner->requestAction(HgAction(ACT_ANNOTATE, workFolderPath, params));
jtkorhonen@0 327 }
jtkorhonen@0 328 }
jtkorhonen@0 329
Chris@109 330 void MainWindow::hgResolveList()
Chris@109 331 {
Chris@109 332 QStringList params;
jtkorhonen@0 333
Chris@109 334 params << "resolve" << "--list";
Chris@109 335 runner->requestAction(HgAction(ACT_RESOLVE_LIST, workFolderPath, params));
Chris@109 336 }
Chris@109 337
Chris@109 338 void MainWindow::hgAdd()
jtkorhonen@0 339 {
Chris@109 340 QStringList params;
jtkorhonen@0 341
Chris@109 342 // hgExplorer permitted adding "all" files -- I'm not sure
Chris@109 343 // that one is a good idea, let's require the user to select
jtkorhonen@0 344
Chris@109 345 QStringList files = hgTabs->getSelectedAddableFiles();
Chris@109 346
Chris@109 347 if (!files.empty()) {
Chris@109 348 params << "add" << "--" << files;
Chris@109 349 runner->requestAction(HgAction(ACT_ADD, workFolderPath, params));
jtkorhonen@0 350 }
jtkorhonen@0 351 }
jtkorhonen@0 352
jtkorhonen@0 353
Chris@98 354 void MainWindow::hgRemove()
Chris@98 355 {
Chris@109 356 QStringList params;
Chris@98 357
Chris@109 358 QStringList files = hgTabs->getSelectedRemovableFiles();
Chris@98 359
Chris@109 360 //!!! todo: confirmation dialog (with file list in it) (or do we
Chris@109 361 // need that? all it does is move the files to the removed
Chris@109 362 // list... doesn't it?)
Chris@98 363
Chris@109 364 if (!files.empty()) {
Chris@109 365 params << "remove" << "--after" << "--force" << "--" << files;
Chris@109 366 runner->requestAction(HgAction(ACT_REMOVE, workFolderPath, params));
Chris@109 367 }
jtkorhonen@0 368 }
jtkorhonen@0 369
jtkorhonen@0 370 void MainWindow::hgCommit()
jtkorhonen@0 371 {
Chris@109 372 QStringList params;
Chris@109 373 QString comment;
Chris@94 374
Chris@157 375 if (justMerged) {
Chris@157 376 comment = mergeCommitComment;
Chris@157 377 }
Chris@157 378
Chris@109 379 QStringList files = hgTabs->getSelectedCommittableFiles();
Chris@127 380 QStringList reportFiles = files;
Chris@127 381 if (reportFiles.empty()) reportFiles = hgTabs->getAllCommittableFiles();
Chris@103 382
Chris@155 383 QString cf(tr("Commit files"));
Chris@155 384
Chris@109 385 if (ConfirmCommentDialog::confirmAndGetLongComment
Chris@109 386 (this,
Chris@155 387 cf,
Chris@155 388 tr("<h3>%1</h3><p>%2").arg(cf)
Chris@155 389 .arg(tr("You are about to commit the following files:")),
Chris@155 390 tr("<h3>%1</h3><p>%2").arg(cf)
Chris@168 391 .arg(tr("You are about to commit %n file(s).", "", reportFiles.size())),
Chris@127 392 reportFiles,
Chris@193 393 comment,
Chris@193 394 tr("Commit"))) {
Chris@103 395
Chris@157 396 if (!justMerged && !files.empty()) {
Chris@157 397 // User wants to commit selected file(s) (and this is not
Chris@157 398 // merge commit, which would fail if we selected files)
Chris@157 399 params << "commit" << "--message" << comment
Chris@157 400 << "--user" << getUserInfo() << "--" << files;
Chris@109 401 } else {
Chris@109 402 // Commit all changes
Chris@157 403 params << "commit" << "--message" << comment
Chris@157 404 << "--user" << getUserInfo();
jtkorhonen@0 405 }
Chris@109 406
Chris@109 407 runner->requestAction(HgAction(ACT_COMMIT, workFolderPath, params));
Chris@173 408 mergeCommitComment = "";
jtkorhonen@0 409 }
jtkorhonen@0 410 }
jtkorhonen@0 411
jtkorhonen@34 412 QString MainWindow::filterTag(QString tag)
jtkorhonen@34 413 {
jtkorhonen@34 414 for(int i = 0; i < tag.size(); i++)
jtkorhonen@34 415 {
jtkorhonen@34 416 if (tag[i].isLower() || tag[i].isUpper() || tag[i].isDigit() || (tag[i] == QChar('.')))
jtkorhonen@34 417 {
jtkorhonen@34 418 //ok
jtkorhonen@34 419 }
jtkorhonen@34 420 else
jtkorhonen@34 421 {
jtkorhonen@34 422 tag[i] = QChar('_');
jtkorhonen@34 423 }
jtkorhonen@34 424 }
jtkorhonen@34 425 return tag;
jtkorhonen@34 426 }
jtkorhonen@34 427
jtkorhonen@34 428
Chris@164 429 void MainWindow::hgTag(QString id)
jtkorhonen@34 430 {
Chris@109 431 QStringList params;
Chris@109 432 QString tag;
jtkorhonen@34 433
Chris@109 434 if (ConfirmCommentDialog::confirmAndGetShortComment
Chris@109 435 (this,
Chris@109 436 tr("Tag"),
Chris@109 437 tr("Enter tag:"),
Chris@193 438 tag,
Chris@193 439 tr("Add Tag"))) {
Chris@164 440 if (!tag.isEmpty()) {//!!! do something better if it is empty
Chris@164 441
Chris@164 442 params << "tag" << "--user" << getUserInfo();
Chris@164 443 params << "--rev" << Changeset::hashOf(id) << filterTag(tag);
Chris@109 444
Chris@109 445 runner->requestAction(HgAction(ACT_TAG, workFolderPath, params));
jtkorhonen@34 446 }
jtkorhonen@34 447 }
jtkorhonen@34 448 }
jtkorhonen@34 449
jtkorhonen@34 450
jtkorhonen@34 451 void MainWindow::hgIgnore()
jtkorhonen@34 452 {
Chris@109 453 QString hgIgnorePath;
Chris@109 454 QStringList params;
Chris@178 455
Chris@109 456 hgIgnorePath = workFolderPath;
Chris@178 457 hgIgnorePath += "/.hgignore";
Chris@109 458
Chris@109 459 params << hgIgnorePath;
Chris@179 460
Chris@179 461 QString editor = findEditorBinaryName();
Chris@112 462
Chris@179 463 if (editor == "") {
Chris@179 464 DEBUG << "Failed to find a text editor" << endl;
Chris@179 465 //!!! visible error!
Chris@179 466 return;
Chris@179 467 }
Chris@179 468
Chris@179 469 HgAction action(ACT_HG_IGNORE, workFolderPath, params);
Chris@179 470 action.executable = editor;
Chris@179 471
Chris@179 472 runner->requestAction(action);
Chris@179 473 }
Chris@179 474
Chris@179 475 QString MainWindow::findDiffBinaryName()
Chris@179 476 {
Chris@179 477 QSettings settings;
Chris@179 478 settings.beginGroup("Locations");
Chris@179 479 QString diff = settings.value("extdiffbinary", "").toString();
Chris@179 480 if (diff == "") {
Chris@179 481 QStringList bases;
Chris@179 482 bases << "opendiff" << "kompare" << "kdiff3" << "meld";
Chris@179 483 bool found = false;
Chris@179 484 foreach (QString base, bases) {
Chris@179 485 diff = findInPath(base, m_myDirPath, true);
Chris@179 486 if (diff != base && diff != base + ".exe") {
Chris@179 487 found = true;
Chris@179 488 break;
Chris@179 489 }
Chris@179 490 }
Chris@179 491 if (found) {
Chris@179 492 settings.setValue("extdiffbinary", diff);
Chris@179 493 } else {
Chris@179 494 diff = "";
Chris@179 495 }
Chris@179 496 }
Chris@179 497 return diff;
Chris@179 498 }
Chris@179 499
Chris@179 500 QString MainWindow::findMergeBinaryName()
Chris@179 501 {
Chris@179 502 QSettings settings;
Chris@179 503 settings.beginGroup("Locations");
Chris@195 504 QVariant v = settings.value("mergebinary");
Chris@195 505 if (v != QVariant()) {
Chris@195 506 return v.toString(); // even if empty: user may have specified no external tool
Chris@195 507 }
Chris@195 508 QString merge;
Chris@195 509 QStringList bases;
Chris@195 510 bases << "fmdiff3" << "meld" << "diffuse" << "kdiff3";
Chris@195 511 bool found = false;
Chris@195 512 foreach (QString base, bases) {
Chris@195 513 merge = findInPath(base, m_myDirPath, true);
Chris@195 514 if (merge != base && merge != base + ".exe") {
Chris@195 515 found = true;
Chris@195 516 break;
Chris@179 517 }
Chris@195 518 }
Chris@195 519 if (found) {
Chris@195 520 settings.setValue("mergebinary", merge);
Chris@195 521 } else {
Chris@195 522 merge = "";
Chris@179 523 }
Chris@179 524 return merge;
Chris@179 525 }
Chris@179 526
Chris@179 527 QString MainWindow::findEditorBinaryName()
Chris@179 528 {
Chris@178 529 QSettings settings;
Chris@178 530 settings.beginGroup("Locations");
Chris@178 531 QString editor = settings.value("editorbinary", "").toString();
Chris@178 532 if (editor == "") {
Chris@178 533 QStringList bases;
Chris@178 534 bases
Chris@178 535 #if defined Q_OS_WIN32
Chris@178 536 << "wordpad.exe"
Chris@178 537 << "C:\\Program Files\\Windows NT\\Accessories\\wordpad.exe"
Chris@178 538 << "notepad.exe"
Chris@178 539 #elif defined Q_OS_MAC
Chris@178 540 << "textedit"
Chris@178 541 #else
Chris@178 542 << "gedit" << "kate"
Chris@178 543 #endif
Chris@178 544 ;
Chris@178 545 bool found = false;
Chris@178 546 foreach (QString base, bases) {
Chris@178 547 editor = findInPath(base, m_myDirPath, true);
Chris@178 548 if (editor != base && editor != base + ".exe") {
Chris@178 549 found = true;
Chris@178 550 break;
Chris@178 551 }
Chris@178 552 }
Chris@178 553 if (found) {
Chris@178 554 settings.setValue("editorbinary", editor);
Chris@178 555 } else {
Chris@178 556 editor = "";
Chris@178 557 }
Chris@178 558 }
Chris@179 559 return editor;
Chris@163 560 }
Chris@163 561
Chris@168 562 void MainWindow::hgShowSummary()
Chris@168 563 {
Chris@168 564 QStringList params;
Chris@168 565
Chris@168 566 params << "diff" << "--stat";
Chris@168 567
Chris@168 568 runner->requestAction(HgAction(ACT_DIFF_SUMMARY, workFolderPath, params));
Chris@168 569 }
Chris@168 570
jtkorhonen@0 571 void MainWindow::hgFolderDiff()
jtkorhonen@0 572 {
Chris@179 573 QString diff = findDiffBinaryName();
Chris@179 574 if (diff == "") return;
Chris@112 575
Chris@109 576 QStringList params;
jtkorhonen@0 577
Chris@112 578 // Diff parent against working folder (folder diff)
Chris@112 579
Chris@148 580 params << "--config" << "extensions.extdiff=" << "extdiff";
Chris@179 581 params << "--program" << diff;
Chris@109 582
Chris@153 583 params << hgTabs->getSelectedCommittableFiles(); // may be none: whole dir
Chris@153 584
Chris@109 585 runner->requestAction(HgAction(ACT_FOLDERDIFF, workFolderPath, params));
jtkorhonen@0 586 }
jtkorhonen@0 587
jtkorhonen@0 588
Chris@148 589 void MainWindow::hgDiffToCurrent(QString id)
jtkorhonen@0 590 {
Chris@179 591 QString diff = findDiffBinaryName();
Chris@179 592 if (diff == "") return;
Chris@163 593
Chris@148 594 QStringList params;
jtkorhonen@0 595
Chris@148 596 // Diff given revision against working folder
jtkorhonen@0 597
Chris@148 598 params << "--config" << "extensions.extdiff=" << "extdiff";
Chris@179 599 params << "--program" << diff;
Chris@153 600 params << "--rev" << Changeset::hashOf(id);
Chris@148 601
Chris@148 602 runner->requestAction(HgAction(ACT_FOLDERDIFF, workFolderPath, params));
jtkorhonen@0 603 }
jtkorhonen@0 604
jtkorhonen@0 605
Chris@148 606 void MainWindow::hgDiffToParent(QString child, QString parent)
Chris@148 607 {
Chris@179 608 QString diff = findDiffBinaryName();
Chris@179 609 if (diff == "") return;
Chris@163 610
Chris@148 611 QStringList params;
Chris@148 612
Chris@148 613 // Diff given revision against working folder
Chris@148 614
Chris@148 615 params << "--config" << "extensions.extdiff=" << "extdiff";
Chris@179 616 params << "--program" << diff;
Chris@153 617 params << "--rev" << Changeset::hashOf(parent)
Chris@153 618 << "--rev" << Changeset::hashOf(child);
Chris@148 619
Chris@148 620 runner->requestAction(HgAction(ACT_CHGSETDIFF, workFolderPath, params));
Chris@148 621 }
Chris@148 622
jtkorhonen@0 623
jtkorhonen@0 624 void MainWindow::hgUpdate()
jtkorhonen@0 625 {
Chris@109 626 QStringList params;
jtkorhonen@0 627
Chris@109 628 params << "update";
Chris@109 629
Chris@109 630 runner->requestAction(HgAction(ACT_UPDATE, workFolderPath, params));
jtkorhonen@0 631 }
jtkorhonen@0 632
jtkorhonen@0 633
Chris@148 634 void MainWindow::hgUpdateToRev(QString id)
jtkorhonen@0 635 {
Chris@148 636 QStringList params;
jtkorhonen@0 637
Chris@153 638 params << "update" << "--rev" << Changeset::hashOf(id) << "--check";
jtkorhonen@0 639
Chris@148 640 runner->requestAction(HgAction(ACT_UPDATE, workFolderPath, params));
jtkorhonen@0 641 }
jtkorhonen@0 642
jtkorhonen@0 643
jtkorhonen@0 644 void MainWindow::hgRevert()
jtkorhonen@0 645 {
Chris@109 646 QStringList params;
Chris@109 647 QString comment;
Chris@98 648
Chris@109 649 QStringList files = hgTabs->getSelectedRevertableFiles();
Chris@109 650 if (files.empty()) files = hgTabs->getAllRevertableFiles();
Chris@155 651
Chris@155 652 QString rf(tr("Revert files"));
Chris@109 653
Chris@109 654 if (ConfirmCommentDialog::confirmDangerousFilesAction
Chris@109 655 (this,
Chris@155 656 rf,
Chris@155 657 tr("<h3>%1</h3><p>%2").arg(rf)
Chris@155 658 .arg(tr("You are about to <b>revert</b> the following files to their previous committed state.<br><br>This will <b>throw away any changes</b> that you have made to these files but have not committed:")),
Chris@155 659 tr("<h3>%1</h3><p>%2").arg(rf)
Chris@155 660 .arg(tr("You are about to <b>revert</b> %n file(s).<br><br>This will <b>throw away any changes</b> that you have made to these files but have not committed.", "", files.size())),
Chris@193 661 files,
Chris@193 662 tr("Revert"))) {
Chris@163 663
Chris@163 664 lastRevertedFiles = files;
Chris@109 665
Chris@98 666 if (files.empty()) {
Chris@98 667 params << "revert" << "--no-backup";
Chris@98 668 } else {
Chris@164 669 params << "revert" << "--" << files;
Chris@98 670 }
Chris@163 671
Chris@163 672 //!!! This is problematic. If you've got an uncommitted
Chris@163 673 //!!! merge, you can't revert it without declaring which
Chris@163 674 //!!! parent of the merge you want to revert to (reasonably
Chris@163 675 //!!! enough). We're OK if we just did the merge in easyhg a
Chris@163 676 //!!! moment ago, because we have a record of which parent was
Chris@163 677 //!!! the target -- but if you exit and restart, we've lost
Chris@163 678 //!!! that record and it doesn't appear to be possible to get
Chris@163 679 //!!! it back from Hg. Even if you just switched from one
Chris@163 680 //!!! repo to another, the record is lost. What to do?
Chris@163 681
Chris@163 682 if (justMerged && mergeTargetRevision != "") {
Chris@163 683 params << "--rev" << mergeTargetRevision;
Chris@163 684 }
Chris@109 685
Chris@109 686 runner->requestAction(HgAction(ACT_REVERT, workFolderPath, params));
jtkorhonen@0 687 }
jtkorhonen@0 688 }
jtkorhonen@0 689
Chris@163 690
Chris@163 691 void MainWindow::hgMarkResolved(QStringList files)
Chris@163 692 {
Chris@163 693 QStringList params;
Chris@163 694
Chris@163 695 params << "resolve" << "--mark";
Chris@163 696
Chris@163 697 if (files.empty()) {
Chris@163 698 params << "--all";
Chris@163 699 } else {
Chris@164 700 params << "--" << files;
Chris@163 701 }
Chris@163 702
Chris@163 703 runner->requestAction(HgAction(ACT_RESOLVE_MARK, workFolderPath, params));
Chris@163 704 }
Chris@163 705
Chris@163 706
jtkorhonen@33 707 void MainWindow::hgRetryMerge()
jtkorhonen@33 708 {
Chris@109 709 QStringList params;
jtkorhonen@33 710
Chris@163 711 params << "resolve";
Chris@163 712
Chris@179 713 QString merge = findMergeBinaryName();
Chris@179 714 if (merge != "") {
Chris@179 715 params << "--tool" << merge;
Chris@163 716 }
Chris@163 717
Chris@163 718 QStringList files = hgTabs->getSelectedUnresolvedFiles();
Chris@163 719 if (files.empty()) {
Chris@163 720 params << "--all";
Chris@163 721 } else {
Chris@164 722 params << "--" << files;
Chris@163 723 }
Chris@163 724
Chris@109 725 runner->requestAction(HgAction(ACT_RETRY_MERGE, workFolderPath, params));
Chris@163 726
Chris@163 727 mergeCommitComment = tr("Merge");
jtkorhonen@33 728 }
jtkorhonen@33 729
jtkorhonen@33 730
jtkorhonen@0 731 void MainWindow::hgMerge()
jtkorhonen@0 732 {
Chris@163 733 if (hgTabs->canResolve()) {
Chris@163 734 hgRetryMerge();
Chris@163 735 return;
Chris@163 736 }
Chris@163 737
Chris@109 738 QStringList params;
jtkorhonen@0 739
Chris@109 740 params << "merge";
Chris@163 741
Chris@179 742 QString merge = findMergeBinaryName();
Chris@179 743 if (merge != "") {
Chris@179 744 params << "--tool" << merge;
Chris@163 745 }
Chris@163 746
Chris@163 747 if (currentParents.size() == 1) {
Chris@163 748 mergeTargetRevision = currentParents[0]->id();
Chris@163 749 }
Chris@163 750
Chris@109 751 runner->requestAction(HgAction(ACT_MERGE, workFolderPath, params));
Chris@157 752
Chris@157 753 mergeCommitComment = tr("Merge");
jtkorhonen@0 754 }
jtkorhonen@0 755
jtkorhonen@0 756
Chris@148 757 void MainWindow::hgMergeFrom(QString id)
Chris@148 758 {
Chris@148 759 QStringList params;
Chris@148 760
Chris@148 761 params << "merge";
Chris@153 762 params << "--rev" << Changeset::hashOf(id);
Chris@163 763
Chris@179 764 QString merge = findMergeBinaryName();
Chris@179 765 if (merge != "") {
Chris@179 766 params << "--tool" << merge;
Chris@163 767 }
Chris@148 768
Chris@148 769 runner->requestAction(HgAction(ACT_MERGE, workFolderPath, params));
Chris@157 770
Chris@157 771 mergeCommitComment = "";
Chris@157 772
Chris@157 773 foreach (Changeset *cs, currentHeads) {
Chris@157 774 if (cs->id() == id && !cs->isOnBranch(currentBranch)) {
Chris@157 775 if (cs->branch() == "" || cs->branch() == "default") {
Chris@157 776 mergeCommitComment = tr("Merge from the default branch");
Chris@157 777 } else {
Chris@157 778 mergeCommitComment = tr("Merge from branch \"%1\"").arg(cs->branch());
Chris@157 779 }
Chris@157 780 }
Chris@157 781 }
Chris@157 782
Chris@157 783 if (mergeCommitComment == "") {
Chris@157 784 mergeCommitComment = tr("Merge from %1").arg(id);
Chris@157 785 }
Chris@148 786 }
Chris@148 787
Chris@148 788
jtkorhonen@0 789 void MainWindow::hgCloneFromRemote()
jtkorhonen@0 790 {
Chris@109 791 QStringList params;
jtkorhonen@0 792
Chris@109 793 if (!QDir(workFolderPath).exists()) {
Chris@109 794 if (!QDir().mkpath(workFolderPath)) {
Chris@109 795 DEBUG << "hgCloneFromRemote: Failed to create target path "
Chris@109 796 << workFolderPath << endl;
Chris@109 797 //!!! report error
Chris@109 798 return;
Chris@104 799 }
Chris@109 800 }
Chris@104 801
Chris@109 802 params << "clone" << remoteRepoPath << workFolderPath;
Chris@109 803
Chris@109 804 hgTabs->setWorkFolderAndRepoNames(workFolderPath, remoteRepoPath);
Chris@113 805 hgTabs->updateWorkFolderFileList("");
jtkorhonen@0 806
Chris@109 807 runner->requestAction(HgAction(ACT_CLONEFROMREMOTE, workFolderPath, params));
jtkorhonen@0 808 }
jtkorhonen@0 809
jtkorhonen@0 810 void MainWindow::hgInit()
jtkorhonen@0 811 {
Chris@109 812 QStringList params;
jtkorhonen@0 813
Chris@109 814 params << "init";
Chris@109 815 params << workFolderPath;
jtkorhonen@0 816
Chris@109 817 runner->requestAction(HgAction(ACT_INIT, workFolderPath, params));
jtkorhonen@0 818 }
jtkorhonen@0 819
jtkorhonen@0 820 void MainWindow::hgIncoming()
jtkorhonen@0 821 {
Chris@109 822 QStringList params;
jtkorhonen@0 823
Chris@109 824 params << "incoming" << "--newest-first" << remoteRepoPath;
Chris@125 825 params << "--template" << Changeset::getLogTemplate();
jtkorhonen@0 826
Chris@109 827 runner->requestAction(HgAction(ACT_INCOMING, workFolderPath, params));
jtkorhonen@0 828 }
jtkorhonen@0 829
jtkorhonen@0 830 void MainWindow::hgPull()
jtkorhonen@0 831 {
Chris@193 832 if (ConfirmCommentDialog::confirm
Chris@126 833 (this, tr("Confirm pull"),
Chris@126 834 format3(tr("Confirm pull from remote repository"),
Chris@192 835 tr("You are about to pull changes from the following remote repository:"),
Chris@126 836 remoteRepoPath),
Chris@194 837 tr("Pull"))) {
jtkorhonen@0 838
Chris@126 839 QStringList params;
Chris@126 840 params << "pull" << remoteRepoPath;
Chris@126 841 runner->requestAction(HgAction(ACT_PULL, workFolderPath, params));
Chris@126 842 }
jtkorhonen@0 843 }
jtkorhonen@0 844
jtkorhonen@0 845 void MainWindow::hgPush()
jtkorhonen@0 846 {
Chris@193 847 if (ConfirmCommentDialog::confirm
Chris@126 848 (this, tr("Confirm push"),
Chris@126 849 format3(tr("Confirm push to remote repository"),
Chris@192 850 tr("You are about to push your changes to the following remote repository:"),
Chris@126 851 remoteRepoPath),
Chris@194 852 tr("Push"))) {
jtkorhonen@0 853
Chris@126 854 QStringList params;
Chris@154 855 params << "push" << "--new-branch" << remoteRepoPath;
Chris@126 856 runner->requestAction(HgAction(ACT_PUSH, workFolderPath, params));
Chris@126 857 }
jtkorhonen@0 858 }
jtkorhonen@0 859
Chris@182 860 QStringList MainWindow::listAllUpIpV4Addresses()
jtkorhonen@26 861 {
Chris@182 862 QStringList ret;
jtkorhonen@26 863 QList<QNetworkInterface> ifaces = QNetworkInterface::allInterfaces();
jtkorhonen@26 864
Chris@182 865 for (int i = 0; i < ifaces.count(); i++) {
jtkorhonen@26 866 QNetworkInterface iface = ifaces.at(i);
Chris@182 867 if (iface.flags().testFlag(QNetworkInterface::IsUp)
Chris@182 868 && !iface.flags().testFlag(QNetworkInterface::IsLoopBack)) {
Chris@182 869 for (int j=0; j<iface.addressEntries().count(); j++) {
jtkorhonen@28 870 QHostAddress tmp = iface.addressEntries().at(j).ip();
Chris@182 871 if (QAbstractSocket::IPv4Protocol == tmp.protocol()) {
Chris@182 872 ret.push_back(tmp.toString());
jtkorhonen@24 873 }
jtkorhonen@24 874 }
jtkorhonen@22 875 }
jtkorhonen@17 876 }
jtkorhonen@28 877 return ret;
jtkorhonen@28 878 }
jtkorhonen@17 879
Chris@120 880 void MainWindow::clearState()
Chris@120 881 {
Chris@120 882 foreach (Changeset *cs, currentParents) delete cs;
Chris@120 883 currentParents.clear();
Chris@120 884 foreach (Changeset *cs, currentHeads) delete cs;
Chris@120 885 currentHeads.clear();
Chris@120 886 currentBranch = "";
Chris@163 887 lastStatOutput = "";
Chris@163 888 lastRevertedFiles.clear();
Chris@163 889 mergeTargetRevision = "";
Chris@163 890 mergeCommitComment = "";
Chris@173 891 stateUnknown = true;
Chris@120 892 needNewLog = true;
Chris@199 893 if (fsWatcher) {
Chris@199 894 delete fsWatcher;
Chris@199 895 fsWatcher = 0;
Chris@199 896 }
Chris@120 897 }
jtkorhonen@17 898
jtkorhonen@11 899 void MainWindow::hgServe()
jtkorhonen@11 900 {
Chris@109 901 QStringList params;
Chris@109 902 QString msg;
jtkorhonen@11 903
Chris@182 904 QStringList addrs = listAllUpIpV4Addresses();
Chris@182 905
Chris@182 906 if (addrs.empty()) {
Chris@182 907 QMessageBox::critical
Chris@182 908 (this, tr("Serve"), tr("Failed to identify an active IPv4 address"));
Chris@182 909 return;
Chris@182 910 }
Chris@182 911
Chris@182 912 //!!! should find available port as well
Chris@182 913
Chris@182 914 QTextStream ts(&msg);
Chris@182 915 ts << QString("<qt><p>%1</p>")
Chris@182 916 .arg(tr("Running temporary server at %n address(es):", "", addrs.size()));
Chris@182 917 foreach (QString addr, addrs) {
Chris@182 918 ts << QString("<pre>&nbsp;&nbsp;http://%1:8000</pre>").arg(xmlEncode(addr));
Chris@182 919 }
Chris@182 920 ts << tr("<p>Press Close to stop the server and return.</p>");
Chris@182 921 ts.flush();
Chris@182 922
Chris@109 923 params << "serve";
jtkorhonen@11 924
Chris@109 925 runner->requestAction(HgAction(ACT_SERVE, workFolderPath, params));
Chris@109 926
Chris@182 927 QMessageBox::information(this, tr("Serve"), msg, QMessageBox::Close);
Chris@182 928
Chris@182 929 runner->killCurrentActions();
jtkorhonen@11 930 }
jtkorhonen@11 931
Chris@64 932 void MainWindow::startupDialog()
Chris@64 933 {
Chris@64 934 StartupDialog *dlg = new StartupDialog(this);
Chris@65 935 if (dlg->exec()) firstStart = false;
Chris@64 936 }
jtkorhonen@11 937
Chris@69 938 void MainWindow::open()
Chris@69 939 {
Chris@86 940 bool done = false;
Chris@69 941
Chris@86 942 while (!done) {
Chris@69 943
Chris@86 944 MultiChoiceDialog *d = new MultiChoiceDialog
Chris@86 945 (tr("Open Repository"),
Chris@86 946 tr("<qt><big>What would you like to open?</big></qt>"),
Chris@86 947 this);
Chris@69 948
Chris@86 949 d->addChoice("remote",
Chris@86 950 tr("<qt><center><img src=\":images/browser-64.png\"><br>Remote repository</center></qt>"),
Chris@86 951 tr("Open a remote Mercurial repository, by cloning from its URL into a local folder."),
Chris@86 952 MultiChoiceDialog::UrlToDirectoryArg);
Chris@69 953
Chris@86 954 d->addChoice("local",
Chris@86 955 tr("<qt><center><img src=\":images/hglogo-64.png\"><br>Local repository</center></qt>"),
Chris@86 956 tr("Open an existing local Mercurial repository."),
Chris@86 957 MultiChoiceDialog::DirectoryArg);
Chris@69 958
Chris@86 959 d->addChoice("init",
Chris@86 960 tr("<qt><center><img src=\":images/hdd_unmount-64.png\"><br>File folder</center></qt>"),
Chris@86 961 tr("Open a local folder, by creating a Mercurial repository in it."),
Chris@86 962 MultiChoiceDialog::DirectoryArg);
Chris@79 963
Chris@86 964 d->setCurrentChoice("local");
Chris@86 965
Chris@86 966 if (d->exec() == QDialog::Accepted) {
Chris@86 967
Chris@86 968 QString choice = d->getCurrentChoice();
Chris@86 969 QString arg = d->getArgument().trimmed();
Chris@86 970
Chris@86 971 bool result = false;
Chris@86 972
Chris@86 973 if (choice == "local") {
Chris@86 974 result = openLocal(arg);
Chris@86 975 } else if (choice == "remote") {
Chris@86 976 result = openRemote(arg, d->getAdditionalArgument().trimmed());
Chris@86 977 } else if (choice == "init") {
Chris@86 978 result = openInit(arg);
Chris@86 979 }
Chris@86 980
Chris@86 981 if (result) {
Chris@86 982 enableDisableActions();
Chris@120 983 clearState();
Chris@109 984 hgQueryPaths();
Chris@91 985 done = true;
Chris@91 986 }
Chris@86 987
Chris@86 988 } else {
Chris@86 989
Chris@86 990 // cancelled
Chris@86 991 done = true;
Chris@69 992 }
Chris@79 993
Chris@86 994 delete d;
Chris@69 995 }
Chris@69 996 }
Chris@69 997
Chris@182 998 void MainWindow::changeRemoteRepo()
Chris@182 999 {
Chris@183 1000 // This will involve rewriting the local .hgrc
Chris@183 1001
Chris@184 1002 QDir hgDir(workFolderPath + "/.hg");
Chris@184 1003 if (!hgDir.exists()) {
Chris@184 1004 //!!! visible error!
Chris@184 1005 return;
Chris@184 1006 }
Chris@184 1007
Chris@183 1008 QFileInfo hgrc(workFolderPath + "/.hg/hgrc");
Chris@184 1009 if (hgrc.exists() && !hgrc.isWritable()) {
Chris@183 1010 //!!! visible error!
Chris@183 1011 return;
Chris@183 1012 }
Chris@183 1013
Chris@183 1014 MultiChoiceDialog *d = new MultiChoiceDialog
Chris@183 1015 (tr("Change Remote Location"),
Chris@183 1016 tr("<qt><big>Change the remote location</big></qt>"),
Chris@183 1017 this);
Chris@183 1018
Chris@183 1019 d->addChoice("remote",
Chris@183 1020 tr("<qt><center><img src=\":images/browser-64.png\"><br>Remote repository</center></qt>"),
Chris@183 1021 tr("Provide a new URL to use for push and pull actions from the current local repository."),
Chris@183 1022 MultiChoiceDialog::UrlArg);
Chris@183 1023
Chris@183 1024 if (d->exec() == QDialog::Accepted) {
Chris@183 1025 QSettings s(hgrc.canonicalFilePath(), QSettings::IniFormat);
Chris@183 1026 s.beginGroup("paths");
Chris@183 1027 s.setValue("default", d->getArgument());
Chris@186 1028 stateUnknown = true;
Chris@183 1029 hgQueryPaths();
Chris@183 1030 }
Chris@183 1031
Chris@183 1032 delete d;
Chris@182 1033 }
Chris@182 1034
Chris@145 1035 void MainWindow::open(QString local)
Chris@145 1036 {
Chris@145 1037 if (openLocal(local)) {
Chris@145 1038 enableDisableActions();
Chris@145 1039 clearState();
Chris@145 1040 hgQueryPaths();
Chris@145 1041 }
Chris@145 1042 }
Chris@145 1043
Chris@79 1044 bool MainWindow::complainAboutFilePath(QString arg)
Chris@79 1045 {
Chris@79 1046 QMessageBox::critical
Chris@79 1047 (this, tr("File chosen"),
Chris@84 1048 tr("<qt><b>Folder required</b><br><br>You asked to open \"%1\".<br>This is a file; to open a repository, you need to choose a folder.</qt>").arg(xmlEncode(arg)));
Chris@79 1049 return false;
Chris@79 1050 }
Chris@79 1051
Chris@79 1052 bool MainWindow::complainAboutUnknownFolder(QString arg)
Chris@79 1053 {
Chris@79 1054 QMessageBox::critical
Chris@79 1055 (this, tr("Folder does not exist"),
Chris@84 1056 tr("<qt><b>Folder does not exist</b><br><br>You asked to open \"%1\".<br>This folder does not exist, and it cannot be created because its parent does not exist either.</qt>").arg(xmlEncode(arg)));
Chris@84 1057 return false;
Chris@84 1058 }
Chris@84 1059
Chris@84 1060 bool MainWindow::complainAboutInitInRepo(QString arg)
Chris@84 1061 {
Chris@84 1062 QMessageBox::critical
Chris@84 1063 (this, tr("Path is in existing repository"),
Chris@84 1064 tr("<qt><b>Path is in an existing repository</b><br><br>You asked to initialise a repository at \"%1\".<br>This path is already inside an existing repository.</qt>").arg(xmlEncode(arg)));
Chris@84 1065 return false;
Chris@84 1066 }
Chris@84 1067
Chris@84 1068 bool MainWindow::complainAboutInitFile(QString arg)
Chris@84 1069 {
Chris@84 1070 QMessageBox::critical
Chris@84 1071 (this, tr("Path is a file"),
Chris@84 1072 tr("<qt><b>Path is a file</b><br><br>You asked to initialise a repository at \"%1\".<br>This is an existing file; it is only possible to initialise in folders.</qt>").arg(xmlEncode(arg)));
Chris@84 1073 return false;
Chris@84 1074 }
Chris@84 1075
Chris@84 1076 bool MainWindow::complainAboutCloneToExisting(QString arg)
Chris@84 1077 {
Chris@84 1078 QMessageBox::critical
Chris@84 1079 (this, tr("Path is in existing repository"),
Chris@84 1080 tr("<qt><b>Local path is in an existing repository</b><br><br>You asked to open a remote repository by cloning it to the local path \"%1\".<br>This path is already inside an existing repository.<br>Please provide a new folder name for the local repository.</qt>").arg(xmlEncode(arg)));
Chris@84 1081 return false;
Chris@84 1082 }
Chris@84 1083
Chris@84 1084 bool MainWindow::complainAboutCloneToFile(QString arg)
Chris@84 1085 {
Chris@84 1086 QMessageBox::critical
Chris@84 1087 (this, tr("Path is a file"),
Chris@84 1088 tr("<qt><b>Local path is a file</b><br><br>You asked to open a remote repository by cloning it to the local path \"%1\".<br>This path is an existing file.<br>Please provide a new folder name for the local repository.</qt>").arg(xmlEncode(arg)));
Chris@84 1089 return false;
Chris@84 1090 }
Chris@84 1091
Chris@84 1092 bool MainWindow::complainAboutCloneToExistingFolder(QString arg)
Chris@84 1093 {
Chris@84 1094 QMessageBox::critical
Chris@84 1095 (this, tr("Folder exists"),
Chris@84 1096 tr("<qt><b>Local folder already exists</b><br><br>You asked to open a remote repository by cloning it to the local path \"%1\".<br>This is the path of an existing folder.<br>Please provide a new folder name for the local repository.</qt>").arg(xmlEncode(arg)));
Chris@79 1097 return false;
Chris@79 1098 }
Chris@79 1099
Chris@79 1100 bool MainWindow::askToOpenParentRepo(QString arg, QString parent)
Chris@79 1101 {
Chris@79 1102 return (QMessageBox::question
Chris@84 1103 (this, tr("Path is inside a repository"),
Chris@86 1104 tr("<qt><b>Open the repository that contains this path?</b><br><br>You asked to open \"%1\".<br>This is not the root folder of a repository.<br>But it is inside a repository, whose root is at \"%2\". <br><br>Would you like to open that repository instead?</qt>")
Chris@79 1105 .arg(xmlEncode(arg)).arg(xmlEncode(parent)),
Chris@79 1106 QMessageBox::Ok | QMessageBox::Cancel,
Chris@79 1107 QMessageBox::Ok)
Chris@79 1108 == QMessageBox::Ok);
Chris@79 1109 }
Chris@79 1110
Chris@79 1111 bool MainWindow::askToInitExisting(QString arg)
Chris@79 1112 {
Chris@79 1113 return (QMessageBox::question
Chris@84 1114 (this, tr("Folder has no repository"),
Chris@84 1115 tr("<qt><b>Initialise a repository here?</b><br><br>You asked to open \"%1\".<br>This folder does not contain a Mercurial repository.<br><br>Would you like to initialise a repository here?</qt>")
Chris@79 1116 .arg(xmlEncode(arg)),
Chris@79 1117 QMessageBox::Ok | QMessageBox::Cancel,
Chris@79 1118 QMessageBox::Ok)
Chris@79 1119 == QMessageBox::Ok);
Chris@79 1120 }
Chris@79 1121
Chris@79 1122 bool MainWindow::askToInitNew(QString arg)
Chris@79 1123 {
Chris@79 1124 return (QMessageBox::question
Chris@84 1125 (this, tr("Folder does not exist"),
Chris@84 1126 tr("<qt><b>Initialise a new repository?</b><br><br>You asked to open \"%1\".<br>This folder does not yet exist.<br><br>Would you like to create the folder and initialise a new empty repository in it?</qt>")
Chris@84 1127 .arg(xmlEncode(arg)),
Chris@84 1128 QMessageBox::Ok | QMessageBox::Cancel,
Chris@84 1129 QMessageBox::Ok)
Chris@84 1130 == QMessageBox::Ok);
Chris@84 1131 }
Chris@84 1132
Chris@84 1133 bool MainWindow::askToOpenInsteadOfInit(QString arg)
Chris@84 1134 {
Chris@84 1135 return (QMessageBox::question
Chris@84 1136 (this, tr("Repository exists"),
Chris@84 1137 tr("<qt><b>Open existing repository?</b><br><br>You asked to initialise a new repository at \"%1\".<br>This folder already contains a repository. Would you like to open it?</qt>")
Chris@79 1138 .arg(xmlEncode(arg)),
Chris@79 1139 QMessageBox::Ok | QMessageBox::Cancel,
Chris@79 1140 QMessageBox::Ok)
Chris@79 1141 == QMessageBox::Ok);
Chris@79 1142 }
Chris@79 1143
Chris@79 1144 bool MainWindow::openLocal(QString local)
Chris@79 1145 {
Chris@79 1146 DEBUG << "open " << local << endl;
Chris@79 1147
Chris@79 1148 FolderStatus status = getFolderStatus(local);
Chris@79 1149 QString containing = getContainingRepoFolder(local);
Chris@79 1150
Chris@79 1151 switch (status) {
Chris@79 1152
Chris@79 1153 case FolderHasRepo:
Chris@79 1154 // fine
Chris@79 1155 break;
Chris@79 1156
Chris@79 1157 case FolderExists:
Chris@79 1158 if (containing != "") {
Chris@79 1159 if (!askToOpenParentRepo(local, containing)) return false;
Chris@84 1160 local = containing;
Chris@79 1161 } else {
Chris@86 1162 //!!! No -- this is likely to happen far more by accident
Chris@86 1163 // than because the user actually wanted to init something.
Chris@86 1164 // Don't ask, just politely reject.
Chris@79 1165 if (!askToInitExisting(local)) return false;
Chris@79 1166 return openInit(local);
Chris@79 1167 }
Chris@79 1168 break;
Chris@79 1169
Chris@79 1170 case FolderParentExists:
Chris@79 1171 if (containing != "") {
Chris@79 1172 if (!askToOpenParentRepo(local, containing)) return false;
Chris@84 1173 local = containing;
Chris@79 1174 } else {
Chris@79 1175 if (!askToInitNew(local)) return false;
Chris@79 1176 return openInit(local);
Chris@79 1177 }
Chris@79 1178 break;
Chris@79 1179
Chris@79 1180 case FolderUnknown:
Chris@84 1181 if (containing != "") {
Chris@84 1182 if (!askToOpenParentRepo(local, containing)) return false;
Chris@84 1183 local = containing;
Chris@84 1184 } else {
Chris@84 1185 return complainAboutUnknownFolder(local);
Chris@84 1186 }
Chris@84 1187 break;
Chris@79 1188
Chris@79 1189 case FolderIsFile:
Chris@79 1190 return complainAboutFilePath(local);
Chris@79 1191 }
Chris@79 1192
Chris@79 1193 workFolderPath = local;
Chris@79 1194 remoteRepoPath = "";
Chris@79 1195 return true;
Chris@79 1196 }
Chris@79 1197
Chris@79 1198 bool MainWindow::openRemote(QString remote, QString local)
Chris@79 1199 {
Chris@79 1200 DEBUG << "clone " << remote << " to " << local << endl;
Chris@84 1201
Chris@84 1202 FolderStatus status = getFolderStatus(local);
Chris@84 1203 QString containing = getContainingRepoFolder(local);
Chris@84 1204
Chris@84 1205 DEBUG << "status = " << status << ", containing = " << containing << endl;
Chris@84 1206
Chris@84 1207 if (status == FolderHasRepo || containing != "") {
Chris@84 1208 return complainAboutCloneToExisting(local);
Chris@84 1209 }
Chris@84 1210
Chris@84 1211 if (status == FolderIsFile) {
Chris@84 1212 return complainAboutCloneToFile(local);
Chris@84 1213 }
Chris@84 1214
Chris@84 1215 if (status == FolderUnknown) {
Chris@84 1216 return complainAboutUnknownFolder(local);
Chris@84 1217 }
Chris@84 1218
Chris@84 1219 if (status == FolderExists) {
Chris@84 1220 //!!! we can do better than this surely?
Chris@84 1221 return complainAboutCloneToExistingFolder(local);
Chris@84 1222 }
Chris@84 1223
Chris@84 1224 workFolderPath = local;
Chris@84 1225 remoteRepoPath = remote;
Chris@84 1226 hgCloneFromRemote();
Chris@84 1227
Chris@79 1228 return true;
Chris@79 1229 }
Chris@79 1230
Chris@84 1231 bool MainWindow::openInit(QString local)
Chris@79 1232 {
Chris@84 1233 DEBUG << "openInit " << local << endl;
Chris@84 1234
Chris@84 1235 FolderStatus status = getFolderStatus(local);
Chris@84 1236 QString containing = getContainingRepoFolder(local);
Chris@84 1237
Chris@84 1238 DEBUG << "status = " << status << ", containing = " << containing << endl;
Chris@84 1239
Chris@84 1240 if (status == FolderHasRepo) {
Chris@84 1241 if (!askToOpenInsteadOfInit(local)) return false;
Chris@84 1242 }
Chris@84 1243
Chris@84 1244 if (containing != "") {
Chris@84 1245 return complainAboutInitInRepo(local);
Chris@84 1246 }
Chris@84 1247
Chris@84 1248 if (status == FolderIsFile) {
Chris@84 1249 return complainAboutInitFile(local);
Chris@84 1250 }
Chris@84 1251
Chris@84 1252 if (status == FolderUnknown) {
Chris@84 1253 return complainAboutUnknownFolder(local);
Chris@84 1254 }
Chris@84 1255
Chris@84 1256 workFolderPath = local;
Chris@84 1257 remoteRepoPath = "";
Chris@84 1258 hgInit();
Chris@79 1259 return true;
Chris@79 1260 }
Chris@79 1261
jtkorhonen@0 1262 void MainWindow::settings()
jtkorhonen@0 1263 {
jtkorhonen@0 1264 SettingsDialog *settingsDlg = new SettingsDialog(this);
jtkorhonen@0 1265 settingsDlg->exec();
jtkorhonen@0 1266 }
jtkorhonen@0 1267
jtkorhonen@2 1268 #define STDOUT_NEEDS_BIG_WINDOW 512
jtkorhonen@2 1269 #define SMALL_WND_W 500
jtkorhonen@2 1270 #define SMALL_WND_H 300
jtkorhonen@2 1271
jtkorhonen@2 1272 #define BIG_WND_W 1024
jtkorhonen@2 1273 #define BIG_WND_H 768
jtkorhonen@2 1274
jtkorhonen@2 1275
jtkorhonen@2 1276 void MainWindow::presentLongStdoutToUser(QString stdo)
jtkorhonen@0 1277 {
jtkorhonen@2 1278 if (!stdo.isEmpty())
jtkorhonen@2 1279 {
jtkorhonen@2 1280 QDialog dlg;
jtkorhonen@0 1281
jtkorhonen@2 1282 if (stdo.length() > STDOUT_NEEDS_BIG_WINDOW)
jtkorhonen@2 1283 {
jtkorhonen@2 1284 dlg.setMinimumWidth(BIG_WND_W);
jtkorhonen@2 1285 dlg.setMinimumHeight(BIG_WND_H);
jtkorhonen@2 1286 }
jtkorhonen@2 1287 else
jtkorhonen@2 1288 {
jtkorhonen@2 1289 dlg.setMinimumWidth(SMALL_WND_W);
jtkorhonen@2 1290 dlg.setMinimumHeight(SMALL_WND_H);
jtkorhonen@2 1291 }
jtkorhonen@0 1292
jtkorhonen@2 1293 QVBoxLayout *box = new QVBoxLayout;
jtkorhonen@2 1294 QListWidget *list = new QListWidget;
jtkorhonen@2 1295 list-> addItems(stdo.split("\n"));
jtkorhonen@2 1296 QPushButton *btn = new QPushButton(tr("Ok"));
jtkorhonen@2 1297 connect(btn, SIGNAL(clicked()), &dlg, SLOT(accept()));
jtkorhonen@0 1298
jtkorhonen@2 1299 box -> addWidget(list);
jtkorhonen@2 1300 box -> addWidget(btn);
jtkorhonen@2 1301 dlg.setLayout(box);
jtkorhonen@2 1302
jtkorhonen@2 1303 dlg.exec();
jtkorhonen@2 1304 }
jtkorhonen@2 1305 else
jtkorhonen@2 1306 {
Chris@98 1307 QMessageBox::information(this, tr("EasyMercurial"), tr("Mercurial command did not return any output."));
jtkorhonen@2 1308 }
jtkorhonen@0 1309 }
jtkorhonen@0 1310
Chris@90 1311 void MainWindow::updateFileSystemWatcher()
Chris@90 1312 {
Chris@199 1313 bool justCreated = false;
Chris@199 1314 if (!fsWatcher) {
Chris@199 1315 fsWatcher = new QFileSystemWatcher();
Chris@199 1316 justCreated = true;
Chris@199 1317 }
Chris@199 1318
Chris@199 1319 // QFileSystemWatcher will refuse to add a file or directory to
Chris@199 1320 // its watch list that it is already watching -- fine, that's what
Chris@199 1321 // we want -- but it prints a warning when this happens, which is
Chris@199 1322 // annoying because it would be the normal case for us. So we'll
Chris@199 1323 // check for duplicates ourselves.
Chris@199 1324 QSet<QString> alreadyWatched;
Chris@199 1325 QStringList dl(fsWatcher->directories());
Chris@199 1326 foreach (QString d, dl) alreadyWatched.insert(d);
Chris@199 1327
Chris@90 1328 std::deque<QString> pending;
Chris@90 1329 pending.push_back(workFolderPath);
Chris@199 1330
Chris@90 1331 while (!pending.empty()) {
Chris@199 1332
Chris@90 1333 QString path = pending.front();
Chris@90 1334 pending.pop_front();
Chris@199 1335 if (!alreadyWatched.contains(path)) {
Chris@199 1336 fsWatcher->addPath(path);
Chris@199 1337 DEBUG << "Added to file system watcher: " << path << endl;
Chris@199 1338 }
Chris@199 1339
Chris@90 1340 QDir d(path);
Chris@90 1341 if (d.exists()) {
Chris@199 1342 d.setFilter(QDir::Dirs | QDir::NoDotAndDotDot |
Chris@199 1343 QDir::Readable | QDir::NoSymLinks);
Chris@90 1344 foreach (QString entry, d.entryList()) {
Chris@199 1345 if (entry.startsWith('.')) continue;
Chris@90 1346 QString entryPath = d.absoluteFilePath(entry);
Chris@90 1347 pending.push_back(entryPath);
Chris@90 1348 }
Chris@90 1349 }
Chris@90 1350 }
Chris@199 1351
Chris@199 1352 if (justCreated) {
Chris@199 1353 connect(fsWatcher, SIGNAL(directoryChanged(QString)),
Chris@199 1354 this, SLOT(fsDirectoryChanged(QString)));
Chris@199 1355 connect(fsWatcher, SIGNAL(fileChanged(QString)),
Chris@199 1356 this, SLOT(fsFileChanged(QString)));
Chris@199 1357 }
Chris@90 1358 }
Chris@90 1359
Chris@122 1360 void MainWindow::fsDirectoryChanged(QString d)
Chris@90 1361 {
Chris@122 1362 DEBUG << "MainWindow::fsDirectoryChanged " << d << endl;
Chris@90 1363 hgStat();
Chris@90 1364 }
Chris@90 1365
Chris@122 1366 void MainWindow::fsFileChanged(QString f)
Chris@90 1367 {
Chris@122 1368 DEBUG << "MainWindow::fsFileChanged " << f << endl;
Chris@90 1369 hgStat();
Chris@90 1370 }
Chris@90 1371
Chris@125 1372 QString MainWindow::format3(QString head, QString intro, QString code)
Chris@125 1373 {
Chris@196 1374 code = xmlEncode(code).replace("\n", "<br>")
Chris@196 1375 #ifndef Q_OS_WIN32
Chris@196 1376 // The hard hyphen comes out funny on Windows
Chris@196 1377 .replace("-", "&#8209;")
Chris@196 1378 #endif
Chris@196 1379 .replace(" ", "&nbsp;");
Chris@125 1380 if (intro == "") {
Chris@168 1381 return QString("<qt><h3>%1</h3><p><code>%2</code></p>")
Chris@168 1382 .arg(head).arg(code);
Chris@126 1383 } else if (code == "") {
Chris@126 1384 return QString("<qt><h3>%1</h3><p>%2</p>")
Chris@126 1385 .arg(head).arg(intro);
Chris@125 1386 } else {
Chris@168 1387 return QString("<qt><h3>%1</h3><p>%2</p><p><code>%3</code></p>")
Chris@168 1388 .arg(head).arg(intro).arg(code);
Chris@125 1389 }
Chris@125 1390 }
Chris@125 1391
Chris@120 1392 void MainWindow::showIncoming(QString output)
Chris@120 1393 {
Chris@120 1394 runner->hide();
Chris@125 1395 IncomingDialog *d = new IncomingDialog(this, output);
Chris@125 1396 d->exec();
Chris@125 1397 delete d;
Chris@125 1398 }
Chris@125 1399
Chris@125 1400 int MainWindow::extractChangeCount(QString text)
Chris@125 1401 {
Chris@125 1402 QRegExp re("added (\\d+) ch\\w+ with (\\d+) ch\\w+ to (\\d+) f\\w+");
Chris@125 1403 if (re.indexIn(text) >= 0) {
Chris@125 1404 return re.cap(1).toInt();
Chris@125 1405 } else if (text.contains("no changes")) {
Chris@125 1406 return 0;
Chris@125 1407 } else {
Chris@125 1408 return -1; // unknown
Chris@125 1409 }
Chris@120 1410 }
Chris@120 1411
Chris@120 1412 void MainWindow::showPushResult(QString output)
Chris@120 1413 {
Chris@125 1414 QString report;
Chris@125 1415 int n = extractChangeCount(output);
Chris@125 1416 if (n > 0) {
Chris@125 1417 report = tr("Pushed %n changeset(s)", "", n);
Chris@125 1418 } else if (n == 0) {
Chris@125 1419 report = tr("No changes to push");
Chris@125 1420 } else {
Chris@125 1421 report = tr("Push complete");
Chris@125 1422 }
Chris@125 1423 report = format3(report, tr("The push command output was:"), output);
Chris@120 1424 runner->hide();
Chris@125 1425 QMessageBox::information(this, "Push complete", report);
Chris@120 1426 }
Chris@120 1427
Chris@120 1428 void MainWindow::showPullResult(QString output)
Chris@120 1429 {
Chris@125 1430 QString report;
Chris@125 1431 int n = extractChangeCount(output);
Chris@125 1432 if (n > 0) {
Chris@125 1433 report = tr("Pulled %n changeset(s)", "", n);
Chris@125 1434 } else if (n == 0) {
Chris@125 1435 report = tr("No changes to pull");
Chris@125 1436 } else {
Chris@125 1437 report = tr("Pull complete");
Chris@125 1438 }
Chris@125 1439 report = format3(report, tr("The pull command output was:"), output);
Chris@120 1440 runner->hide();
Chris@125 1441
Chris@125 1442 //!!! and something about updating
Chris@125 1443
Chris@125 1444 QMessageBox::information(this, "Pull complete", report);
Chris@120 1445 }
Chris@120 1446
Chris@174 1447 void MainWindow::reportNewRemoteHeads(QString output)
Chris@174 1448 {
Chris@174 1449 bool headsAreLocal = false;
Chris@174 1450
Chris@174 1451 if (currentParents.size() == 1) {
Chris@174 1452 int currentBranchHeads = 0;
Chris@174 1453 bool parentIsHead = false;
Chris@174 1454 Changeset *parent = currentParents[0];
Chris@174 1455 foreach (Changeset *head, currentHeads) {
Chris@174 1456 if (head->isOnBranch(currentBranch)) {
Chris@174 1457 ++currentBranchHeads;
Chris@174 1458 }
Chris@174 1459 if (parent->id() == head->id()) {
Chris@174 1460 parentIsHead = true;
Chris@174 1461 }
Chris@174 1462 }
Chris@174 1463 if (currentBranchHeads == 2 && parentIsHead) {
Chris@174 1464 headsAreLocal = true;
Chris@174 1465 }
Chris@174 1466 }
Chris@174 1467
Chris@174 1468 if (headsAreLocal) {
Chris@174 1469 QMessageBox::warning
Chris@174 1470 (this, tr("Push failed"),
Chris@174 1471 format3(tr("Push failed"),
Chris@174 1472 tr("Your local repository could not be pushed to the remote repository.<br><br>You may need to merge the changes locally first.<br><br>The output of the push command was:"),
Chris@174 1473 output));
Chris@174 1474 } else {
Chris@174 1475 QMessageBox::warning
Chris@174 1476 (this, tr("Push failed"),
Chris@174 1477 format3(tr("Push failed"),
Chris@174 1478 tr("Your local repository could not be pushed to the remote repository.<br><br>The remote repository may have been changed by someone else since you last pushed. Try pulling and merging their changes into your local repository first.<br><br>The output of the push command was:"),
Chris@174 1479 output));
Chris@174 1480 }
Chris@174 1481 }
Chris@174 1482
Chris@143 1483 void MainWindow::commandFailed(HgAction action, QString output)
Chris@62 1484 {
Chris@62 1485 DEBUG << "MainWindow::commandFailed" << endl;
Chris@74 1486
Chris@113 1487 // Some commands we just have to ignore bad return values from:
Chris@113 1488
Chris@113 1489 switch(action.action) {
Chris@113 1490 case ACT_NONE:
Chris@113 1491 // uh huh
Chris@113 1492 return;
Chris@175 1493 case ACT_TEST_HG:
Chris@175 1494 QMessageBox::warning
Chris@175 1495 (this, tr("Failed to run Mercurial"),
Chris@175 1496 format3(tr("Failed to run Mercurial"),
Chris@200 1497 tr("The Mercurial program either could not be found or failed to run.<br>Check that the Mercurial program path is correct in Settings.<br><br>%1").arg(output == "" ? QString("") : tr("The test command said:")),
Chris@200 1498 output));
Chris@200 1499 settings();
Chris@200 1500 return;
Chris@200 1501 case ACT_TEST_HG_EXT:
Chris@200 1502 QMessageBox::warning
Chris@200 1503 (this, tr("Failed to run Mercurial"),
Chris@200 1504 format3(tr("Failed to run Mercurial with extension enabled"),
Chris@200 1505 tr("The Mercurial program failed to run with the EasyMercurial interaction extension enabled.<br>This may indicate an installation problem with EasyMercurial.<br><br>You may be able to continue working if you switch off &ldquo;Use EasyHg Mercurial Extension&rdquo; in Settings. Note that remote repositories that require authentication may not work if you do this.<br><br>%1").arg(output == "" ? QString("") : tr("The test command said:")),
Chris@175 1506 output));
Chris@175 1507 settings();
Chris@175 1508 return;
Chris@113 1509 case ACT_INCOMING:
Chris@113 1510 // returns non-zero code if the check was successful but there
Chris@113 1511 // are no changes pending
Chris@143 1512 if (output.trimmed() == "") showIncoming("");
Chris@113 1513 return;
Chris@162 1514 case ACT_QUERY_HEADS:
Chris@162 1515 // fails if repo is empty; we don't care (if there's a genuine
Chris@162 1516 // problem, something else will fail too). Need to do this,
Chris@162 1517 // otherwise empty repo state will not be reflected properly
Chris@162 1518 // (since heads/log procedure never completes for empty repo)
Chris@162 1519 enableDisableActions();
Chris@162 1520 return;
Chris@113 1521 case ACT_FOLDERDIFF:
Chris@113 1522 case ACT_CHGSETDIFF:
Chris@113 1523 // external program, unlikely to be anything useful in stderr
Chris@113 1524 // and some return with failure codes when something as basic
Chris@113 1525 // as the user closing the window via the wm happens
Chris@113 1526 return;
Chris@174 1527 case ACT_PUSH:
Chris@174 1528 if (output.contains("creates new remote heads")) {
Chris@174 1529 reportNewRemoteHeads(output);
Chris@174 1530 return;
Chris@174 1531 }
Chris@199 1532 case ACT_STAT:
Chris@199 1533 if (fsWatcher) fsWatcher->blockSignals(false);
Chris@199 1534 break; // go on and report
Chris@113 1535 default:
Chris@114 1536 break;
Chris@113 1537 }
Chris@113 1538
Chris@113 1539 QString command = action.executable;
Chris@113 1540 if (command == "") command = "hg";
Chris@113 1541 foreach (QString arg, action.params) {
Chris@113 1542 command += " " + arg;
Chris@113 1543 }
Chris@113 1544
Chris@113 1545 QString message = tr("<qt><h3>Command failed</h3>"
Chris@113 1546 "<p>The following command failed:</p>"
Chris@113 1547 "<code>%1</code>"
Chris@113 1548 "%2</qt>")
Chris@113 1549 .arg(command)
Chris@143 1550 .arg((output.trimmed() != "") ?
Chris@113 1551 tr("<p>Its output said:</p><code>%1</code>")
Chris@143 1552 .arg(xmlEncode(output.left(800))
Chris@113 1553 .replace("\n", "<br>"))
Chris@113 1554 : "");
Chris@113 1555
Chris@113 1556 QMessageBox::warning(this, tr("Command failed"), message);
Chris@62 1557 }
Chris@62 1558
Chris@109 1559 void MainWindow::commandCompleted(HgAction completedAction, QString output)
jtkorhonen@0 1560 {
Chris@109 1561 HGACTIONS action = completedAction.action;
Chris@109 1562
Chris@109 1563 if (action == ACT_NONE) return;
Chris@109 1564
Chris@150 1565 bool headsChanged = false;
Chris@150 1566 QStringList oldHeadIds;
Chris@150 1567
Chris@150 1568 switch (action) {
Chris@109 1569
Chris@175 1570 case ACT_TEST_HG:
Chris@175 1571 break;
Chris@175 1572
Chris@200 1573 case ACT_TEST_HG_EXT:
Chris@200 1574 break;
Chris@200 1575
Chris@109 1576 case ACT_QUERY_PATHS:
jtkorhonen@0 1577 {
Chris@109 1578 DEBUG << "stdout is " << output << endl;
Chris@109 1579 LogParser lp(output, "=");
Chris@109 1580 LogList ll = lp.parse();
Chris@109 1581 DEBUG << ll.size() << " results" << endl;
Chris@109 1582 if (!ll.empty()) {
Chris@109 1583 remoteRepoPath = lp.parse()[0]["default"].trimmed();
Chris@109 1584 DEBUG << "Set remote path to " << remoteRepoPath << endl;
Chris@109 1585 }
Chris@109 1586 MultiChoiceDialog::addRecentArgument("local", workFolderPath);
Chris@109 1587 MultiChoiceDialog::addRecentArgument("remote", remoteRepoPath);
Chris@109 1588 hgTabs->setWorkFolderAndRepoNames(workFolderPath, remoteRepoPath);
Chris@109 1589 break;
Chris@109 1590 }
jtkorhonen@0 1591
Chris@109 1592 case ACT_QUERY_BRANCH:
Chris@109 1593 currentBranch = output.trimmed();
Chris@109 1594 break;
jtkorhonen@0 1595
Chris@109 1596 case ACT_STAT:
Chris@163 1597 lastStatOutput = output;
Chris@109 1598 updateFileSystemWatcher();
Chris@199 1599 if (fsWatcher) fsWatcher->blockSignals(false);
Chris@109 1600 break;
Chris@163 1601
Chris@163 1602 case ACT_RESOLVE_LIST:
Chris@163 1603 if (output != "") {
Chris@163 1604 // Remove lines beginning with R (they are resolved,
Chris@163 1605 // and the file stat parser treats R as removed)
Chris@163 1606 QStringList outList = output.split('\n');
Chris@163 1607 QStringList winnowed;
Chris@163 1608 foreach (QString line, outList) {
Chris@163 1609 if (!line.startsWith("R ")) winnowed.push_back(line);
Chris@163 1610 }
Chris@163 1611 output = winnowed.join("\n");
Chris@163 1612 }
Chris@163 1613 DEBUG << "lastStatOutput = " << lastStatOutput << endl;
Chris@199 1614 DEBUG << "resolve output = " << output << endl;
Chris@163 1615 hgTabs->updateWorkFolderFileList(lastStatOutput + output);
Chris@163 1616 break;
Chris@163 1617
Chris@163 1618 case ACT_RESOLVE_MARK:
Chris@163 1619 shouldHgStat = true;
Chris@163 1620 break;
Chris@109 1621
Chris@109 1622 case ACT_INCOMING:
Chris@120 1623 showIncoming(output);
Chris@120 1624 break;
Chris@120 1625
Chris@109 1626 case ACT_ANNOTATE:
Chris@109 1627 presentLongStdoutToUser(output);
Chris@109 1628 shouldHgStat = true;
Chris@109 1629 break;
Chris@109 1630
Chris@109 1631 case ACT_PULL:
Chris@120 1632 showPullResult(output);
Chris@109 1633 shouldHgStat = true;
Chris@109 1634 break;
Chris@109 1635
Chris@109 1636 case ACT_PUSH:
Chris@120 1637 showPushResult(output);
Chris@109 1638 break;
Chris@109 1639
Chris@109 1640 case ACT_INIT:
Chris@109 1641 MultiChoiceDialog::addRecentArgument("init", workFolderPath);
Chris@109 1642 MultiChoiceDialog::addRecentArgument("local", workFolderPath);
Chris@109 1643 enableDisableActions();
Chris@109 1644 shouldHgStat = true;
Chris@109 1645 break;
Chris@109 1646
Chris@109 1647 case ACT_CLONEFROMREMOTE:
Chris@109 1648 MultiChoiceDialog::addRecentArgument("local", workFolderPath);
Chris@109 1649 MultiChoiceDialog::addRecentArgument("remote", remoteRepoPath);
Chris@109 1650 MultiChoiceDialog::addRecentArgument("remote", workFolderPath, true);
Chris@109 1651 QMessageBox::information(this, "Clone", output);
Chris@109 1652 enableDisableActions();
Chris@109 1653 shouldHgStat = true;
Chris@109 1654 break;
Chris@109 1655
Chris@109 1656 case ACT_LOG:
Chris@120 1657 hgTabs->setNewLog(output);
Chris@120 1658 needNewLog = false;
Chris@120 1659 break;
Chris@120 1660
Chris@120 1661 case ACT_LOG_INCREMENTAL:
Chris@120 1662 hgTabs->addIncrementalLog(output);
Chris@109 1663 break;
Chris@109 1664
Chris@109 1665 case ACT_QUERY_PARENTS:
Chris@152 1666 {
Chris@109 1667 foreach (Changeset *cs, currentParents) delete cs;
Chris@109 1668 currentParents = Changeset::parseChangesets(output);
Chris@152 1669 QStringList parentIds = Changeset::getIds(currentParents);
Chris@153 1670 hgTabs->setCurrent(parentIds, currentBranch);
Chris@152 1671 }
Chris@109 1672 break;
Chris@109 1673
Chris@109 1674 case ACT_QUERY_HEADS:
Chris@150 1675 {
Chris@150 1676 oldHeadIds = Changeset::getIds(currentHeads);
Chris@150 1677 Changesets newHeads = Changeset::parseChangesets(output);
Chris@150 1678 QStringList newHeadIds = Changeset::getIds(newHeads);
Chris@150 1679 if (oldHeadIds != newHeadIds) {
Chris@150 1680 DEBUG << "Heads changed, will prompt an incremental log if appropriate" << endl;
Chris@150 1681 headsChanged = true;
Chris@150 1682 foreach (Changeset *cs, currentHeads) delete cs;
Chris@150 1683 currentHeads = newHeads;
Chris@150 1684 }
Chris@150 1685 }
Chris@109 1686 break;
Chris@130 1687
Chris@130 1688 case ACT_COMMIT:
Chris@130 1689 hgTabs->clearSelections();
Chris@163 1690 justMerged = false;
Chris@130 1691 shouldHgStat = true;
Chris@130 1692 break;
Chris@163 1693
Chris@163 1694 case ACT_REVERT:
Chris@163 1695 hgMarkResolved(lastRevertedFiles);
Chris@163 1696 justMerged = false;
Chris@163 1697 break;
Chris@109 1698
Chris@109 1699 case ACT_REMOVE:
Chris@109 1700 case ACT_ADD:
Chris@116 1701 hgTabs->clearSelections();
Chris@116 1702 shouldHgStat = true;
Chris@116 1703 break;
Chris@116 1704
Chris@164 1705 case ACT_TAG:
Chris@164 1706 needNewLog = true;
Chris@164 1707 shouldHgStat = true;
Chris@164 1708 break;
Chris@164 1709
Chris@168 1710 case ACT_DIFF_SUMMARY:
Chris@168 1711 QMessageBox::information(this, tr("Change summary"),
Chris@168 1712 format3(tr("Summary of uncommitted changes"),
Chris@168 1713 "",
Chris@168 1714 output));
Chris@168 1715 break;
Chris@168 1716
Chris@109 1717 case ACT_FOLDERDIFF:
Chris@109 1718 case ACT_CHGSETDIFF:
Chris@109 1719 case ACT_SERVE:
Chris@109 1720 case ACT_HG_IGNORE:
Chris@109 1721 shouldHgStat = true;
Chris@109 1722 break;
Chris@109 1723
Chris@109 1724 case ACT_UPDATE:
Chris@162 1725 QMessageBox::information(this, tr("Update"), tr("<qt><h3>Update successful</h3><p>%1</p>").arg(xmlEncode(output)));
Chris@109 1726 shouldHgStat = true;
Chris@109 1727 break;
Chris@109 1728
Chris@109 1729 case ACT_MERGE:
Chris@196 1730 //!!! use format3?
Chris@196 1731 QMessageBox::information(this, tr("Merge"), tr("<qt><h3>Merge successful</h3><pre>%1</pre>").arg(xmlEncode(output)));
Chris@109 1732 shouldHgStat = true;
Chris@109 1733 justMerged = true;
Chris@109 1734 break;
Chris@109 1735
Chris@109 1736 case ACT_RETRY_MERGE:
Chris@163 1737 QMessageBox::information(this, tr("Resolved"),
Chris@163 1738 tr("<qt><h3>Merge resolved</h3><p>Merge resolved successfully.</p>"));
Chris@109 1739 shouldHgStat = true;
Chris@109 1740 justMerged = true;
Chris@109 1741 break;
Chris@109 1742
Chris@109 1743 default:
Chris@109 1744 break;
Chris@109 1745 }
Chris@108 1746
Chris@121 1747 // Sequence when no full log required:
Chris@163 1748 // paths -> branch -> stat -> resolve-list -> heads ->
Chris@150 1749 // incremental-log (only if heads changed) -> parents
Chris@150 1750 //
Chris@121 1751 // Sequence when full log required:
Chris@163 1752 // paths -> branch -> stat -> resolve-list -> heads -> parents -> log
Chris@150 1753 //
Chris@150 1754 // Note we want to call enableDisableActions only once, at the end
Chris@150 1755 // of whichever sequence is in use.
Chris@150 1756
Chris@156 1757 bool noMore = false;
Chris@156 1758
Chris@150 1759 switch (action) {
Chris@175 1760
Chris@175 1761 case ACT_TEST_HG:
Chris@200 1762 hgTestExtension();
Chris@200 1763 break;
Chris@200 1764
Chris@200 1765 case ACT_TEST_HG_EXT:
Chris@175 1766 hgQueryPaths();
Chris@175 1767 break;
Chris@150 1768
Chris@150 1769 case ACT_QUERY_PATHS:
Chris@109 1770 hgQueryBranch();
Chris@150 1771 break;
Chris@150 1772
Chris@150 1773 case ACT_QUERY_BRANCH:
Chris@109 1774 hgStat();
Chris@150 1775 break;
Chris@150 1776
Chris@150 1777 case ACT_STAT:
Chris@163 1778 hgResolveList();
Chris@163 1779 break;
Chris@163 1780
Chris@163 1781 case ACT_RESOLVE_LIST:
Chris@150 1782 hgQueryHeads();
Chris@150 1783 break;
Chris@150 1784
Chris@150 1785 case ACT_QUERY_HEADS:
Chris@150 1786 if (headsChanged && !needNewLog) {
Chris@150 1787 hgLogIncremental(oldHeadIds);
Chris@121 1788 } else {
Chris@150 1789 hgQueryParents();
Chris@121 1790 }
Chris@150 1791 break;
Chris@150 1792
Chris@150 1793 case ACT_LOG_INCREMENTAL:
Chris@109 1794 hgQueryParents();
Chris@150 1795 break;
Chris@150 1796
Chris@150 1797 case ACT_QUERY_PARENTS:
Chris@120 1798 if (needNewLog) {
Chris@120 1799 hgLog();
Chris@150 1800 } else {
Chris@150 1801 // we're done
Chris@156 1802 noMore = true;
Chris@120 1803 }
Chris@150 1804 break;
Chris@150 1805
Chris@150 1806 case ACT_LOG:
Chris@150 1807 // we're done
Chris@156 1808 noMore = true;
Chris@198 1809 break;
Chris@150 1810
Chris@150 1811 default:
Chris@109 1812 if (shouldHgStat) {
Chris@163 1813 shouldHgStat = false;
Chris@109 1814 hgQueryPaths();
Chris@150 1815 } else {
Chris@156 1816 noMore = true;
jtkorhonen@0 1817 }
Chris@150 1818 break;
Chris@150 1819 }
Chris@156 1820
Chris@156 1821 if (noMore) {
Chris@173 1822 stateUnknown = false;
Chris@156 1823 enableDisableActions();
Chris@156 1824 hgTabs->updateHistory();
Chris@156 1825 }
jtkorhonen@0 1826 }
jtkorhonen@0 1827
jtkorhonen@0 1828 void MainWindow::connectActions()
jtkorhonen@0 1829 {
jtkorhonen@0 1830 connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
jtkorhonen@0 1831 connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));
jtkorhonen@0 1832
Chris@120 1833 connect(hgRefreshAct, SIGNAL(triggered()), this, SLOT(hgRefresh()));
jtkorhonen@0 1834 connect(hgRemoveAct, SIGNAL(triggered()), this, SLOT(hgRemove()));
jtkorhonen@0 1835 connect(hgAddAct, SIGNAL(triggered()), this, SLOT(hgAdd()));
jtkorhonen@0 1836 connect(hgCommitAct, SIGNAL(triggered()), this, SLOT(hgCommit()));
jtkorhonen@0 1837 connect(hgFolderDiffAct, SIGNAL(triggered()), this, SLOT(hgFolderDiff()));
jtkorhonen@0 1838 connect(hgUpdateAct, SIGNAL(triggered()), this, SLOT(hgUpdate()));
jtkorhonen@0 1839 connect(hgRevertAct, SIGNAL(triggered()), this, SLOT(hgRevert()));
jtkorhonen@0 1840 connect(hgMergeAct, SIGNAL(triggered()), this, SLOT(hgMerge()));
jtkorhonen@34 1841 connect(hgIgnoreAct, SIGNAL(triggered()), this, SLOT(hgIgnore()));
jtkorhonen@0 1842
jtkorhonen@0 1843 connect(settingsAct, SIGNAL(triggered()), this, SLOT(settings()));
Chris@69 1844 connect(openAct, SIGNAL(triggered()), this, SLOT(open()));
Chris@182 1845 connect(changeRemoteRepoAct, SIGNAL(triggered()), this, SLOT(changeRemoteRepo()));
jtkorhonen@0 1846
jtkorhonen@0 1847 connect(hgIncomingAct, SIGNAL(triggered()), this, SLOT(hgIncoming()));
jtkorhonen@0 1848 connect(hgPullAct, SIGNAL(triggered()), this, SLOT(hgPull()));
jtkorhonen@0 1849 connect(hgPushAct, SIGNAL(triggered()), this, SLOT(hgPush()));
jtkorhonen@0 1850
jtkorhonen@0 1851 connect(hgAnnotateAct, SIGNAL(triggered()), this, SLOT(hgAnnotate()));
jtkorhonen@11 1852 connect(hgServeAct, SIGNAL(triggered()), this, SLOT(hgServe()));
jtkorhonen@0 1853 }
Chris@141 1854
Chris@141 1855 void MainWindow::connectTabsSignals()
Chris@141 1856 {
Chris@141 1857 connect(hgTabs, SIGNAL(commit()),
Chris@141 1858 this, SLOT(hgCommit()));
Chris@141 1859
Chris@141 1860 connect(hgTabs, SIGNAL(revert()),
Chris@141 1861 this, SLOT(hgRevert()));
Chris@141 1862
Chris@141 1863 connect(hgTabs, SIGNAL(diffWorkingFolder()),
Chris@141 1864 this, SLOT(hgFolderDiff()));
Chris@168 1865
Chris@168 1866 connect(hgTabs, SIGNAL(showSummary()),
Chris@168 1867 this, SLOT(hgShowSummary()));
Chris@148 1868
Chris@141 1869 connect(hgTabs, SIGNAL(updateTo(QString)),
Chris@148 1870 this, SLOT(hgUpdateToRev(QString)));
Chris@141 1871
Chris@141 1872 connect(hgTabs, SIGNAL(diffToCurrent(QString)),
Chris@148 1873 this, SLOT(hgDiffToCurrent(QString)));
Chris@141 1874
Chris@148 1875 connect(hgTabs, SIGNAL(diffToParent(QString, QString)),
Chris@148 1876 this, SLOT(hgDiffToParent(QString, QString)));
Chris@141 1877
Chris@141 1878 connect(hgTabs, SIGNAL(mergeFrom(QString)),
Chris@148 1879 this, SLOT(hgMergeFrom(QString)));
Chris@164 1880
Chris@141 1881 connect(hgTabs, SIGNAL(tag(QString)),
Chris@148 1882 this, SLOT(hgTag(QString)));
Chris@141 1883 }
Chris@141 1884
jtkorhonen@0 1885 void MainWindow::enableDisableActions()
jtkorhonen@0 1886 {
Chris@90 1887 DEBUG << "MainWindow::enableDisableActions" << endl;
Chris@90 1888
Chris@115 1889 //!!! should also do things like set the status texts for the
Chris@115 1890 //!!! actions appropriately by context
Chris@115 1891
jtkorhonen@0 1892 QDir localRepoDir;
jtkorhonen@0 1893 QDir workFolderDir;
Chris@145 1894 bool workFolderExist = true;
Chris@145 1895 bool localRepoExist = true;
jtkorhonen@0 1896
jtkorhonen@0 1897 remoteRepoActionsEnabled = true;
Chris@90 1898 if (remoteRepoPath.isEmpty()) {
jtkorhonen@0 1899 remoteRepoActionsEnabled = false;
jtkorhonen@0 1900 }
jtkorhonen@0 1901
jtkorhonen@0 1902 localRepoActionsEnabled = true;
Chris@90 1903 if (workFolderPath.isEmpty()) {
jtkorhonen@0 1904 localRepoActionsEnabled = false;
jtkorhonen@0 1905 workFolderExist = false;
jtkorhonen@0 1906 }
jtkorhonen@0 1907
Chris@90 1908 if (!workFolderDir.exists(workFolderPath)) {
jtkorhonen@0 1909 localRepoActionsEnabled = false;
jtkorhonen@0 1910 workFolderExist = false;
Chris@90 1911 } else {
jtkorhonen@0 1912 workFolderExist = true;
jtkorhonen@0 1913 }
jtkorhonen@0 1914
Chris@112 1915 if (!localRepoDir.exists(workFolderPath + "/.hg")) {
jtkorhonen@0 1916 localRepoActionsEnabled = false;
jtkorhonen@0 1917 localRepoExist = false;
jtkorhonen@0 1918 }
jtkorhonen@0 1919
jtkorhonen@0 1920 hgIncomingAct -> setEnabled(remoteRepoActionsEnabled && remoteRepoActionsEnabled);
jtkorhonen@0 1921 hgPullAct -> setEnabled(remoteRepoActionsEnabled && remoteRepoActionsEnabled);
jtkorhonen@0 1922 hgPushAct -> setEnabled(remoteRepoActionsEnabled && remoteRepoActionsEnabled);
Chris@169 1923
Chris@179 1924 bool haveDiff = false;
Chris@179 1925 QSettings settings;
Chris@179 1926 settings.beginGroup("Locations");
Chris@179 1927 if (settings.value("extdiffbinary", "").toString() != "") {
Chris@179 1928 haveDiff = true;
Chris@179 1929 }
Chris@179 1930 settings.endGroup();
Chris@112 1931
Chris@120 1932 hgRefreshAct -> setEnabled(localRepoActionsEnabled);
Chris@112 1933 hgFolderDiffAct -> setEnabled(localRepoActionsEnabled && haveDiff);
jtkorhonen@0 1934 hgRevertAct -> setEnabled(localRepoActionsEnabled);
jtkorhonen@0 1935 hgAddAct -> setEnabled(localRepoActionsEnabled);
jtkorhonen@0 1936 hgRemoveAct -> setEnabled(localRepoActionsEnabled);
jtkorhonen@0 1937 hgUpdateAct -> setEnabled(localRepoActionsEnabled);
jtkorhonen@0 1938 hgCommitAct -> setEnabled(localRepoActionsEnabled);
jtkorhonen@0 1939 hgMergeAct -> setEnabled(localRepoActionsEnabled);
jtkorhonen@2 1940 hgAnnotateAct -> setEnabled(localRepoActionsEnabled);
jtkorhonen@11 1941 hgServeAct -> setEnabled(localRepoActionsEnabled);
jtkorhonen@34 1942 hgIgnoreAct -> setEnabled(localRepoActionsEnabled);
jtkorhonen@0 1943
Chris@90 1944 DEBUG << "localRepoActionsEnabled = " << localRepoActionsEnabled << endl;
Chris@98 1945 DEBUG << "canCommit = " << hgTabs->canCommit() << endl;
Chris@90 1946
Chris@98 1947 hgAddAct->setEnabled(localRepoActionsEnabled && hgTabs->canAdd());
Chris@98 1948 hgRemoveAct->setEnabled(localRepoActionsEnabled && hgTabs->canRemove());
Chris@98 1949 hgCommitAct->setEnabled(localRepoActionsEnabled && hgTabs->canCommit());
Chris@163 1950 hgRevertAct->setEnabled(localRepoActionsEnabled && hgTabs->canRevert());
Chris@163 1951 hgFolderDiffAct->setEnabled(localRepoActionsEnabled && hgTabs->canDiff());
Chris@90 1952
Chris@108 1953 // A default merge makes sense if:
Chris@108 1954 // * there is only one parent (if there are two, we have an uncommitted merge) and
Chris@108 1955 // * there are exactly two heads that have the same branch as the current branch and
Chris@108 1956 // * our parent is one of those heads
Chris@108 1957 //
Chris@108 1958 // A default update makes sense if:
Chris@108 1959 // * there is only one parent and
Chris@108 1960 // * the parent is not one of the current heads
Chris@156 1961
Chris@108 1962 bool canMerge = false;
Chris@108 1963 bool canUpdate = false;
Chris@156 1964 bool haveMerge = false;
Chris@162 1965 bool emptyRepo = false;
Chris@156 1966 int currentBranchHeads = 0;
Chris@156 1967
Chris@108 1968 if (currentParents.size() == 1) {
Chris@156 1969 bool parentIsHead = false;
Chris@108 1970 Changeset *parent = currentParents[0];
Chris@108 1971 foreach (Changeset *head, currentHeads) {
Chris@108 1972 DEBUG << "head branch " << head->branch() << ", current branch " << currentBranch << endl;
Chris@108 1973 if (head->isOnBranch(currentBranch)) {
Chris@108 1974 ++currentBranchHeads;
Chris@108 1975 if (parent->id() == head->id()) {
Chris@108 1976 parentIsHead = true;
Chris@108 1977 }
Chris@108 1978 }
Chris@108 1979 }
Chris@108 1980 if (currentBranchHeads == 2 && parentIsHead) {
Chris@108 1981 canMerge = true;
Chris@108 1982 }
Chris@108 1983 if (!parentIsHead) {
Chris@108 1984 canUpdate = true;
Chris@108 1985 DEBUG << "parent id = " << parent->id() << endl;
Chris@108 1986 DEBUG << " head ids "<<endl;
Chris@108 1987 foreach (Changeset *h, currentHeads) {
Chris@108 1988 DEBUG << "head id = " << h->id() << endl;
Chris@108 1989 }
Chris@108 1990 }
Chris@162 1991 } else if (currentParents.size() == 0) {
Chris@162 1992 emptyRepo = true;
Chris@156 1993 } else {
Chris@156 1994 haveMerge = true;
Chris@163 1995 justMerged = true;
Chris@108 1996 }
Chris@156 1997
Chris@163 1998 hgMergeAct->setEnabled(localRepoActionsEnabled &&
Chris@163 1999 (canMerge || hgTabs->canResolve()));
Chris@163 2000 hgUpdateAct->setEnabled(localRepoActionsEnabled &&
Chris@172 2001 (canUpdate && !hgTabs->haveChangesToCommit()));
Chris@115 2002
Chris@115 2003 // Set the state field on the file status widget
Chris@115 2004
Chris@115 2005 QString branchText;
Chris@115 2006 if (currentBranch == "" || currentBranch == "default") {
Chris@115 2007 branchText = tr("the default branch");
Chris@115 2008 } else {
Chris@115 2009 branchText = tr("branch \"%1\"").arg(currentBranch);
Chris@115 2010 }
Chris@156 2011
Chris@173 2012 if (stateUnknown) {
Chris@173 2013 hgTabs->setState(tr("(Examining repository)"));
Chris@173 2014 } else if (emptyRepo) {
Chris@162 2015 hgTabs->setState(tr("Nothing committed to this repository yet"));
Chris@162 2016 } else if (canMerge) {
Chris@156 2017 hgTabs->setState(tr("<b>Awaiting merge</b> on %1").arg(branchText));
Chris@163 2018 } else if (!hgTabs->getAllUnresolvedFiles().empty()) {
Chris@163 2019 hgTabs->setState(tr("Have unresolved files following merge on %1").arg(branchText));
Chris@156 2020 } else if (haveMerge) {
Chris@157 2021 hgTabs->setState(tr("Have merged but not yet committed on %1").arg(branchText));
Chris@156 2022 } else if (canUpdate) {
Chris@172 2023 if (hgTabs->haveChangesToCommit()) {
Chris@163 2024 // have uncommitted changes
Chris@163 2025 hgTabs->setState(tr("On %1. Not at the head of the branch").arg(branchText));
Chris@163 2026 } else {
Chris@163 2027 // no uncommitted changes
Chris@163 2028 hgTabs->setState(tr("On %1. Not at the head of the branch: consider updating").arg(branchText));
Chris@163 2029 }
Chris@156 2030 } else if (currentBranchHeads > 1) {
Chris@156 2031 hgTabs->setState(tr("At one of %n heads of %1", "", currentBranchHeads).arg(branchText));
Chris@115 2032 } else {
Chris@115 2033 hgTabs->setState(tr("At the head of %1").arg(branchText));
Chris@115 2034 }
jtkorhonen@0 2035 }
jtkorhonen@0 2036
jtkorhonen@0 2037 void MainWindow::createActions()
jtkorhonen@0 2038 {
jtkorhonen@0 2039 //File actions
Chris@69 2040 openAct = new QAction(QIcon(":/images/fileopen.png"), tr("Open..."), this);
Chris@199 2041 openAct -> setStatusTip(tr("Open an existing repository or working folder"));
Chris@69 2042
Chris@182 2043 changeRemoteRepoAct = new QAction(tr("Change Remote Location..."), this);
Chris@182 2044 changeRemoteRepoAct->setStatusTip(tr("Change the default remote repository for pull and push actions"));
Chris@182 2045
jtkorhonen@0 2046 settingsAct = new QAction(QIcon(":/images/settings.png"), tr("Settings..."), this);
jtkorhonen@0 2047 settingsAct -> setStatusTip(tr("View and change application settings"));
jtkorhonen@0 2048
Chris@169 2049 exitAct = new QAction(QIcon(":/images/exit.png"), tr("Quit"), this);
jtkorhonen@0 2050 exitAct->setShortcuts(QKeySequence::Quit);
Chris@169 2051 exitAct->setStatusTip(tr("Quit EasyMercurial"));
jtkorhonen@0 2052
jtkorhonen@0 2053 //Repository actions
Chris@120 2054 hgRefreshAct = new QAction(QIcon(":/images/status.png"), tr("Refresh"), this);
Chris@178 2055 hgRefreshAct->setStatusTip(tr("Refresh the window to show the current state of the working folder"));
Chris@61 2056
Chris@61 2057 hgIncomingAct = new QAction(QIcon(":/images/incoming.png"), tr("Preview"), this);
Chris@169 2058 hgIncomingAct -> setStatusTip(tr("See what changes are available in the remote repository waiting to be pulled"));
jtkorhonen@0 2059
Chris@61 2060 hgPullAct = new QAction(QIcon(":/images/pull.png"), tr("Pull"), this);
Chris@169 2061 hgPullAct -> setStatusTip(tr("Pull changes from the remote repository to the local repository"));
jtkorhonen@0 2062
Chris@61 2063 hgPushAct = new QAction(QIcon(":/images/push.png"), tr("Push"), this);
Chris@169 2064 hgPushAct->setStatusTip(tr("Push changes from the local repository to the remote repository"));
jtkorhonen@0 2065
jtkorhonen@0 2066 //Workfolder actions
Chris@99 2067 hgFolderDiffAct = new QAction(QIcon(":/images/folderdiff.png"), tr("Diff"), this);
Chris@169 2068 hgFolderDiffAct->setStatusTip(tr("See what has changed in the working folder compared with the last committed state"));
jtkorhonen@0 2069
Chris@61 2070 hgRevertAct = new QAction(QIcon(":/images/undo.png"), tr("Revert"), this);
Chris@169 2071 hgRevertAct->setStatusTip(tr("Throw away your changes and return to the last committed state"));
jtkorhonen@0 2072
Chris@61 2073 hgAddAct = new QAction(QIcon(":/images/add.png"), tr("Add"), this);
Chris@169 2074 hgAddAct -> setStatusTip(tr("Mark the selected file(s) to be added on the next commit"));
jtkorhonen@0 2075
Chris@169 2076 //!!! needs to be modified for number
Chris@61 2077 hgRemoveAct = new QAction(QIcon(":/images/remove.png"), tr("Remove"), this);
Chris@169 2078 hgRemoveAct -> setStatusTip(tr("Mark the selected file(s) to be removed from version control on the next commit"));
jtkorhonen@0 2079
Chris@61 2080 hgUpdateAct = new QAction(QIcon(":/images/update.png"), tr("Update"), this);
Chris@169 2081 hgUpdateAct->setStatusTip(tr("Update the working folder to the head of the current repository branch"));
jtkorhonen@0 2082
Chris@169 2083 //!!! needs to be modified when files selected
Chris@61 2084 hgCommitAct = new QAction(QIcon(":/images/commit.png"), tr("Commit"), this);
Chris@169 2085 hgCommitAct->setStatusTip(tr("Commit your changes to the local repository"));
jtkorhonen@0 2086
jtkorhonen@0 2087 hgMergeAct = new QAction(QIcon(":/images/merge.png"), tr("Merge"), this);
Chris@169 2088 hgMergeAct->setStatusTip(tr("Merge the two independent sets of changes in the local repository into the working folder"));
jtkorhonen@0 2089
jtkorhonen@0 2090 //Advanced actions
Chris@169 2091 //!!! needs to be modified for number
jtkorhonen@0 2092 hgAnnotateAct = new QAction(tr("Annotate"), this);
jtkorhonen@0 2093 hgAnnotateAct -> setStatusTip(tr("Show line-by-line version information for selected file"));
jtkorhonen@0 2094
Chris@182 2095 hgIgnoreAct = new QAction(tr("Edit .hgignore File"), this);
Chris@169 2096 hgIgnoreAct -> setStatusTip(tr("Edit the .hgignore file, containing the names of files that should be ignored by Mercurial"));
jtkorhonen@34 2097
Chris@182 2098 hgServeAct = new QAction(tr("Serve via HTTP"), this);
jtkorhonen@11 2099 hgServeAct -> setStatusTip(tr("Serve local repository via http for workgroup access"));
jtkorhonen@11 2100
jtkorhonen@0 2101 //Help actions
Chris@190 2102 aboutAct = new QAction(tr("About EasyMercurial"), this);
Chris@94 2103
Chris@94 2104 // Miscellaneous
Chris@199 2105 QShortcut *clearSelectionsShortcut = new QShortcut(Qt::Key_Escape, this);
Chris@199 2106 connect(clearSelectionsShortcut, SIGNAL(activated()),
Chris@199 2107 this, SLOT(clearSelections()));
jtkorhonen@0 2108 }
jtkorhonen@0 2109
jtkorhonen@0 2110 void MainWindow::createMenus()
jtkorhonen@0 2111 {
jtkorhonen@0 2112 fileMenu = menuBar()->addMenu(tr("File"));
Chris@169 2113
Chris@69 2114 fileMenu -> addAction(openAct);
Chris@182 2115 fileMenu -> addAction(changeRemoteRepoAct);
Chris@191 2116 fileMenu -> addSeparator();
Chris@191 2117
Chris@191 2118 advancedMenu = fileMenu->addMenu(tr("Advanced"));
Chris@191 2119
jtkorhonen@0 2120 fileMenu -> addAction(settingsAct);
Chris@191 2121
jtkorhonen@0 2122 fileMenu -> addSeparator();
jtkorhonen@0 2123 fileMenu -> addAction(exitAct);
jtkorhonen@0 2124
jtkorhonen@34 2125 advancedMenu -> addAction(hgIgnoreAct);
Chris@199 2126 advancedMenu -> addSeparator();
jtkorhonen@11 2127 advancedMenu -> addAction(hgServeAct);
jtkorhonen@0 2128
jtkorhonen@0 2129 helpMenu = menuBar()->addMenu(tr("Help"));
jtkorhonen@0 2130 helpMenu->addAction(aboutAct);
jtkorhonen@0 2131 }
jtkorhonen@0 2132
jtkorhonen@0 2133 void MainWindow::createToolBars()
jtkorhonen@0 2134 {
jtkorhonen@0 2135 fileToolBar = addToolBar(tr("File"));
jtkorhonen@0 2136 fileToolBar -> setIconSize(QSize(MY_ICON_SIZE, MY_ICON_SIZE));
Chris@69 2137 fileToolBar -> addAction(openAct);
Chris@120 2138 fileToolBar -> addAction(hgRefreshAct);
jtkorhonen@0 2139 fileToolBar -> addSeparator();
jtkorhonen@0 2140 fileToolBar -> setMovable(false);
jtkorhonen@0 2141
jtkorhonen@0 2142 repoToolBar = addToolBar(tr(REPOMENU_TITLE));
jtkorhonen@0 2143 repoToolBar -> setIconSize(QSize(MY_ICON_SIZE, MY_ICON_SIZE));
jtkorhonen@0 2144 repoToolBar->addAction(hgIncomingAct);
jtkorhonen@0 2145 repoToolBar->addAction(hgPullAct);
jtkorhonen@0 2146 repoToolBar->addAction(hgPushAct);
jtkorhonen@0 2147 repoToolBar -> setMovable(false);
jtkorhonen@0 2148
jtkorhonen@0 2149 workFolderToolBar = addToolBar(tr(WORKFOLDERMENU_TITLE));
jtkorhonen@0 2150 addToolBar(Qt::LeftToolBarArea, workFolderToolBar);
jtkorhonen@0 2151 workFolderToolBar -> setIconSize(QSize(MY_ICON_SIZE, MY_ICON_SIZE));
Chris@95 2152 workFolderToolBar->addAction(hgFolderDiffAct);
jtkorhonen@0 2153 workFolderToolBar->addSeparator();
jtkorhonen@0 2154 workFolderToolBar->addAction(hgRevertAct);
jtkorhonen@0 2155 workFolderToolBar->addAction(hgUpdateAct);
jtkorhonen@0 2156 workFolderToolBar->addAction(hgCommitAct);
jtkorhonen@0 2157 workFolderToolBar->addAction(hgMergeAct);
jtkorhonen@0 2158 workFolderToolBar->addSeparator();
jtkorhonen@0 2159 workFolderToolBar->addAction(hgAddAct);
jtkorhonen@0 2160 workFolderToolBar->addAction(hgRemoveAct);
jtkorhonen@0 2161 workFolderToolBar -> setMovable(false);
Chris@61 2162
Chris@61 2163 foreach (QToolButton *tb, findChildren<QToolButton *>()) {
Chris@61 2164 tb->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
Chris@61 2165 }
jtkorhonen@0 2166 }
jtkorhonen@0 2167
jtkorhonen@0 2168
jtkorhonen@0 2169 void MainWindow::createStatusBar()
jtkorhonen@0 2170 {
jtkorhonen@0 2171 statusBar()->showMessage(tr("Ready"));
jtkorhonen@0 2172 }
jtkorhonen@0 2173
Chris@69 2174
Chris@69 2175 //!!! review these:
Chris@69 2176
jtkorhonen@0 2177 void MainWindow::readSettings()
jtkorhonen@0 2178 {
jtkorhonen@0 2179 QDir workFolder;
jtkorhonen@0 2180
Chris@61 2181 QSettings settings;
jtkorhonen@0 2182
jtkorhonen@30 2183 remoteRepoPath = settings.value("remoterepopath", "").toString();
jtkorhonen@0 2184 workFolderPath = settings.value("workfolderpath", "").toString();
jtkorhonen@0 2185 if (!workFolder.exists(workFolderPath))
jtkorhonen@0 2186 {
jtkorhonen@0 2187 workFolderPath = "";
jtkorhonen@0 2188 }
jtkorhonen@0 2189
jtkorhonen@0 2190 QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
jtkorhonen@0 2191 QSize size = settings.value("size", QSize(400, 400)).toSize();
jtkorhonen@0 2192 firstStart = settings.value("firststart", QVariant(true)).toBool();
jtkorhonen@0 2193
Chris@109 2194 //!!! initialFileTypesBits = (unsigned char) settings.value("viewFileTypes", QVariant(DEFAULT_HG_STAT_BITS)).toInt();
jtkorhonen@0 2195 resize(size);
jtkorhonen@0 2196 move(pos);
jtkorhonen@0 2197 }
jtkorhonen@0 2198
jtkorhonen@17 2199
jtkorhonen@0 2200 void MainWindow::writeSettings()
jtkorhonen@0 2201 {
Chris@61 2202 QSettings settings;
jtkorhonen@0 2203 settings.setValue("pos", pos());
jtkorhonen@0 2204 settings.setValue("size", size());
jtkorhonen@0 2205 settings.setValue("remoterepopath", remoteRepoPath);
jtkorhonen@0 2206 settings.setValue("workfolderpath", workFolderPath);
jtkorhonen@0 2207 settings.setValue("firststart", firstStart);
Chris@98 2208 //!!!settings.setValue("viewFileTypes", hgTabs -> getFileTypesBits());
jtkorhonen@0 2209 }
jtkorhonen@0 2210
jtkorhonen@0 2211
jtkorhonen@0 2212
jtkorhonen@0 2213