annotate mainwindow.cpp @ 363:f89e50d748ed feature_93

Enable Push button whenever the repo is non-empty, even when there is no remote location -- ask for remote location when it is pressed. Also change "Change Remote..." to "Set Remote..." to be consistent with this new usage
author Chris Cannam
date Thu, 17 Mar 2011 17:48:18 +0000
parents 73fb5ef55744
children 1482f5b9f9dc
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@244 8 Copyright (c) 2011 Chris Cannam
Chris@244 9 Copyright (c) 2011 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@359 32 #include <QWidgetAction>
Chris@125 33 #include <QRegExp>
Chris@199 34 #include <QShortcut>
Chris@237 35 #include <QUrl>
Chris@238 36 #include <QTimer>
jtkorhonen@0 37
Chris@53 38 #include "mainwindow.h"
Chris@69 39 #include "multichoicedialog.h"
Chris@64 40 #include "startupdialog.h"
Chris@53 41 #include "colourset.h"
Chris@62 42 #include "debug.h"
Chris@74 43 #include "logparser.h"
Chris@103 44 #include "confirmcommentdialog.h"
Chris@125 45 #include "incomingdialog.h"
Chris@175 46 #include "settingsdialog.h"
Chris@275 47 #include "moreinformationdialog.h"
Chris@331 48 #include "annotatedialog.h"
Chris@229 49 #include "version.h"
Chris@287 50 #include "workstatuswidget.h"
Chris@53 51
jtkorhonen@0 52
Chris@172 53 MainWindow::MainWindow(QString myDirPath) :
Chris@238 54 m_myDirPath(myDirPath),
Chris@238 55 m_fsWatcherGeneralTimer(0),
Chris@241 56 m_fsWatcherRestoreTimer(0),
Chris@241 57 m_fsWatcherSuspended(false)
jtkorhonen@0 58 {
Chris@197 59 setWindowIcon(QIcon(":images/easyhg-icon.png"));
Chris@197 60
jtkorhonen@0 61 QString wndTitle;
jtkorhonen@0 62
Chris@284 63 m_showAllFiles = false;
Chris@273 64
Chris@284 65 m_fsWatcher = 0;
Chris@284 66 m_commitsSincePush = 0;
Chris@284 67 m_shouldHgStat = true;
Chris@90 68
jtkorhonen@0 69 createActions();
jtkorhonen@0 70 createMenus();
jtkorhonen@0 71 createToolBars();
jtkorhonen@0 72 createStatusBar();
jtkorhonen@0 73
Chris@284 74 m_runner = new HgRunner(m_myDirPath, this);
Chris@284 75 connect(m_runner, SIGNAL(commandStarting(HgAction)),
Chris@241 76 this, SLOT(commandStarting(HgAction)));
Chris@284 77 connect(m_runner, SIGNAL(commandCompleted(HgAction, QString)),
Chris@109 78 this, SLOT(commandCompleted(HgAction, QString)));
Chris@284 79 connect(m_runner, SIGNAL(commandFailed(HgAction, QString)),
Chris@109 80 this, SLOT(commandFailed(HgAction, QString)));
Chris@284 81 statusBar()->addPermanentWidget(m_runner);
jtkorhonen@0 82
Chris@61 83 setWindowTitle(tr("EasyMercurial"));
jtkorhonen@0 84
Chris@284 85 m_remoteRepoPath = "";
Chris@284 86 m_workFolderPath = "";
jtkorhonen@0 87
jtkorhonen@0 88 readSettings();
jtkorhonen@0 89
Chris@284 90 m_justMerged = false;
Chris@210 91
Chris@210 92 QWidget *central = new QWidget(this);
Chris@210 93 setCentralWidget(central);
Chris@210 94
Chris@210 95 QGridLayout *cl = new QGridLayout(central);
Chris@287 96 int row = 0;
Chris@210 97
Chris@210 98 #ifndef Q_OS_MAC
Chris@210 99 cl->setMargin(0);
Chris@210 100 #endif
jtkorhonen@0 101
Chris@287 102 m_workStatus = new WorkStatusWidget(this);
Chris@287 103 cl->addWidget(m_workStatus, row++, 0);
Chris@287 104
Chris@287 105 m_hgTabs = new HgTabWidget(central, m_workFolderPath);
Chris@287 106 connectTabsSignals();
Chris@287 107
Chris@287 108 cl->addWidget(m_hgTabs, row++, 0);
Chris@287 109
Chris@284 110 connect(m_hgTabs, SIGNAL(selectionChanged()),
Chris@95 111 this, SLOT(enableDisableActions()));
Chris@284 112 connect(m_hgTabs, SIGNAL(showAllChanged(bool)),
Chris@199 113 this, SLOT(showAllChanged(bool)));
Chris@95 114
jtkorhonen@0 115 setUnifiedTitleAndToolBarOnMac(true);
jtkorhonen@0 116 connectActions();
Chris@120 117 clearState();
jtkorhonen@0 118 enableDisableActions();
jtkorhonen@0 119
Chris@284 120 if (m_firstStart) {
Chris@64 121 startupDialog();
jtkorhonen@0 122 }
jtkorhonen@0 123
Chris@239 124 SettingsDialog::findDefaultLocations(m_myDirPath);
Chris@112 125
Chris@64 126 ColourSet *cs = ColourSet::instance();
Chris@64 127 cs->clearDefaultNames();
Chris@64 128 cs->addDefaultName("");
Chris@153 129 cs->addDefaultName("default");
Chris@64 130 cs->addDefaultName(getUserInfo());
Chris@62 131
Chris@175 132 hgTest();
Chris@359 133 updateRecentMenu();
jtkorhonen@0 134 }
jtkorhonen@0 135
jtkorhonen@0 136
jtkorhonen@0 137 void MainWindow::closeEvent(QCloseEvent *)
jtkorhonen@0 138 {
jtkorhonen@0 139 writeSettings();
Chris@284 140 delete m_fsWatcher;
jtkorhonen@0 141 }
jtkorhonen@0 142
jtkorhonen@0 143
Chris@64 144 QString MainWindow::getUserInfo() const
Chris@64 145 {
Chris@64 146 QSettings settings;
Chris@64 147 settings.beginGroup("User Information");
Chris@64 148 QString name = settings.value("name", getUserRealName()).toString();
Chris@64 149 QString email = settings.value("email", "").toString();
Chris@64 150
Chris@64 151 QString identifier;
Chris@64 152
Chris@64 153 if (email != "") {
Chris@64 154 identifier = QString("%1 <%2>").arg(name).arg(email);
Chris@64 155 } else {
Chris@64 156 identifier = name;
Chris@64 157 }
Chris@64 158
Chris@64 159 return identifier;
Chris@64 160 }
Chris@64 161
jtkorhonen@0 162 void MainWindow::about()
jtkorhonen@0 163 {
Chris@97 164 QMessageBox::about(this, tr("About EasyMercurial"),
Chris@229 165 tr("<qt><h2>EasyMercurial v%1</h2>"
Chris@228 166 #ifdef Q_OS_MAC
Chris@228 167 "<font size=-1>"
Chris@228 168 #endif
Chris@97 169 "<p>EasyMercurial is a simple user interface for the "
Chris@186 170 "Mercurial</a> version control system.</p>"
Chris@186 171 "<h4>Credits and Copyright</h4>"
Chris@186 172 "<p>Development carried out by Chris Cannam for "
Chris@186 173 "SoundSoftware.ac.uk at the Centre for Digital Music, "
Chris@186 174 "Queen Mary, University of London.</p>"
Chris@186 175 "<p>EasyMercurial is based on HgExplorer by "
Chris@186 176 "Jari Korhonen, with thanks.</p>"
Chris@186 177 "<p style=\"margin-left: 2em;\">"
Chris@244 178 "Copyright &copy; 2011 Queen Mary, University of London.<br>"
Chris@186 179 "Copyright &copy; 2010 Jari Korhonen.<br>"
Chris@244 180 "Copyright &copy; 2011 Chris Cannam."
Chris@186 181 "</p>"
Chris@186 182 "<p style=\"margin-left: 2em;\">"
Chris@186 183 "This program requires Mercurial, by Matt Mackall and others.<br>"
Chris@186 184 "This program uses Qt by Nokia.<br>"
Chris@186 185 "This program uses Nuvola icons by David Vignoni.<br>"
Chris@186 186 "This program may use KDiff3 by Joachim Eibl.<br>"
Chris@186 187 "This program may use PyQt by River Bank Computing.<br>"
Chris@186 188 "Packaging for Mercurial and other dependencies on Windows is derived from TortoiseHg by Steve Borho and others."
Chris@186 189 "</p>"
Chris@186 190 "<h4>License</h4>"
Chris@186 191 "<p>This program is free software; you can redistribute it and/or "
Chris@97 192 "modify it under the terms of the GNU General Public License as "
Chris@97 193 "published by the Free Software Foundation; either version 2 of the "
Chris@97 194 "License, or (at your option) any later version. See the file "
Chris@223 195 "COPYING included with this distribution for more information.</p>"
Chris@228 196 #ifdef Q_OS_MAC
Chris@228 197 "</font>"
Chris@228 198 #endif
Chris@229 199 ).arg(EASYHG_VERSION));
jtkorhonen@0 200 }
jtkorhonen@0 201
Chris@94 202 void MainWindow::clearSelections()
Chris@94 203 {
Chris@284 204 m_hgTabs->clearSelections();
Chris@94 205 }
jtkorhonen@0 206
Chris@199 207 void MainWindow::showAllChanged(bool s)
Chris@199 208 {
Chris@284 209 m_showAllFiles = s;
Chris@199 210 hgQueryPaths();
Chris@199 211 }
Chris@199 212
Chris@120 213 void MainWindow::hgRefresh()
Chris@120 214 {
Chris@120 215 clearState();
Chris@120 216 hgQueryPaths();
Chris@120 217 }
Chris@120 218
Chris@175 219 void MainWindow::hgTest()
Chris@175 220 {
Chris@175 221 QStringList params;
Chris@207 222 //!!! should we test version output? Really we want at least 1.7.x
Chris@207 223 //!!! for options such as merge --tool
Chris@175 224 params << "--version";
Chris@284 225 m_runner->requestAction(HgAction(ACT_TEST_HG, m_myDirPath, params));
Chris@175 226 }
Chris@175 227
Chris@200 228 void MainWindow::hgTestExtension()
Chris@200 229 {
Chris@200 230 QStringList params;
Chris@200 231 params << "--version";
Chris@284 232 m_runner->requestAction(HgAction(ACT_TEST_HG_EXT, m_myDirPath, params));
Chris@200 233 }
Chris@200 234
jtkorhonen@0 235 void MainWindow::hgStat()
jtkorhonen@0 236 {
Chris@109 237 QStringList params;
Chris@199 238
Chris@284 239 if (m_showAllFiles) {
Chris@199 240 params << "stat" << "-A";
Chris@199 241 } else {
Chris@199 242 params << "stat" << "-ardum";
Chris@199 243 }
Chris@153 244
Chris@284 245 m_lastStatOutput = "";
Chris@273 246
Chris@284 247 m_runner->requestAction(HgAction(ACT_STAT, m_workFolderPath, params));
jtkorhonen@0 248 }
jtkorhonen@0 249
Chris@109 250 void MainWindow::hgQueryPaths()
Chris@74 251 {
Chris@210 252 // Quickest is to just read the file
Chris@198 253
Chris@284 254 QFileInfo hgrc(m_workFolderPath + "/.hg/hgrc");
Chris@198 255
Chris@210 256 QString path;
Chris@210 257
Chris@198 258 if (hgrc.exists()) {
Chris@198 259 QSettings s(hgrc.canonicalFilePath(), QSettings::IniFormat);
Chris@198 260 s.beginGroup("paths");
Chris@210 261 path = s.value("default").toString();
Chris@210 262 }
Chris@198 263
Chris@284 264 m_remoteRepoPath = path;
Chris@198 265
Chris@210 266 // We have to do this here, because commandCompleted won't be called
Chris@284 267 MultiChoiceDialog::addRecentArgument("local", m_workFolderPath);
Chris@284 268 MultiChoiceDialog::addRecentArgument("remote", m_remoteRepoPath);
Chris@287 269 updateWorkFolderAndRepoNames();
Chris@210 270
Chris@210 271 hgQueryBranch();
Chris@210 272 return;
Chris@210 273
Chris@210 274 /* The classic method!
Chris@198 275
Chris@109 276 QStringList params;
Chris@109 277 params << "paths";
Chris@284 278 m_runner->requestAction(HgAction(ACT_QUERY_PATHS, m_workFolderPath, params));
Chris@210 279 */
Chris@74 280 }
Chris@74 281
Chris@109 282 void MainWindow::hgQueryBranch()
Chris@106 283 {
Chris@210 284 // Quickest is to just read the file
Chris@198 285
Chris@284 286 QFile hgbr(m_workFolderPath + "/.hg/branch");
Chris@198 287
Chris@210 288 QString br = "default";
Chris@210 289
Chris@198 290 if (hgbr.exists() && hgbr.open(QFile::ReadOnly)) {
Chris@210 291 QByteArray ba = hgbr.readLine();
Chris@210 292 br = QString::fromUtf8(ba).trimmed();
Chris@210 293 }
Chris@210 294
Chris@284 295 m_currentBranch = br;
Chris@210 296
Chris@210 297 // We have to do this here, because commandCompleted won't be called
Chris@210 298 hgStat();
Chris@210 299 return;
Chris@198 300
Chris@210 301 /* The classic method!
Chris@198 302
Chris@109 303 QStringList params;
Chris@109 304 params << "branch";
Chris@284 305 m_runner->requestAction(HgAction(ACT_QUERY_BRANCH, m_workFolderPath, params));
Chris@210 306 */
Chris@106 307 }
Chris@106 308
Chris@109 309 void MainWindow::hgQueryHeads()
jtkorhonen@0 310 {
Chris@109 311 QStringList params;
Chris@137 312 // On empty repos, "hg heads" will fail -- we don't care about
Chris@137 313 // that. Use --closed option so as to include closed branches;
Chris@137 314 // otherwise we'll be stuck if the user updates into one, and our
Chris@137 315 // incremental log will end up with spurious stuff in it because
Chris@137 316 // we won't be pruning at the ends of closed branches
Chris@137 317 params << "heads" << "--closed";
Chris@284 318 m_runner->requestAction(HgAction(ACT_QUERY_HEADS, m_workFolderPath, params));
jtkorhonen@0 319 }
jtkorhonen@0 320
jtkorhonen@0 321 void MainWindow::hgLog()
jtkorhonen@0 322 {
Chris@109 323 QStringList params;
Chris@109 324 params << "log";
Chris@109 325 params << "--template";
Chris@125 326 params << Changeset::getLogTemplate();
Chris@109 327
Chris@284 328 m_runner->requestAction(HgAction(ACT_LOG, m_workFolderPath, params));
Chris@109 329 }
Chris@109 330
Chris@150 331 void MainWindow::hgLogIncremental(QStringList prune)
Chris@120 332 {
Chris@305 333 // Sometimes we can be called with prune empty -- it represents
Chris@305 334 // the current heads, but if we have none already and for some
Chris@305 335 // reason are being prompted for an incremental update, we may run
Chris@305 336 // into trouble. In that case, make this a full log instead
Chris@305 337
Chris@305 338 if (prune.empty()) {
Chris@305 339 hgLog();
Chris@305 340 return;
Chris@305 341 }
Chris@305 342
Chris@120 343 QStringList params;
Chris@120 344 params << "log";
Chris@120 345
Chris@150 346 foreach (QString p, prune) {
Chris@153 347 params << "--prune" << Changeset::hashOf(p);
Chris@120 348 }
Chris@120 349
Chris@120 350 params << "--template";
Chris@125 351 params << Changeset::getLogTemplate();
Chris@120 352
Chris@284 353 m_runner->requestAction(HgAction(ACT_LOG_INCREMENTAL, m_workFolderPath, params));
Chris@120 354 }
Chris@109 355
Chris@109 356 void MainWindow::hgQueryParents()
Chris@109 357 {
Chris@109 358 QStringList params;
Chris@109 359 params << "parents";
Chris@284 360 m_runner->requestAction(HgAction(ACT_QUERY_PARENTS, m_workFolderPath, params));
Chris@109 361 }
Chris@109 362
Chris@326 363 void MainWindow::hgAnnotateFiles(QStringList files)
Chris@326 364 {
Chris@326 365 QStringList params;
Chris@326 366
Chris@326 367 if (!files.isEmpty()) {
Chris@332 368 params << "annotate" << "-udqc" << "--" << files;
Chris@326 369 m_runner->requestAction(HgAction(ACT_ANNOTATE, m_workFolderPath, params));
Chris@326 370 }
Chris@326 371 }
Chris@326 372
Chris@109 373 void MainWindow::hgResolveList()
Chris@109 374 {
Chris@109 375 QStringList params;
jtkorhonen@0 376
Chris@109 377 params << "resolve" << "--list";
Chris@284 378 m_runner->requestAction(HgAction(ACT_RESOLVE_LIST, m_workFolderPath, params));
Chris@109 379 }
Chris@109 380
Chris@109 381 void MainWindow::hgAdd()
jtkorhonen@0 382 {
Chris@109 383 // hgExplorer permitted adding "all" files -- I'm not sure
Chris@109 384 // that one is a good idea, let's require the user to select
jtkorhonen@0 385
Chris@326 386 hgAddFiles(m_hgTabs->getSelectedAddableFiles());
Chris@326 387 }
Chris@326 388
Chris@326 389 void MainWindow::hgAddFiles(QStringList files)
Chris@326 390 {
Chris@326 391 QStringList params;
Chris@109 392
Chris@109 393 if (!files.empty()) {
Chris@109 394 params << "add" << "--" << files;
Chris@284 395 m_runner->requestAction(HgAction(ACT_ADD, m_workFolderPath, params));
jtkorhonen@0 396 }
jtkorhonen@0 397 }
jtkorhonen@0 398
Chris@98 399 void MainWindow::hgRemove()
Chris@98 400 {
Chris@326 401 hgRemoveFiles(m_hgTabs->getSelectedRemovableFiles());
Chris@326 402 }
Chris@326 403
Chris@326 404 void MainWindow::hgRemoveFiles(QStringList files)
Chris@326 405 {
Chris@109 406 QStringList params;
Chris@98 407
Chris@109 408 if (!files.empty()) {
Chris@109 409 params << "remove" << "--after" << "--force" << "--" << files;
Chris@284 410 m_runner->requestAction(HgAction(ACT_REMOVE, m_workFolderPath, params));
Chris@109 411 }
jtkorhonen@0 412 }
jtkorhonen@0 413
jtkorhonen@0 414 void MainWindow::hgCommit()
jtkorhonen@0 415 {
Chris@326 416 hgCommitFiles(QStringList());
Chris@326 417 }
Chris@326 418
Chris@326 419 void MainWindow::hgCommitFiles(QStringList files)
Chris@326 420 {
Chris@109 421 QStringList params;
Chris@109 422 QString comment;
Chris@94 423
Chris@284 424 if (m_justMerged) {
Chris@284 425 comment = m_mergeCommitComment;
Chris@157 426 }
Chris@157 427
Chris@284 428 QStringList allFiles = m_hgTabs->getAllCommittableFiles();
Chris@127 429 QStringList reportFiles = files;
Chris@237 430 if (reportFiles.empty()) {
Chris@237 431 reportFiles = allFiles;
Chris@237 432 }
Chris@103 433
Chris@237 434 QString subsetNote;
Chris@237 435 if (reportFiles != allFiles) {
Chris@237 436 subsetNote = tr("<p><b>Note:</b> you are committing only the files you have selected, not all of the files that have been changed!");
Chris@237 437 }
Chris@237 438
Chris@155 439 QString cf(tr("Commit files"));
Chris@155 440
Chris@311 441 QString branchText;
Chris@311 442 if (m_currentBranch == "" || m_currentBranch == "default") {
Chris@311 443 branchText = tr("the default branch");
Chris@311 444 } else {
Chris@311 445 branchText = tr("branch \"%1\"").arg(m_currentBranch);
Chris@311 446 }
Chris@311 447
Chris@109 448 if (ConfirmCommentDialog::confirmAndGetLongComment
Chris@109 449 (this,
Chris@155 450 cf,
Chris@237 451 tr("<h3>%1</h3><p>%2%3").arg(cf)
Chris@313 452 .arg(tr("You are about to commit the following files to %1:").arg(branchText))
Chris@237 453 .arg(subsetNote),
Chris@237 454 tr("<h3>%1</h3><p>%2%3").arg(cf)
Chris@311 455 .arg(tr("You are about to commit %n file(s) to %1.", "", reportFiles.size()).arg(branchText))
Chris@237 456 .arg(subsetNote),
Chris@127 457 reportFiles,
Chris@193 458 comment,
Chris@193 459 tr("Commit"))) {
Chris@103 460
Chris@284 461 if (!m_justMerged && !files.empty()) {
Chris@157 462 // User wants to commit selected file(s) (and this is not
Chris@157 463 // merge commit, which would fail if we selected files)
Chris@157 464 params << "commit" << "--message" << comment
Chris@157 465 << "--user" << getUserInfo() << "--" << files;
Chris@109 466 } else {
Chris@109 467 // Commit all changes
Chris@157 468 params << "commit" << "--message" << comment
Chris@157 469 << "--user" << getUserInfo();
jtkorhonen@0 470 }
Chris@109 471
Chris@284 472 m_runner->requestAction(HgAction(ACT_COMMIT, m_workFolderPath, params));
Chris@284 473 m_mergeCommitComment = "";
jtkorhonen@0 474 }
jtkorhonen@0 475 }
jtkorhonen@0 476
jtkorhonen@34 477 QString MainWindow::filterTag(QString tag)
jtkorhonen@34 478 {
Chris@278 479 for(int i = 0; i < tag.size(); i++) {
Chris@278 480 if (tag[i].isLower() || tag[i].isUpper() ||
Chris@278 481 tag[i].isDigit() || (tag[i] == QChar('.'))) {
jtkorhonen@34 482 //ok
Chris@278 483 } else {
jtkorhonen@34 484 tag[i] = QChar('_');
jtkorhonen@34 485 }
jtkorhonen@34 486 }
jtkorhonen@34 487 return tag;
jtkorhonen@34 488 }
jtkorhonen@34 489
jtkorhonen@34 490
Chris@311 491 void MainWindow::hgNewBranch()
Chris@278 492 {
Chris@278 493 QStringList params;
Chris@278 494 QString branch;
Chris@278 495
Chris@278 496 if (ConfirmCommentDialog::confirmAndGetShortComment
Chris@278 497 (this,
Chris@278 498 tr("New Branch"),
Chris@278 499 tr("Enter new branch name:"),
Chris@278 500 branch,
Chris@278 501 tr("Start Branch"))) {
Chris@278 502 if (!branch.isEmpty()) {//!!! do something better if it is empty
Chris@278 503
Chris@278 504 params << "branch" << filterTag(branch);
Chris@307 505 m_runner->requestAction(HgAction(ACT_NEW_BRANCH, m_workFolderPath, params));
Chris@278 506 }
Chris@278 507 }
Chris@278 508 }
Chris@278 509
Chris@311 510 void MainWindow::hgNoBranch()
Chris@311 511 {
Chris@311 512 if (m_currentParents.empty()) return;
Chris@311 513
Chris@311 514 QString parentBranch = m_currentParents[0]->branch();
Chris@311 515 if (parentBranch == "") parentBranch = "default";
Chris@311 516
Chris@311 517 QStringList params;
Chris@311 518 params << "branch" << parentBranch;
Chris@311 519 m_runner->requestAction(HgAction(ACT_NEW_BRANCH, m_workFolderPath, params));
Chris@311 520 }
Chris@311 521
Chris@164 522 void MainWindow::hgTag(QString id)
jtkorhonen@34 523 {
Chris@109 524 QStringList params;
Chris@109 525 QString tag;
jtkorhonen@34 526
Chris@109 527 if (ConfirmCommentDialog::confirmAndGetShortComment
Chris@109 528 (this,
Chris@109 529 tr("Tag"),
Chris@109 530 tr("Enter tag:"),
Chris@193 531 tag,
Chris@193 532 tr("Add Tag"))) {
Chris@164 533 if (!tag.isEmpty()) {//!!! do something better if it is empty
Chris@164 534
Chris@164 535 params << "tag" << "--user" << getUserInfo();
Chris@164 536 params << "--rev" << Changeset::hashOf(id) << filterTag(tag);
Chris@109 537
Chris@284 538 m_runner->requestAction(HgAction(ACT_TAG, m_workFolderPath, params));
jtkorhonen@34 539 }
jtkorhonen@34 540 }
jtkorhonen@34 541 }
jtkorhonen@34 542
jtkorhonen@34 543 void MainWindow::hgIgnore()
jtkorhonen@34 544 {
Chris@109 545 QString hgIgnorePath;
Chris@109 546 QStringList params;
Chris@178 547
Chris@284 548 hgIgnorePath = m_workFolderPath;
Chris@178 549 hgIgnorePath += "/.hgignore";
Chris@249 550
Chris@284 551 if (!QDir(m_workFolderPath).exists()) return;
Chris@249 552 QFile f(hgIgnorePath);
Chris@249 553 if (!f.exists()) {
Chris@249 554 f.open(QFile::WriteOnly);
Chris@249 555 QTextStream *ts = new QTextStream(&f);
Chris@249 556 *ts << "syntax: glob\n";
Chris@249 557 delete ts;
Chris@249 558 f.close();
Chris@249 559 }
Chris@109 560
Chris@109 561 params << hgIgnorePath;
Chris@179 562
Chris@239 563 QString editor = getEditorBinaryName();
Chris@112 564
Chris@179 565 if (editor == "") {
Chris@179 566 DEBUG << "Failed to find a text editor" << endl;
Chris@179 567 //!!! visible error!
Chris@179 568 return;
Chris@179 569 }
Chris@179 570
Chris@284 571 HgAction action(ACT_HG_IGNORE, m_workFolderPath, params);
Chris@179 572 action.executable = editor;
Chris@179 573
Chris@284 574 m_runner->requestAction(action);
Chris@179 575 }
Chris@179 576
Chris@326 577 void MainWindow::hgIgnoreFiles(QStringList files)
Chris@326 578 {
Chris@326 579 //!!! not implemented yet
Chris@326 580 DEBUG << "MainWindow::hgIgnoreFiles: Not implemented" << endl;
Chris@326 581 }
Chris@326 582
Chris@326 583 void MainWindow::hgUnIgnoreFiles(QStringList files)
Chris@326 584 {
Chris@326 585 //!!! not implemented yet
Chris@326 586 DEBUG << "MainWindow::hgUnIgnoreFiles: Not implemented" << endl;
Chris@326 587 }
Chris@326 588
Chris@239 589 QString MainWindow::getDiffBinaryName()
Chris@179 590 {
Chris@179 591 QSettings settings;
Chris@179 592 settings.beginGroup("Locations");
Chris@239 593 return settings.value("extdiffbinary", "").toString();
Chris@179 594 }
Chris@179 595
Chris@239 596 QString MainWindow::getMergeBinaryName()
Chris@179 597 {
Chris@179 598 QSettings settings;
Chris@179 599 settings.beginGroup("Locations");
Chris@239 600 return settings.value("mergebinary", "").toString();
Chris@179 601 }
Chris@179 602
Chris@239 603 QString MainWindow::getEditorBinaryName()
Chris@179 604 {
Chris@178 605 QSettings settings;
Chris@178 606 settings.beginGroup("Locations");
Chris@239 607 return settings.value("editorbinary", "").toString();
Chris@163 608 }
Chris@163 609
Chris@168 610 void MainWindow::hgShowSummary()
Chris@168 611 {
Chris@168 612 QStringList params;
Chris@168 613
Chris@168 614 params << "diff" << "--stat";
Chris@168 615
Chris@288 616 m_runner->requestAction(HgAction(ACT_UNCOMMITTED_SUMMARY, m_workFolderPath, params));
Chris@168 617 }
Chris@168 618
jtkorhonen@0 619 void MainWindow::hgFolderDiff()
jtkorhonen@0 620 {
Chris@326 621 hgDiffFiles(QStringList());
Chris@326 622 }
Chris@326 623
Chris@326 624 void MainWindow::hgDiffFiles(QStringList files)
Chris@326 625 {
Chris@239 626 QString diff = getDiffBinaryName();
Chris@179 627 if (diff == "") return;
Chris@112 628
Chris@109 629 QStringList params;
jtkorhonen@0 630
Chris@112 631 // Diff parent against working folder (folder diff)
Chris@112 632
Chris@148 633 params << "--config" << "extensions.extdiff=" << "extdiff";
Chris@179 634 params << "--program" << diff;
Chris@109 635
Chris@331 636 params << "--" << files; // may be none: whole dir
Chris@273 637
Chris@284 638 m_runner->requestAction(HgAction(ACT_FOLDERDIFF, m_workFolderPath, params));
jtkorhonen@0 639 }
jtkorhonen@0 640
Chris@148 641 void MainWindow::hgDiffToCurrent(QString id)
jtkorhonen@0 642 {
Chris@239 643 QString diff = getDiffBinaryName();
Chris@179 644 if (diff == "") return;
Chris@163 645
Chris@148 646 QStringList params;
jtkorhonen@0 647
Chris@148 648 // Diff given revision against working folder
jtkorhonen@0 649
Chris@148 650 params << "--config" << "extensions.extdiff=" << "extdiff";
Chris@179 651 params << "--program" << diff;
Chris@153 652 params << "--rev" << Changeset::hashOf(id);
Chris@148 653
Chris@284 654 m_runner->requestAction(HgAction(ACT_FOLDERDIFF, m_workFolderPath, params));
jtkorhonen@0 655 }
jtkorhonen@0 656
Chris@148 657 void MainWindow::hgDiffToParent(QString child, QString parent)
Chris@148 658 {
Chris@239 659 QString diff = getDiffBinaryName();
Chris@179 660 if (diff == "") return;
Chris@163 661
Chris@148 662 QStringList params;
Chris@148 663
Chris@281 664 // Diff given revision against parent revision
Chris@148 665
Chris@148 666 params << "--config" << "extensions.extdiff=" << "extdiff";
Chris@179 667 params << "--program" << diff;
Chris@153 668 params << "--rev" << Changeset::hashOf(parent)
Chris@153 669 << "--rev" << Changeset::hashOf(child);
Chris@148 670
Chris@284 671 m_runner->requestAction(HgAction(ACT_CHGSETDIFF, m_workFolderPath, params));
Chris@273 672 }
Chris@326 673
Chris@273 674
Chris@289 675 void MainWindow::hgShowSummaryFor(Changeset *cs)
Chris@288 676 {
Chris@288 677 QStringList params;
Chris@288 678
Chris@289 679 // This will pick a default parent if there is more than one
Chris@289 680 // (whereas with diff we need to supply one). But it does need a
Chris@289 681 // bit more parsing
Chris@289 682 params << "log" << "--stat" << "--rev" << Changeset::hashOf(cs->id());
Chris@289 683
Chris@289 684 m_runner->requestAction(HgAction(ACT_DIFF_SUMMARY, m_workFolderPath,
Chris@289 685 params, cs));
Chris@148 686 }
Chris@148 687
jtkorhonen@0 688
jtkorhonen@0 689 void MainWindow::hgUpdate()
jtkorhonen@0 690 {
Chris@109 691 QStringList params;
jtkorhonen@0 692
Chris@109 693 params << "update";
Chris@109 694
Chris@284 695 m_runner->requestAction(HgAction(ACT_UPDATE, m_workFolderPath, params));
jtkorhonen@0 696 }
jtkorhonen@0 697
jtkorhonen@0 698
Chris@148 699 void MainWindow::hgUpdateToRev(QString id)
jtkorhonen@0 700 {
Chris@148 701 QStringList params;
jtkorhonen@0 702
Chris@153 703 params << "update" << "--rev" << Changeset::hashOf(id) << "--check";
jtkorhonen@0 704
Chris@284 705 m_runner->requestAction(HgAction(ACT_UPDATE, m_workFolderPath, params));
jtkorhonen@0 706 }
jtkorhonen@0 707
jtkorhonen@0 708
jtkorhonen@0 709 void MainWindow::hgRevert()
jtkorhonen@0 710 {
Chris@326 711 hgRevertFiles(QStringList());
Chris@326 712 }
Chris@326 713
Chris@326 714 void MainWindow::hgRevertFiles(QStringList files)
Chris@326 715 {
Chris@109 716 QStringList params;
Chris@109 717 QString comment;
Chris@237 718 bool all = false;
Chris@98 719
Chris@284 720 QStringList allFiles = m_hgTabs->getAllRevertableFiles();
Chris@237 721 if (files.empty() || files == allFiles) {
Chris@237 722 files = allFiles;
Chris@237 723 all = true;
Chris@237 724 }
Chris@237 725
Chris@237 726 QString subsetNote;
Chris@237 727 if (!all) {
Chris@237 728 subsetNote = tr("<p><b>Note:</b> you are reverting only the files you have selected, not all of the files that have been changed!");
Chris@237 729 }
Chris@155 730
Chris@155 731 QString rf(tr("Revert files"));
Chris@237 732
Chris@237 733 // Set up params before asking for confirmation, because there is
Chris@237 734 // a failure case here that we would need to report on early
Chris@237 735
Chris@284 736 DEBUG << "hgRevert: m_justMerged = " << m_justMerged << ", m_mergeTargetRevision = " << m_mergeTargetRevision << endl;
Chris@273 737
Chris@284 738 if (m_justMerged) {
Chris@237 739
Chris@237 740 // This is a little fiddly. The proper way to "revert" the
Chris@237 741 // whole of an uncommitted merge is with "hg update --clean ."
Chris@237 742 // But if the user has selected only some files, we're sort of
Chris@237 743 // promising to revert only those, which means we need to
Chris@237 744 // specify which parent to revert to. We can only do that if
Chris@237 745 // we have a record of it, which we do if you just did the
Chris@237 746 // merge from within easyhg but don't if you've exited and
Chris@237 747 // restarted, or changed repository, since then. Hmmm.
Chris@237 748
Chris@237 749 if (all) {
Chris@237 750 params << "update" << "--clean" << ".";
Chris@237 751 } else {
Chris@284 752 if (m_mergeTargetRevision != "") {
Chris@237 753 params << "revert" << "--rev"
Chris@284 754 << Changeset::hashOf(m_mergeTargetRevision)
Chris@237 755 << "--" << files;
Chris@237 756 } else {
Chris@237 757 QMessageBox::information
Chris@237 758 (this, tr("Unable to revert"),
Chris@237 759 tr("<qt><b>Sorry, unable to revert these files</b><br><br>EasyMercurial can only revert a subset of files during a merge if it still has a record of which parent was the original merge target; that information is no longer available.<br><br>This is a limitation of EasyMercurial. Consider reverting all files, or using hg revert with a specific revision at the command-line instead.</qt>"));
Chris@237 760 return;
Chris@237 761 }
Chris@237 762 }
Chris@237 763 } else {
Chris@237 764 params << "revert" << "--" << files;
Chris@237 765 }
Chris@237 766
Chris@109 767 if (ConfirmCommentDialog::confirmDangerousFilesAction
Chris@109 768 (this,
Chris@155 769 rf,
Chris@237 770 tr("<h3>%1</h3><p>%2%3").arg(rf)
Chris@237 771 .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@237 772 .arg(subsetNote),
Chris@237 773 tr("<h3>%1</h3><p>%2%3").arg(rf)
Chris@237 774 .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@237 775 .arg(subsetNote),
Chris@193 776 files,
Chris@193 777 tr("Revert"))) {
Chris@163 778
Chris@284 779 m_lastRevertedFiles = files;
Chris@109 780
Chris@284 781 m_runner->requestAction(HgAction(ACT_REVERT, m_workFolderPath, params));
jtkorhonen@0 782 }
jtkorhonen@0 783 }
jtkorhonen@0 784
Chris@163 785
Chris@361 786 void MainWindow::hgRenameFiles(QStringList files)
Chris@361 787 {
Chris@361 788 QString renameTo;
Chris@361 789
Chris@361 790 QString file;
Chris@361 791 if (files.empty()) return;
Chris@361 792 file = files[0];
Chris@361 793
Chris@361 794 if (ConfirmCommentDialog::confirmAndGetShortComment
Chris@361 795 (this,
Chris@361 796 tr("Rename"),
Chris@361 797 tr("Rename <code>%1</code> to:").arg(xmlEncode(file)),
Chris@361 798 renameTo,
Chris@361 799 tr("Rename"))) {
Chris@361 800
Chris@361 801 if (renameTo != "" && renameTo != file) {
Chris@361 802
Chris@361 803 QStringList params;
Chris@361 804
Chris@361 805 params << "rename" << "--" << file << renameTo;
Chris@361 806
Chris@361 807 m_runner->requestAction(HgAction(ACT_RENAME_FILE, m_workFolderPath, params));
Chris@361 808 }
Chris@361 809 }
Chris@361 810 }
Chris@361 811
Chris@361 812
Chris@361 813 void MainWindow::hgCopyFiles(QStringList files)
Chris@361 814 {
Chris@361 815 QString copyTo;
Chris@361 816
Chris@361 817 QString file;
Chris@361 818 if (files.empty()) return;
Chris@361 819 file = files[0];
Chris@361 820
Chris@361 821 if (ConfirmCommentDialog::confirmAndGetShortComment
Chris@361 822 (this,
Chris@361 823 tr("Copy"),
Chris@361 824 tr("Copy <code>%1</code> to:").arg(xmlEncode(file)),
Chris@361 825 copyTo,
Chris@361 826 tr("Copy"))) {
Chris@361 827
Chris@361 828 if (copyTo != "" && copyTo != file) {
Chris@361 829
Chris@361 830 QStringList params;
Chris@361 831
Chris@361 832 params << "copy" << "--" << file << copyTo;
Chris@361 833
Chris@361 834 m_runner->requestAction(HgAction(ACT_COPY_FILE, m_workFolderPath, params));
Chris@361 835 }
Chris@361 836 }
Chris@361 837 }
Chris@361 838
Chris@361 839
Chris@326 840 void MainWindow::hgMarkFilesResolved(QStringList files)
Chris@163 841 {
Chris@163 842 QStringList params;
Chris@163 843
Chris@163 844 params << "resolve" << "--mark";
Chris@163 845
Chris@163 846 if (files.empty()) {
Chris@163 847 params << "--all";
Chris@163 848 } else {
Chris@164 849 params << "--" << files;
Chris@163 850 }
Chris@163 851
Chris@284 852 m_runner->requestAction(HgAction(ACT_RESOLVE_MARK, m_workFolderPath, params));
Chris@163 853 }
Chris@163 854
Chris@163 855
Chris@326 856 void MainWindow::hgRedoMerge()
Chris@326 857 {
Chris@326 858 hgRedoFileMerges(QStringList());
Chris@326 859 }
Chris@326 860
Chris@326 861
Chris@326 862 void MainWindow::hgRedoFileMerges(QStringList files)
jtkorhonen@33 863 {
Chris@109 864 QStringList params;
jtkorhonen@33 865
Chris@163 866 params << "resolve";
Chris@163 867
Chris@239 868 QString merge = getMergeBinaryName();
Chris@179 869 if (merge != "") {
Chris@179 870 params << "--tool" << merge;
Chris@163 871 }
Chris@163 872
Chris@163 873 if (files.empty()) {
Chris@163 874 params << "--all";
Chris@163 875 } else {
Chris@164 876 params << "--" << files;
Chris@163 877 }
Chris@163 878
Chris@284 879 if (m_currentParents.size() == 1) {
Chris@284 880 m_mergeTargetRevision = m_currentParents[0]->id();
Chris@237 881 }
Chris@237 882
Chris@284 883 m_runner->requestAction(HgAction(ACT_RETRY_MERGE, m_workFolderPath, params));
Chris@273 884
Chris@284 885 m_mergeCommitComment = tr("Merge");
jtkorhonen@33 886 }
Chris@326 887
jtkorhonen@33 888
jtkorhonen@0 889 void MainWindow::hgMerge()
jtkorhonen@0 890 {
Chris@284 891 if (m_hgTabs->canResolve()) {
Chris@326 892 hgRedoMerge();
Chris@163 893 return;
Chris@163 894 }
Chris@163 895
Chris@109 896 QStringList params;
jtkorhonen@0 897
Chris@109 898 params << "merge";
Chris@163 899
Chris@239 900 QString merge = getMergeBinaryName();
Chris@179 901 if (merge != "") {
Chris@179 902 params << "--tool" << merge;
Chris@163 903 }
Chris@163 904
Chris@284 905 if (m_currentParents.size() == 1) {
Chris@284 906 m_mergeTargetRevision = m_currentParents[0]->id();
Chris@163 907 }
Chris@163 908
Chris@284 909 m_runner->requestAction(HgAction(ACT_MERGE, m_workFolderPath, params));
Chris@273 910
Chris@284 911 m_mergeCommitComment = tr("Merge");
jtkorhonen@0 912 }
jtkorhonen@0 913
jtkorhonen@0 914
Chris@148 915 void MainWindow::hgMergeFrom(QString id)
Chris@148 916 {
Chris@148 917 QStringList params;
Chris@148 918
Chris@148 919 params << "merge";
Chris@153 920 params << "--rev" << Changeset::hashOf(id);
Chris@163 921
Chris@239 922 QString merge = getMergeBinaryName();
Chris@179 923 if (merge != "") {
Chris@179 924 params << "--tool" << merge;
Chris@163 925 }
Chris@148 926
Chris@284 927 if (m_currentParents.size() == 1) {
Chris@284 928 m_mergeTargetRevision = m_currentParents[0]->id();
Chris@237 929 }
Chris@237 930
Chris@284 931 m_runner->requestAction(HgAction(ACT_MERGE, m_workFolderPath, params));
Chris@273 932
Chris@284 933 m_mergeCommitComment = "";
Chris@273 934
Chris@284 935 foreach (Changeset *cs, m_currentHeads) {
Chris@284 936 if (cs->id() == id && !cs->isOnBranch(m_currentBranch)) {
Chris@157 937 if (cs->branch() == "" || cs->branch() == "default") {
Chris@284 938 m_mergeCommitComment = tr("Merge from the default branch");
Chris@157 939 } else {
Chris@284 940 m_mergeCommitComment = tr("Merge from branch \"%1\"").arg(cs->branch());
Chris@157 941 }
Chris@157 942 }
Chris@157 943 }
Chris@157 944
Chris@284 945 if (m_mergeCommitComment == "") {
Chris@284 946 m_mergeCommitComment = tr("Merge from %1").arg(id);
Chris@157 947 }
Chris@148 948 }
Chris@148 949
Chris@148 950
jtkorhonen@0 951 void MainWindow::hgCloneFromRemote()
jtkorhonen@0 952 {
Chris@109 953 QStringList params;
jtkorhonen@0 954
Chris@284 955 if (!QDir(m_workFolderPath).exists()) {
Chris@284 956 if (!QDir().mkpath(m_workFolderPath)) {
Chris@109 957 DEBUG << "hgCloneFromRemote: Failed to create target path "
Chris@284 958 << m_workFolderPath << endl;
Chris@109 959 //!!! report error
Chris@109 960 return;
Chris@104 961 }
Chris@109 962 }
Chris@104 963
Chris@284 964 params << "clone" << m_remoteRepoPath << m_workFolderPath;
Chris@109 965
Chris@287 966 updateWorkFolderAndRepoNames();
Chris@284 967 m_hgTabs->updateWorkFolderFileList("");
Chris@273 968
Chris@284 969 m_runner->requestAction(HgAction(ACT_CLONEFROMREMOTE, m_workFolderPath, params));
jtkorhonen@0 970 }
jtkorhonen@0 971
jtkorhonen@0 972 void MainWindow::hgInit()
jtkorhonen@0 973 {
Chris@109 974 QStringList params;
jtkorhonen@0 975
Chris@109 976 params << "init";
Chris@284 977 params << m_workFolderPath;
Chris@273 978
Chris@284 979 m_runner->requestAction(HgAction(ACT_INIT, m_workFolderPath, params));
jtkorhonen@0 980 }
jtkorhonen@0 981
jtkorhonen@0 982 void MainWindow::hgIncoming()
jtkorhonen@0 983 {
Chris@109 984 QStringList params;
jtkorhonen@0 985
Chris@284 986 params << "incoming" << "--newest-first" << m_remoteRepoPath;
Chris@125 987 params << "--template" << Changeset::getLogTemplate();
jtkorhonen@0 988
Chris@284 989 m_runner->requestAction(HgAction(ACT_INCOMING, m_workFolderPath, params));
jtkorhonen@0 990 }
jtkorhonen@0 991
jtkorhonen@0 992 void MainWindow::hgPull()
jtkorhonen@0 993 {
Chris@193 994 if (ConfirmCommentDialog::confirm
Chris@126 995 (this, tr("Confirm pull"),
Chris@299 996 tr("<qt><h3>Pull from remote repository?</h3></qt>"),
Chris@299 997 tr("<qt><p>You are about to pull changes from the remote repository at <code>%1</code>.</p></qt>").arg(xmlEncode(m_remoteRepoPath)),
Chris@194 998 tr("Pull"))) {
jtkorhonen@0 999
Chris@126 1000 QStringList params;
Chris@284 1001 params << "pull" << m_remoteRepoPath;
Chris@284 1002 m_runner->requestAction(HgAction(ACT_PULL, m_workFolderPath, params));
Chris@126 1003 }
jtkorhonen@0 1004 }
jtkorhonen@0 1005
jtkorhonen@0 1006 void MainWindow::hgPush()
jtkorhonen@0 1007 {
Chris@363 1008 if (m_remoteRepoPath.isEmpty()) {
Chris@363 1009 changeRemoteRepo(true);
Chris@363 1010 if (m_remoteRepoPath.isEmpty()) return;
Chris@363 1011 }
Chris@363 1012
Chris@356 1013 QString uncommittedNote;
Chris@356 1014 if (m_hgTabs->canCommit()) {
Chris@356 1015 uncommittedNote = tr("<p><b>Note:</b> You have uncommitted changes. If you want to push these changes to the remote repository, you need to commit them first.");
Chris@356 1016 }
Chris@356 1017
Chris@193 1018 if (ConfirmCommentDialog::confirm
Chris@126 1019 (this, tr("Confirm push"),
Chris@299 1020 tr("<qt><h3>Push to remote repository?</h3></qt>"),
Chris@356 1021 tr("<qt><p>You are about to push your commits to the remote repository at <code>%1</code>.</p>%2</qt>").arg(xmlEncode(m_remoteRepoPath)).arg(uncommittedNote),
Chris@194 1022 tr("Push"))) {
jtkorhonen@0 1023
Chris@126 1024 QStringList params;
Chris@284 1025 params << "push" << "--new-branch" << m_remoteRepoPath;
Chris@284 1026 m_runner->requestAction(HgAction(ACT_PUSH, m_workFolderPath, params));
Chris@126 1027 }
jtkorhonen@0 1028 }
jtkorhonen@0 1029
Chris@182 1030 QStringList MainWindow::listAllUpIpV4Addresses()
jtkorhonen@26 1031 {
Chris@182 1032 QStringList ret;
jtkorhonen@26 1033 QList<QNetworkInterface> ifaces = QNetworkInterface::allInterfaces();
jtkorhonen@26 1034
Chris@182 1035 for (int i = 0; i < ifaces.count(); i++) {
jtkorhonen@26 1036 QNetworkInterface iface = ifaces.at(i);
Chris@182 1037 if (iface.flags().testFlag(QNetworkInterface::IsUp)
Chris@182 1038 && !iface.flags().testFlag(QNetworkInterface::IsLoopBack)) {
Chris@182 1039 for (int j=0; j<iface.addressEntries().count(); j++) {
jtkorhonen@28 1040 QHostAddress tmp = iface.addressEntries().at(j).ip();
Chris@182 1041 if (QAbstractSocket::IPv4Protocol == tmp.protocol()) {
Chris@182 1042 ret.push_back(tmp.toString());
jtkorhonen@24 1043 }
jtkorhonen@24 1044 }
jtkorhonen@22 1045 }
jtkorhonen@17 1046 }
jtkorhonen@28 1047 return ret;
jtkorhonen@28 1048 }
jtkorhonen@17 1049
Chris@120 1050 void MainWindow::clearState()
Chris@120 1051 {
Chris@305 1052 DEBUG << "MainWindow::clearState" << endl;
Chris@284 1053 foreach (Changeset *cs, m_currentParents) delete cs;
Chris@284 1054 m_currentParents.clear();
Chris@284 1055 foreach (Changeset *cs, m_currentHeads) delete cs;
Chris@284 1056 m_currentHeads.clear();
Chris@284 1057 m_currentBranch = "";
Chris@284 1058 m_lastStatOutput = "";
Chris@284 1059 m_lastRevertedFiles.clear();
Chris@284 1060 m_mergeTargetRevision = "";
Chris@284 1061 m_mergeCommitComment = "";
Chris@284 1062 m_stateUnknown = true;
Chris@284 1063 m_needNewLog = true;
Chris@284 1064 if (m_fsWatcher) {
Chris@241 1065 delete m_fsWatcherGeneralTimer;
Chris@241 1066 m_fsWatcherGeneralTimer = 0;
Chris@241 1067 delete m_fsWatcherRestoreTimer;
Chris@241 1068 m_fsWatcherRestoreTimer = 0;
Chris@284 1069 delete m_fsWatcher;
Chris@284 1070 m_fsWatcher = 0;
Chris@199 1071 }
Chris@120 1072 }
jtkorhonen@17 1073
jtkorhonen@11 1074 void MainWindow::hgServe()
jtkorhonen@11 1075 {
Chris@109 1076 QStringList params;
Chris@109 1077 QString msg;
jtkorhonen@11 1078
Chris@182 1079 QStringList addrs = listAllUpIpV4Addresses();
Chris@182 1080
Chris@182 1081 if (addrs.empty()) {
Chris@182 1082 QMessageBox::critical
Chris@182 1083 (this, tr("Serve"), tr("Failed to identify an active IPv4 address"));
Chris@182 1084 return;
Chris@182 1085 }
Chris@182 1086
Chris@182 1087 //!!! should find available port as well
Chris@182 1088
Chris@182 1089 QTextStream ts(&msg);
Chris@182 1090 ts << QString("<qt><p>%1</p>")
Chris@182 1091 .arg(tr("Running temporary server at %n address(es):", "", addrs.size()));
Chris@182 1092 foreach (QString addr, addrs) {
Chris@182 1093 ts << QString("<pre>&nbsp;&nbsp;http://%1:8000</pre>").arg(xmlEncode(addr));
Chris@182 1094 }
Chris@182 1095 ts << tr("<p>Press Close to stop the server and return.</p>");
Chris@182 1096 ts.flush();
Chris@182 1097
Chris@109 1098 params << "serve";
jtkorhonen@11 1099
Chris@284 1100 m_runner->requestAction(HgAction(ACT_SERVE, m_workFolderPath, params));
Chris@109 1101
Chris@182 1102 QMessageBox::information(this, tr("Serve"), msg, QMessageBox::Close);
Chris@182 1103
Chris@284 1104 m_runner->killCurrentActions();
jtkorhonen@11 1105 }
jtkorhonen@11 1106
Chris@64 1107 void MainWindow::startupDialog()
Chris@64 1108 {
Chris@64 1109 StartupDialog *dlg = new StartupDialog(this);
Chris@284 1110 if (dlg->exec()) m_firstStart = false;
Chris@344 1111 else exit(0);
Chris@64 1112 }
jtkorhonen@11 1113
Chris@69 1114 void MainWindow::open()
Chris@69 1115 {
Chris@86 1116 bool done = false;
Chris@69 1117
Chris@86 1118 while (!done) {
Chris@69 1119
Chris@86 1120 MultiChoiceDialog *d = new MultiChoiceDialog
Chris@86 1121 (tr("Open Repository"),
Chris@86 1122 tr("<qt><big>What would you like to open?</big></qt>"),
Chris@86 1123 this);
Chris@69 1124
Chris@345 1125 d->addChoice("remote",
Chris@345 1126 tr("<qt><center><img src=\":images/browser-64.png\"><br>Remote repository</center></qt>"),
Chris@345 1127 tr("Open a remote Mercurial repository, by cloning from its URL into a local folder."),
Chris@345 1128 MultiChoiceDialog::UrlToDirectoryArg);
Chris@345 1129
Chris@339 1130 d->addChoice("local",
Chris@339 1131 tr("<qt><center><img src=\":images/hglogo-64.png\"><br>Local repository</center></qt>"),
Chris@339 1132 tr("Open an existing local Mercurial repository."),
Chris@339 1133 MultiChoiceDialog::DirectoryArg);
Chris@339 1134
Chris@339 1135 d->addChoice("init",
Chris@339 1136 tr("<qt><center><img src=\":images/hdd_unmount-64.png\"><br>File folder</center></qt>"),
Chris@339 1137 tr("Open a local folder, by creating a Mercurial repository in it."),
Chris@339 1138 MultiChoiceDialog::DirectoryArg);
Chris@339 1139
Chris@248 1140 QSettings settings;
Chris@248 1141 settings.beginGroup("General");
Chris@359 1142 QString lastChoice = settings.value("lastopentype", "remote").toString();
Chris@248 1143 if (lastChoice != "local" &&
Chris@248 1144 lastChoice != "remote" &&
Chris@248 1145 lastChoice != "init") {
Chris@359 1146 lastChoice = "remote";
Chris@248 1147 }
Chris@248 1148
Chris@248 1149 d->setCurrentChoice(lastChoice);
Chris@86 1150
Chris@86 1151 if (d->exec() == QDialog::Accepted) {
Chris@86 1152
Chris@86 1153 QString choice = d->getCurrentChoice();
Chris@248 1154 settings.setValue("lastopentype", choice);
Chris@248 1155
Chris@86 1156 QString arg = d->getArgument().trimmed();
Chris@86 1157
Chris@86 1158 bool result = false;
Chris@86 1159
Chris@86 1160 if (choice == "local") {
Chris@86 1161 result = openLocal(arg);
Chris@86 1162 } else if (choice == "remote") {
Chris@86 1163 result = openRemote(arg, d->getAdditionalArgument().trimmed());
Chris@86 1164 } else if (choice == "init") {
Chris@86 1165 result = openInit(arg);
Chris@86 1166 }
Chris@86 1167
Chris@86 1168 if (result) {
Chris@86 1169 enableDisableActions();
Chris@120 1170 clearState();
Chris@109 1171 hgQueryPaths();
Chris@91 1172 done = true;
Chris@91 1173 }
Chris@86 1174
Chris@86 1175 } else {
Chris@86 1176
Chris@86 1177 // cancelled
Chris@86 1178 done = true;
Chris@69 1179 }
Chris@79 1180
Chris@86 1181 delete d;
Chris@69 1182 }
Chris@69 1183 }
Chris@69 1184
Chris@359 1185 void MainWindow::recentMenuActivated()
Chris@359 1186 {
Chris@359 1187 QAction *a = qobject_cast<QAction *>(sender());
Chris@359 1188 if (!a) return;
Chris@359 1189 QString local = a->text();
Chris@359 1190 open(local);
Chris@359 1191 }
Chris@359 1192
Chris@182 1193 void MainWindow::changeRemoteRepo()
Chris@182 1194 {
Chris@363 1195 changeRemoteRepo(false);
Chris@363 1196 }
Chris@363 1197
Chris@363 1198 void MainWindow::changeRemoteRepo(bool initial)
Chris@363 1199 {
Chris@183 1200 // This will involve rewriting the local .hgrc
Chris@183 1201
Chris@284 1202 QDir hgDir(m_workFolderPath + "/.hg");
Chris@184 1203 if (!hgDir.exists()) {
Chris@184 1204 //!!! visible error!
Chris@184 1205 return;
Chris@184 1206 }
Chris@184 1207
Chris@284 1208 QFileInfo hgrc(m_workFolderPath + "/.hg/hgrc");
Chris@184 1209 if (hgrc.exists() && !hgrc.isWritable()) {
Chris@183 1210 //!!! visible error!
Chris@183 1211 return;
Chris@183 1212 }
Chris@183 1213
Chris@183 1214 MultiChoiceDialog *d = new MultiChoiceDialog
Chris@363 1215 (tr("Set Remote Location"),
Chris@363 1216 tr("<qt><big>Set the remote location</big></qt>"),
Chris@183 1217 this);
Chris@183 1218
Chris@363 1219 QString explanation;
Chris@363 1220 if (initial) {
Chris@363 1221 explanation = tr("Provide a URL to use for push and pull actions from the current local repository.<br>This will be the default for subsequent pushes and pulls.<br>You can change it using &ldquo;Set Remote Location&rdquo; on the File menu.");
Chris@363 1222 } else {
Chris@363 1223 explanation = tr("Provide a new URL to use for push and pull actions from the current local repository.");
Chris@363 1224 }
Chris@363 1225
Chris@183 1226 d->addChoice("remote",
Chris@183 1227 tr("<qt><center><img src=\":images/browser-64.png\"><br>Remote repository</center></qt>"),
Chris@363 1228 explanation,
Chris@183 1229 MultiChoiceDialog::UrlArg);
Chris@183 1230
Chris@183 1231 if (d->exec() == QDialog::Accepted) {
Chris@210 1232
Chris@210 1233 // New block to ensure QSettings is deleted before
Chris@210 1234 // hgQueryPaths called. NB use of absoluteFilePath instead of
Chris@210 1235 // canonicalFilePath, which would fail if the file did not yet
Chris@210 1236 // exist
Chris@210 1237
Chris@210 1238 {
Chris@210 1239 QSettings s(hgrc.absoluteFilePath(), QSettings::IniFormat);
Chris@210 1240 s.beginGroup("paths");
Chris@210 1241 s.setValue("default", d->getArgument());
Chris@210 1242 }
Chris@210 1243
Chris@284 1244 m_stateUnknown = true;
Chris@183 1245 hgQueryPaths();
Chris@183 1246 }
Chris@183 1247
Chris@183 1248 delete d;
Chris@182 1249 }
Chris@182 1250
Chris@145 1251 void MainWindow::open(QString local)
Chris@145 1252 {
Chris@145 1253 if (openLocal(local)) {
Chris@145 1254 enableDisableActions();
Chris@145 1255 clearState();
Chris@145 1256 hgQueryPaths();
Chris@145 1257 }
Chris@145 1258 }
Chris@145 1259
Chris@79 1260 bool MainWindow::complainAboutFilePath(QString arg)
Chris@79 1261 {
Chris@79 1262 QMessageBox::critical
Chris@79 1263 (this, tr("File chosen"),
Chris@84 1264 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 1265 return false;
Chris@79 1266 }
Chris@79 1267
Chris@248 1268 bool MainWindow::askAboutUnknownFolder(QString arg)
Chris@248 1269 {
Chris@248 1270 bool result = (QMessageBox::question
Chris@248 1271 (this, tr("Path does not exist"),
Chris@248 1272 tr("<qt><b>Path does not exist: create it?</b><br><br>You asked to open a remote repository by cloning it to \"%1\". This folder does not exist, and neither does its parent.<br><br>Would you like to create the parent folder as well?</qt>").arg(xmlEncode(arg)),
Chris@248 1273 QMessageBox::Ok | QMessageBox::Cancel,
Chris@248 1274 QMessageBox::Cancel)
Chris@248 1275 == QMessageBox::Ok);
Chris@248 1276 if (result) {
Chris@248 1277 QDir dir(arg);
Chris@248 1278 dir.cdUp();
Chris@248 1279 if (!dir.mkpath(dir.absolutePath())) {
Chris@248 1280 QMessageBox::critical
Chris@248 1281 (this, tr("Failed to create folder"),
Chris@248 1282 tr("<qt><b>Failed to create folder</b><br><br>Sorry, the path for the parent folder \"%1\" could not be created.</qt>").arg(dir.absolutePath()));
Chris@248 1283 return false;
Chris@248 1284 }
Chris@248 1285 return true;
Chris@248 1286 }
Chris@248 1287 return false;
Chris@248 1288 }
Chris@248 1289
Chris@79 1290 bool MainWindow::complainAboutUnknownFolder(QString arg)
Chris@79 1291 {
Chris@79 1292 QMessageBox::critical
Chris@79 1293 (this, tr("Folder does not exist"),
Chris@84 1294 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 1295 return false;
Chris@84 1296 }
Chris@84 1297
Chris@84 1298 bool MainWindow::complainAboutInitInRepo(QString arg)
Chris@84 1299 {
Chris@84 1300 QMessageBox::critical
Chris@84 1301 (this, tr("Path is in existing repository"),
Chris@84 1302 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 1303 return false;
Chris@84 1304 }
Chris@84 1305
Chris@84 1306 bool MainWindow::complainAboutInitFile(QString arg)
Chris@84 1307 {
Chris@84 1308 QMessageBox::critical
Chris@84 1309 (this, tr("Path is a file"),
Chris@84 1310 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 1311 return false;
Chris@84 1312 }
Chris@84 1313
Chris@84 1314 bool MainWindow::complainAboutCloneToExisting(QString arg)
Chris@84 1315 {
Chris@84 1316 QMessageBox::critical
Chris@84 1317 (this, tr("Path is in existing repository"),
Chris@237 1318 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 different folder name for the local repository.</qt>").arg(xmlEncode(arg)));
Chris@84 1319 return false;
Chris@84 1320 }
Chris@84 1321
Chris@84 1322 bool MainWindow::complainAboutCloneToFile(QString arg)
Chris@84 1323 {
Chris@84 1324 QMessageBox::critical
Chris@84 1325 (this, tr("Path is a file"),
Chris@84 1326 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 1327 return false;
Chris@84 1328 }
Chris@84 1329
Chris@237 1330 QString MainWindow::complainAboutCloneToExistingFolder(QString arg, QString remote)
Chris@84 1331 {
Chris@348 1332 // If the directory "arg" exists but is empty, then we accept it.
Chris@348 1333
Chris@348 1334 // If the directory "arg" exists and is non-empty, but "arg" plus
Chris@348 1335 // the last path component of "remote" does not exist, then offer
Chris@348 1336 // the latter as an alternative path.
Chris@237 1337
Chris@237 1338 QString offer;
Chris@237 1339
Chris@237 1340 QDir d(arg);
Chris@348 1341
Chris@237 1342 if (d.exists()) {
Chris@348 1343
Chris@348 1344 if (d.entryList(QDir::Dirs | QDir::Files |
Chris@348 1345 QDir::NoDotAndDotDot |
Chris@348 1346 QDir::Hidden | QDir::System).empty()) {
Chris@348 1347 // directory is empty; accept it
Chris@348 1348 return arg;
Chris@348 1349 }
Chris@348 1350
Chris@237 1351 if (QRegExp("^\\w+://").indexIn(remote) >= 0) {
Chris@237 1352 QString rpath = QUrl(remote).path();
Chris@237 1353 if (rpath != "") {
Chris@237 1354 rpath = QDir(rpath).dirName();
Chris@237 1355 if (rpath != "" && !d.exists(rpath)) {
Chris@237 1356 offer = d.filePath(rpath);
Chris@237 1357 }
Chris@237 1358 }
Chris@237 1359 }
Chris@237 1360 }
Chris@237 1361
Chris@237 1362 if (offer != "") {
Chris@237 1363 bool result = (QMessageBox::question
Chris@237 1364 (this, tr("Folder exists"),
Chris@237 1365 tr("<qt><b>Local folder already exists</b><br><br>You asked to open a remote repository by cloning it to \"%1\", but this folder already exists and so cannot be cloned to.<br><br>Would you like to create the new folder \"%2\" instead?</qt>")
Chris@237 1366 .arg(xmlEncode(arg)).arg(xmlEncode(offer)),
Chris@237 1367 QMessageBox::Ok | QMessageBox::Cancel,
Chris@237 1368 QMessageBox::Cancel)
Chris@237 1369 == QMessageBox::Ok);
Chris@237 1370 if (result) return offer;
Chris@237 1371 else return "";
Chris@237 1372 }
Chris@237 1373
Chris@84 1374 QMessageBox::critical
Chris@84 1375 (this, tr("Folder exists"),
Chris@237 1376 tr("<qt><b>Local folder already exists</b><br><br>You asked to open a remote repository by cloning it to \"%1\", but this file or folder already exists and so cannot be cloned to.<br>Please provide a different folder name for the local repository.</qt>").arg(xmlEncode(arg)));
Chris@237 1377 return "";
Chris@79 1378 }
Chris@79 1379
Chris@79 1380 bool MainWindow::askToOpenParentRepo(QString arg, QString parent)
Chris@79 1381 {
Chris@79 1382 return (QMessageBox::question
Chris@84 1383 (this, tr("Path is inside a repository"),
Chris@86 1384 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 1385 .arg(xmlEncode(arg)).arg(xmlEncode(parent)),
Chris@79 1386 QMessageBox::Ok | QMessageBox::Cancel,
Chris@79 1387 QMessageBox::Ok)
Chris@79 1388 == QMessageBox::Ok);
Chris@79 1389 }
Chris@79 1390
Chris@79 1391 bool MainWindow::askToInitExisting(QString arg)
Chris@79 1392 {
Chris@79 1393 return (QMessageBox::question
Chris@84 1394 (this, tr("Folder has no repository"),
Chris@359 1395 tr("<qt><b>Initialise a repository here?</b><br><br>You asked to open \"%1\".<br>This folder is not a Mercurial working copy.<br><br>Would you like to initialise a repository here?</qt>")
Chris@79 1396 .arg(xmlEncode(arg)),
Chris@79 1397 QMessageBox::Ok | QMessageBox::Cancel,
Chris@79 1398 QMessageBox::Ok)
Chris@79 1399 == QMessageBox::Ok);
Chris@79 1400 }
Chris@79 1401
Chris@79 1402 bool MainWindow::askToInitNew(QString arg)
Chris@79 1403 {
Chris@79 1404 return (QMessageBox::question
Chris@84 1405 (this, tr("Folder does not exist"),
Chris@84 1406 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 1407 .arg(xmlEncode(arg)),
Chris@84 1408 QMessageBox::Ok | QMessageBox::Cancel,
Chris@84 1409 QMessageBox::Ok)
Chris@84 1410 == QMessageBox::Ok);
Chris@84 1411 }
Chris@84 1412
Chris@84 1413 bool MainWindow::askToOpenInsteadOfInit(QString arg)
Chris@84 1414 {
Chris@84 1415 return (QMessageBox::question
Chris@84 1416 (this, tr("Repository exists"),
Chris@84 1417 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 1418 .arg(xmlEncode(arg)),
Chris@79 1419 QMessageBox::Ok | QMessageBox::Cancel,
Chris@79 1420 QMessageBox::Ok)
Chris@79 1421 == QMessageBox::Ok);
Chris@79 1422 }
Chris@79 1423
Chris@79 1424 bool MainWindow::openLocal(QString local)
Chris@79 1425 {
Chris@79 1426 DEBUG << "open " << local << endl;
Chris@79 1427
Chris@79 1428 FolderStatus status = getFolderStatus(local);
Chris@79 1429 QString containing = getContainingRepoFolder(local);
Chris@79 1430
Chris@79 1431 switch (status) {
Chris@79 1432
Chris@79 1433 case FolderHasRepo:
Chris@79 1434 // fine
Chris@79 1435 break;
Chris@79 1436
Chris@79 1437 case FolderExists:
Chris@79 1438 if (containing != "") {
Chris@79 1439 if (!askToOpenParentRepo(local, containing)) return false;
Chris@84 1440 local = containing;
Chris@79 1441 } else {
Chris@86 1442 //!!! No -- this is likely to happen far more by accident
Chris@86 1443 // than because the user actually wanted to init something.
Chris@86 1444 // Don't ask, just politely reject.
Chris@79 1445 if (!askToInitExisting(local)) return false;
Chris@79 1446 return openInit(local);
Chris@79 1447 }
Chris@79 1448 break;
Chris@79 1449
Chris@79 1450 case FolderParentExists:
Chris@79 1451 if (containing != "") {
Chris@79 1452 if (!askToOpenParentRepo(local, containing)) return false;
Chris@84 1453 local = containing;
Chris@79 1454 } else {
Chris@79 1455 if (!askToInitNew(local)) return false;
Chris@79 1456 return openInit(local);
Chris@79 1457 }
Chris@79 1458 break;
Chris@79 1459
Chris@79 1460 case FolderUnknown:
Chris@84 1461 if (containing != "") {
Chris@84 1462 if (!askToOpenParentRepo(local, containing)) return false;
Chris@84 1463 local = containing;
Chris@84 1464 } else {
Chris@84 1465 return complainAboutUnknownFolder(local);
Chris@84 1466 }
Chris@84 1467 break;
Chris@79 1468
Chris@79 1469 case FolderIsFile:
Chris@79 1470 return complainAboutFilePath(local);
Chris@79 1471 }
Chris@79 1472
Chris@284 1473 m_workFolderPath = local;
Chris@284 1474 m_remoteRepoPath = "";
Chris@79 1475 return true;
Chris@79 1476 }
Chris@79 1477
Chris@79 1478 bool MainWindow::openRemote(QString remote, QString local)
Chris@79 1479 {
Chris@79 1480 DEBUG << "clone " << remote << " to " << local << endl;
Chris@84 1481
Chris@84 1482 FolderStatus status = getFolderStatus(local);
Chris@84 1483 QString containing = getContainingRepoFolder(local);
Chris@84 1484
Chris@84 1485 DEBUG << "status = " << status << ", containing = " << containing << endl;
Chris@84 1486
Chris@84 1487 if (status == FolderHasRepo || containing != "") {
Chris@84 1488 return complainAboutCloneToExisting(local);
Chris@84 1489 }
Chris@84 1490
Chris@84 1491 if (status == FolderIsFile) {
Chris@84 1492 return complainAboutCloneToFile(local);
Chris@84 1493 }
Chris@84 1494
Chris@84 1495 if (status == FolderUnknown) {
Chris@248 1496 if (!askAboutUnknownFolder(local)) {
Chris@248 1497 return false;
Chris@248 1498 }
Chris@84 1499 }
Chris@84 1500
Chris@84 1501 if (status == FolderExists) {
Chris@237 1502 local = complainAboutCloneToExistingFolder(local, remote);
Chris@237 1503 if (local == "") return false;
Chris@84 1504 }
Chris@84 1505
Chris@284 1506 m_workFolderPath = local;
Chris@284 1507 m_remoteRepoPath = remote;
Chris@84 1508 hgCloneFromRemote();
Chris@84 1509
Chris@79 1510 return true;
Chris@79 1511 }
Chris@79 1512
Chris@84 1513 bool MainWindow::openInit(QString local)
Chris@79 1514 {
Chris@84 1515 DEBUG << "openInit " << local << endl;
Chris@84 1516
Chris@84 1517 FolderStatus status = getFolderStatus(local);
Chris@84 1518 QString containing = getContainingRepoFolder(local);
Chris@84 1519
Chris@84 1520 DEBUG << "status = " << status << ", containing = " << containing << endl;
Chris@84 1521
Chris@84 1522 if (status == FolderHasRepo) {
Chris@84 1523 if (!askToOpenInsteadOfInit(local)) return false;
Chris@84 1524 }
Chris@84 1525
Chris@84 1526 if (containing != "") {
Chris@84 1527 return complainAboutInitInRepo(local);
Chris@84 1528 }
Chris@84 1529
Chris@84 1530 if (status == FolderIsFile) {
Chris@84 1531 return complainAboutInitFile(local);
Chris@84 1532 }
Chris@84 1533
Chris@84 1534 if (status == FolderUnknown) {
Chris@84 1535 return complainAboutUnknownFolder(local);
Chris@84 1536 }
Chris@84 1537
Chris@284 1538 m_workFolderPath = local;
Chris@284 1539 m_remoteRepoPath = "";
Chris@84 1540 hgInit();
Chris@79 1541 return true;
Chris@79 1542 }
Chris@79 1543
jtkorhonen@0 1544 void MainWindow::settings()
jtkorhonen@0 1545 {
jtkorhonen@0 1546 SettingsDialog *settingsDlg = new SettingsDialog(this);
jtkorhonen@0 1547 settingsDlg->exec();
Chris@230 1548
Chris@230 1549 if (settingsDlg->presentationChanged()) {
Chris@284 1550 m_hgTabs->updateFileStates();
Chris@230 1551 updateToolBarStyle();
Chris@273 1552 hgRefresh();
Chris@230 1553 }
jtkorhonen@0 1554 }
jtkorhonen@0 1555
Chris@90 1556 void MainWindow::updateFileSystemWatcher()
Chris@90 1557 {
Chris@199 1558 bool justCreated = false;
Chris@284 1559 if (!m_fsWatcher) {
Chris@284 1560 m_fsWatcher = new QFileSystemWatcher();
Chris@199 1561 justCreated = true;
Chris@199 1562 }
Chris@199 1563
Chris@199 1564 // QFileSystemWatcher will refuse to add a file or directory to
Chris@199 1565 // its watch list that it is already watching -- fine, that's what
Chris@199 1566 // we want -- but it prints a warning when this happens, which is
Chris@199 1567 // annoying because it would be the normal case for us. So we'll
Chris@199 1568 // check for duplicates ourselves.
Chris@199 1569 QSet<QString> alreadyWatched;
Chris@284 1570 QStringList dl(m_fsWatcher->directories());
Chris@199 1571 foreach (QString d, dl) alreadyWatched.insert(d);
Chris@199 1572
Chris@90 1573 std::deque<QString> pending;
Chris@284 1574 pending.push_back(m_workFolderPath);
Chris@199 1575
Chris@90 1576 while (!pending.empty()) {
Chris@199 1577
Chris@90 1578 QString path = pending.front();
Chris@90 1579 pending.pop_front();
Chris@199 1580 if (!alreadyWatched.contains(path)) {
Chris@284 1581 m_fsWatcher->addPath(path);
Chris@199 1582 DEBUG << "Added to file system watcher: " << path << endl;
Chris@199 1583 }
Chris@199 1584
Chris@90 1585 QDir d(path);
Chris@90 1586 if (d.exists()) {
Chris@199 1587 d.setFilter(QDir::Dirs | QDir::NoDotAndDotDot |
Chris@199 1588 QDir::Readable | QDir::NoSymLinks);
Chris@90 1589 foreach (QString entry, d.entryList()) {
Chris@199 1590 if (entry.startsWith('.')) continue;
Chris@90 1591 QString entryPath = d.absoluteFilePath(entry);
Chris@90 1592 pending.push_back(entryPath);
Chris@90 1593 }
Chris@90 1594 }
Chris@90 1595 }
Chris@199 1596
Chris@238 1597 // The general timer isn't really related to the fs watcher
Chris@238 1598 // object, it just does something similar -- every now and then we
Chris@238 1599 // do a refresh just to update the history dates etc
Chris@238 1600
Chris@238 1601 m_fsWatcherGeneralTimer = new QTimer(this);
Chris@238 1602 connect(m_fsWatcherGeneralTimer, SIGNAL(timeout()),
Chris@238 1603 this, SLOT(checkFilesystem()));
Chris@238 1604 m_fsWatcherGeneralTimer->setInterval(30 * 60 * 1000); // half an hour
Chris@238 1605 m_fsWatcherGeneralTimer->start();
Chris@238 1606
Chris@199 1607 if (justCreated) {
Chris@284 1608 connect(m_fsWatcher, SIGNAL(directoryChanged(QString)),
Chris@199 1609 this, SLOT(fsDirectoryChanged(QString)));
Chris@284 1610 connect(m_fsWatcher, SIGNAL(fileChanged(QString)),
Chris@199 1611 this, SLOT(fsFileChanged(QString)));
Chris@199 1612 }
Chris@90 1613 }
Chris@90 1614
Chris@238 1615 void MainWindow::suspendFileSystemWatcher()
Chris@238 1616 {
Chris@238 1617 DEBUG << "MainWindow::suspendFileSystemWatcher" << endl;
Chris@284 1618 if (m_fsWatcher) {
Chris@241 1619 m_fsWatcherSuspended = true;
Chris@241 1620 if (m_fsWatcherRestoreTimer) {
Chris@241 1621 delete m_fsWatcherRestoreTimer;
Chris@241 1622 m_fsWatcherRestoreTimer = 0;
Chris@241 1623 }
Chris@238 1624 m_fsWatcherGeneralTimer->stop();
Chris@238 1625 }
Chris@238 1626 }
Chris@238 1627
Chris@238 1628 void MainWindow::restoreFileSystemWatcher()
Chris@238 1629 {
Chris@238 1630 DEBUG << "MainWindow::restoreFileSystemWatcher" << endl;
Chris@238 1631 if (m_fsWatcherRestoreTimer) delete m_fsWatcherRestoreTimer;
Chris@238 1632
Chris@238 1633 // The restore timer is used to leave a polite interval between
Chris@238 1634 // being asked to restore the watcher and actually doing so. It's
Chris@238 1635 // a single shot timer each time it's used, but we don't use
Chris@238 1636 // QTimer::singleShot because we want to stop the previous one if
Chris@238 1637 // it's running (via deleting it)
Chris@238 1638
Chris@238 1639 m_fsWatcherRestoreTimer = new QTimer(this);
Chris@238 1640 connect(m_fsWatcherRestoreTimer, SIGNAL(timeout()),
Chris@238 1641 this, SLOT(actuallyRestoreFileSystemWatcher()));
Chris@238 1642 m_fsWatcherRestoreTimer->setInterval(1000);
Chris@238 1643 m_fsWatcherRestoreTimer->setSingleShot(true);
Chris@238 1644 m_fsWatcherRestoreTimer->start();
Chris@238 1645 }
Chris@238 1646
Chris@238 1647 void MainWindow::actuallyRestoreFileSystemWatcher()
Chris@238 1648 {
Chris@238 1649 DEBUG << "MainWindow::actuallyRestoreFileSystemWatcher" << endl;
Chris@284 1650 if (m_fsWatcher) {
Chris@241 1651 m_fsWatcherSuspended = false;
Chris@241 1652 m_fsWatcherGeneralTimer->start();
Chris@241 1653 }
Chris@238 1654 }
Chris@238 1655
Chris@238 1656 void MainWindow::checkFilesystem()
Chris@238 1657 {
Chris@238 1658 DEBUG << "MainWindow::checkFilesystem" << endl;
Chris@273 1659 hgRefresh();
Chris@238 1660 }
Chris@238 1661
Chris@122 1662 void MainWindow::fsDirectoryChanged(QString d)
Chris@90 1663 {
Chris@122 1664 DEBUG << "MainWindow::fsDirectoryChanged " << d << endl;
Chris@241 1665 if (!m_fsWatcherSuspended) {
Chris@241 1666 hgStat();
Chris@241 1667 }
Chris@90 1668 }
Chris@90 1669
Chris@122 1670 void MainWindow::fsFileChanged(QString f)
Chris@90 1671 {
Chris@122 1672 DEBUG << "MainWindow::fsFileChanged " << f << endl;
Chris@241 1673 if (!m_fsWatcherSuspended) {
Chris@241 1674 hgStat();
Chris@241 1675 }
Chris@90 1676 }
Chris@90 1677
Chris@275 1678 QString MainWindow::format1(QString head)
Chris@275 1679 {
Chris@275 1680 return QString("<qt><h3>%1</h3></qt>").arg(head);
Chris@275 1681 }
Chris@275 1682
Chris@125 1683 QString MainWindow::format3(QString head, QString intro, QString code)
Chris@125 1684 {
Chris@196 1685 code = xmlEncode(code).replace("\n", "<br>")
Chris@196 1686 #ifndef Q_OS_WIN32
Chris@196 1687 // The hard hyphen comes out funny on Windows
Chris@196 1688 .replace("-", "&#8209;")
Chris@196 1689 #endif
Chris@196 1690 .replace(" ", "&nbsp;");
Chris@125 1691 if (intro == "") {
Chris@168 1692 return QString("<qt><h3>%1</h3><p><code>%2</code></p>")
Chris@168 1693 .arg(head).arg(code);
Chris@126 1694 } else if (code == "") {
Chris@126 1695 return QString("<qt><h3>%1</h3><p>%2</p>")
Chris@126 1696 .arg(head).arg(intro);
Chris@125 1697 } else {
Chris@168 1698 return QString("<qt><h3>%1</h3><p>%2</p><p><code>%3</code></p>")
Chris@168 1699 .arg(head).arg(intro).arg(code);
Chris@125 1700 }
Chris@125 1701 }
Chris@125 1702
Chris@120 1703 void MainWindow::showIncoming(QString output)
Chris@120 1704 {
Chris@284 1705 m_runner->hide();
Chris@125 1706 IncomingDialog *d = new IncomingDialog(this, output);
Chris@125 1707 d->exec();
Chris@125 1708 delete d;
Chris@125 1709 }
Chris@125 1710
Chris@125 1711 int MainWindow::extractChangeCount(QString text)
Chris@125 1712 {
Chris@125 1713 QRegExp re("added (\\d+) ch\\w+ with (\\d+) ch\\w+ to (\\d+) f\\w+");
Chris@125 1714 if (re.indexIn(text) >= 0) {
Chris@125 1715 return re.cap(1).toInt();
Chris@125 1716 } else if (text.contains("no changes")) {
Chris@125 1717 return 0;
Chris@125 1718 } else {
Chris@125 1719 return -1; // unknown
Chris@125 1720 }
Chris@120 1721 }
Chris@120 1722
Chris@120 1723 void MainWindow::showPushResult(QString output)
Chris@120 1724 {
Chris@291 1725 QString head;
Chris@125 1726 QString report;
Chris@125 1727 int n = extractChangeCount(output);
Chris@125 1728 if (n > 0) {
Chris@291 1729 head = tr("Pushed %n changeset(s)", "", n);
Chris@300 1730 report = tr("<qt>Successfully pushed to the remote repository at <code>%1</code>.</qt>").arg(xmlEncode(m_remoteRepoPath));
Chris@125 1731 } else if (n == 0) {
Chris@291 1732 head = tr("No changes to push");
Chris@291 1733 report = tr("The remote repository already contains all changes that have been committed locally.");
Chris@294 1734 if (m_hgTabs->canCommit()) {
Chris@291 1735 report = tr("%1<p>You do have some uncommitted changes. If you wish to push those to the remote repository, commit them locally first.").arg(report);
Chris@291 1736 }
Chris@125 1737 } else {
Chris@291 1738 head = tr("Push complete");
Chris@125 1739 }
Chris@284 1740 m_runner->hide();
Chris@291 1741
Chris@291 1742 MoreInformationDialog::information(this, tr("Push complete"),
Chris@291 1743 head, report, output);
Chris@120 1744 }
Chris@120 1745
Chris@120 1746 void MainWindow::showPullResult(QString output)
Chris@120 1747 {
Chris@291 1748 QString head;
Chris@125 1749 QString report;
Chris@125 1750 int n = extractChangeCount(output);
Chris@125 1751 if (n > 0) {
Chris@291 1752 head = tr("Pulled %n changeset(s)", "", n);
Chris@322 1753 report = tr("New changes will be highlighted in yellow in the history.");
Chris@125 1754 } else if (n == 0) {
Chris@291 1755 head = tr("No changes to pull");
Chris@291 1756 report = tr("Your local repository already contains all changes found in the remote repository.");
Chris@125 1757 } else {
Chris@291 1758 head = tr("Pull complete");
Chris@125 1759 }
Chris@284 1760 m_runner->hide();
Chris@275 1761
Chris@275 1762 MoreInformationDialog::information(this, tr("Pull complete"),
Chris@291 1763 head, report, output);
Chris@120 1764 }
Chris@120 1765
Chris@174 1766 void MainWindow::reportNewRemoteHeads(QString output)
Chris@174 1767 {
Chris@174 1768 bool headsAreLocal = false;
Chris@174 1769
Chris@284 1770 if (m_currentParents.size() == 1) {
Chris@284 1771 int m_currentBranchHeads = 0;
Chris@174 1772 bool parentIsHead = false;
Chris@284 1773 Changeset *parent = m_currentParents[0];
Chris@284 1774 foreach (Changeset *head, m_currentHeads) {
Chris@284 1775 if (head->isOnBranch(m_currentBranch)) {
Chris@284 1776 ++m_currentBranchHeads;
Chris@174 1777 }
Chris@174 1778 if (parent->id() == head->id()) {
Chris@174 1779 parentIsHead = true;
Chris@174 1780 }
Chris@174 1781 }
Chris@284 1782 if (m_currentBranchHeads == 2 && parentIsHead) {
Chris@174 1783 headsAreLocal = true;
Chris@174 1784 }
Chris@174 1785 }
Chris@174 1786
Chris@174 1787 if (headsAreLocal) {
Chris@291 1788 MoreInformationDialog::warning
Chris@291 1789 (this,
Chris@291 1790 tr("Push failed"),
Chris@291 1791 tr("Push failed"),
Chris@291 1792 tr("Your local repository could not be pushed to the remote repository.<br><br>You may need to merge the changes locally first."),
Chris@291 1793 output);
Chris@318 1794 } else if (m_hgTabs->canCommit() && m_currentParents.size() > 1) {
Chris@318 1795 MoreInformationDialog::warning
Chris@318 1796 (this,
Chris@318 1797 tr("Push failed"),
Chris@318 1798 tr("Push failed"),
Chris@318 1799 tr("Your local repository could not be pushed to the remote repository.<br><br>You have an uncommitted merge in your local folder. You probably need to commit it before you push."),
Chris@318 1800 output);
Chris@174 1801 } else {
Chris@291 1802 MoreInformationDialog::warning
Chris@291 1803 (this,
Chris@291 1804 tr("Push failed"),
Chris@291 1805 tr("Push failed"),
Chris@291 1806 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."),
Chris@291 1807 output);
Chris@174 1808 }
Chris@174 1809 }
Chris@174 1810
Chris@353 1811 void MainWindow::reportAuthFailed(QString output)
Chris@353 1812 {
Chris@353 1813 MoreInformationDialog::warning
Chris@353 1814 (this,
Chris@353 1815 tr("Authorization failed"),
Chris@353 1816 tr("Authorization failed"),
Chris@353 1817 tr("You may have entered an incorrect user name or password, or the remote URL may be wrong.<br><br>Or you may lack the necessary permissions on the remote repository.<br><br>Check with the administrator of your remote repository if necessary."),
Chris@353 1818 output);
Chris@353 1819 }
Chris@353 1820
Chris@238 1821 void MainWindow::commandStarting(HgAction action)
Chris@238 1822 {
Chris@238 1823 // Annoyingly, hg stat actually modifies the working directory --
Chris@238 1824 // it creates files called hg-checklink and hg-checkexec to test
Chris@238 1825 // properties of the filesystem. For safety's sake, suspend the
Chris@238 1826 // fs watcher while running commands, and restore it shortly after
Chris@238 1827 // a command has finished.
Chris@238 1828
Chris@246 1829 if (action.action == ACT_STAT) {
Chris@246 1830 suspendFileSystemWatcher();
Chris@246 1831 }
Chris@238 1832 }
Chris@238 1833
Chris@143 1834 void MainWindow::commandFailed(HgAction action, QString output)
Chris@62 1835 {
Chris@62 1836 DEBUG << "MainWindow::commandFailed" << endl;
Chris@238 1837 restoreFileSystemWatcher();
Chris@74 1838
Chris@210 1839 QString setstr;
Chris@210 1840 #ifdef Q_OS_MAC
Chris@210 1841 setstr = tr("Preferences");
Chris@210 1842 #else
Chris@210 1843 setstr = tr("Settings");
Chris@210 1844 #endif
Chris@210 1845
Chris@353 1846 // Some commands we just have to ignore bad return values from,
Chris@353 1847 // and some output gets special treatment.
Chris@353 1848
Chris@353 1849 // Note our fallback case should always be to report a
Chris@353 1850 // non-specific error and show the text -- in case output scraping
Chris@353 1851 // fails (as it surely will). Note also that we must force the
Chris@353 1852 // locale in order to ensure the output is scrapable; this happens
Chris@353 1853 // in HgRunner and may break some system encodings.
Chris@113 1854
Chris@113 1855 switch(action.action) {
Chris@113 1856 case ACT_NONE:
Chris@113 1857 // uh huh
Chris@113 1858 return;
Chris@175 1859 case ACT_TEST_HG:
Chris@291 1860 MoreInformationDialog::warning
Chris@291 1861 (this,
Chris@291 1862 tr("Failed to run Mercurial"),
Chris@291 1863 tr("Failed to run Mercurial"),
Chris@291 1864 tr("The Mercurial program either could not be found or failed to run.<br>Check that the Mercurial program path is correct in %1.").arg(setstr),
Chris@291 1865 output);
Chris@200 1866 settings();
Chris@200 1867 return;
Chris@200 1868 case ACT_TEST_HG_EXT:
Chris@309 1869 MoreInformationDialog::warning
Chris@291 1870 (this,
Chris@291 1871 tr("Failed to run Mercurial"),
Chris@291 1872 tr("Failed to run Mercurial with extension enabled"),
Chris@310 1873 tr("The Mercurial program failed to run with the EasyMercurial interaction extension enabled.<br>This may indicate an installation problem.<br><br>You may be able to continue working if you switch off &ldquo;Use EasyHg Mercurial Extension&rdquo; in %1. Note that remote repositories that require authentication might not work if you do this.").arg(setstr),
Chris@291 1874 output);
Chris@175 1875 settings();
Chris@175 1876 return;
Chris@252 1877 case ACT_CLONEFROMREMOTE:
Chris@252 1878 // if clone fails, we have no repo
Chris@284 1879 m_workFolderPath = "";
Chris@252 1880 enableDisableActions();
Chris@330 1881 break; // go on to default report
Chris@113 1882 case ACT_INCOMING:
Chris@353 1883 if (output.contains("authorization failed")) {
Chris@353 1884 reportAuthFailed(output);
Chris@353 1885 return;
Chris@358 1886 } else if (output.contains("entry cancelled")) {
Chris@358 1887 // ignore this, user cancelled username or password dialog
Chris@358 1888 return;
Chris@353 1889 } else {
Chris@353 1890 // Incoming returns non-zero code and no output if the
Chris@353 1891 // check was successful but there are no changes
Chris@353 1892 // pending. This is the only case where we need to remove
Chris@353 1893 // warning messages, because it's the only case where a
Chris@353 1894 // non-zero code can be returned even though the command
Chris@353 1895 // has for our purposes succeeded
Chris@352 1896 QString replaced = output;
Chris@352 1897 while (1) {
Chris@352 1898 QString r1 = replaced;
Chris@352 1899 r1.replace(QRegExp("warning: [^\\n]*"), "");
Chris@352 1900 if (r1 == replaced) break;
Chris@352 1901 replaced = r1.trimmed();
Chris@352 1902 }
Chris@352 1903 if (replaced == "") {
Chris@352 1904 showIncoming("");
Chris@352 1905 return;
Chris@352 1906 }
Chris@211 1907 }
Chris@330 1908 break; // go on to default report
Chris@353 1909 case ACT_PULL:
Chris@353 1910 if (output.contains("authorization failed")) {
Chris@353 1911 reportAuthFailed(output);
Chris@353 1912 return;
Chris@358 1913 } else if (output.contains("entry cancelled")) {
Chris@358 1914 // ignore this, user cancelled username or password dialog
Chris@358 1915 return;
Chris@353 1916 }
Chris@353 1917 break; // go on to default report
Chris@353 1918 case ACT_PUSH:
Chris@353 1919 if (output.contains("creates new remote heads")) {
Chris@353 1920 reportNewRemoteHeads(output);
Chris@353 1921 return;
Chris@353 1922 } else if (output.contains("authorization failed")) {
Chris@353 1923 reportAuthFailed(output);
Chris@353 1924 return;
Chris@358 1925 } else if (output.contains("entry cancelled")) {
Chris@358 1926 // ignore this, user cancelled username or password dialog
Chris@358 1927 return;
Chris@353 1928 }
Chris@353 1929 break; // go on to default report
Chris@162 1930 case ACT_QUERY_HEADS:
Chris@162 1931 // fails if repo is empty; we don't care (if there's a genuine
Chris@202 1932 // problem, something else will fail too). Pretend it
Chris@202 1933 // succeeded, so that any further actions that are contingent
Chris@202 1934 // on the success of the heads query get carried out properly.
Chris@202 1935 commandCompleted(action, "");
Chris@162 1936 return;
Chris@113 1937 case ACT_FOLDERDIFF:
Chris@113 1938 case ACT_CHGSETDIFF:
Chris@113 1939 // external program, unlikely to be anything useful in stderr
Chris@113 1940 // and some return with failure codes when something as basic
Chris@113 1941 // as the user closing the window via the wm happens
Chris@113 1942 return;
Chris@328 1943 case ACT_MERGE:
Chris@328 1944 case ACT_RETRY_MERGE:
Chris@328 1945 MoreInformationDialog::information
Chris@328 1946 (this, tr("Merge"), tr("Merge failed"),
Chris@328 1947 tr("Some files were not merged successfully.<p>You can Merge again to repeat the interactive merge; use Revert to abandon the merge entirely; or edit the files that are in conflict in an editor and, when you are happy with them, choose Mark Resolved in each file's right-button menu."),
Chris@328 1948 output);
Chris@328 1949 return;
Chris@199 1950 case ACT_STAT:
Chris@330 1951 break; // go on to default report
Chris@113 1952 default:
Chris@114 1953 break;
Chris@113 1954 }
Chris@113 1955
Chris@113 1956 QString command = action.executable;
Chris@113 1957 if (command == "") command = "hg";
Chris@113 1958 foreach (QString arg, action.params) {
Chris@113 1959 command += " " + arg;
Chris@113 1960 }
Chris@113 1961
Chris@309 1962 MoreInformationDialog::warning
Chris@309 1963 (this,
Chris@309 1964 tr("Command failed"),
Chris@309 1965 tr("Command failed"),
Chris@309 1966 tr("A Mercurial command failed to run correctly. This may indicate an installation problem or some other problem with EasyMercurial.<br><br>See &ldquo;More Details&rdquo; for the command output."),
Chris@309 1967 output);
Chris@62 1968 }
Chris@62 1969
Chris@109 1970 void MainWindow::commandCompleted(HgAction completedAction, QString output)
jtkorhonen@0 1971 {
Chris@238 1972 restoreFileSystemWatcher();
Chris@109 1973 HGACTIONS action = completedAction.action;
Chris@109 1974
Chris@109 1975 if (action == ACT_NONE) return;
Chris@109 1976
Chris@150 1977 bool headsChanged = false;
Chris@150 1978 QStringList oldHeadIds;
Chris@150 1979
Chris@150 1980 switch (action) {
Chris@109 1981
Chris@175 1982 case ACT_TEST_HG:
Chris@175 1983 break;
Chris@175 1984
Chris@200 1985 case ACT_TEST_HG_EXT:
Chris@200 1986 break;
Chris@200 1987
Chris@109 1988 case ACT_QUERY_PATHS:
jtkorhonen@0 1989 {
Chris@109 1990 DEBUG << "stdout is " << output << endl;
Chris@109 1991 LogParser lp(output, "=");
Chris@109 1992 LogList ll = lp.parse();
Chris@109 1993 DEBUG << ll.size() << " results" << endl;
Chris@109 1994 if (!ll.empty()) {
Chris@284 1995 m_remoteRepoPath = lp.parse()[0]["default"].trimmed();
Chris@284 1996 DEBUG << "Set remote path to " << m_remoteRepoPath << endl;
Chris@210 1997 } else {
Chris@284 1998 m_remoteRepoPath = "";
Chris@109 1999 }
Chris@284 2000 MultiChoiceDialog::addRecentArgument("local", m_workFolderPath);
Chris@284 2001 MultiChoiceDialog::addRecentArgument("remote", m_remoteRepoPath);
Chris@287 2002 updateWorkFolderAndRepoNames();
Chris@109 2003 break;
Chris@109 2004 }
jtkorhonen@0 2005
Chris@109 2006 case ACT_QUERY_BRANCH:
Chris@284 2007 m_currentBranch = output.trimmed();
Chris@109 2008 break;
jtkorhonen@0 2009
Chris@109 2010 case ACT_STAT:
Chris@284 2011 m_lastStatOutput = output;
Chris@109 2012 updateFileSystemWatcher();
Chris@109 2013 break;
Chris@163 2014
Chris@163 2015 case ACT_RESOLVE_LIST:
Chris@163 2016 if (output != "") {
Chris@163 2017 // Remove lines beginning with R (they are resolved,
Chris@163 2018 // and the file stat parser treats R as removed)
Chris@163 2019 QStringList outList = output.split('\n');
Chris@163 2020 QStringList winnowed;
Chris@163 2021 foreach (QString line, outList) {
Chris@163 2022 if (!line.startsWith("R ")) winnowed.push_back(line);
Chris@163 2023 }
Chris@163 2024 output = winnowed.join("\n");
Chris@163 2025 }
Chris@284 2026 DEBUG << "m_lastStatOutput = " << m_lastStatOutput << endl;
Chris@199 2027 DEBUG << "resolve output = " << output << endl;
Chris@284 2028 m_hgTabs->updateWorkFolderFileList(m_lastStatOutput + output);
Chris@163 2029 break;
Chris@163 2030
Chris@163 2031 case ACT_RESOLVE_MARK:
Chris@284 2032 m_shouldHgStat = true;
Chris@163 2033 break;
Chris@109 2034
Chris@109 2035 case ACT_INCOMING:
Chris@120 2036 showIncoming(output);
Chris@120 2037 break;
Chris@120 2038
Chris@109 2039 case ACT_ANNOTATE:
Chris@331 2040 {
Chris@331 2041 AnnotateDialog dialog(this, output);
Chris@331 2042 dialog.exec();
Chris@284 2043 m_shouldHgStat = true;
Chris@109 2044 break;
Chris@331 2045 }
Chris@109 2046
Chris@109 2047 case ACT_PULL:
Chris@120 2048 showPullResult(output);
Chris@284 2049 m_shouldHgStat = true;
Chris@109 2050 break;
Chris@109 2051
Chris@109 2052 case ACT_PUSH:
Chris@120 2053 showPushResult(output);
Chris@109 2054 break;
Chris@109 2055
Chris@109 2056 case ACT_INIT:
Chris@284 2057 MultiChoiceDialog::addRecentArgument("init", m_workFolderPath);
Chris@284 2058 MultiChoiceDialog::addRecentArgument("local", m_workFolderPath);
Chris@109 2059 enableDisableActions();
Chris@284 2060 m_shouldHgStat = true;
Chris@109 2061 break;
Chris@109 2062
Chris@109 2063 case ACT_CLONEFROMREMOTE:
Chris@284 2064 MultiChoiceDialog::addRecentArgument("local", m_workFolderPath);
Chris@284 2065 MultiChoiceDialog::addRecentArgument("remote", m_remoteRepoPath);
Chris@284 2066 MultiChoiceDialog::addRecentArgument("remote", m_workFolderPath, true);
Chris@291 2067 MoreInformationDialog::information
Chris@291 2068 (this,
Chris@291 2069 tr("Clone"),
Chris@291 2070 tr("Clone successful"),
Chris@295 2071 tr("The remote repository was successfully cloned to the local folder <code>%1</code>.").arg(xmlEncode(m_workFolderPath)),
Chris@291 2072 output);
Chris@109 2073 enableDisableActions();
Chris@284 2074 m_shouldHgStat = true;
Chris@109 2075 break;
Chris@109 2076
Chris@109 2077 case ACT_LOG:
Chris@284 2078 m_hgTabs->setNewLog(output);
Chris@284 2079 m_needNewLog = false;
Chris@120 2080 break;
Chris@120 2081
Chris@120 2082 case ACT_LOG_INCREMENTAL:
Chris@284 2083 m_hgTabs->addIncrementalLog(output);
Chris@109 2084 break;
Chris@109 2085
Chris@109 2086 case ACT_QUERY_PARENTS:
Chris@152 2087 {
Chris@284 2088 foreach (Changeset *cs, m_currentParents) delete cs;
Chris@284 2089 m_currentParents = Changeset::parseChangesets(output);
Chris@284 2090 QStringList parentIds = Changeset::getIds(m_currentParents);
Chris@284 2091 m_hgTabs->setCurrent(parentIds, m_currentBranch);
Chris@152 2092 }
Chris@109 2093 break;
Chris@109 2094
Chris@109 2095 case ACT_QUERY_HEADS:
Chris@150 2096 {
Chris@284 2097 oldHeadIds = Changeset::getIds(m_currentHeads);
Chris@150 2098 Changesets newHeads = Changeset::parseChangesets(output);
Chris@150 2099 QStringList newHeadIds = Changeset::getIds(newHeads);
Chris@150 2100 if (oldHeadIds != newHeadIds) {
Chris@150 2101 DEBUG << "Heads changed, will prompt an incremental log if appropriate" << endl;
Chris@305 2102 DEBUG << "Old heads: " << oldHeadIds.join(",") << endl;
Chris@305 2103 DEBUG << "New heads: " << newHeadIds.join(",") << endl;
Chris@150 2104 headsChanged = true;
Chris@284 2105 foreach (Changeset *cs, m_currentHeads) delete cs;
Chris@284 2106 m_currentHeads = newHeads;
Chris@150 2107 }
Chris@150 2108 }
Chris@109 2109 break;
Chris@130 2110
Chris@130 2111 case ACT_COMMIT:
Chris@347 2112 if (m_currentParents.empty()) {
Chris@347 2113 // first commit to empty repo
Chris@347 2114 m_needNewLog = true;
Chris@347 2115 }
Chris@284 2116 m_hgTabs->clearSelections();
Chris@284 2117 m_justMerged = false;
Chris@284 2118 m_shouldHgStat = true;
Chris@130 2119 break;
Chris@163 2120
Chris@163 2121 case ACT_REVERT:
Chris@326 2122 hgMarkFilesResolved(m_lastRevertedFiles);
Chris@284 2123 m_justMerged = false;
Chris@163 2124 break;
Chris@109 2125
Chris@109 2126 case ACT_REMOVE:
Chris@109 2127 case ACT_ADD:
Chris@284 2128 m_hgTabs->clearSelections();
Chris@284 2129 m_shouldHgStat = true;
Chris@116 2130 break;
Chris@116 2131
Chris@164 2132 case ACT_TAG:
Chris@284 2133 m_needNewLog = true;
Chris@284 2134 m_shouldHgStat = true;
Chris@164 2135 break;
Chris@164 2136
Chris@278 2137 case ACT_NEW_BRANCH:
Chris@307 2138 m_shouldHgStat = true;
Chris@278 2139 break;
Chris@278 2140
Chris@288 2141 case ACT_UNCOMMITTED_SUMMARY:
Chris@168 2142 QMessageBox::information(this, tr("Change summary"),
Chris@168 2143 format3(tr("Summary of uncommitted changes"),
Chris@168 2144 "",
Chris@168 2145 output));
Chris@168 2146 break;
Chris@168 2147
Chris@288 2148 case ACT_DIFF_SUMMARY:
Chris@289 2149 {
Chris@289 2150 // Output has log info first, diff following after a blank line
Chris@289 2151 output.replace("\r\n", "\n");
Chris@289 2152 QStringList olist = output.split("\n\n", QString::SkipEmptyParts);
Chris@289 2153 if (olist.size() > 1) output = olist[1];
Chris@289 2154
Chris@289 2155 Changeset *cs = (Changeset *)completedAction.extraData;
Chris@289 2156 if (cs) {
Chris@289 2157 QMessageBox::information
Chris@289 2158 (this, tr("Change summary"),
Chris@289 2159 format3(tr("Summary of changes"),
Chris@289 2160 cs->formatHtml(),
Chris@289 2161 output));
Chris@289 2162 } else if (output == "") {
Chris@289 2163 // Can happen, for a merge commit (depending on parent)
Chris@288 2164 QMessageBox::information(this, tr("Change summary"),
Chris@288 2165 format3(tr("Summary of changes"),
Chris@288 2166 tr("No changes"),
Chris@288 2167 output));
Chris@288 2168 } else {
Chris@288 2169 QMessageBox::information(this, tr("Change summary"),
Chris@288 2170 format3(tr("Summary of changes"),
Chris@288 2171 "",
Chris@288 2172 output));
Chris@288 2173 }
Chris@288 2174 break;
Chris@289 2175 }
Chris@288 2176
Chris@109 2177 case ACT_FOLDERDIFF:
Chris@109 2178 case ACT_CHGSETDIFF:
Chris@109 2179 case ACT_SERVE:
Chris@109 2180 case ACT_HG_IGNORE:
Chris@284 2181 m_shouldHgStat = true;
Chris@109 2182 break;
Chris@109 2183
Chris@109 2184 case ACT_UPDATE:
Chris@162 2185 QMessageBox::information(this, tr("Update"), tr("<qt><h3>Update successful</h3><p>%1</p>").arg(xmlEncode(output)));
Chris@284 2186 m_shouldHgStat = true;
Chris@109 2187 break;
Chris@109 2188
Chris@109 2189 case ACT_MERGE:
Chris@294 2190 MoreInformationDialog::information
Chris@294 2191 (this, tr("Merge"), tr("Merge successful"),
Chris@302 2192 tr("Remember to test and commit the result before making any further changes."),
Chris@294 2193 output);
Chris@284 2194 m_shouldHgStat = true;
Chris@284 2195 m_justMerged = true;
Chris@109 2196 break;
Chris@109 2197
Chris@109 2198 case ACT_RETRY_MERGE:
Chris@163 2199 QMessageBox::information(this, tr("Resolved"),
Chris@302 2200 tr("<qt><h3>Merge resolved</h3><p>Merge resolved successfully.<br>Remember to test and commit the result before making any further changes.</p>"));
Chris@284 2201 m_shouldHgStat = true;
Chris@284 2202 m_justMerged = true;
Chris@109 2203 break;
Chris@109 2204
Chris@109 2205 default:
Chris@109 2206 break;
Chris@109 2207 }
Chris@108 2208
Chris@121 2209 // Sequence when no full log required:
Chris@163 2210 // paths -> branch -> stat -> resolve-list -> heads ->
Chris@150 2211 // incremental-log (only if heads changed) -> parents
Chris@150 2212 //
Chris@121 2213 // Sequence when full log required:
Chris@163 2214 // paths -> branch -> stat -> resolve-list -> heads -> parents -> log
Chris@150 2215 //
Chris@150 2216 // Note we want to call enableDisableActions only once, at the end
Chris@150 2217 // of whichever sequence is in use.
Chris@150 2218
Chris@156 2219 bool noMore = false;
Chris@156 2220
Chris@150 2221 switch (action) {
Chris@175 2222
Chris@175 2223 case ACT_TEST_HG:
Chris@248 2224 {
Chris@248 2225 QSettings settings;
Chris@248 2226 settings.beginGroup("General");
Chris@248 2227 if (settings.value("useextension", true).toBool()) {
Chris@248 2228 hgTestExtension();
Chris@284 2229 } else if (m_workFolderPath == "") {
Chris@248 2230 open();
Chris@248 2231 } else {
Chris@248 2232 hgQueryPaths();
Chris@248 2233 }
Chris@200 2234 break;
Chris@248 2235 }
Chris@200 2236
Chris@200 2237 case ACT_TEST_HG_EXT:
Chris@284 2238 if (m_workFolderPath == "") {
Chris@248 2239 open();
Chris@248 2240 } else{
Chris@248 2241 hgQueryPaths();
Chris@248 2242 }
Chris@175 2243 break;
Chris@150 2244
Chris@150 2245 case ACT_QUERY_PATHS:
Chris@109 2246 hgQueryBranch();
Chris@150 2247 break;
Chris@150 2248
Chris@150 2249 case ACT_QUERY_BRANCH:
Chris@109 2250 hgStat();
Chris@150 2251 break;
Chris@150 2252
Chris@150 2253 case ACT_STAT:
Chris@163 2254 hgResolveList();
Chris@163 2255 break;
Chris@163 2256
Chris@163 2257 case ACT_RESOLVE_LIST:
Chris@150 2258 hgQueryHeads();
Chris@150 2259 break;
Chris@150 2260
Chris@150 2261 case ACT_QUERY_HEADS:
Chris@284 2262 if (headsChanged && !m_needNewLog) {
Chris@150 2263 hgLogIncremental(oldHeadIds);
Chris@121 2264 } else {
Chris@150 2265 hgQueryParents();
Chris@121 2266 }
Chris@150 2267 break;
Chris@150 2268
Chris@150 2269 case ACT_LOG_INCREMENTAL:
Chris@109 2270 hgQueryParents();
Chris@150 2271 break;
Chris@150 2272
Chris@150 2273 case ACT_QUERY_PARENTS:
Chris@284 2274 if (m_needNewLog) {
Chris@120 2275 hgLog();
Chris@150 2276 } else {
Chris@150 2277 // we're done
Chris@156 2278 noMore = true;
Chris@120 2279 }
Chris@150 2280 break;
Chris@150 2281
Chris@150 2282 case ACT_LOG:
Chris@150 2283 // we're done
Chris@156 2284 noMore = true;
Chris@198 2285 break;
Chris@150 2286
Chris@150 2287 default:
Chris@284 2288 if (m_shouldHgStat) {
Chris@284 2289 m_shouldHgStat = false;
Chris@109 2290 hgQueryPaths();
Chris@150 2291 } else {
Chris@156 2292 noMore = true;
jtkorhonen@0 2293 }
Chris@150 2294 break;
Chris@150 2295 }
Chris@156 2296
Chris@156 2297 if (noMore) {
Chris@284 2298 m_stateUnknown = false;
Chris@156 2299 enableDisableActions();
Chris@284 2300 m_hgTabs->updateHistory();
Chris@359 2301 updateRecentMenu();
Chris@156 2302 }
jtkorhonen@0 2303 }
jtkorhonen@0 2304
jtkorhonen@0 2305 void MainWindow::connectActions()
jtkorhonen@0 2306 {
Chris@284 2307 connect(m_exitAct, SIGNAL(triggered()), this, SLOT(close()));
Chris@284 2308 connect(m_aboutAct, SIGNAL(triggered()), this, SLOT(about()));
Chris@273 2309
Chris@284 2310 connect(m_hgRefreshAct, SIGNAL(triggered()), this, SLOT(hgRefresh()));
Chris@284 2311 connect(m_hgRemoveAct, SIGNAL(triggered()), this, SLOT(hgRemove()));
Chris@284 2312 connect(m_hgAddAct, SIGNAL(triggered()), this, SLOT(hgAdd()));
Chris@284 2313 connect(m_hgCommitAct, SIGNAL(triggered()), this, SLOT(hgCommit()));
Chris@284 2314 connect(m_hgFolderDiffAct, SIGNAL(triggered()), this, SLOT(hgFolderDiff()));
Chris@284 2315 connect(m_hgUpdateAct, SIGNAL(triggered()), this, SLOT(hgUpdate()));
Chris@284 2316 connect(m_hgRevertAct, SIGNAL(triggered()), this, SLOT(hgRevert()));
Chris@284 2317 connect(m_hgMergeAct, SIGNAL(triggered()), this, SLOT(hgMerge()));
Chris@284 2318 connect(m_hgIgnoreAct, SIGNAL(triggered()), this, SLOT(hgIgnore()));
Chris@273 2319
Chris@284 2320 connect(m_settingsAct, SIGNAL(triggered()), this, SLOT(settings()));
Chris@284 2321 connect(m_openAct, SIGNAL(triggered()), this, SLOT(open()));
Chris@284 2322 connect(m_changeRemoteRepoAct, SIGNAL(triggered()), this, SLOT(changeRemoteRepo()));
Chris@273 2323
Chris@284 2324 connect(m_hgIncomingAct, SIGNAL(triggered()), this, SLOT(hgIncoming()));
Chris@284 2325 connect(m_hgPullAct, SIGNAL(triggered()), this, SLOT(hgPull()));
Chris@284 2326 connect(m_hgPushAct, SIGNAL(triggered()), this, SLOT(hgPush()));
Chris@273 2327
Chris@284 2328 connect(m_hgServeAct, SIGNAL(triggered()), this, SLOT(hgServe()));
jtkorhonen@0 2329 }
Chris@141 2330
Chris@141 2331 void MainWindow::connectTabsSignals()
Chris@141 2332 {
Chris@327 2333 connect(m_hgTabs, SIGNAL(currentChanged(int)),
Chris@327 2334 this, SLOT(enableDisableActions()));
Chris@327 2335
Chris@284 2336 connect(m_hgTabs, SIGNAL(commit()),
Chris@141 2337 this, SLOT(hgCommit()));
Chris@141 2338
Chris@284 2339 connect(m_hgTabs, SIGNAL(revert()),
Chris@141 2340 this, SLOT(hgRevert()));
Chris@141 2341
Chris@284 2342 connect(m_hgTabs, SIGNAL(diffWorkingFolder()),
Chris@141 2343 this, SLOT(hgFolderDiff()));
Chris@168 2344
Chris@284 2345 connect(m_hgTabs, SIGNAL(showSummary()),
Chris@168 2346 this, SLOT(hgShowSummary()));
Chris@311 2347
Chris@311 2348 connect(m_hgTabs, SIGNAL(newBranch()),
Chris@311 2349 this, SLOT(hgNewBranch()));
Chris@311 2350
Chris@311 2351 connect(m_hgTabs, SIGNAL(noBranch()),
Chris@311 2352 this, SLOT(hgNoBranch()));
Chris@148 2353
Chris@284 2354 connect(m_hgTabs, SIGNAL(updateTo(QString)),
Chris@148 2355 this, SLOT(hgUpdateToRev(QString)));
Chris@141 2356
Chris@284 2357 connect(m_hgTabs, SIGNAL(diffToCurrent(QString)),
Chris@148 2358 this, SLOT(hgDiffToCurrent(QString)));
Chris@141 2359
Chris@284 2360 connect(m_hgTabs, SIGNAL(diffToParent(QString, QString)),
Chris@148 2361 this, SLOT(hgDiffToParent(QString, QString)));
Chris@141 2362
Chris@289 2363 connect(m_hgTabs, SIGNAL(showSummary(Changeset *)),
Chris@289 2364 this, SLOT(hgShowSummaryFor(Changeset *)));
Chris@288 2365
Chris@284 2366 connect(m_hgTabs, SIGNAL(mergeFrom(QString)),
Chris@148 2367 this, SLOT(hgMergeFrom(QString)));
Chris@164 2368
Chris@307 2369 connect(m_hgTabs, SIGNAL(newBranch(QString)),
Chris@311 2370 this, SLOT(hgNewBranch()));
Chris@278 2371
Chris@284 2372 connect(m_hgTabs, SIGNAL(tag(QString)),
Chris@148 2373 this, SLOT(hgTag(QString)));
Chris@326 2374
Chris@326 2375 connect(m_hgTabs, SIGNAL(annotateFiles(QStringList)),
Chris@326 2376 this, SLOT(hgAnnotateFiles(QStringList)));
Chris@326 2377
Chris@326 2378 connect(m_hgTabs, SIGNAL(diffFiles(QStringList)),
Chris@326 2379 this, SLOT(hgDiffFiles(QStringList)));
Chris@326 2380
Chris@326 2381 connect(m_hgTabs, SIGNAL(commitFiles(QStringList)),
Chris@326 2382 this, SLOT(hgCommitFiles(QStringList)));
Chris@326 2383
Chris@326 2384 connect(m_hgTabs, SIGNAL(revertFiles(QStringList)),
Chris@326 2385 this, SLOT(hgRevertFiles(QStringList)));
Chris@326 2386
Chris@361 2387 connect(m_hgTabs, SIGNAL(renameFiles(QStringList)),
Chris@361 2388 this, SLOT(hgRenameFiles(QStringList)));
Chris@361 2389
Chris@361 2390 connect(m_hgTabs, SIGNAL(copyFiles(QStringList)),
Chris@361 2391 this, SLOT(hgCopyFiles(QStringList)));
Chris@361 2392
Chris@326 2393 connect(m_hgTabs, SIGNAL(addFiles(QStringList)),
Chris@326 2394 this, SLOT(hgAddFiles(QStringList)));
Chris@326 2395
Chris@326 2396 connect(m_hgTabs, SIGNAL(removeFiles(QStringList)),
Chris@326 2397 this, SLOT(hgRemoveFiles(QStringList)));
Chris@326 2398
Chris@326 2399 connect(m_hgTabs, SIGNAL(redoFileMerges(QStringList)),
Chris@326 2400 this, SLOT(hgRedoFileMerges(QStringList)));
Chris@326 2401
Chris@326 2402 connect(m_hgTabs, SIGNAL(markFilesResolved(QStringList)),
Chris@326 2403 this, SLOT(hgMarkFilesResolved(QStringList)));
Chris@326 2404
Chris@326 2405 connect(m_hgTabs, SIGNAL(ignoreFiles(QStringList)),
Chris@326 2406 this, SLOT(hgIgnoreFiles(QStringList)));
Chris@326 2407
Chris@326 2408 connect(m_hgTabs, SIGNAL(unIgnoreFiles(QStringList)),
Chris@326 2409 this, SLOT(hgUnIgnoreFiles(QStringList)));
Chris@141 2410 }
Chris@141 2411
jtkorhonen@0 2412 void MainWindow::enableDisableActions()
jtkorhonen@0 2413 {
Chris@90 2414 DEBUG << "MainWindow::enableDisableActions" << endl;
Chris@90 2415
Chris@284 2416 QString dirname = QDir(m_workFolderPath).dirName();
Chris@340 2417
Chris@340 2418 if (m_workFolderPath != "") { // dirname of "" is ".", so test path instead
Chris@202 2419 setWindowTitle(tr("EasyMercurial: %1").arg(dirname));
Chris@202 2420 } else {
Chris@202 2421 setWindowTitle(tr("EasyMercurial"));
Chris@202 2422 }
Chris@202 2423
Chris@115 2424 //!!! should also do things like set the status texts for the
Chris@115 2425 //!!! actions appropriately by context
Chris@115 2426
jtkorhonen@0 2427 QDir localRepoDir;
jtkorhonen@0 2428 QDir workFolderDir;
Chris@145 2429 bool workFolderExist = true;
Chris@145 2430 bool localRepoExist = true;
jtkorhonen@0 2431
Chris@284 2432 m_remoteRepoActionsEnabled = true;
Chris@284 2433 if (m_remoteRepoPath.isEmpty()) {
Chris@284 2434 m_remoteRepoActionsEnabled = false;
jtkorhonen@0 2435 }
jtkorhonen@0 2436
Chris@284 2437 m_localRepoActionsEnabled = true;
Chris@284 2438 if (m_workFolderPath.isEmpty()) {
Chris@284 2439 m_localRepoActionsEnabled = false;
jtkorhonen@0 2440 workFolderExist = false;
jtkorhonen@0 2441 }
jtkorhonen@0 2442
Chris@284 2443 if (m_workFolderPath == "" || !workFolderDir.exists(m_workFolderPath)) {
Chris@284 2444 m_localRepoActionsEnabled = false;
jtkorhonen@0 2445 workFolderExist = false;
Chris@90 2446 } else {
jtkorhonen@0 2447 workFolderExist = true;
jtkorhonen@0 2448 }
jtkorhonen@0 2449
Chris@284 2450 if (!localRepoDir.exists(m_workFolderPath + "/.hg")) {
Chris@284 2451 m_localRepoActionsEnabled = false;
jtkorhonen@0 2452 localRepoExist = false;
jtkorhonen@0 2453 }
jtkorhonen@0 2454
Chris@179 2455 bool haveDiff = false;
Chris@179 2456 QSettings settings;
Chris@179 2457 settings.beginGroup("Locations");
Chris@179 2458 if (settings.value("extdiffbinary", "").toString() != "") {
Chris@179 2459 haveDiff = true;
Chris@179 2460 }
Chris@179 2461 settings.endGroup();
Chris@112 2462
Chris@284 2463 m_hgRefreshAct -> setEnabled(m_localRepoActionsEnabled);
Chris@284 2464 m_hgFolderDiffAct -> setEnabled(m_localRepoActionsEnabled && haveDiff);
Chris@284 2465 m_hgRevertAct -> setEnabled(m_localRepoActionsEnabled);
Chris@284 2466 m_hgAddAct -> setEnabled(m_localRepoActionsEnabled);
Chris@284 2467 m_hgRemoveAct -> setEnabled(m_localRepoActionsEnabled);
Chris@284 2468 m_hgUpdateAct -> setEnabled(m_localRepoActionsEnabled);
Chris@284 2469 m_hgCommitAct -> setEnabled(m_localRepoActionsEnabled);
Chris@284 2470 m_hgMergeAct -> setEnabled(m_localRepoActionsEnabled);
Chris@284 2471 m_hgServeAct -> setEnabled(m_localRepoActionsEnabled);
Chris@284 2472 m_hgIgnoreAct -> setEnabled(m_localRepoActionsEnabled);
Chris@273 2473
Chris@284 2474 DEBUG << "m_localRepoActionsEnabled = " << m_localRepoActionsEnabled << endl;
Chris@284 2475 DEBUG << "canCommit = " << m_hgTabs->canCommit() << endl;
Chris@273 2476
Chris@284 2477 m_hgAddAct->setEnabled(m_localRepoActionsEnabled && m_hgTabs->canAdd());
Chris@284 2478 m_hgRemoveAct->setEnabled(m_localRepoActionsEnabled && m_hgTabs->canRemove());
Chris@284 2479 m_hgCommitAct->setEnabled(m_localRepoActionsEnabled && m_hgTabs->canCommit());
Chris@284 2480 m_hgRevertAct->setEnabled(m_localRepoActionsEnabled && m_hgTabs->canRevert());
Chris@284 2481 m_hgFolderDiffAct->setEnabled(m_localRepoActionsEnabled && m_hgTabs->canDiff());
Chris@90 2482
Chris@108 2483 // A default merge makes sense if:
Chris@108 2484 // * there is only one parent (if there are two, we have an uncommitted merge) and
Chris@108 2485 // * there are exactly two heads that have the same branch as the current branch and
Chris@108 2486 // * our parent is one of those heads
Chris@108 2487 //
Chris@108 2488 // A default update makes sense if:
Chris@108 2489 // * there is only one parent and
Chris@108 2490 // * the parent is not one of the current heads
Chris@156 2491
Chris@108 2492 bool canMerge = false;
Chris@108 2493 bool canUpdate = false;
Chris@156 2494 bool haveMerge = false;
Chris@162 2495 bool emptyRepo = false;
Chris@225 2496 bool noWorkingCopy = false;
Chris@235 2497 bool newBranch = false;
Chris@284 2498 int m_currentBranchHeads = 0;
Chris@273 2499
Chris@284 2500 if (m_currentParents.size() == 1) {
Chris@156 2501 bool parentIsHead = false;
Chris@284 2502 Changeset *parent = m_currentParents[0];
Chris@284 2503 foreach (Changeset *head, m_currentHeads) {
Chris@284 2504 DEBUG << "head branch " << head->branch() << ", current branch " << m_currentBranch << endl;
Chris@284 2505 if (head->isOnBranch(m_currentBranch)) {
Chris@284 2506 ++m_currentBranchHeads;
Chris@235 2507 }
Chris@235 2508 if (parent->id() == head->id()) {
Chris@235 2509 parentIsHead = true;
Chris@108 2510 }
Chris@108 2511 }
Chris@284 2512 if (m_currentBranchHeads == 2 && parentIsHead) {
Chris@108 2513 canMerge = true;
Chris@108 2514 }
Chris@284 2515 if (m_currentBranchHeads == 0 && parentIsHead) {
Chris@235 2516 // Just created a new branch
Chris@235 2517 newBranch = true;
Chris@235 2518 }
Chris@108 2519 if (!parentIsHead) {
Chris@108 2520 canUpdate = true;
Chris@108 2521 DEBUG << "parent id = " << parent->id() << endl;
Chris@108 2522 DEBUG << " head ids "<<endl;
Chris@284 2523 foreach (Changeset *h, m_currentHeads) {
Chris@108 2524 DEBUG << "head id = " << h->id() << endl;
Chris@108 2525 }
Chris@108 2526 }
Chris@284 2527 m_justMerged = false;
Chris@284 2528 } else if (m_currentParents.size() == 0) {
Chris@284 2529 if (m_currentHeads.size() == 0) {
Chris@225 2530 // No heads -> empty repo
Chris@225 2531 emptyRepo = true;
Chris@225 2532 } else {
Chris@225 2533 // Heads, but no parents -> no working copy, e.g. we have
Chris@225 2534 // just converted this repo but haven't updated in it yet.
Chris@225 2535 // Uncommon but confusing; probably merits a special case
Chris@225 2536 noWorkingCopy = true;
Chris@225 2537 canUpdate = true;
Chris@225 2538 }
Chris@284 2539 m_justMerged = false;
Chris@156 2540 } else {
Chris@156 2541 haveMerge = true;
Chris@284 2542 m_justMerged = true;
Chris@108 2543 }
Chris@156 2544
Chris@363 2545 m_hgIncomingAct->setEnabled(m_remoteRepoActionsEnabled);
Chris@363 2546 m_hgPullAct->setEnabled(m_remoteRepoActionsEnabled);
Chris@363 2547 // permit push even if no remote yet; we'll ask for one
Chris@363 2548 m_hgPushAct->setEnabled(m_localRepoActionsEnabled && !emptyRepo);
Chris@363 2549
Chris@284 2550 m_hgMergeAct->setEnabled(m_localRepoActionsEnabled &&
Chris@284 2551 (canMerge || m_hgTabs->canResolve()));
Chris@284 2552 m_hgUpdateAct->setEnabled(m_localRepoActionsEnabled &&
Chris@284 2553 (canUpdate && !m_hgTabs->haveChangesToCommit()));
Chris@115 2554
Chris@115 2555 // Set the state field on the file status widget
Chris@115 2556
Chris@115 2557 QString branchText;
Chris@284 2558 if (m_currentBranch == "" || m_currentBranch == "default") {
Chris@115 2559 branchText = tr("the default branch");
Chris@115 2560 } else {
Chris@284 2561 branchText = tr("branch \"%1\"").arg(m_currentBranch);
Chris@115 2562 }
Chris@156 2563
Chris@284 2564 if (m_stateUnknown) {
Chris@284 2565 if (m_workFolderPath == "") {
Chris@287 2566 m_workStatus->setState(tr("No repository open"));
Chris@248 2567 } else {
Chris@287 2568 m_workStatus->setState(tr("(Examining repository)"));
Chris@248 2569 }
Chris@173 2570 } else if (emptyRepo) {
Chris@287 2571 m_workStatus->setState(tr("Nothing committed to this repository yet"));
Chris@225 2572 } else if (noWorkingCopy) {
Chris@287 2573 m_workStatus->setState(tr("No working copy yet: consider updating"));
Chris@162 2574 } else if (canMerge) {
Chris@287 2575 m_workStatus->setState(tr("<b>Awaiting merge</b> on %1").arg(branchText));
Chris@284 2576 } else if (!m_hgTabs->getAllUnresolvedFiles().empty()) {
Chris@287 2577 m_workStatus->setState(tr("Have unresolved files following merge on %1").arg(branchText));
Chris@156 2578 } else if (haveMerge) {
Chris@287 2579 m_workStatus->setState(tr("Have merged but not yet committed on %1").arg(branchText));
Chris@235 2580 } else if (newBranch) {
Chris@287 2581 m_workStatus->setState(tr("On %1. New branch: has not yet been committed").arg(branchText));
Chris@156 2582 } else if (canUpdate) {
Chris@284 2583 if (m_hgTabs->haveChangesToCommit()) {
Chris@163 2584 // have uncommitted changes
Chris@287 2585 m_workStatus->setState(tr("On %1. Not at the head of the branch").arg(branchText));
Chris@163 2586 } else {
Chris@163 2587 // no uncommitted changes
Chris@287 2588 m_workStatus->setState(tr("On %1. Not at the head of the branch: consider updating").arg(branchText));
Chris@163 2589 }
Chris@284 2590 } else if (m_currentBranchHeads > 1) {
Chris@287 2591 m_workStatus->setState(tr("At one of %n heads of %1", "", m_currentBranchHeads).arg(branchText));
Chris@115 2592 } else {
Chris@287 2593 m_workStatus->setState(tr("At the head of %1").arg(branchText));
Chris@115 2594 }
jtkorhonen@0 2595 }
jtkorhonen@0 2596
Chris@359 2597
Chris@359 2598 void MainWindow::updateRecentMenu()
Chris@359 2599 {
Chris@359 2600 m_recentMenu->clear();
Chris@359 2601 RecentFiles rf("Recent-local");
Chris@359 2602 QStringList recent = rf.getRecent();
Chris@359 2603 if (recent.empty()) {
Chris@359 2604 QLabel *label = new QLabel(tr("No recent local repositories"));
Chris@359 2605 QWidgetAction *wa = new QWidgetAction(m_recentMenu);
Chris@359 2606 wa->setDefaultWidget(label);
Chris@359 2607 return;
Chris@359 2608 }
Chris@359 2609 foreach (QString r, recent) {
Chris@359 2610 QAction *a = m_recentMenu->addAction(r);
Chris@359 2611 connect(a, SIGNAL(activated()), this, SLOT(recentMenuActivated()));
Chris@359 2612 }
Chris@359 2613 }
Chris@359 2614
jtkorhonen@0 2615 void MainWindow::createActions()
jtkorhonen@0 2616 {
jtkorhonen@0 2617 //File actions
Chris@284 2618 m_openAct = new QAction(QIcon(":/images/fileopen.png"), tr("Open..."), this);
Chris@284 2619 m_openAct -> setStatusTip(tr("Open an existing repository or working folder"));
Chris@273 2620
Chris@363 2621 m_changeRemoteRepoAct = new QAction(tr("Set Remote Location..."), this);
Chris@363 2622 m_changeRemoteRepoAct->setStatusTip(tr("Set or change the default remote repository for pull and push actions"));
Chris@273 2623
Chris@284 2624 m_settingsAct = new QAction(QIcon(":/images/settings.png"), tr("Settings..."), this);
Chris@284 2625 m_settingsAct -> setStatusTip(tr("View and change application settings"));
Chris@273 2626
Chris@343 2627 #ifdef Q_OS_WIN32
Chris@343 2628 m_exitAct = new QAction(QIcon(":/images/exit.png"), tr("Exit"), this);
Chris@343 2629 #else
Chris@284 2630 m_exitAct = new QAction(QIcon(":/images/exit.png"), tr("Quit"), this);
Chris@343 2631 #endif
Chris@284 2632 m_exitAct->setShortcuts(QKeySequence::Quit);
Chris@284 2633 m_exitAct->setStatusTip(tr("Quit EasyMercurial"));
jtkorhonen@0 2634
jtkorhonen@0 2635 //Repository actions
Chris@284 2636 m_hgRefreshAct = new QAction(QIcon(":/images/status.png"), tr("Refresh"), this);
Chris@284 2637 m_hgRefreshAct->setStatusTip(tr("Refresh the window to show the current state of the working folder"));
Chris@273 2638
Chris@284 2639 m_hgIncomingAct = new QAction(QIcon(":/images/incoming.png"), tr("Preview"), this);
Chris@284 2640 m_hgIncomingAct -> setStatusTip(tr("See what changes are available in the remote repository waiting to be pulled"));
Chris@273 2641
Chris@284 2642 m_hgPullAct = new QAction(QIcon(":/images/pull.png"), tr("Pull"), this);
Chris@284 2643 m_hgPullAct -> setStatusTip(tr("Pull changes from the remote repository to the local repository"));
Chris@273 2644
Chris@284 2645 m_hgPushAct = new QAction(QIcon(":/images/push.png"), tr("Push"), this);
Chris@284 2646 m_hgPushAct->setStatusTip(tr("Push changes from the local repository to the remote repository"));
jtkorhonen@0 2647
jtkorhonen@0 2648 //Workfolder actions
Chris@284 2649 m_hgFolderDiffAct = new QAction(QIcon(":/images/folderdiff.png"), tr("Diff"), this);
Chris@284 2650 m_hgFolderDiffAct->setStatusTip(tr("See what has changed in the working folder compared with the last committed state"));
Chris@273 2651
Chris@284 2652 m_hgRevertAct = new QAction(QIcon(":/images/undo.png"), tr("Revert"), this);
Chris@284 2653 m_hgRevertAct->setStatusTip(tr("Throw away your changes and return to the last committed state"));
Chris@273 2654
Chris@284 2655 m_hgAddAct = new QAction(QIcon(":/images/add.png"), tr("Add"), this);
Chris@284 2656 m_hgAddAct -> setStatusTip(tr("Mark the selected file(s) to be added on the next commit"));
jtkorhonen@0 2657
Chris@169 2658 //!!! needs to be modified for number
Chris@284 2659 m_hgRemoveAct = new QAction(QIcon(":/images/remove.png"), tr("Remove"), this);
Chris@284 2660 m_hgRemoveAct -> setStatusTip(tr("Mark the selected file(s) to be removed from version control on the next commit"));
Chris@273 2661
Chris@284 2662 m_hgUpdateAct = new QAction(QIcon(":/images/update.png"), tr("Update"), this);
Chris@284 2663 m_hgUpdateAct->setStatusTip(tr("Update the working folder to the head of the current repository branch"));
jtkorhonen@0 2664
Chris@169 2665 //!!! needs to be modified when files selected
Chris@284 2666 m_hgCommitAct = new QAction(QIcon(":/images/commit.png"), tr("Commit"), this);
Chris@284 2667 m_hgCommitAct->setStatusTip(tr("Commit your changes to the local repository"));
Chris@273 2668
Chris@284 2669 m_hgMergeAct = new QAction(QIcon(":/images/merge.png"), tr("Merge"), this);
Chris@284 2670 m_hgMergeAct->setStatusTip(tr("Merge the two independent sets of changes in the local repository into the working folder"));
jtkorhonen@0 2671
jtkorhonen@0 2672 //Advanced actions
Chris@273 2673
Chris@284 2674 m_hgIgnoreAct = new QAction(tr("Edit .hgignore File"), this);
Chris@284 2675 m_hgIgnoreAct -> setStatusTip(tr("Edit the .hgignore file, containing the names of files that should be ignored by Mercurial"));
Chris@273 2676
Chris@284 2677 m_hgServeAct = new QAction(tr("Serve via HTTP"), this);
Chris@284 2678 m_hgServeAct -> setStatusTip(tr("Serve local repository via http for workgroup access"));
jtkorhonen@11 2679
jtkorhonen@0 2680 //Help actions
Chris@284 2681 m_aboutAct = new QAction(tr("About EasyMercurial"), this);
Chris@94 2682
Chris@94 2683 // Miscellaneous
Chris@199 2684 QShortcut *clearSelectionsShortcut = new QShortcut(Qt::Key_Escape, this);
Chris@199 2685 connect(clearSelectionsShortcut, SIGNAL(activated()),
Chris@199 2686 this, SLOT(clearSelections()));
jtkorhonen@0 2687 }
jtkorhonen@0 2688
jtkorhonen@0 2689 void MainWindow::createMenus()
jtkorhonen@0 2690 {
Chris@284 2691 m_fileMenu = menuBar()->addMenu(tr("File"));
Chris@273 2692
Chris@284 2693 m_fileMenu -> addAction(m_openAct);
Chris@359 2694 m_recentMenu = m_fileMenu->addMenu(tr("Open Recent"));
Chris@284 2695 m_fileMenu -> addAction(m_changeRemoteRepoAct);
Chris@284 2696 m_fileMenu -> addSeparator();
Chris@273 2697
Chris@284 2698 m_advancedMenu = m_fileMenu->addMenu(tr("Advanced"));
Chris@273 2699
Chris@284 2700 m_fileMenu -> addAction(m_settingsAct);
Chris@273 2701
Chris@284 2702 m_fileMenu -> addSeparator();
Chris@284 2703 m_fileMenu -> addAction(m_exitAct);
Chris@273 2704
Chris@284 2705 m_advancedMenu -> addAction(m_hgIgnoreAct);
Chris@284 2706 m_advancedMenu -> addSeparator();
Chris@284 2707 m_advancedMenu -> addAction(m_hgServeAct);
Chris@273 2708
Chris@284 2709 m_helpMenu = menuBar()->addMenu(tr("Help"));
Chris@284 2710 m_helpMenu->addAction(m_aboutAct);
jtkorhonen@0 2711 }
jtkorhonen@0 2712
jtkorhonen@0 2713 void MainWindow::createToolBars()
jtkorhonen@0 2714 {
Chris@284 2715 m_fileToolBar = addToolBar(tr("File"));
Chris@284 2716 m_fileToolBar -> setIconSize(QSize(MY_ICON_SIZE, MY_ICON_SIZE));
Chris@284 2717 m_fileToolBar -> addAction(m_openAct);
Chris@284 2718 m_fileToolBar -> addAction(m_hgRefreshAct);
Chris@284 2719 m_fileToolBar -> setMovable(false);
Chris@273 2720
Chris@284 2721 m_repoToolBar = addToolBar(tr(REPOMENU_TITLE));
Chris@284 2722 m_repoToolBar -> setIconSize(QSize(MY_ICON_SIZE, MY_ICON_SIZE));
Chris@284 2723 m_repoToolBar->addAction(m_hgIncomingAct);
Chris@284 2724 m_repoToolBar->addAction(m_hgPullAct);
Chris@284 2725 m_repoToolBar->addAction(m_hgPushAct);
Chris@284 2726 m_repoToolBar -> setMovable(false);
Chris@273 2727
Chris@284 2728 m_workFolderToolBar = addToolBar(tr(WORKFOLDERMENU_TITLE));
Chris@284 2729 addToolBar(Qt::LeftToolBarArea, m_workFolderToolBar);
Chris@284 2730 m_workFolderToolBar -> setIconSize(QSize(MY_ICON_SIZE, MY_ICON_SIZE));
Chris@284 2731 m_workFolderToolBar->addAction(m_hgFolderDiffAct);
Chris@284 2732 m_workFolderToolBar->addSeparator();
Chris@284 2733 m_workFolderToolBar->addAction(m_hgRevertAct);
Chris@284 2734 m_workFolderToolBar->addAction(m_hgUpdateAct);
Chris@284 2735 m_workFolderToolBar->addAction(m_hgCommitAct);
Chris@284 2736 m_workFolderToolBar->addAction(m_hgMergeAct);
Chris@284 2737 m_workFolderToolBar->addSeparator();
Chris@284 2738 m_workFolderToolBar->addAction(m_hgAddAct);
Chris@284 2739 m_workFolderToolBar->addAction(m_hgRemoveAct);
Chris@284 2740 m_workFolderToolBar -> setMovable(false);
Chris@61 2741
Chris@230 2742 updateToolBarStyle();
jtkorhonen@0 2743 }
jtkorhonen@0 2744
Chris@230 2745 void MainWindow::updateToolBarStyle()
Chris@230 2746 {
Chris@230 2747 QSettings settings;
Chris@230 2748 settings.beginGroup("Presentation");
Chris@230 2749 bool showText = settings.value("showiconlabels", true).toBool();
Chris@230 2750 settings.endGroup();
Chris@230 2751
Chris@230 2752 foreach (QToolButton *tb, findChildren<QToolButton *>()) {
Chris@230 2753 tb->setToolButtonStyle(showText ?
Chris@230 2754 Qt::ToolButtonTextUnderIcon :
Chris@230 2755 Qt::ToolButtonIconOnly);
Chris@230 2756 }
Chris@230 2757 }
jtkorhonen@0 2758
Chris@287 2759 void MainWindow::updateWorkFolderAndRepoNames()
Chris@287 2760 {
Chris@287 2761 m_hgTabs->setLocalPath(m_workFolderPath);
Chris@287 2762
Chris@287 2763 m_workStatus->setLocalPath(m_workFolderPath);
Chris@287 2764 m_workStatus->setRemoteURL(m_remoteRepoPath);
Chris@287 2765 }
Chris@287 2766
jtkorhonen@0 2767 void MainWindow::createStatusBar()
jtkorhonen@0 2768 {
jtkorhonen@0 2769 statusBar()->showMessage(tr("Ready"));
jtkorhonen@0 2770 }
jtkorhonen@0 2771
jtkorhonen@0 2772 void MainWindow::readSettings()
jtkorhonen@0 2773 {
jtkorhonen@0 2774 QDir workFolder;
jtkorhonen@0 2775
Chris@61 2776 QSettings settings;
jtkorhonen@0 2777
Chris@284 2778 m_remoteRepoPath = settings.value("remoterepopath", "").toString();
Chris@284 2779 m_workFolderPath = settings.value("workfolderpath", "").toString();
Chris@284 2780 if (!workFolder.exists(m_workFolderPath))
jtkorhonen@0 2781 {
Chris@284 2782 m_workFolderPath = "";
jtkorhonen@0 2783 }
jtkorhonen@0 2784
jtkorhonen@0 2785 QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
Chris@340 2786 QSize size = settings.value("size", QSize(550, 550)).toSize();
Chris@284 2787 m_firstStart = settings.value("firststart", QVariant(true)).toBool();
jtkorhonen@0 2788
jtkorhonen@0 2789 resize(size);
jtkorhonen@0 2790 move(pos);
jtkorhonen@0 2791 }
jtkorhonen@0 2792
jtkorhonen@0 2793 void MainWindow::writeSettings()
jtkorhonen@0 2794 {
Chris@61 2795 QSettings settings;
jtkorhonen@0 2796 settings.setValue("pos", pos());
jtkorhonen@0 2797 settings.setValue("size", size());
Chris@284 2798 settings.setValue("remoterepopath", m_remoteRepoPath);
Chris@284 2799 settings.setValue("workfolderpath", m_workFolderPath);
Chris@284 2800 settings.setValue("firststart", m_firstStart);
jtkorhonen@0 2801 }
jtkorhonen@0 2802
jtkorhonen@0 2803
jtkorhonen@0 2804
jtkorhonen@0 2805