annotate mainwindow.cpp @ 145:644bd31e8301

* Include the uncommitted item in general graph layout (in case it is not at the head, when other items will need to avoid it)
author Chris Cannam
date Wed, 01 Dec 2010 17:41:14 +0000
parents f61f032b06f9
children 465c8d51c6d5
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>
jtkorhonen@0 33
Chris@53 34 #include "mainwindow.h"
Chris@69 35 #include "multichoicedialog.h"
Chris@64 36 #include "startupdialog.h"
Chris@53 37 #include "colourset.h"
Chris@62 38 #include "debug.h"
Chris@74 39 #include "logparser.h"
Chris@103 40 #include "confirmcommentdialog.h"
Chris@125 41 #include "incomingdialog.h"
Chris@53 42
jtkorhonen@0 43
jtkorhonen@0 44 MainWindow::MainWindow()
jtkorhonen@0 45 {
jtkorhonen@0 46 QString wndTitle;
jtkorhonen@0 47
Chris@90 48 fsWatcher = 0;
Chris@133 49 commitsSincePush = 0;
Chris@90 50
jtkorhonen@0 51 createActions();
jtkorhonen@0 52 createMenus();
jtkorhonen@0 53 createToolBars();
jtkorhonen@0 54 createStatusBar();
jtkorhonen@0 55
jtkorhonen@0 56 runner = new HgRunner(this);
Chris@109 57 connect(runner, SIGNAL(commandCompleted(HgAction, QString)),
Chris@109 58 this, SLOT(commandCompleted(HgAction, QString)));
Chris@109 59 connect(runner, SIGNAL(commandFailed(HgAction, QString)),
Chris@109 60 this, SLOT(commandFailed(HgAction, QString)));
jtkorhonen@0 61 statusBar()->addPermanentWidget(runner);
jtkorhonen@0 62
Chris@61 63 setWindowTitle(tr("EasyMercurial"));
jtkorhonen@0 64
jtkorhonen@0 65 remoteRepoPath = "";
jtkorhonen@0 66 workFolderPath = "";
jtkorhonen@0 67
jtkorhonen@0 68 readSettings();
jtkorhonen@0 69
jtkorhonen@33 70 justMerged = false;
Chris@98 71 hgTabs = new HgTabWidget((QWidget *) this, remoteRepoPath, workFolderPath);
Chris@141 72 connectTabsSignals();
Chris@98 73 setCentralWidget(hgTabs);
jtkorhonen@0 74
Chris@98 75 connect(hgTabs, SIGNAL(selectionChanged()),
Chris@95 76 this, SLOT(enableDisableActions()));
Chris@95 77
jtkorhonen@0 78 setUnifiedTitleAndToolBarOnMac(true);
jtkorhonen@0 79 connectActions();
Chris@120 80 clearState();
jtkorhonen@0 81 enableDisableActions();
jtkorhonen@0 82
Chris@64 83 if (firstStart) {
Chris@64 84 startupDialog();
jtkorhonen@0 85 }
jtkorhonen@0 86
Chris@112 87 findDiffBinaryName();
Chris@112 88
Chris@64 89 ColourSet *cs = ColourSet::instance();
Chris@64 90 cs->clearDefaultNames();
Chris@64 91 cs->addDefaultName("");
Chris@64 92 cs->addDefaultName(getUserInfo());
Chris@62 93
Chris@72 94 if (workFolderPath == "") {
Chris@72 95 open();
Chris@72 96 }
Chris@72 97
Chris@109 98 hgQueryPaths();
jtkorhonen@0 99 }
jtkorhonen@0 100
jtkorhonen@0 101
jtkorhonen@0 102 void MainWindow::closeEvent(QCloseEvent *)
jtkorhonen@0 103 {
jtkorhonen@0 104 writeSettings();
Chris@90 105 delete fsWatcher;
jtkorhonen@0 106 }
jtkorhonen@0 107
jtkorhonen@0 108
Chris@64 109 QString MainWindow::getUserInfo() const
Chris@64 110 {
Chris@64 111 QSettings settings;
Chris@64 112 settings.beginGroup("User Information");
Chris@64 113 QString name = settings.value("name", getUserRealName()).toString();
Chris@64 114 QString email = settings.value("email", "").toString();
Chris@64 115
Chris@64 116 QString identifier;
Chris@64 117
Chris@64 118 if (email != "") {
Chris@64 119 identifier = QString("%1 <%2>").arg(name).arg(email);
Chris@64 120 } else {
Chris@64 121 identifier = name;
Chris@64 122 }
Chris@64 123
Chris@64 124 return identifier;
Chris@64 125 }
Chris@64 126
jtkorhonen@0 127 void MainWindow::about()
jtkorhonen@0 128 {
Chris@97 129 QMessageBox::about(this, tr("About EasyMercurial"),
Chris@97 130 tr("<qt><h2>About EasyMercurial</h2>"
Chris@97 131 "<p>EasyMercurial is a simple user interface for the "
Chris@97 132 "Mercurial version control system.</p>"
Chris@98 133 "<p>EasyMercurial is based on hgExplorer by "
Chris@97 134 "Jari Korhonen, with thanks.<br>EasyMercurial development carried out by "
Chris@97 135 "Chris Cannam for soundsoftware.ac.uk at the Centre for Digital Music, Queen Mary, University of London."
Chris@97 136 "<ul><li>Copyright &copy; 2010 Jari Korhonen</li>"
Chris@97 137 "<li>Copyright &copy; 2010 Chris Cannam</li>"
Chris@97 138 "<li>Copyright &copy; 2010 Queen Mary, University of London</li>"
Chris@97 139 "</ul>"
Chris@97 140 "<p> This program is free software; you can redistribute it and/or "
Chris@97 141 "modify it under the terms of the GNU General Public License as "
Chris@97 142 "published by the Free Software Foundation; either version 2 of the "
Chris@97 143 "License, or (at your option) any later version. See the file "
Chris@97 144 "COPYING included with this distribution for more information."));
jtkorhonen@0 145 }
jtkorhonen@0 146
Chris@94 147 void MainWindow::clearSelections()
Chris@94 148 {
Chris@98 149 hgTabs->clearSelections();
Chris@94 150 }
jtkorhonen@0 151
Chris@120 152 void MainWindow::hgRefresh()
Chris@120 153 {
Chris@120 154 clearState();
Chris@120 155 hgQueryPaths();
Chris@120 156 }
Chris@120 157
jtkorhonen@0 158 void MainWindow::hgStat()
jtkorhonen@0 159 {
Chris@109 160 QStringList params;
Chris@109 161 params << "stat" << "-ardum";
Chris@109 162 runner->requestAction(HgAction(ACT_STAT, workFolderPath, params));
jtkorhonen@0 163 }
jtkorhonen@0 164
Chris@109 165 void MainWindow::hgQueryPaths()
Chris@74 166 {
Chris@109 167 QStringList params;
Chris@109 168 params << "paths";
Chris@109 169 runner->requestAction(HgAction(ACT_QUERY_PATHS, workFolderPath, params));
Chris@74 170 }
Chris@74 171
Chris@109 172 void MainWindow::hgQueryBranch()
Chris@106 173 {
Chris@109 174 QStringList params;
Chris@109 175 params << "branch";
Chris@109 176 runner->requestAction(HgAction(ACT_QUERY_BRANCH, workFolderPath, params));
Chris@106 177 }
Chris@106 178
Chris@109 179 void MainWindow::hgQueryHeads()
jtkorhonen@0 180 {
Chris@109 181 QStringList params;
Chris@137 182 // On empty repos, "hg heads" will fail -- we don't care about
Chris@137 183 // that. Use --closed option so as to include closed branches;
Chris@137 184 // otherwise we'll be stuck if the user updates into one, and our
Chris@137 185 // incremental log will end up with spurious stuff in it because
Chris@137 186 // we won't be pruning at the ends of closed branches
Chris@137 187 params << "heads" << "--closed";
Chris@109 188 runner->requestAction(HgAction(ACT_QUERY_HEADS, workFolderPath, params));
jtkorhonen@0 189 }
jtkorhonen@0 190
jtkorhonen@0 191 void MainWindow::hgLog()
jtkorhonen@0 192 {
Chris@109 193 QStringList params;
Chris@109 194 params << "log";
Chris@109 195 params << "--template";
Chris@125 196 params << Changeset::getLogTemplate();
Chris@109 197
Chris@109 198 runner->requestAction(HgAction(ACT_LOG, workFolderPath, params));
Chris@109 199 }
Chris@109 200
Chris@120 201 void MainWindow::hgLogIncremental()
Chris@120 202 {
Chris@120 203 QStringList params;
Chris@120 204 params << "log";
Chris@120 205
Chris@120 206 foreach (Changeset *head, currentHeads) {
Chris@120 207 int n = head->number();
Chris@120 208 params << "--prune" << QString("%1").arg(n);
Chris@120 209 }
Chris@120 210
Chris@120 211 params << "--template";
Chris@125 212 params << Changeset::getLogTemplate();
Chris@120 213
Chris@120 214 runner->requestAction(HgAction(ACT_LOG_INCREMENTAL, workFolderPath, params));
Chris@120 215 }
Chris@109 216
Chris@109 217 void MainWindow::hgQueryParents()
Chris@109 218 {
Chris@109 219 QStringList params;
Chris@109 220 params << "parents";
Chris@109 221 runner->requestAction(HgAction(ACT_QUERY_PARENTS, workFolderPath, params));
Chris@109 222 }
Chris@109 223
Chris@109 224 void MainWindow::hgAnnotate()
Chris@109 225 {
Chris@109 226 QStringList params;
Chris@109 227 QString currentFile;//!!! = hgTabs -> getCurrentFileListLine();
Chris@109 228
Chris@109 229 if (!currentFile.isEmpty())
jtkorhonen@0 230 {
Chris@109 231 params << "annotate" << "--" << currentFile.mid(2); //Jump over status marker characters (e.g "M ")
jtkorhonen@0 232
Chris@109 233 runner->requestAction(HgAction(ACT_ANNOTATE, workFolderPath, params));
jtkorhonen@0 234 }
jtkorhonen@0 235 }
jtkorhonen@0 236
Chris@109 237 void MainWindow::hgResolveMark()
Chris@109 238 {
Chris@109 239 QStringList params;
Chris@109 240 QString currentFile;//!!! = hgTabs -> getCurrentFileListLine();
jtkorhonen@0 241
Chris@109 242 if (!currentFile.isEmpty())
jtkorhonen@0 243 {
Chris@109 244 params << "resolve" << "--mark" << "--" << currentFile.mid(2); //Jump over status marker characters (e.g "M ")
jtkorhonen@0 245
Chris@109 246 runner->requestAction(HgAction(ACT_RESOLVE_MARK, workFolderPath, params));
jtkorhonen@0 247 }
jtkorhonen@0 248 }
jtkorhonen@0 249
Chris@109 250 void MainWindow::hgResolveList()
Chris@109 251 {
Chris@109 252 QStringList params;
jtkorhonen@0 253
Chris@109 254 params << "resolve" << "--list";
Chris@109 255 runner->requestAction(HgAction(ACT_RESOLVE_LIST, workFolderPath, params));
Chris@109 256 }
Chris@109 257
Chris@109 258 void MainWindow::hgAdd()
jtkorhonen@0 259 {
Chris@109 260 QStringList params;
jtkorhonen@0 261
Chris@109 262 // hgExplorer permitted adding "all" files -- I'm not sure
Chris@109 263 // that one is a good idea, let's require the user to select
jtkorhonen@0 264
Chris@109 265 QStringList files = hgTabs->getSelectedAddableFiles();
Chris@109 266
Chris@109 267 if (!files.empty()) {
Chris@109 268 params << "add" << "--" << files;
Chris@109 269 runner->requestAction(HgAction(ACT_ADD, workFolderPath, params));
jtkorhonen@0 270 }
jtkorhonen@0 271 }
jtkorhonen@0 272
jtkorhonen@0 273
Chris@98 274 void MainWindow::hgRemove()
Chris@98 275 {
Chris@109 276 QStringList params;
Chris@98 277
Chris@109 278 QStringList files = hgTabs->getSelectedRemovableFiles();
Chris@98 279
Chris@109 280 //!!! todo: confirmation dialog (with file list in it) (or do we
Chris@109 281 // need that? all it does is move the files to the removed
Chris@109 282 // list... doesn't it?)
Chris@98 283
Chris@109 284 if (!files.empty()) {
Chris@109 285 params << "remove" << "--after" << "--force" << "--" << files;
Chris@109 286 runner->requestAction(HgAction(ACT_REMOVE, workFolderPath, params));
Chris@109 287 }
Chris@98 288
Chris@91 289 /*!!!
Chris@98 290 QString currentFile;//!!! = hgTabs -> getCurrentFileListLine();
Chris@98 291
Chris@98 292 if (!currentFile.isEmpty())
jtkorhonen@5 293 {
Chris@98 294 if (QMessageBox::Ok == QMessageBox::warning(this, "Remove file", "Really remove file " + currentFile.mid(2) + "?",
Chris@98 295 QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel))
Chris@98 296 {
Chris@98 297 params << "remove" << "--after" << "--force" << "--" << currentFile.mid(2); //Jump over status marker characters (e.g "M ")
jtkorhonen@17 298
Chris@98 299 runner -> startHgCommand(workFolderPath, params);
Chris@98 300 runningAction = ACT_REMOVE;
jtkorhonen@17 301 }
jtkorhonen@5 302 }
Chris@98 303 */
jtkorhonen@0 304 }
jtkorhonen@0 305
jtkorhonen@0 306 void MainWindow::hgCommit()
jtkorhonen@0 307 {
Chris@109 308 QStringList params;
Chris@109 309 QString comment;
Chris@94 310
Chris@109 311 QStringList files = hgTabs->getSelectedCommittableFiles();
Chris@127 312 QStringList reportFiles = files;
Chris@127 313 if (reportFiles.empty()) reportFiles = hgTabs->getAllCommittableFiles();
Chris@103 314
Chris@109 315 if (ConfirmCommentDialog::confirmAndGetLongComment
Chris@109 316 (this,
Chris@109 317 tr("Commit files"),
Chris@112 318 tr("<h3>Commit files</h3><p>You are about to commit the following files:"),
Chris@112 319 tr("<h3>Commit files</h3><p>You are about to commit %1 files."),
Chris@127 320 reportFiles,
Chris@109 321 comment)) {
Chris@103 322
Chris@109 323 if ((justMerged == false) && //!!! review usage of justMerged, and how it interacts with asynchronous request queue
Chris@109 324 !files.empty()) {
Chris@109 325 // User wants to commit selected file(s) (and this is not merge commit, which would fail if we selected files)
Chris@109 326 params << "commit" << "--message" << comment << "--user" << getUserInfo() << "--" << files;
Chris@109 327 } else {
Chris@109 328 // Commit all changes
Chris@109 329 params << "commit" << "--message" << comment << "--user" << getUserInfo();
jtkorhonen@0 330 }
Chris@109 331
Chris@109 332 runner->requestAction(HgAction(ACT_COMMIT, workFolderPath, params));
jtkorhonen@0 333 }
jtkorhonen@0 334 }
jtkorhonen@0 335
jtkorhonen@34 336 QString MainWindow::filterTag(QString tag)
jtkorhonen@34 337 {
jtkorhonen@34 338 for(int i = 0; i < tag.size(); i++)
jtkorhonen@34 339 {
jtkorhonen@34 340 if (tag[i].isLower() || tag[i].isUpper() || tag[i].isDigit() || (tag[i] == QChar('.')))
jtkorhonen@34 341 {
jtkorhonen@34 342 //ok
jtkorhonen@34 343 }
jtkorhonen@34 344 else
jtkorhonen@34 345 {
jtkorhonen@34 346 tag[i] = QChar('_');
jtkorhonen@34 347 }
jtkorhonen@34 348 }
jtkorhonen@34 349 return tag;
jtkorhonen@34 350 }
jtkorhonen@34 351
jtkorhonen@34 352
jtkorhonen@34 353 void MainWindow::hgTag()
jtkorhonen@34 354 {
Chris@109 355 QStringList params;
Chris@109 356 QString tag;
jtkorhonen@34 357
Chris@109 358 if (ConfirmCommentDialog::confirmAndGetShortComment
Chris@109 359 (this,
Chris@109 360 tr("Tag"),
Chris@109 361 tr("Enter tag:"),
Chris@109 362 tag)) {
Chris@109 363 if (!tag.isEmpty()) //!!! do something better if it is empty
Chris@109 364 {
Chris@109 365 params << "tag" << "--user" << getUserInfo() << filterTag(tag);
Chris@109 366
Chris@109 367 runner->requestAction(HgAction(ACT_TAG, workFolderPath, params));
jtkorhonen@34 368 }
jtkorhonen@34 369 }
jtkorhonen@34 370 }
jtkorhonen@34 371
jtkorhonen@34 372
jtkorhonen@34 373 void MainWindow::hgIgnore()
jtkorhonen@34 374 {
Chris@109 375 QString hgIgnorePath;
Chris@109 376 QStringList params;
Chris@109 377 QString editorName;
Chris@109 378
Chris@109 379 hgIgnorePath = workFolderPath;
Chris@109 380 hgIgnorePath += ".hgignore";
Chris@109 381
Chris@109 382 params << hgIgnorePath;
Chris@112 383
Chris@112 384 //!!!
Chris@112 385 #ifdef Q_OS_LINUX
Chris@112 386
Chris@109 387 editorName = "gedit";
Chris@112 388
Chris@112 389 #else
Chris@112 390
Chris@109 391 editorName = """C:\\Program Files\\Windows NT\\Accessories\\wordpad.exe""";
Chris@112 392
Chris@112 393 #endif
jtkorhonen@34 394
Chris@109 395 HgAction action(ACT_HG_IGNORE, workFolderPath, params);
Chris@109 396 action.executable = editorName;
jtkorhonen@34 397
Chris@109 398 runner->requestAction(action);
jtkorhonen@34 399 }
jtkorhonen@34 400
Chris@112 401 void MainWindow::findDiffBinaryName()
Chris@112 402 {
Chris@112 403 QSettings settings;
Chris@112 404 QString diff = settings.value("extdiffbinary", "").toString();
Chris@112 405 if (diff == "") {
Chris@112 406 QStringList bases;
Chris@112 407 bases << "opendiff" << "kompare" << "kdiff3" << "meld";
Chris@112 408 bool found = false;
Chris@112 409 foreach (QString base, bases) {
Chris@112 410 diff = findExecutable(base);
Chris@112 411 if (diff != base) {
Chris@112 412 found = true;
Chris@112 413 break;
Chris@112 414 }
Chris@112 415 }
Chris@112 416 if (found) {
Chris@112 417 settings.setValue("extdiffbinary", diff);
Chris@112 418 } else {
Chris@112 419 diff = "";
Chris@112 420 }
Chris@112 421 }
Chris@112 422 diffBinaryName = diff;
Chris@112 423 }
jtkorhonen@34 424
jtkorhonen@0 425 void MainWindow::hgFileDiff()
jtkorhonen@0 426 {
jtkorhonen@0 427 QStringList params;
Chris@91 428 /*!!!
Chris@98 429 QString currentFile = hgTabs -> getCurrentFileListLine();
jtkorhonen@0 430
jtkorhonen@0 431 if (!currentFile.isEmpty())
jtkorhonen@0 432 {
jtkorhonen@0 433 //Diff parent file against working folder file
mg@41 434 params << "kdiff3" << "--" << currentFile.mid(2);
Chris@62 435 runner -> startHgCommand(workFolderPath, params);
jtkorhonen@0 436 runningAction = ACT_FILEDIFF;
jtkorhonen@0 437 }
Chris@91 438 */
jtkorhonen@0 439 }
jtkorhonen@0 440
jtkorhonen@0 441
jtkorhonen@0 442 void MainWindow::hgFolderDiff()
jtkorhonen@0 443 {
Chris@112 444 if (diffBinaryName == "") return;
Chris@112 445
Chris@109 446 QStringList params;
jtkorhonen@0 447
Chris@112 448 // Diff parent against working folder (folder diff)
Chris@112 449
Chris@109 450 params << "--config" << "extensions.extdiff=" << "extdiff" << "-p";
Chris@112 451 params << diffBinaryName;
Chris@109 452
Chris@109 453 runner->requestAction(HgAction(ACT_FOLDERDIFF, workFolderPath, params));
jtkorhonen@0 454 }
jtkorhonen@0 455
jtkorhonen@0 456
jtkorhonen@0 457 void MainWindow::hgChgSetDiff()
jtkorhonen@0 458 {
jtkorhonen@0 459 QStringList params;
jtkorhonen@0 460
jtkorhonen@0 461 //Diff 2 history log versions against each other
jtkorhonen@0 462 QString revA;
jtkorhonen@0 463 QString revB;
Chris@91 464 /*!!!
Chris@98 465 hgTabs -> getHistoryDiffRevisions(revA, revB);
jtkorhonen@0 466
jtkorhonen@0 467 if ((!revA.isEmpty()) && (!revB.isEmpty()))
jtkorhonen@0 468 {
jtkorhonen@0 469 params << "kdiff3" << "--rev" << revA << "--rev" << revB;
Chris@62 470 runner -> startHgCommand(workFolderPath, params);
jtkorhonen@0 471 runningAction = ACT_CHGSETDIFF;
jtkorhonen@0 472 }
jtkorhonen@0 473 else
jtkorhonen@0 474 {
jtkorhonen@0 475 QMessageBox::information(this, tr("Changeset diff"), tr("Please select two changesets from history list or heads list first."));
jtkorhonen@0 476 }
Chris@91 477 */
jtkorhonen@0 478 }
jtkorhonen@0 479
jtkorhonen@0 480
jtkorhonen@0 481
jtkorhonen@0 482 void MainWindow::hgUpdate()
jtkorhonen@0 483 {
Chris@109 484 QStringList params;
jtkorhonen@0 485
Chris@109 486 params << "update";
Chris@109 487
Chris@109 488 runner->requestAction(HgAction(ACT_UPDATE, workFolderPath, params));
jtkorhonen@0 489 }
jtkorhonen@0 490
jtkorhonen@0 491
jtkorhonen@0 492 void MainWindow::hgUpdateToRev()
jtkorhonen@0 493 {
jtkorhonen@0 494 QStringList params;
jtkorhonen@0 495 QString rev;
Chris@91 496 /*!!!
Chris@98 497 hgTabs -> getUpdateToRevRevision(rev);
jtkorhonen@0 498
Chris@98 499 hgTabs -> setCurrentIndex(WORKTAB);
jtkorhonen@0 500 enableDisableActions();
jtkorhonen@0 501
jtkorhonen@0 502 params << "update" << "--rev" << rev << "--clean";
jtkorhonen@0 503
Chris@62 504 runner -> startHgCommand(workFolderPath, params);
jtkorhonen@0 505
jtkorhonen@0 506 runningAction = ACT_UPDATE;
Chris@91 507 */
Chris@109 508
jtkorhonen@0 509 }
jtkorhonen@0 510
jtkorhonen@0 511
jtkorhonen@0 512 void MainWindow::hgRevert()
jtkorhonen@0 513 {
Chris@109 514 QStringList params;
Chris@109 515 QString comment;
Chris@98 516
Chris@109 517 QStringList files = hgTabs->getSelectedRevertableFiles();
Chris@109 518 if (files.empty()) files = hgTabs->getAllRevertableFiles();
Chris@109 519
Chris@109 520 if (ConfirmCommentDialog::confirmDangerousFilesAction
Chris@109 521 (this,
Chris@109 522 tr("Revert files"),
Chris@127 523 tr("<h3>Revert files</h3><p>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@112 524 tr("<h3>Revert files</h3><p>You are about to <b>revert</b> %1 files.<br><br>This will <b>throw away any changes</b> that you have made to these files but have not committed."),
Chris@109 525 files)) {
Chris@109 526
Chris@98 527 if (files.empty()) {
Chris@98 528 params << "revert" << "--no-backup";
Chris@98 529 } else {
Chris@98 530 params << "revert" << "--no-backup" << "--" << files;
Chris@98 531 }
Chris@109 532
Chris@109 533 runner->requestAction(HgAction(ACT_REVERT, workFolderPath, params));
jtkorhonen@0 534 }
jtkorhonen@0 535 }
jtkorhonen@0 536
jtkorhonen@33 537 void MainWindow::hgRetryMerge()
jtkorhonen@33 538 {
Chris@109 539 QStringList params;
jtkorhonen@33 540
Chris@109 541 params << "resolve" << "--all";
Chris@109 542 runner->requestAction(HgAction(ACT_RETRY_MERGE, workFolderPath, params));
jtkorhonen@33 543 }
jtkorhonen@33 544
jtkorhonen@33 545
jtkorhonen@0 546 void MainWindow::hgMerge()
jtkorhonen@0 547 {
Chris@109 548 QStringList params;
jtkorhonen@0 549
Chris@109 550 params << "merge";
Chris@109 551
Chris@109 552 runner->requestAction(HgAction(ACT_MERGE, workFolderPath, params));
jtkorhonen@0 553 }
jtkorhonen@0 554
jtkorhonen@0 555
jtkorhonen@0 556 void MainWindow::hgCloneFromRemote()
jtkorhonen@0 557 {
Chris@109 558 QStringList params;
jtkorhonen@0 559
Chris@109 560 if (!QDir(workFolderPath).exists()) {
Chris@109 561 if (!QDir().mkpath(workFolderPath)) {
Chris@109 562 DEBUG << "hgCloneFromRemote: Failed to create target path "
Chris@109 563 << workFolderPath << endl;
Chris@109 564 //!!! report error
Chris@109 565 return;
Chris@104 566 }
Chris@109 567 }
Chris@104 568
Chris@109 569 params << "clone" << remoteRepoPath << workFolderPath;
Chris@109 570
Chris@109 571 hgTabs->setWorkFolderAndRepoNames(workFolderPath, remoteRepoPath);
Chris@113 572 hgTabs->updateWorkFolderFileList("");
jtkorhonen@0 573
Chris@109 574 runner->requestAction(HgAction(ACT_CLONEFROMREMOTE, workFolderPath, params));
jtkorhonen@0 575 }
jtkorhonen@0 576
jtkorhonen@0 577 void MainWindow::hgInit()
jtkorhonen@0 578 {
Chris@109 579 QStringList params;
jtkorhonen@0 580
Chris@109 581 params << "init";
Chris@109 582 params << workFolderPath;
jtkorhonen@0 583
Chris@109 584 runner->requestAction(HgAction(ACT_INIT, workFolderPath, params));
jtkorhonen@0 585 }
jtkorhonen@0 586
jtkorhonen@0 587 void MainWindow::hgIncoming()
jtkorhonen@0 588 {
Chris@109 589 QStringList params;
jtkorhonen@0 590
Chris@109 591 params << "incoming" << "--newest-first" << remoteRepoPath;
Chris@125 592 params << "--template" << Changeset::getLogTemplate();
jtkorhonen@0 593
Chris@109 594 runner->requestAction(HgAction(ACT_INCOMING, workFolderPath, params));
jtkorhonen@0 595 }
jtkorhonen@0 596
jtkorhonen@0 597 void MainWindow::hgPull()
jtkorhonen@0 598 {
Chris@126 599 if (QMessageBox::question
Chris@126 600 (this, tr("Confirm pull"),
Chris@126 601 format3(tr("Confirm pull from remote repository"),
Chris@126 602 tr("You are about to pull from the following remote repository:"),
Chris@126 603 remoteRepoPath),
Chris@126 604 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) {
jtkorhonen@0 605
Chris@126 606 QStringList params;
Chris@126 607 params << "pull" << remoteRepoPath;
Chris@126 608 runner->requestAction(HgAction(ACT_PULL, workFolderPath, params));
Chris@126 609 }
jtkorhonen@0 610 }
jtkorhonen@0 611
jtkorhonen@0 612 void MainWindow::hgPush()
jtkorhonen@0 613 {
Chris@126 614 if (QMessageBox::question
Chris@126 615 (this, tr("Confirm push"),
Chris@126 616 format3(tr("Confirm push to remote repository"),
Chris@126 617 tr("You are about to push to the following remote repository:"),
Chris@126 618 remoteRepoPath),
Chris@126 619 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) {
jtkorhonen@0 620
Chris@126 621 QStringList params;
Chris@126 622 params << "push" << remoteRepoPath;
Chris@126 623 runner->requestAction(HgAction(ACT_PUSH, workFolderPath, params));
Chris@126 624 }
jtkorhonen@0 625 }
jtkorhonen@0 626
jtkorhonen@28 627 QString MainWindow::listAllUpIpV4Addresses()
jtkorhonen@26 628 {
jtkorhonen@28 629 QString ret;
jtkorhonen@26 630 QList<QNetworkInterface> ifaces = QNetworkInterface::allInterfaces();
jtkorhonen@26 631
jtkorhonen@26 632 for (int i = 0; i < ifaces.count(); i++)
jtkorhonen@26 633 {
jtkorhonen@26 634 QNetworkInterface iface = ifaces.at(i);
jtkorhonen@26 635
jtkorhonen@26 636 if (iface.flags().testFlag(QNetworkInterface::IsUp) && !iface.flags().testFlag(QNetworkInterface::IsLoopBack))
jtkorhonen@26 637 {
jtkorhonen@26 638 for (int j=0; j<iface.addressEntries().count(); j++)
jtkorhonen@26 639 {
jtkorhonen@28 640 QHostAddress tmp = iface.addressEntries().at(j).ip();
jtkorhonen@28 641 if (QAbstractSocket::IPv4Protocol == tmp.protocol())
jtkorhonen@24 642 {
jtkorhonen@28 643 if (!ret.isEmpty())
jtkorhonen@28 644 {
jtkorhonen@28 645 ret += " ";
jtkorhonen@28 646 }
jtkorhonen@28 647 ret += tmp.toString();
jtkorhonen@24 648 }
jtkorhonen@24 649 }
jtkorhonen@22 650 }
jtkorhonen@17 651 }
jtkorhonen@28 652 return ret;
jtkorhonen@28 653 }
jtkorhonen@17 654
Chris@120 655 void MainWindow::clearState()
Chris@120 656 {
Chris@120 657 foreach (Changeset *cs, currentParents) delete cs;
Chris@120 658 currentParents.clear();
Chris@120 659 foreach (Changeset *cs, currentHeads) delete cs;
Chris@120 660 currentHeads.clear();
Chris@120 661 currentBranch = "";
Chris@120 662 needNewLog = true;
Chris@120 663 }
jtkorhonen@17 664
jtkorhonen@11 665 void MainWindow::hgServe()
jtkorhonen@11 666 {
Chris@109 667 QStringList params;
Chris@109 668 QString msg;
jtkorhonen@11 669
Chris@109 670 QString addrs = listAllUpIpV4Addresses();
Chris@109 671 QTextStream(&msg) << "Server running on address(es) (" << addrs << "), port 8000";
Chris@109 672 params << "serve";
jtkorhonen@11 673
Chris@109 674 runner->requestAction(HgAction(ACT_SERVE, workFolderPath, params));
Chris@109 675
Chris@109 676 QMessageBox::information(this, "Serve", msg, QMessageBox::Close);
Chris@109 677 //!!! runner -> killCurrentCommand();
jtkorhonen@11 678 }
jtkorhonen@11 679
Chris@64 680 void MainWindow::startupDialog()
Chris@64 681 {
Chris@64 682 StartupDialog *dlg = new StartupDialog(this);
Chris@65 683 if (dlg->exec()) firstStart = false;
Chris@64 684 }
jtkorhonen@11 685
Chris@69 686 void MainWindow::open()
Chris@69 687 {
Chris@86 688 bool done = false;
Chris@69 689
Chris@86 690 while (!done) {
Chris@69 691
Chris@86 692 MultiChoiceDialog *d = new MultiChoiceDialog
Chris@86 693 (tr("Open Repository"),
Chris@86 694 tr("<qt><big>What would you like to open?</big></qt>"),
Chris@86 695 this);
Chris@69 696
Chris@86 697 d->addChoice("remote",
Chris@86 698 tr("<qt><center><img src=\":images/browser-64.png\"><br>Remote repository</center></qt>"),
Chris@86 699 tr("Open a remote Mercurial repository, by cloning from its URL into a local folder."),
Chris@86 700 MultiChoiceDialog::UrlToDirectoryArg);
Chris@69 701
Chris@86 702 d->addChoice("local",
Chris@86 703 tr("<qt><center><img src=\":images/hglogo-64.png\"><br>Local repository</center></qt>"),
Chris@86 704 tr("Open an existing local Mercurial repository."),
Chris@86 705 MultiChoiceDialog::DirectoryArg);
Chris@69 706
Chris@86 707 d->addChoice("init",
Chris@86 708 tr("<qt><center><img src=\":images/hdd_unmount-64.png\"><br>File folder</center></qt>"),
Chris@86 709 tr("Open a local folder, by creating a Mercurial repository in it."),
Chris@86 710 MultiChoiceDialog::DirectoryArg);
Chris@79 711
Chris@86 712 d->setCurrentChoice("local");
Chris@86 713
Chris@86 714 if (d->exec() == QDialog::Accepted) {
Chris@86 715
Chris@86 716 QString choice = d->getCurrentChoice();
Chris@86 717 QString arg = d->getArgument().trimmed();
Chris@86 718
Chris@86 719 bool result = false;
Chris@86 720
Chris@86 721 if (choice == "local") {
Chris@86 722 result = openLocal(arg);
Chris@86 723 } else if (choice == "remote") {
Chris@86 724 result = openRemote(arg, d->getAdditionalArgument().trimmed());
Chris@86 725 } else if (choice == "init") {
Chris@86 726 result = openInit(arg);
Chris@86 727 }
Chris@86 728
Chris@86 729 if (result) {
Chris@86 730 enableDisableActions();
Chris@120 731 clearState();
Chris@109 732 hgQueryPaths();
Chris@91 733 done = true;
Chris@91 734 }
Chris@86 735
Chris@86 736 } else {
Chris@86 737
Chris@86 738 // cancelled
Chris@86 739 done = true;
Chris@69 740 }
Chris@79 741
Chris@86 742 delete d;
Chris@69 743 }
Chris@69 744 }
Chris@69 745
Chris@145 746 void MainWindow::open(QString local)
Chris@145 747 {
Chris@145 748 if (openLocal(local)) {
Chris@145 749 enableDisableActions();
Chris@145 750 clearState();
Chris@145 751 hgQueryPaths();
Chris@145 752 }
Chris@145 753 }
Chris@145 754
Chris@79 755 bool MainWindow::complainAboutFilePath(QString arg)
Chris@79 756 {
Chris@79 757 QMessageBox::critical
Chris@79 758 (this, tr("File chosen"),
Chris@84 759 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 760 return false;
Chris@79 761 }
Chris@79 762
Chris@79 763 bool MainWindow::complainAboutUnknownFolder(QString arg)
Chris@79 764 {
Chris@79 765 QMessageBox::critical
Chris@79 766 (this, tr("Folder does not exist"),
Chris@84 767 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 768 return false;
Chris@84 769 }
Chris@84 770
Chris@84 771 bool MainWindow::complainAboutInitInRepo(QString arg)
Chris@84 772 {
Chris@84 773 QMessageBox::critical
Chris@84 774 (this, tr("Path is in existing repository"),
Chris@84 775 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 776 return false;
Chris@84 777 }
Chris@84 778
Chris@84 779 bool MainWindow::complainAboutInitFile(QString arg)
Chris@84 780 {
Chris@84 781 QMessageBox::critical
Chris@84 782 (this, tr("Path is a file"),
Chris@84 783 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 784 return false;
Chris@84 785 }
Chris@84 786
Chris@84 787 bool MainWindow::complainAboutCloneToExisting(QString arg)
Chris@84 788 {
Chris@84 789 QMessageBox::critical
Chris@84 790 (this, tr("Path is in existing repository"),
Chris@84 791 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 792 return false;
Chris@84 793 }
Chris@84 794
Chris@84 795 bool MainWindow::complainAboutCloneToFile(QString arg)
Chris@84 796 {
Chris@84 797 QMessageBox::critical
Chris@84 798 (this, tr("Path is a file"),
Chris@84 799 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 800 return false;
Chris@84 801 }
Chris@84 802
Chris@84 803 bool MainWindow::complainAboutCloneToExistingFolder(QString arg)
Chris@84 804 {
Chris@84 805 QMessageBox::critical
Chris@84 806 (this, tr("Folder exists"),
Chris@84 807 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 808 return false;
Chris@79 809 }
Chris@79 810
Chris@79 811 bool MainWindow::askToOpenParentRepo(QString arg, QString parent)
Chris@79 812 {
Chris@79 813 return (QMessageBox::question
Chris@84 814 (this, tr("Path is inside a repository"),
Chris@86 815 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 816 .arg(xmlEncode(arg)).arg(xmlEncode(parent)),
Chris@79 817 QMessageBox::Ok | QMessageBox::Cancel,
Chris@79 818 QMessageBox::Ok)
Chris@79 819 == QMessageBox::Ok);
Chris@79 820 }
Chris@79 821
Chris@79 822 bool MainWindow::askToInitExisting(QString arg)
Chris@79 823 {
Chris@79 824 return (QMessageBox::question
Chris@84 825 (this, tr("Folder has no repository"),
Chris@84 826 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 827 .arg(xmlEncode(arg)),
Chris@79 828 QMessageBox::Ok | QMessageBox::Cancel,
Chris@79 829 QMessageBox::Ok)
Chris@79 830 == QMessageBox::Ok);
Chris@79 831 }
Chris@79 832
Chris@79 833 bool MainWindow::askToInitNew(QString arg)
Chris@79 834 {
Chris@79 835 return (QMessageBox::question
Chris@84 836 (this, tr("Folder does not exist"),
Chris@84 837 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 838 .arg(xmlEncode(arg)),
Chris@84 839 QMessageBox::Ok | QMessageBox::Cancel,
Chris@84 840 QMessageBox::Ok)
Chris@84 841 == QMessageBox::Ok);
Chris@84 842 }
Chris@84 843
Chris@84 844 bool MainWindow::askToOpenInsteadOfInit(QString arg)
Chris@84 845 {
Chris@84 846 return (QMessageBox::question
Chris@84 847 (this, tr("Repository exists"),
Chris@84 848 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 849 .arg(xmlEncode(arg)),
Chris@79 850 QMessageBox::Ok | QMessageBox::Cancel,
Chris@79 851 QMessageBox::Ok)
Chris@79 852 == QMessageBox::Ok);
Chris@79 853 }
Chris@79 854
Chris@79 855 bool MainWindow::openLocal(QString local)
Chris@79 856 {
Chris@79 857 DEBUG << "open " << local << endl;
Chris@79 858
Chris@79 859 FolderStatus status = getFolderStatus(local);
Chris@79 860 QString containing = getContainingRepoFolder(local);
Chris@79 861
Chris@79 862 switch (status) {
Chris@79 863
Chris@79 864 case FolderHasRepo:
Chris@79 865 // fine
Chris@79 866 break;
Chris@79 867
Chris@79 868 case FolderExists:
Chris@79 869 if (containing != "") {
Chris@79 870 if (!askToOpenParentRepo(local, containing)) return false;
Chris@84 871 local = containing;
Chris@79 872 } else {
Chris@86 873 //!!! No -- this is likely to happen far more by accident
Chris@86 874 // than because the user actually wanted to init something.
Chris@86 875 // Don't ask, just politely reject.
Chris@79 876 if (!askToInitExisting(local)) return false;
Chris@79 877 return openInit(local);
Chris@79 878 }
Chris@79 879 break;
Chris@79 880
Chris@79 881 case FolderParentExists:
Chris@79 882 if (containing != "") {
Chris@79 883 if (!askToOpenParentRepo(local, containing)) return false;
Chris@84 884 local = containing;
Chris@79 885 } else {
Chris@79 886 if (!askToInitNew(local)) return false;
Chris@79 887 return openInit(local);
Chris@79 888 }
Chris@79 889 break;
Chris@79 890
Chris@79 891 case FolderUnknown:
Chris@84 892 if (containing != "") {
Chris@84 893 if (!askToOpenParentRepo(local, containing)) return false;
Chris@84 894 local = containing;
Chris@84 895 } else {
Chris@84 896 return complainAboutUnknownFolder(local);
Chris@84 897 }
Chris@84 898 break;
Chris@79 899
Chris@79 900 case FolderIsFile:
Chris@79 901 return complainAboutFilePath(local);
Chris@79 902 }
Chris@79 903
Chris@79 904 workFolderPath = local;
Chris@79 905 remoteRepoPath = "";
Chris@79 906 return true;
Chris@79 907 }
Chris@79 908
Chris@79 909 bool MainWindow::openRemote(QString remote, QString local)
Chris@79 910 {
Chris@79 911 DEBUG << "clone " << remote << " to " << local << endl;
Chris@84 912
Chris@84 913 FolderStatus status = getFolderStatus(local);
Chris@84 914 QString containing = getContainingRepoFolder(local);
Chris@84 915
Chris@84 916 DEBUG << "status = " << status << ", containing = " << containing << endl;
Chris@84 917
Chris@84 918 if (status == FolderHasRepo || containing != "") {
Chris@84 919 return complainAboutCloneToExisting(local);
Chris@84 920 }
Chris@84 921
Chris@84 922 if (status == FolderIsFile) {
Chris@84 923 return complainAboutCloneToFile(local);
Chris@84 924 }
Chris@84 925
Chris@84 926 if (status == FolderUnknown) {
Chris@84 927 return complainAboutUnknownFolder(local);
Chris@84 928 }
Chris@84 929
Chris@84 930 if (status == FolderExists) {
Chris@84 931 //!!! we can do better than this surely?
Chris@84 932 return complainAboutCloneToExistingFolder(local);
Chris@84 933 }
Chris@84 934
Chris@84 935 workFolderPath = local;
Chris@84 936 remoteRepoPath = remote;
Chris@84 937 hgCloneFromRemote();
Chris@84 938
Chris@79 939 return true;
Chris@79 940 }
Chris@79 941
Chris@84 942 bool MainWindow::openInit(QString local)
Chris@79 943 {
Chris@84 944 DEBUG << "openInit " << local << endl;
Chris@84 945
Chris@84 946 FolderStatus status = getFolderStatus(local);
Chris@84 947 QString containing = getContainingRepoFolder(local);
Chris@84 948
Chris@84 949 DEBUG << "status = " << status << ", containing = " << containing << endl;
Chris@84 950
Chris@84 951 if (status == FolderHasRepo) {
Chris@84 952 if (!askToOpenInsteadOfInit(local)) return false;
Chris@84 953 }
Chris@84 954
Chris@84 955 if (containing != "") {
Chris@84 956 return complainAboutInitInRepo(local);
Chris@84 957 }
Chris@84 958
Chris@84 959 if (status == FolderIsFile) {
Chris@84 960 return complainAboutInitFile(local);
Chris@84 961 }
Chris@84 962
Chris@84 963 if (status == FolderUnknown) {
Chris@84 964 return complainAboutUnknownFolder(local);
Chris@84 965 }
Chris@84 966
Chris@84 967 workFolderPath = local;
Chris@84 968 remoteRepoPath = "";
Chris@84 969 hgInit();
Chris@79 970 return true;
Chris@79 971 }
Chris@79 972
jtkorhonen@0 973 void MainWindow::settings()
jtkorhonen@0 974 {
Chris@69 975 /*!!!
jtkorhonen@0 976 SettingsDialog *settingsDlg = new SettingsDialog(this);
jtkorhonen@0 977 settingsDlg->setModal(true);
jtkorhonen@0 978 settingsDlg->exec();
Chris@98 979 hgTabs -> clearLists();
jtkorhonen@0 980 enableDisableActions();
jtkorhonen@0 981 hgStat();
Chris@69 982 */
jtkorhonen@0 983 }
jtkorhonen@0 984
jtkorhonen@2 985 #define STDOUT_NEEDS_BIG_WINDOW 512
jtkorhonen@2 986 #define SMALL_WND_W 500
jtkorhonen@2 987 #define SMALL_WND_H 300
jtkorhonen@2 988
jtkorhonen@2 989 #define BIG_WND_W 1024
jtkorhonen@2 990 #define BIG_WND_H 768
jtkorhonen@2 991
jtkorhonen@2 992
jtkorhonen@2 993 void MainWindow::presentLongStdoutToUser(QString stdo)
jtkorhonen@0 994 {
jtkorhonen@2 995 if (!stdo.isEmpty())
jtkorhonen@2 996 {
jtkorhonen@2 997 QDialog dlg;
jtkorhonen@0 998
jtkorhonen@2 999 if (stdo.length() > STDOUT_NEEDS_BIG_WINDOW)
jtkorhonen@2 1000 {
jtkorhonen@2 1001 dlg.setMinimumWidth(BIG_WND_W);
jtkorhonen@2 1002 dlg.setMinimumHeight(BIG_WND_H);
jtkorhonen@2 1003 }
jtkorhonen@2 1004 else
jtkorhonen@2 1005 {
jtkorhonen@2 1006 dlg.setMinimumWidth(SMALL_WND_W);
jtkorhonen@2 1007 dlg.setMinimumHeight(SMALL_WND_H);
jtkorhonen@2 1008 }
jtkorhonen@0 1009
jtkorhonen@2 1010 QVBoxLayout *box = new QVBoxLayout;
jtkorhonen@2 1011 QListWidget *list = new QListWidget;
jtkorhonen@2 1012 list-> addItems(stdo.split("\n"));
jtkorhonen@2 1013 QPushButton *btn = new QPushButton(tr("Ok"));
jtkorhonen@2 1014 connect(btn, SIGNAL(clicked()), &dlg, SLOT(accept()));
jtkorhonen@0 1015
jtkorhonen@2 1016 box -> addWidget(list);
jtkorhonen@2 1017 box -> addWidget(btn);
jtkorhonen@2 1018 dlg.setLayout(box);
jtkorhonen@2 1019
jtkorhonen@2 1020 dlg.exec();
jtkorhonen@2 1021 }
jtkorhonen@2 1022 else
jtkorhonen@2 1023 {
Chris@98 1024 QMessageBox::information(this, tr("EasyMercurial"), tr("Mercurial command did not return any output."));
jtkorhonen@2 1025 }
jtkorhonen@0 1026 }
jtkorhonen@0 1027
Chris@90 1028 void MainWindow::updateFileSystemWatcher()
Chris@90 1029 {
Chris@90 1030 delete fsWatcher;
Chris@90 1031 fsWatcher = new QFileSystemWatcher();
Chris@90 1032 std::deque<QString> pending;
Chris@90 1033 pending.push_back(workFolderPath);
Chris@90 1034 while (!pending.empty()) {
Chris@90 1035 QString path = pending.front();
Chris@90 1036 pending.pop_front();
Chris@90 1037 fsWatcher->addPath(path);
Chris@90 1038 DEBUG << "Added to file system watcher: " << path << endl;
Chris@90 1039 QDir d(path);
Chris@90 1040 if (d.exists()) {
Chris@115 1041 d.setFilter(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable);
Chris@90 1042 foreach (QString entry, d.entryList()) {
Chris@90 1043 if (entry == ".hg") continue;
Chris@90 1044 QString entryPath = d.absoluteFilePath(entry);
Chris@90 1045 pending.push_back(entryPath);
Chris@90 1046 }
Chris@90 1047 }
Chris@90 1048 }
Chris@90 1049 connect(fsWatcher, SIGNAL(directoryChanged(QString)),
Chris@90 1050 this, SLOT(fsDirectoryChanged(QString)));
Chris@90 1051 connect(fsWatcher, SIGNAL(fileChanged(QString)),
Chris@90 1052 this, SLOT(fsFileChanged(QString)));
Chris@90 1053 }
Chris@90 1054
Chris@122 1055 void MainWindow::fsDirectoryChanged(QString d)
Chris@90 1056 {
Chris@122 1057 DEBUG << "MainWindow::fsDirectoryChanged " << d << endl;
Chris@90 1058 hgStat();
Chris@90 1059 }
Chris@90 1060
Chris@122 1061 void MainWindow::fsFileChanged(QString f)
Chris@90 1062 {
Chris@122 1063 DEBUG << "MainWindow::fsFileChanged " << f << endl;
Chris@90 1064 hgStat();
Chris@90 1065 }
Chris@90 1066
Chris@125 1067 QString MainWindow::format3(QString head, QString intro, QString code)
Chris@125 1068 {
Chris@125 1069 if (intro == "") {
Chris@125 1070 return QString("<qt><h3>%1</h3><code>%2</code>")
Chris@125 1071 .arg(head).arg(xmlEncode(code).replace("\n", "<br>"));
Chris@126 1072 } else if (code == "") {
Chris@126 1073 return QString("<qt><h3>%1</h3><p>%2</p>")
Chris@126 1074 .arg(head).arg(intro);
Chris@125 1075 } else {
Chris@125 1076 return QString("<qt><h3>%1</h3><p>%2</p><code>%3</code>")
Chris@125 1077 .arg(head).arg(intro).arg(xmlEncode(code).replace("\n", "<br>"));
Chris@125 1078 }
Chris@125 1079 }
Chris@125 1080
Chris@120 1081 void MainWindow::showIncoming(QString output)
Chris@120 1082 {
Chris@120 1083 runner->hide();
Chris@125 1084 IncomingDialog *d = new IncomingDialog(this, output);
Chris@125 1085 d->exec();
Chris@125 1086 delete d;
Chris@125 1087 }
Chris@125 1088
Chris@125 1089 int MainWindow::extractChangeCount(QString text)
Chris@125 1090 {
Chris@125 1091 QRegExp re("added (\\d+) ch\\w+ with (\\d+) ch\\w+ to (\\d+) f\\w+");
Chris@125 1092 if (re.indexIn(text) >= 0) {
Chris@125 1093 return re.cap(1).toInt();
Chris@125 1094 } else if (text.contains("no changes")) {
Chris@125 1095 return 0;
Chris@125 1096 } else {
Chris@125 1097 return -1; // unknown
Chris@125 1098 }
Chris@120 1099 }
Chris@120 1100
Chris@120 1101 void MainWindow::showPushResult(QString output)
Chris@120 1102 {
Chris@125 1103 QString report;
Chris@125 1104 int n = extractChangeCount(output);
Chris@125 1105 if (n > 0) {
Chris@125 1106 report = tr("Pushed %n changeset(s)", "", n);
Chris@125 1107 } else if (n == 0) {
Chris@125 1108 report = tr("No changes to push");
Chris@125 1109 } else {
Chris@125 1110 report = tr("Push complete");
Chris@125 1111 }
Chris@125 1112 report = format3(report, tr("The push command output was:"), output);
Chris@120 1113 runner->hide();
Chris@125 1114 QMessageBox::information(this, "Push complete", report);
Chris@120 1115 }
Chris@120 1116
Chris@120 1117 void MainWindow::showPullResult(QString output)
Chris@120 1118 {
Chris@125 1119 QString report;
Chris@125 1120 int n = extractChangeCount(output);
Chris@125 1121 if (n > 0) {
Chris@125 1122 report = tr("Pulled %n changeset(s)", "", n);
Chris@125 1123 } else if (n == 0) {
Chris@125 1124 report = tr("No changes to pull");
Chris@125 1125 } else {
Chris@125 1126 report = tr("Pull complete");
Chris@125 1127 }
Chris@125 1128 report = format3(report, tr("The pull command output was:"), output);
Chris@120 1129 runner->hide();
Chris@125 1130
Chris@125 1131 //!!! and something about updating
Chris@125 1132
Chris@125 1133 QMessageBox::information(this, "Pull complete", report);
Chris@120 1134 }
Chris@120 1135
Chris@143 1136 void MainWindow::commandFailed(HgAction action, QString output)
Chris@62 1137 {
Chris@62 1138 DEBUG << "MainWindow::commandFailed" << endl;
Chris@74 1139
Chris@113 1140 // Some commands we just have to ignore bad return values from:
Chris@113 1141
Chris@113 1142 switch(action.action) {
Chris@113 1143 case ACT_NONE:
Chris@113 1144 // uh huh
Chris@113 1145 return;
Chris@113 1146 case ACT_INCOMING:
Chris@113 1147 // returns non-zero code if the check was successful but there
Chris@113 1148 // are no changes pending
Chris@143 1149 if (output.trimmed() == "") showIncoming("");
Chris@113 1150 return;
Chris@113 1151 case ACT_FOLDERDIFF:
Chris@113 1152 case ACT_FILEDIFF:
Chris@113 1153 case ACT_CHGSETDIFF:
Chris@113 1154 // external program, unlikely to be anything useful in stderr
Chris@113 1155 // and some return with failure codes when something as basic
Chris@113 1156 // as the user closing the window via the wm happens
Chris@113 1157 return;
Chris@113 1158
Chris@113 1159 default:
Chris@114 1160 break;
Chris@113 1161 }
Chris@113 1162
Chris@113 1163 QString command = action.executable;
Chris@113 1164 if (command == "") command = "hg";
Chris@113 1165 foreach (QString arg, action.params) {
Chris@113 1166 command += " " + arg;
Chris@113 1167 }
Chris@113 1168
Chris@113 1169 QString message = tr("<qt><h3>Command failed</h3>"
Chris@113 1170 "<p>The following command failed:</p>"
Chris@113 1171 "<code>%1</code>"
Chris@113 1172 "%2</qt>")
Chris@113 1173 .arg(command)
Chris@143 1174 .arg((output.trimmed() != "") ?
Chris@113 1175 tr("<p>Its output said:</p><code>%1</code>")
Chris@143 1176 .arg(xmlEncode(output.left(800))
Chris@113 1177 .replace("\n", "<br>"))
Chris@113 1178 : "");
Chris@113 1179
Chris@113 1180 QMessageBox::warning(this, tr("Command failed"), message);
Chris@62 1181 }
Chris@62 1182
Chris@109 1183 void MainWindow::commandCompleted(HgAction completedAction, QString output)
jtkorhonen@0 1184 {
jtkorhonen@0 1185 bool shouldHgStat = false;
jtkorhonen@0 1186
Chris@109 1187 HGACTIONS action = completedAction.action;
Chris@109 1188
Chris@109 1189 if (action == ACT_NONE) return;
Chris@109 1190
Chris@109 1191 switch(action) {
Chris@109 1192
Chris@109 1193 case ACT_QUERY_PATHS:
jtkorhonen@0 1194 {
Chris@109 1195 DEBUG << "stdout is " << output << endl;
Chris@109 1196 LogParser lp(output, "=");
Chris@109 1197 LogList ll = lp.parse();
Chris@109 1198 DEBUG << ll.size() << " results" << endl;
Chris@109 1199 if (!ll.empty()) {
Chris@109 1200 remoteRepoPath = lp.parse()[0]["default"].trimmed();
Chris@109 1201 DEBUG << "Set remote path to " << remoteRepoPath << endl;
Chris@109 1202 }
Chris@109 1203 MultiChoiceDialog::addRecentArgument("local", workFolderPath);
Chris@109 1204 MultiChoiceDialog::addRecentArgument("remote", remoteRepoPath);
Chris@109 1205 hgTabs->setWorkFolderAndRepoNames(workFolderPath, remoteRepoPath);
Chris@109 1206 break;
Chris@109 1207 }
jtkorhonen@0 1208
Chris@109 1209 case ACT_QUERY_BRANCH:
Chris@109 1210 currentBranch = output.trimmed();
Chris@109 1211 break;
jtkorhonen@0 1212
Chris@109 1213 case ACT_STAT:
Chris@113 1214 hgTabs->updateWorkFolderFileList(output);
Chris@109 1215 updateFileSystemWatcher();
Chris@109 1216 break;
Chris@109 1217
Chris@109 1218 case ACT_INCOMING:
Chris@120 1219 showIncoming(output);
Chris@120 1220 break;
Chris@120 1221
Chris@109 1222 case ACT_ANNOTATE:
Chris@109 1223 case ACT_RESOLVE_LIST:
Chris@109 1224 case ACT_RESOLVE_MARK:
Chris@109 1225 presentLongStdoutToUser(output);
Chris@109 1226 shouldHgStat = true;
Chris@109 1227 break;
Chris@109 1228
Chris@109 1229 case ACT_PULL:
Chris@120 1230 showPullResult(output);
Chris@109 1231 shouldHgStat = true;
Chris@109 1232 break;
Chris@109 1233
Chris@109 1234 case ACT_PUSH:
Chris@120 1235 showPushResult(output);
Chris@109 1236 break;
Chris@109 1237
Chris@109 1238 case ACT_INIT:
Chris@109 1239 MultiChoiceDialog::addRecentArgument("init", workFolderPath);
Chris@109 1240 MultiChoiceDialog::addRecentArgument("local", workFolderPath);
Chris@109 1241 enableDisableActions();
Chris@109 1242 shouldHgStat = true;
Chris@109 1243 break;
Chris@109 1244
Chris@109 1245 case ACT_CLONEFROMREMOTE:
Chris@109 1246 MultiChoiceDialog::addRecentArgument("local", workFolderPath);
Chris@109 1247 MultiChoiceDialog::addRecentArgument("remote", remoteRepoPath);
Chris@109 1248 MultiChoiceDialog::addRecentArgument("remote", workFolderPath, true);
Chris@109 1249 QMessageBox::information(this, "Clone", output);
Chris@109 1250 enableDisableActions();
Chris@109 1251 shouldHgStat = true;
Chris@109 1252 break;
Chris@109 1253
Chris@109 1254 case ACT_LOG:
Chris@120 1255 hgTabs->setNewLog(output);
Chris@120 1256 needNewLog = false;
Chris@120 1257 break;
Chris@120 1258
Chris@120 1259 case ACT_LOG_INCREMENTAL:
Chris@120 1260 hgTabs->addIncrementalLog(output);
Chris@109 1261 break;
Chris@109 1262
Chris@109 1263 case ACT_QUERY_PARENTS:
Chris@109 1264 foreach (Changeset *cs, currentParents) delete cs;
Chris@109 1265 currentParents = Changeset::parseChangesets(output);
Chris@109 1266 break;
Chris@109 1267
Chris@109 1268 case ACT_QUERY_HEADS:
Chris@109 1269 foreach (Changeset *cs, currentHeads) delete cs;
Chris@109 1270 currentHeads = Changeset::parseChangesets(output);
Chris@109 1271 break;
Chris@130 1272
Chris@130 1273 case ACT_COMMIT:
Chris@130 1274 hgTabs->clearSelections();
Chris@130 1275 shouldHgStat = true;
Chris@130 1276 needNewLog = true; // naive incremental log will give us a duplicate tip tag
Chris@130 1277 break;
Chris@109 1278
Chris@109 1279 case ACT_REMOVE:
Chris@109 1280 case ACT_ADD:
Chris@116 1281 case ACT_REVERT:
Chris@116 1282 hgTabs->clearSelections();
Chris@116 1283 shouldHgStat = true;
Chris@116 1284 break;
Chris@116 1285
Chris@109 1286 case ACT_FILEDIFF:
Chris@109 1287 case ACT_FOLDERDIFF:
Chris@109 1288 case ACT_CHGSETDIFF:
Chris@109 1289 case ACT_SERVE:
Chris@109 1290 case ACT_TAG:
Chris@109 1291 case ACT_HG_IGNORE:
Chris@109 1292 shouldHgStat = true;
Chris@109 1293 break;
Chris@109 1294
Chris@109 1295 case ACT_UPDATE:
Chris@109 1296 QMessageBox::information(this, tr("Update"), output);
Chris@109 1297 shouldHgStat = true;
Chris@109 1298 break;
Chris@109 1299
Chris@109 1300 case ACT_MERGE:
Chris@109 1301 QMessageBox::information(this, tr("Merge"), output);
Chris@109 1302 shouldHgStat = true;
Chris@109 1303 justMerged = true;
Chris@109 1304 break;
Chris@109 1305
Chris@109 1306 case ACT_RETRY_MERGE:
Chris@109 1307 QMessageBox::information(this, tr("Merge retry"), tr("Merge retry successful."));
Chris@109 1308 shouldHgStat = true;
Chris@109 1309 justMerged = true;
Chris@109 1310 break;
Chris@109 1311
Chris@109 1312 default:
Chris@109 1313 break;
Chris@109 1314 }
Chris@108 1315
Chris@109 1316 enableDisableActions();
Chris@74 1317
Chris@121 1318 // Sequence when no full log required:
Chris@121 1319 // paths -> branch -> stat -> incremental-log -> heads -> parents
Chris@121 1320 // Sequence when full log required:
Chris@121 1321 // paths -> branch -> stat -> heads -> parents -> log
Chris@109 1322 if (action == ACT_QUERY_PATHS) {
Chris@109 1323 hgQueryBranch();
Chris@109 1324 } else if (action == ACT_QUERY_BRANCH) {
Chris@109 1325 hgStat();
Chris@109 1326 } else if (action == ACT_STAT) {
Chris@121 1327 if (!needNewLog) {
Chris@121 1328 hgLogIncremental();
Chris@121 1329 } else {
Chris@121 1330 hgQueryHeads();
Chris@121 1331 }
Chris@121 1332 } else if (action == ACT_LOG_INCREMENTAL) {
Chris@109 1333 hgQueryHeads();
Chris@109 1334 } else if (action == ACT_QUERY_HEADS) {
Chris@109 1335 hgQueryParents();
Chris@109 1336 } else if (action == ACT_QUERY_PARENTS) {
Chris@120 1337 if (needNewLog) {
Chris@120 1338 hgLog();
Chris@120 1339 }
Chris@109 1340 } else
Chris@109 1341 /* Move to commandFailed
Chris@109 1342 if ((runningAction == ACT_MERGE) && (exitCode != 0))
jtkorhonen@37 1343 {
Chris@106 1344 // If we had a failed merge, offer to retry
jtkorhonen@37 1345 if (QMessageBox::Ok == QMessageBox::information(this, tr("Retry merge ?"), tr("Merge attempt failed. retry ?"), QMessageBox::Ok | QMessageBox::Cancel))
jtkorhonen@37 1346 {
jtkorhonen@37 1347 runningAction = ACT_NONE;
jtkorhonen@37 1348 hgRetryMerge();
jtkorhonen@37 1349 }
jtkorhonen@37 1350 else
jtkorhonen@37 1351 {
jtkorhonen@37 1352 runningAction = ACT_NONE;
jtkorhonen@37 1353 hgStat();
jtkorhonen@37 1354 }
jtkorhonen@37 1355 }
jtkorhonen@0 1356 else
jtkorhonen@0 1357 {
Chris@109 1358 */
Chris@109 1359 if (shouldHgStat) {
Chris@109 1360 hgQueryPaths();
jtkorhonen@0 1361 }
jtkorhonen@0 1362 }
jtkorhonen@0 1363
jtkorhonen@0 1364 void MainWindow::connectActions()
jtkorhonen@0 1365 {
jtkorhonen@0 1366 connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
jtkorhonen@0 1367 connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));
jtkorhonen@0 1368 connect(aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
jtkorhonen@0 1369
Chris@120 1370 connect(hgRefreshAct, SIGNAL(triggered()), this, SLOT(hgRefresh()));
jtkorhonen@0 1371 connect(hgRemoveAct, SIGNAL(triggered()), this, SLOT(hgRemove()));
jtkorhonen@0 1372 connect(hgAddAct, SIGNAL(triggered()), this, SLOT(hgAdd()));
jtkorhonen@0 1373 connect(hgCommitAct, SIGNAL(triggered()), this, SLOT(hgCommit()));
jtkorhonen@0 1374 connect(hgFileDiffAct, SIGNAL(triggered()), this, SLOT(hgFileDiff()));
jtkorhonen@0 1375 connect(hgFolderDiffAct, SIGNAL(triggered()), this, SLOT(hgFolderDiff()));
jtkorhonen@0 1376 connect(hgChgSetDiffAct, SIGNAL(triggered()), this, SLOT(hgChgSetDiff()));
jtkorhonen@0 1377 connect(hgUpdateAct, SIGNAL(triggered()), this, SLOT(hgUpdate()));
jtkorhonen@0 1378 connect(hgRevertAct, SIGNAL(triggered()), this, SLOT(hgRevert()));
jtkorhonen@0 1379 connect(hgMergeAct, SIGNAL(triggered()), this, SLOT(hgMerge()));
jtkorhonen@33 1380 connect(hgRetryMergeAct, SIGNAL(triggered()), this, SLOT(hgRetryMerge()));
jtkorhonen@34 1381 connect(hgTagAct, SIGNAL(triggered()), this, SLOT(hgTag()));
jtkorhonen@34 1382 connect(hgIgnoreAct, SIGNAL(triggered()), this, SLOT(hgIgnore()));
jtkorhonen@0 1383
jtkorhonen@0 1384 connect(settingsAct, SIGNAL(triggered()), this, SLOT(settings()));
Chris@69 1385 connect(openAct, SIGNAL(triggered()), this, SLOT(open()));
jtkorhonen@0 1386
jtkorhonen@0 1387 connect(hgInitAct, SIGNAL(triggered()), this, SLOT(hgInit()));
jtkorhonen@0 1388 connect(hgCloneFromRemoteAct, SIGNAL(triggered()), this, SLOT(hgCloneFromRemote()));
jtkorhonen@0 1389 connect(hgIncomingAct, SIGNAL(triggered()), this, SLOT(hgIncoming()));
jtkorhonen@0 1390 connect(hgPullAct, SIGNAL(triggered()), this, SLOT(hgPull()));
jtkorhonen@0 1391 connect(hgPushAct, SIGNAL(triggered()), this, SLOT(hgPush()));
jtkorhonen@0 1392
Chris@109 1393 // connect(hgTabs, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int)));
jtkorhonen@0 1394
jtkorhonen@0 1395 connect(hgUpdateToRevAct, SIGNAL(triggered()), this, SLOT(hgUpdateToRev()));
jtkorhonen@0 1396 connect(hgAnnotateAct, SIGNAL(triggered()), this, SLOT(hgAnnotate()));
jtkorhonen@0 1397 connect(hgResolveListAct, SIGNAL(triggered()), this, SLOT(hgResolveList()));
jtkorhonen@0 1398 connect(hgResolveMarkAct, SIGNAL(triggered()), this, SLOT(hgResolveMark()));
jtkorhonen@11 1399 connect(hgServeAct, SIGNAL(triggered()), this, SLOT(hgServe()));
Chris@94 1400 connect(clearSelectionsAct, SIGNAL(triggered()), this, SLOT(clearSelections()));
jtkorhonen@0 1401 }
Chris@141 1402
Chris@141 1403 void MainWindow::connectTabsSignals()
Chris@141 1404 {
Chris@141 1405 connect(hgTabs, SIGNAL(commit()),
Chris@141 1406 this, SLOT(hgCommit()));
Chris@141 1407
Chris@141 1408 connect(hgTabs, SIGNAL(revert()),
Chris@141 1409 this, SLOT(hgRevert()));
Chris@141 1410
Chris@141 1411 connect(hgTabs, SIGNAL(diffWorkingFolder()),
Chris@141 1412 this, SLOT(hgFolderDiff()));
Chris@141 1413 /*!!!!
Chris@141 1414 connect(hgTabs, SIGNAL(updateTo(QString)),
Chris@141 1415 this, SIGNAL(updateTo(QString)));
Chris@141 1416
Chris@141 1417 connect(hgTabs, SIGNAL(diffToCurrent(QString)),
Chris@141 1418 this, SIGNAL(diffToCurrent(QString)));
Chris@141 1419
Chris@141 1420 connect(hgTabs, SIGNAL(diffToPrevious(QString)),
Chris@141 1421 this, SIGNAL(diffToPrevious(QString)));
Chris@141 1422
Chris@141 1423 connect(hgTabs, SIGNAL(mergeFrom(QString)),
Chris@141 1424 this, SIGNAL(mergeFrom(QString)));
Chris@141 1425
Chris@141 1426 connect(hgTabs, SIGNAL(tag(QString)),
Chris@141 1427 this, SIGNAL(tag(QString)));
Chris@141 1428 */
Chris@141 1429 }
Chris@141 1430
Chris@109 1431 /*!!!
jtkorhonen@0 1432 void MainWindow::tabChanged(int currTab)
jtkorhonen@0 1433 {
jtkorhonen@0 1434 tabPage = currTab;
jtkorhonen@32 1435
jtkorhonen@0 1436 }
Chris@109 1437 */
jtkorhonen@0 1438 void MainWindow::enableDisableActions()
jtkorhonen@0 1439 {
Chris@90 1440 DEBUG << "MainWindow::enableDisableActions" << endl;
Chris@90 1441
Chris@115 1442 //!!! should also do things like set the status texts for the
Chris@115 1443 //!!! actions appropriately by context
Chris@115 1444
jtkorhonen@0 1445 QDir localRepoDir;
jtkorhonen@0 1446 QDir workFolderDir;
Chris@145 1447 bool workFolderExist = true;
Chris@145 1448 bool localRepoExist = true;
jtkorhonen@0 1449
jtkorhonen@0 1450 remoteRepoActionsEnabled = true;
Chris@90 1451 if (remoteRepoPath.isEmpty()) {
jtkorhonen@0 1452 remoteRepoActionsEnabled = false;
jtkorhonen@0 1453 }
jtkorhonen@0 1454
jtkorhonen@0 1455 localRepoActionsEnabled = true;
Chris@90 1456 if (workFolderPath.isEmpty()) {
jtkorhonen@0 1457 localRepoActionsEnabled = false;
jtkorhonen@0 1458 workFolderExist = false;
jtkorhonen@0 1459 }
jtkorhonen@0 1460
Chris@90 1461 if (!workFolderDir.exists(workFolderPath)) {
jtkorhonen@0 1462 localRepoActionsEnabled = false;
jtkorhonen@0 1463 workFolderExist = false;
Chris@90 1464 } else {
jtkorhonen@0 1465 workFolderExist = true;
jtkorhonen@0 1466 }
jtkorhonen@0 1467
Chris@112 1468 if (!localRepoDir.exists(workFolderPath + "/.hg")) {
jtkorhonen@0 1469 localRepoActionsEnabled = false;
jtkorhonen@0 1470 localRepoExist = false;
jtkorhonen@0 1471 }
jtkorhonen@0 1472
jtkorhonen@0 1473 hgCloneFromRemoteAct -> setEnabled(remoteRepoActionsEnabled);
jtkorhonen@0 1474 hgIncomingAct -> setEnabled(remoteRepoActionsEnabled && remoteRepoActionsEnabled);
jtkorhonen@0 1475 hgPullAct -> setEnabled(remoteRepoActionsEnabled && remoteRepoActionsEnabled);
jtkorhonen@0 1476 hgPushAct -> setEnabled(remoteRepoActionsEnabled && remoteRepoActionsEnabled);
Chris@73 1477 /*
jtkorhonen@0 1478 if (tabPage != WORKTAB)
jtkorhonen@0 1479 {
jtkorhonen@0 1480 localRepoActionsEnabled = false;
jtkorhonen@0 1481 }
Chris@73 1482 */
Chris@112 1483 bool haveDiff = (diffBinaryName != "");
Chris@112 1484
jtkorhonen@0 1485 hgInitAct -> setEnabled((localRepoExist == false) && (workFolderExist==true));
Chris@120 1486 hgRefreshAct -> setEnabled(localRepoActionsEnabled);
Chris@112 1487 hgFileDiffAct -> setEnabled(localRepoActionsEnabled && haveDiff);
Chris@112 1488 hgFolderDiffAct -> setEnabled(localRepoActionsEnabled && haveDiff);
jtkorhonen@0 1489 hgRevertAct -> setEnabled(localRepoActionsEnabled);
jtkorhonen@0 1490 hgAddAct -> setEnabled(localRepoActionsEnabled);
jtkorhonen@0 1491 hgRemoveAct -> setEnabled(localRepoActionsEnabled);
jtkorhonen@0 1492 hgUpdateAct -> setEnabled(localRepoActionsEnabled);
jtkorhonen@0 1493 hgCommitAct -> setEnabled(localRepoActionsEnabled);
jtkorhonen@0 1494 hgMergeAct -> setEnabled(localRepoActionsEnabled);
jtkorhonen@33 1495 hgRetryMergeAct -> setEnabled(localRepoActionsEnabled);
jtkorhonen@2 1496 hgResolveListAct -> setEnabled(localRepoActionsEnabled);
jtkorhonen@2 1497 hgResolveMarkAct -> setEnabled(localRepoActionsEnabled);
jtkorhonen@2 1498 hgAnnotateAct -> setEnabled(localRepoActionsEnabled);
jtkorhonen@11 1499 hgServeAct -> setEnabled(localRepoActionsEnabled);
jtkorhonen@34 1500 hgTagAct -> setEnabled(localRepoActionsEnabled);
jtkorhonen@34 1501 hgIgnoreAct -> setEnabled(localRepoActionsEnabled);
jtkorhonen@0 1502
Chris@98 1503 //!!!hgTabs -> enableDisableOtherTabs(tabPage);
jtkorhonen@0 1504
Chris@90 1505 DEBUG << "localRepoActionsEnabled = " << localRepoActionsEnabled << endl;
Chris@98 1506 DEBUG << "canCommit = " << hgTabs->canCommit() << endl;
Chris@90 1507
Chris@90 1508 //!!! new stuff:
Chris@98 1509 hgAddAct->setEnabled(localRepoActionsEnabled && hgTabs->canAdd());
Chris@98 1510 hgRemoveAct->setEnabled(localRepoActionsEnabled && hgTabs->canRemove());
Chris@98 1511 hgCommitAct->setEnabled(localRepoActionsEnabled && hgTabs->canCommit());
Chris@98 1512 hgRevertAct->setEnabled(localRepoActionsEnabled && hgTabs->canCommit());
Chris@98 1513 hgFolderDiffAct->setEnabled(localRepoActionsEnabled && hgTabs->canDoDiff());
Chris@90 1514
Chris@108 1515 // A default merge makes sense if:
Chris@108 1516 // * there is only one parent (if there are two, we have an uncommitted merge) and
Chris@108 1517 // * there are exactly two heads that have the same branch as the current branch and
Chris@108 1518 // * our parent is one of those heads
Chris@108 1519 //
Chris@108 1520 // A default update makes sense if:
Chris@108 1521 // * there is only one parent and
Chris@108 1522 // * the parent is not one of the current heads
Chris@108 1523 //!!! test this
Chris@108 1524 bool canMerge = false;
Chris@108 1525 bool canUpdate = false;
Chris@108 1526 if (currentParents.size() == 1) {
Chris@108 1527 Changeset *parent = currentParents[0];
Chris@108 1528 int currentBranchHeads = 0;
Chris@108 1529 bool parentIsHead = false;
Chris@108 1530 foreach (Changeset *head, currentHeads) {
Chris@108 1531 DEBUG << "head branch " << head->branch() << ", current branch " << currentBranch << endl;
Chris@108 1532 if (head->isOnBranch(currentBranch)) {
Chris@108 1533 ++currentBranchHeads;
Chris@108 1534 if (parent->id() == head->id()) {
Chris@108 1535 parentIsHead = true;
Chris@108 1536 }
Chris@108 1537 }
Chris@108 1538 }
Chris@108 1539 if (currentBranchHeads == 2 && parentIsHead) {
Chris@108 1540 canMerge = true;
Chris@108 1541 }
Chris@108 1542 if (!parentIsHead) {
Chris@108 1543 canUpdate = true;
Chris@108 1544 DEBUG << "parent id = " << parent->id() << endl;
Chris@108 1545 DEBUG << " head ids "<<endl;
Chris@108 1546 foreach (Changeset *h, currentHeads) {
Chris@108 1547 DEBUG << "head id = " << h->id() << endl;
Chris@108 1548 }
Chris@108 1549 }
Chris@108 1550 }
Chris@108 1551 hgMergeAct->setEnabled(localRepoActionsEnabled && canMerge);
Chris@108 1552 hgUpdateAct->setEnabled(localRepoActionsEnabled && canUpdate);
Chris@115 1553
Chris@129 1554 QStringList ids;
Chris@129 1555 foreach (Changeset *cs, currentParents) ids.push_back(cs->id());
Chris@145 1556 hgTabs->setCurrent(ids, hgTabs->canCommit());
Chris@129 1557
Chris@115 1558 // Set the state field on the file status widget
Chris@115 1559
Chris@115 1560 QString branchText;
Chris@115 1561 if (currentBranch == "" || currentBranch == "default") {
Chris@115 1562 branchText = tr("the default branch");
Chris@115 1563 } else {
Chris@115 1564 branchText = tr("branch \"%1\"").arg(currentBranch);
Chris@115 1565 }
Chris@115 1566 if (canUpdate) {
Chris@115 1567 hgTabs->setState(tr("On %1. Not at the head of the branch: consider updating").arg(branchText));
Chris@115 1568 } else if (canMerge) {
Chris@115 1569 hgTabs->setState(tr("<b>Awaiting merge</b> on %1").arg(branchText));
Chris@115 1570 } else {
Chris@115 1571 hgTabs->setState(tr("At the head of %1").arg(branchText));
Chris@115 1572 }
jtkorhonen@0 1573 }
jtkorhonen@0 1574
jtkorhonen@0 1575 void MainWindow::createActions()
jtkorhonen@0 1576 {
jtkorhonen@0 1577 //File actions
jtkorhonen@0 1578 hgInitAct = new QAction(tr("Init local repository"), this);
jtkorhonen@0 1579 hgInitAct->setStatusTip(tr("Create an empty local repository in selected folder"));
jtkorhonen@0 1580
jtkorhonen@0 1581 hgCloneFromRemoteAct = new QAction(tr("Clone from remote"), this);
jtkorhonen@0 1582 hgCloneFromRemoteAct->setStatusTip(tr("Clone from remote repository into local repository in selected folder"));
jtkorhonen@0 1583
Chris@69 1584 openAct = new QAction(QIcon(":/images/fileopen.png"), tr("Open..."), this);
Chris@69 1585 openAct -> setStatusTip(tr("Open repository"));
Chris@69 1586 openAct -> setIconVisibleInMenu(true);
Chris@69 1587
jtkorhonen@0 1588 settingsAct = new QAction(QIcon(":/images/settings.png"), tr("Settings..."), this);
jtkorhonen@0 1589 settingsAct -> setStatusTip(tr("View and change application settings"));
jtkorhonen@0 1590 settingsAct -> setIconVisibleInMenu(true);
jtkorhonen@0 1591
jtkorhonen@0 1592 exitAct = new QAction(QIcon(":/images/exit.png"), tr("Exit"), this);
jtkorhonen@0 1593 exitAct->setShortcuts(QKeySequence::Quit);
jtkorhonen@0 1594 exitAct->setStatusTip(tr("Exit application"));
jtkorhonen@0 1595 exitAct -> setIconVisibleInMenu(true);
jtkorhonen@0 1596
jtkorhonen@0 1597 //Repository actions
Chris@120 1598 hgRefreshAct = new QAction(QIcon(":/images/status.png"), tr("Refresh"), this);
Chris@120 1599 hgRefreshAct->setStatusTip(tr("Refresh (info of) status of workfolder files"));
Chris@61 1600
Chris@61 1601 hgIncomingAct = new QAction(QIcon(":/images/incoming.png"), tr("Preview"), this);
jtkorhonen@0 1602 hgIncomingAct -> setStatusTip(tr("View info of changesets incoming to us from remote repository (on pull operation)"));
jtkorhonen@0 1603
Chris@61 1604 hgPullAct = new QAction(QIcon(":/images/pull.png"), tr("Pull"), this);
jtkorhonen@0 1605 hgPullAct -> setStatusTip(tr("Pull changesets from remote repository to local repository"));
jtkorhonen@0 1606
Chris@61 1607 hgPushAct = new QAction(QIcon(":/images/push.png"), tr("Push"), this);
jtkorhonen@0 1608 hgPushAct->setStatusTip(tr("Push local changesets to remote repository"));
jtkorhonen@0 1609
jtkorhonen@0 1610 //Workfolder actions
Chris@61 1611 hgFileDiffAct = new QAction(QIcon(":/images/diff.png"), tr("Diff"), this);
jtkorhonen@0 1612 hgFileDiffAct->setStatusTip(tr("Filediff: View differences between selected working folder file and local repository file"));
jtkorhonen@0 1613
Chris@99 1614 hgFolderDiffAct = new QAction(QIcon(":/images/folderdiff.png"), tr("Diff"), this);
jtkorhonen@0 1615 hgFolderDiffAct->setStatusTip(tr("Folderdiff: View all differences between working folder files and local repository files"));
jtkorhonen@0 1616
jtkorhonen@0 1617 hgChgSetDiffAct = new QAction(QIcon(":/images/chgsetdiff.png"), tr("View changesetdiff"), this);
jtkorhonen@0 1618 hgChgSetDiffAct->setStatusTip(tr("Change set diff: View differences between all files of 2 repository changesets"));
jtkorhonen@0 1619
Chris@61 1620 hgRevertAct = new QAction(QIcon(":/images/undo.png"), tr("Revert"), this);
jtkorhonen@0 1621 hgRevertAct->setStatusTip(tr("Undo selected working folder file changes (return to local repository version)"));
jtkorhonen@0 1622
Chris@61 1623 hgAddAct = new QAction(QIcon(":/images/add.png"), tr("Add"), this);
jtkorhonen@17 1624 hgAddAct -> setStatusTip(tr("Add working folder file(s) (selected or all yet untracked) to local repository (on next commit)"));
jtkorhonen@0 1625
Chris@61 1626 hgRemoveAct = new QAction(QIcon(":/images/remove.png"), tr("Remove"), this);
jtkorhonen@0 1627 hgRemoveAct -> setStatusTip(tr("Remove selected working folder file from local repository (on next commit)"));
jtkorhonen@0 1628
Chris@61 1629 hgUpdateAct = new QAction(QIcon(":/images/update.png"), tr("Update"), this);
jtkorhonen@0 1630 hgUpdateAct->setStatusTip(tr("Update working folder from local repository"));
jtkorhonen@0 1631
Chris@61 1632 hgCommitAct = new QAction(QIcon(":/images/commit.png"), tr("Commit"), this);
jtkorhonen@20 1633 hgCommitAct->setStatusTip(tr("Save selected file(s) or all changed files in working folder (and all subfolders) to local repository"));
jtkorhonen@0 1634
jtkorhonen@0 1635 hgMergeAct = new QAction(QIcon(":/images/merge.png"), tr("Merge"), this);
jtkorhonen@0 1636 hgMergeAct->setStatusTip(tr("Merge two local repository changesets to working folder"));
jtkorhonen@0 1637
jtkorhonen@0 1638 //Advanced actions
jtkorhonen@0 1639 hgUpdateToRevAct = new QAction(tr("Update to revision"), this);
jtkorhonen@0 1640 hgUpdateToRevAct -> setStatusTip(tr("Update working folder to version selected from history list"));
jtkorhonen@0 1641
jtkorhonen@0 1642 hgAnnotateAct = new QAction(tr("Annotate"), this);
jtkorhonen@0 1643 hgAnnotateAct -> setStatusTip(tr("Show line-by-line version information for selected file"));
jtkorhonen@0 1644
jtkorhonen@0 1645 hgResolveListAct = new QAction(tr("Resolve (list)"), this);
jtkorhonen@0 1646 hgResolveListAct -> setStatusTip(tr("Resolve (list): Show list of files needing merge"));
jtkorhonen@0 1647
jtkorhonen@0 1648 hgResolveMarkAct = new QAction(tr("Resolve (mark)"), this);
jtkorhonen@0 1649 hgResolveMarkAct -> setStatusTip(tr("Resolve (mark): Mark selected file status as resolved"));
jtkorhonen@0 1650
jtkorhonen@33 1651 hgRetryMergeAct = new QAction(tr("Retry merge"), this);
jtkorhonen@33 1652 hgRetryMergeAct -> setStatusTip(tr("Retry merge after failed merge attempt."));
jtkorhonen@33 1653
jtkorhonen@34 1654 hgTagAct = new QAction(tr("Tag revision"), this);
jtkorhonen@34 1655 hgTagAct -> setStatusTip(tr("Give decsriptive name (tag) to current workfolder parent revision."));
jtkorhonen@34 1656
jtkorhonen@34 1657 hgIgnoreAct = new QAction(tr("Edit .hgignore"), this);
jtkorhonen@34 1658 hgIgnoreAct -> setStatusTip(tr("Edit .hgignore file (file contains names of files that should be ignored by mercurial)"));
jtkorhonen@34 1659
jtkorhonen@11 1660 hgServeAct = new QAction(tr("Serve (via http)"), this);
jtkorhonen@11 1661 hgServeAct -> setStatusTip(tr("Serve local repository via http for workgroup access"));
jtkorhonen@11 1662
jtkorhonen@0 1663 //Help actions
jtkorhonen@0 1664 aboutAct = new QAction(tr("About"), this);
jtkorhonen@0 1665 aboutAct->setStatusTip(tr("Show the application's About box"));
jtkorhonen@0 1666
jtkorhonen@0 1667 aboutQtAct = new QAction(tr("About Qt"), this);
jtkorhonen@0 1668 aboutQtAct->setStatusTip(tr("Show the Qt library's About box"));
Chris@94 1669
Chris@94 1670 // Miscellaneous
Chris@94 1671 clearSelectionsAct = new QAction(tr("Clear selections"), this);
Chris@94 1672 clearSelectionsAct->setShortcut(Qt::Key_Escape);
jtkorhonen@0 1673 }
jtkorhonen@0 1674
jtkorhonen@0 1675 void MainWindow::createMenus()
jtkorhonen@0 1676 {
jtkorhonen@0 1677 fileMenu = menuBar()->addMenu(tr("File"));
Chris@131 1678 /* fileMenu -> addAction(hgInitAct);
jtkorhonen@0 1679 fileMenu -> addAction(hgCloneFromRemoteAct);
Chris@94 1680 fileMenu->addAction(clearSelectionsAct); //!!! can't live here!
jtkorhonen@0 1681 fileMenu -> addSeparator();
Chris@131 1682 */
Chris@69 1683 fileMenu -> addAction(openAct);
jtkorhonen@0 1684 fileMenu -> addAction(settingsAct);
jtkorhonen@0 1685 fileMenu -> addSeparator();
jtkorhonen@0 1686 fileMenu -> addAction(exitAct);
jtkorhonen@0 1687
jtkorhonen@0 1688 advancedMenu = menuBar()->addMenu(tr("Advanced"));
Chris@131 1689 /*
jtkorhonen@0 1690 advancedMenu -> addAction(hgUpdateToRevAct);
jtkorhonen@0 1691 advancedMenu -> addSeparator();
jtkorhonen@0 1692 advancedMenu -> addAction(hgAnnotateAct);
jtkorhonen@0 1693 advancedMenu -> addSeparator();
jtkorhonen@33 1694 advancedMenu -> addAction(hgRetryMergeAct);
jtkorhonen@0 1695 advancedMenu -> addAction(hgResolveListAct);
jtkorhonen@0 1696 advancedMenu -> addAction(hgResolveMarkAct);
jtkorhonen@11 1697 advancedMenu -> addSeparator();
jtkorhonen@34 1698 advancedMenu -> addAction(hgTagAct);
jtkorhonen@34 1699 advancedMenu -> addSeparator();
Chris@131 1700 */
jtkorhonen@34 1701 advancedMenu -> addAction(hgIgnoreAct);
jtkorhonen@34 1702 advancedMenu -> addSeparator();
jtkorhonen@11 1703 advancedMenu -> addAction(hgServeAct);
jtkorhonen@0 1704
jtkorhonen@0 1705 helpMenu = menuBar()->addMenu(tr("Help"));
jtkorhonen@0 1706 helpMenu->addAction(aboutAct);
Chris@128 1707 //!!! helpMenu->addAction(aboutQtAct);
jtkorhonen@0 1708 }
jtkorhonen@0 1709
jtkorhonen@0 1710 void MainWindow::createToolBars()
jtkorhonen@0 1711 {
jtkorhonen@0 1712 fileToolBar = addToolBar(tr("File"));
jtkorhonen@0 1713 fileToolBar -> setIconSize(QSize(MY_ICON_SIZE, MY_ICON_SIZE));
Chris@69 1714 fileToolBar -> addAction(openAct);
Chris@120 1715 fileToolBar -> addAction(hgRefreshAct);
jtkorhonen@0 1716 fileToolBar -> addSeparator();
Chris@61 1717 // fileToolBar -> addAction(hgChgSetDiffAct);
jtkorhonen@0 1718 fileToolBar -> setMovable(false);
jtkorhonen@0 1719
jtkorhonen@0 1720 repoToolBar = addToolBar(tr(REPOMENU_TITLE));
jtkorhonen@0 1721 repoToolBar -> setIconSize(QSize(MY_ICON_SIZE, MY_ICON_SIZE));
jtkorhonen@0 1722 repoToolBar->addAction(hgIncomingAct);
jtkorhonen@0 1723 repoToolBar->addAction(hgPullAct);
jtkorhonen@0 1724 repoToolBar->addAction(hgPushAct);
jtkorhonen@0 1725 repoToolBar -> setMovable(false);
jtkorhonen@0 1726
jtkorhonen@0 1727 workFolderToolBar = addToolBar(tr(WORKFOLDERMENU_TITLE));
jtkorhonen@0 1728 addToolBar(Qt::LeftToolBarArea, workFolderToolBar);
jtkorhonen@0 1729 workFolderToolBar -> setIconSize(QSize(MY_ICON_SIZE, MY_ICON_SIZE));
Chris@61 1730 // workFolderToolBar->addSeparator();
Chris@95 1731 // workFolderToolBar->addAction(hgFileDiffAct);
Chris@95 1732 workFolderToolBar->addAction(hgFolderDiffAct);
jtkorhonen@0 1733 workFolderToolBar->addSeparator();
jtkorhonen@0 1734 workFolderToolBar->addAction(hgRevertAct);
jtkorhonen@0 1735 workFolderToolBar->addAction(hgUpdateAct);
jtkorhonen@0 1736 workFolderToolBar->addAction(hgCommitAct);
jtkorhonen@0 1737 workFolderToolBar->addAction(hgMergeAct);
jtkorhonen@0 1738 workFolderToolBar->addSeparator();
jtkorhonen@0 1739 workFolderToolBar->addAction(hgAddAct);
jtkorhonen@0 1740 workFolderToolBar->addAction(hgRemoveAct);
jtkorhonen@0 1741 workFolderToolBar -> setMovable(false);
Chris@61 1742
Chris@61 1743 foreach (QToolButton *tb, findChildren<QToolButton *>()) {
Chris@61 1744 tb->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
Chris@61 1745 }
jtkorhonen@0 1746 }
jtkorhonen@0 1747
jtkorhonen@0 1748
jtkorhonen@0 1749 void MainWindow::createStatusBar()
jtkorhonen@0 1750 {
jtkorhonen@0 1751 statusBar()->showMessage(tr("Ready"));
jtkorhonen@0 1752 }
jtkorhonen@0 1753
Chris@69 1754
Chris@69 1755 //!!! review these:
Chris@69 1756
jtkorhonen@0 1757 void MainWindow::readSettings()
jtkorhonen@0 1758 {
jtkorhonen@0 1759 QDir workFolder;
jtkorhonen@0 1760
Chris@61 1761 QSettings settings;
jtkorhonen@0 1762
jtkorhonen@30 1763 remoteRepoPath = settings.value("remoterepopath", "").toString();
jtkorhonen@0 1764 workFolderPath = settings.value("workfolderpath", "").toString();
jtkorhonen@0 1765 if (!workFolder.exists(workFolderPath))
jtkorhonen@0 1766 {
jtkorhonen@0 1767 workFolderPath = "";
jtkorhonen@0 1768 }
jtkorhonen@0 1769
jtkorhonen@0 1770 QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
jtkorhonen@0 1771 QSize size = settings.value("size", QSize(400, 400)).toSize();
jtkorhonen@0 1772 firstStart = settings.value("firststart", QVariant(true)).toBool();
jtkorhonen@0 1773
Chris@109 1774 //!!! initialFileTypesBits = (unsigned char) settings.value("viewFileTypes", QVariant(DEFAULT_HG_STAT_BITS)).toInt();
jtkorhonen@0 1775 resize(size);
jtkorhonen@0 1776 move(pos);
jtkorhonen@0 1777 }
jtkorhonen@0 1778
jtkorhonen@17 1779
jtkorhonen@0 1780 void MainWindow::writeSettings()
jtkorhonen@0 1781 {
Chris@61 1782 QSettings settings;
jtkorhonen@0 1783 settings.setValue("pos", pos());
jtkorhonen@0 1784 settings.setValue("size", size());
jtkorhonen@0 1785 settings.setValue("remoterepopath", remoteRepoPath);
jtkorhonen@0 1786 settings.setValue("workfolderpath", workFolderPath);
jtkorhonen@0 1787 settings.setValue("firststart", firstStart);
Chris@98 1788 //!!!settings.setValue("viewFileTypes", hgTabs -> getFileTypesBits());
jtkorhonen@0 1789 }
jtkorhonen@0 1790
jtkorhonen@0 1791
jtkorhonen@0 1792
jtkorhonen@0 1793