annotate src/mainwindow.cpp @ 425:ad106f5fe75f

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