annotate mainwindow.cpp @ 344:ccc55539e066

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