annotate filestatuswidget.cpp @ 180:3d6d826b04ad

* Similarly, tidier lookup for Hg binary and extension
author Chris Cannam
date Fri, 17 Dec 2010 08:53:54 +0000
parents 5c262ac73948
children 6c15700f4103
rev   line source
Chris@88 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@88 2
Chris@88 3 /*
Chris@88 4 EasyMercurial
Chris@88 5
Chris@88 6 Based on HgExplorer by Jari Korhonen
Chris@88 7 Copyright (c) 2010 Jari Korhonen
Chris@88 8 Copyright (c) 2010 Chris Cannam
Chris@88 9 Copyright (c) 2010 Queen Mary, University of London
Chris@88 10
Chris@88 11 This program is free software; you can redistribute it and/or
Chris@88 12 modify it under the terms of the GNU General Public License as
Chris@88 13 published by the Free Software Foundation; either version 2 of the
Chris@88 14 License, or (at your option) any later version. See the file
Chris@88 15 COPYING included with this distribution for more information.
Chris@88 16 */
Chris@88 17
Chris@88 18 #include "filestatuswidget.h"
Chris@115 19 #include "debug.h"
Chris@115 20 #include "multichoicedialog.h"
Chris@88 21
Chris@88 22 #include <QLabel>
Chris@88 23 #include <QListWidget>
Chris@88 24 #include <QGridLayout>
Chris@93 25 #include <QFileInfo>
Chris@93 26 #include <QApplication>
Chris@93 27 #include <QDateTime>
Chris@115 28 #include <QPushButton>
Chris@88 29
Chris@88 30 FileStatusWidget::FileStatusWidget(QWidget *parent) :
Chris@93 31 QWidget(parent),
Chris@93 32 m_dateReference(0)
Chris@88 33 {
Chris@88 34 QGridLayout *layout = new QGridLayout;
Chris@88 35 setLayout(layout);
Chris@88 36
Chris@88 37 int row = 0;
Chris@88 38
Chris@115 39 layout->addItem(new QSpacerItem(1, 1), row, 0);
Chris@115 40
Chris@115 41 ++row;
Chris@88 42 layout->addWidget(new QLabel(tr("Local:")), row, 0);
Chris@88 43 m_localPathLabel = new QLabel;
Chris@89 44 QFont f(m_localPathLabel->font());
Chris@89 45 f.setBold(true);
Chris@89 46 m_localPathLabel->setFont(f);
Chris@88 47 layout->addWidget(m_localPathLabel, row, 1);
Chris@88 48
Chris@88 49 ++row;
Chris@88 50 layout->addWidget(new QLabel(tr("Remote:")), row, 0);
Chris@88 51 m_remoteURLLabel = new QLabel;
Chris@88 52 layout->addWidget(m_remoteURLLabel, row, 1);
Chris@88 53
Chris@106 54 ++row;
Chris@115 55 layout->addWidget(new QLabel(tr("State:")), row, 0);
Chris@115 56 m_stateLabel = new QLabel;
Chris@115 57 layout->addWidget(m_stateLabel, row, 1, 1, 2);
Chris@106 58
Chris@89 59 layout->setColumnStretch(1, 20);
Chris@88 60
Chris@115 61 layout->addWidget(new QLabel("<qt><hr></qt>"), ++row, 0, 1, 3);
Chris@115 62
Chris@115 63 ++row;
Chris@115 64 m_noModificationsLabel = new QLabel
Chris@162 65 (tr("You have no uncommitted changes."));
Chris@115 66 layout->addWidget(m_noModificationsLabel, row, 1, 1, 2);
Chris@106 67
Chris@100 68 m_simpleLabels[FileStates::Clean] = tr("Unmodified:");
Chris@100 69 m_simpleLabels[FileStates::Modified] = tr("Modified:");
Chris@100 70 m_simpleLabels[FileStates::Added] = tr("Added:");
Chris@100 71 m_simpleLabels[FileStates::Removed] = tr("Removed:");
Chris@100 72 m_simpleLabels[FileStates::Missing] = tr("Missing:");
Chris@163 73 m_simpleLabels[FileStates::InConflict] = tr("In Conflict:");
Chris@100 74 m_simpleLabels[FileStates::Unknown] = tr("Untracked:");
Chris@99 75
Chris@100 76 m_descriptions[FileStates::Clean] = tr("You have not changed these files.");
Chris@100 77 m_descriptions[FileStates::Modified] = tr("You have changed these files since you last committed them.");
Chris@100 78 m_descriptions[FileStates::Added] = tr("These files will be added to version control next time you commit.");
Chris@100 79 m_descriptions[FileStates::Removed] = tr("These files will be removed from version control next time you commit.<br>"
Chris@100 80 "They will not be deleted from the local folder.");
Chris@109 81 m_descriptions[FileStates::Missing] = tr("These files are recorded in the version control, but absent from your working folder.<br>"
Chris@153 82 "If you intended to delete them, select them and use Remove to tell the version control system about it.<br>"
Chris@153 83 "If you deleted them by accident, select them and use Revert to restore their previous contents.");
Chris@163 84 m_descriptions[FileStates::InConflict] = tr("These files are unresolved following an incomplete merge.<br>Select a file and use Merge to try to resolve the merge again.");
Chris@100 85 m_descriptions[FileStates::Unknown] = tr("These files are in your working folder but are not under version control.<br>"
Chris@100 86 "Select a file and use Add to place it under version control or Ignore to remove it from this list.");
Chris@100 87
Chris@118 88 m_highlightExplanation = tr("Files highlighted <font color=#d40000>in red</font> "
Chris@100 89 "have appeared since your most recent commit or update.");
Chris@89 90
Chris@94 91 for (int i = int(FileStates::FirstState);
Chris@163 92 i <= int(FileStates::LastState); ++i) {
Chris@89 93
Chris@94 94 FileStates::State s = FileStates::State(i);
Chris@89 95
Chris@89 96 QWidget *box = new QWidget;
Chris@89 97 QGridLayout *boxlayout = new QGridLayout;
Chris@99 98 boxlayout->setMargin(0);
Chris@89 99 box->setLayout(boxlayout);
Chris@89 100
Chris@106 101 boxlayout->addItem(new QSpacerItem(5, 5), 0, 0);
Chris@101 102
Chris@101 103 boxlayout->addWidget(new QLabel(labelFor(s)), 1, 0);
Chris@89 104
Chris@94 105 QListWidget *w = new QListWidget;
Chris@94 106 m_stateListMap[s] = w;
Chris@94 107 w->setSelectionMode(QListWidget::ExtendedSelection);
Chris@101 108 boxlayout->addWidget(w, 2, 0);
Chris@89 109
Chris@95 110 connect(w, SIGNAL(itemSelectionChanged()),
Chris@95 111 this, SLOT(itemSelectionChanged()));
Chris@95 112
Chris@115 113 layout->addWidget(box, ++row, 0, 1, 3);
Chris@89 114 box->hide();
Chris@89 115 }
Chris@89 116
Chris@89 117 layout->setRowStretch(++row, 20);
Chris@88 118 }
Chris@88 119
Chris@93 120 FileStatusWidget::~FileStatusWidget()
Chris@93 121 {
Chris@93 122 delete m_dateReference;
Chris@93 123 }
Chris@93 124
Chris@100 125 QString FileStatusWidget::labelFor(FileStates::State s, bool addHighlightExplanation)
Chris@100 126 {
Chris@100 127 if (addHighlightExplanation) {
Chris@100 128 return QString("<qt><b>%1</b><br>%2<br>%3</qt>")
Chris@100 129 .arg(m_simpleLabels[s])
Chris@100 130 .arg(m_descriptions[s])
Chris@100 131 .arg(m_highlightExplanation);
Chris@100 132 } else {
Chris@100 133 return QString("<qt><b>%1</b><br>%2</qt>")
Chris@100 134 .arg(m_simpleLabels[s])
Chris@100 135 .arg(m_descriptions[s]);
Chris@100 136 }
Chris@100 137 }
Chris@100 138
Chris@95 139 void FileStatusWidget::itemSelectionChanged()
Chris@95 140 {
Chris@135 141 DEBUG << "FileStatusWidget::itemSelectionChanged" << endl;
Chris@135 142
Chris@135 143 QListWidget *list = qobject_cast<QListWidget *>(sender());
Chris@135 144
Chris@135 145 if (list) {
Chris@135 146 foreach (QListWidget *w, m_stateListMap) {
Chris@135 147 if (w != list) {
Chris@135 148 w->blockSignals(true);
Chris@135 149 w->clearSelection();
Chris@135 150 w->blockSignals(false);
Chris@135 151 }
Chris@135 152 }
Chris@135 153 }
Chris@135 154
Chris@95 155 m_selectedFiles.clear();
Chris@95 156
Chris@95 157 foreach (QListWidget *w, m_stateListMap) {
Chris@95 158 QList<QListWidgetItem *> sel = w->selectedItems();
Chris@95 159 foreach (QListWidgetItem *i, sel) {
Chris@95 160 m_selectedFiles.push_back(i->text());
Chris@95 161 DEBUG << "file " << i->text() << " is selected" << endl;
Chris@95 162 }
Chris@95 163 }
Chris@95 164
Chris@95 165 emit selectionChanged();
Chris@95 166 }
Chris@95 167
Chris@94 168 void FileStatusWidget::clearSelections()
Chris@94 169 {
Chris@95 170 m_selectedFiles.clear();
Chris@94 171 foreach (QListWidget *w, m_stateListMap) {
Chris@94 172 w->clearSelection();
Chris@94 173 }
Chris@94 174 }
Chris@94 175
Chris@95 176 bool FileStatusWidget::haveChangesToCommit() const
Chris@95 177 {
Chris@95 178 return !m_fileStates.added().empty() ||
Chris@95 179 !m_fileStates.removed().empty() ||
Chris@95 180 !m_fileStates.modified().empty();
Chris@95 181 }
Chris@95 182
Chris@95 183 bool FileStatusWidget::haveSelection() const
Chris@95 184 {
Chris@95 185 return !m_selectedFiles.empty();
Chris@95 186 }
Chris@95 187
Chris@95 188 QStringList FileStatusWidget::getAllSelectedFiles() const
Chris@95 189 {
Chris@95 190 return m_selectedFiles;
Chris@95 191 }
Chris@95 192
Chris@95 193 QStringList FileStatusWidget::getSelectedCommittableFiles() const
Chris@95 194 {
Chris@95 195 QStringList files;
Chris@95 196 foreach (QString f, m_selectedFiles) {
Chris@95 197 switch (m_fileStates.getStateOfFile(f)) {
Chris@95 198 case FileStates::Added:
Chris@95 199 case FileStates::Modified:
Chris@95 200 case FileStates::Removed:
Chris@95 201 files.push_back(f);
Chris@95 202 break;
Chris@95 203 default: break;
Chris@95 204 }
Chris@95 205 }
Chris@95 206 return files;
Chris@95 207 }
Chris@95 208
Chris@103 209 QStringList FileStatusWidget::getAllCommittableFiles() const
Chris@103 210 {
Chris@103 211 QStringList files;
Chris@103 212 files << m_fileStates.getFilesInState(FileStates::Modified);
Chris@103 213 files << m_fileStates.getFilesInState(FileStates::Added);
Chris@103 214 files << m_fileStates.getFilesInState(FileStates::Removed);
Chris@103 215 return files;
Chris@103 216 }
Chris@103 217
Chris@109 218 QStringList FileStatusWidget::getSelectedRevertableFiles() const
Chris@109 219 {
Chris@109 220 QStringList files;
Chris@109 221 foreach (QString f, m_selectedFiles) {
Chris@109 222 switch (m_fileStates.getStateOfFile(f)) {
Chris@109 223 case FileStates::Added:
Chris@109 224 case FileStates::Modified:
Chris@109 225 case FileStates::Removed:
Chris@109 226 case FileStates::Missing:
Chris@163 227 case FileStates::InConflict:
Chris@109 228 files.push_back(f);
Chris@109 229 break;
Chris@109 230 default: break;
Chris@109 231 }
Chris@109 232 }
Chris@109 233 return files;
Chris@109 234 }
Chris@109 235
Chris@109 236 QStringList FileStatusWidget::getAllRevertableFiles() const
Chris@109 237 {
Chris@109 238 QStringList files;
Chris@109 239 files << m_fileStates.getFilesInState(FileStates::Modified);
Chris@109 240 files << m_fileStates.getFilesInState(FileStates::Added);
Chris@109 241 files << m_fileStates.getFilesInState(FileStates::Removed);
Chris@109 242 files << m_fileStates.getFilesInState(FileStates::Missing);
Chris@163 243 files << m_fileStates.getFilesInState(FileStates::InConflict);
Chris@163 244 return files;
Chris@163 245 }
Chris@163 246
Chris@163 247 QStringList FileStatusWidget::getSelectedUnresolvedFiles() const
Chris@163 248 {
Chris@163 249 QStringList files;
Chris@163 250 foreach (QString f, m_selectedFiles) {
Chris@163 251 switch (m_fileStates.getStateOfFile(f)) {
Chris@163 252 case FileStates::InConflict:
Chris@163 253 files.push_back(f);
Chris@163 254 break;
Chris@163 255 default: break;
Chris@163 256 }
Chris@163 257 }
Chris@163 258 return files;
Chris@163 259 }
Chris@163 260
Chris@163 261 QStringList FileStatusWidget::getAllUnresolvedFiles() const
Chris@163 262 {
Chris@163 263 QStringList files;
Chris@163 264 files << m_fileStates.getFilesInState(FileStates::InConflict);
Chris@109 265 return files;
Chris@109 266 }
Chris@109 267
Chris@95 268 QStringList FileStatusWidget::getSelectedAddableFiles() const
Chris@95 269 {
Chris@95 270 QStringList files;
Chris@95 271 foreach (QString f, m_selectedFiles) {
Chris@95 272 switch (m_fileStates.getStateOfFile(f)) {
Chris@95 273 case FileStates::Unknown:
Chris@95 274 case FileStates::Removed:
Chris@95 275 files.push_back(f);
Chris@95 276 break;
Chris@95 277 default: break;
Chris@95 278 }
Chris@95 279 }
Chris@95 280 return files;
Chris@95 281 }
Chris@95 282
Chris@103 283 QStringList FileStatusWidget::getAllAddableFiles() const
Chris@103 284 {
Chris@103 285 QStringList files;
Chris@103 286 files << m_fileStates.getFilesInState(FileStates::Removed);
Chris@103 287 files << m_fileStates.getFilesInState(FileStates::Unknown);
Chris@103 288 return files;
Chris@103 289 }
Chris@103 290
Chris@95 291 QStringList FileStatusWidget::getSelectedRemovableFiles() const
Chris@95 292 {
Chris@95 293 QStringList files;
Chris@95 294 foreach (QString f, m_selectedFiles) {
Chris@95 295 switch (m_fileStates.getStateOfFile(f)) {
Chris@95 296 case FileStates::Clean:
Chris@95 297 case FileStates::Added:
Chris@95 298 case FileStates::Modified:
Chris@95 299 case FileStates::Missing:
Chris@163 300 case FileStates::InConflict:
Chris@95 301 files.push_back(f);
Chris@95 302 break;
Chris@95 303 default: break;
Chris@95 304 }
Chris@95 305 }
Chris@95 306 return files;
Chris@95 307 }
Chris@95 308
Chris@103 309 QStringList FileStatusWidget::getAllRemovableFiles() const
Chris@103 310 {
Chris@103 311 QStringList files;
Chris@103 312 files << m_fileStates.getFilesInState(FileStates::Clean);
Chris@103 313 files << m_fileStates.getFilesInState(FileStates::Added);
Chris@103 314 files << m_fileStates.getFilesInState(FileStates::Modified);
Chris@103 315 files << m_fileStates.getFilesInState(FileStates::Missing);
Chris@163 316 files << m_fileStates.getFilesInState(FileStates::InConflict);
Chris@103 317 return files;
Chris@103 318 }
Chris@103 319
Chris@88 320 void
Chris@88 321 FileStatusWidget::setLocalPath(QString p)
Chris@88 322 {
Chris@88 323 m_localPath = p;
Chris@88 324 m_localPathLabel->setText(p);
Chris@93 325 delete m_dateReference;
Chris@93 326 m_dateReference = new QFileInfo(p + "/.hg/dirstate");
Chris@93 327 if (!m_dateReference->exists() ||
Chris@93 328 !m_dateReference->isFile() ||
Chris@93 329 !m_dateReference->isReadable()) {
Chris@93 330 DEBUG << "FileStatusWidget::setLocalPath: date reference file "
Chris@93 331 << m_dateReference->absoluteFilePath()
Chris@93 332 << " does not exist, is not a file, or cannot be read"
Chris@93 333 << endl;
Chris@93 334 delete m_dateReference;
Chris@93 335 m_dateReference = 0;
Chris@93 336 }
Chris@88 337 }
Chris@88 338
Chris@88 339 void
Chris@88 340 FileStatusWidget::setRemoteURL(QString r)
Chris@88 341 {
Chris@88 342 m_remoteURL = r;
Chris@88 343 m_remoteURLLabel->setText(r);
Chris@88 344 }
Chris@88 345
Chris@88 346 void
Chris@92 347 FileStatusWidget::setFileStates(FileStates p)
Chris@88 348 {
Chris@92 349 m_fileStates = p;
Chris@88 350 updateWidgets();
Chris@88 351 }
Chris@88 352
Chris@88 353 void
Chris@115 354 FileStatusWidget::setState(QString b)
Chris@106 355 {
Chris@115 356 m_state = b;
Chris@115 357 updateStateLabel();
Chris@106 358 }
Chris@106 359
Chris@106 360 void
Chris@88 361 FileStatusWidget::updateWidgets()
Chris@88 362 {
Chris@95 363 QDateTime lastInteractionTime;
Chris@95 364 if (m_dateReference) {
Chris@95 365 lastInteractionTime = m_dateReference->lastModified();
Chris@95 366 DEBUG << "reference time: " << lastInteractionTime << endl;
Chris@95 367 }
Chris@95 368
Chris@95 369 QSet<QString> selectedFiles;
Chris@95 370 foreach (QString f, m_selectedFiles) selectedFiles.insert(f);
Chris@95 371
Chris@115 372 bool haveAnything = false;
Chris@115 373
Chris@94 374 foreach (FileStates::State s, m_stateListMap.keys()) {
Chris@95 375
Chris@94 376 QListWidget *w = m_stateListMap[s];
Chris@94 377 w->clear();
Chris@95 378 QStringList files = m_fileStates.getFilesInState(s);
Chris@93 379
Chris@95 380 QStringList highPriority, lowPriority;
Chris@95 381
Chris@95 382 foreach (QString file, files) {
Chris@95 383
Chris@95 384 bool highlighted = false;
Chris@95 385
Chris@95 386 if (s == FileStates::Unknown) {
Chris@95 387 // We want to highlight untracked files that have appeared
Chris@95 388 // since the last interaction with the repo
Chris@95 389 QString fn(m_localPath + "/" + file);
Chris@95 390 DEBUG << "comparing with " << fn << endl;
Chris@95 391 QFileInfo fi(fn);
Chris@100 392 if (fi.exists() && fi.created() > lastInteractionTime) {
Chris@95 393 DEBUG << "file " << fn << " is newer (" << fi.lastModified()
Chris@95 394 << ") than reference" << endl;
Chris@95 395 highlighted = true;
Chris@95 396 }
Chris@95 397 }
Chris@95 398
Chris@95 399 if (highlighted) {
Chris@95 400 highPriority.push_back(file);
Chris@95 401 } else {
Chris@95 402 lowPriority.push_back(file);
Chris@93 403 }
Chris@93 404 }
Chris@95 405
Chris@95 406 foreach (QString file, highPriority) {
Chris@95 407 QListWidgetItem *item = new QListWidgetItem(file);
Chris@95 408 w->addItem(item);
Chris@118 409 item->setForeground(QColor("#d40000")); //!!! and a nice gold star
Chris@95 410 item->setSelected(selectedFiles.contains(file));
Chris@95 411 }
Chris@95 412
Chris@95 413 foreach (QString file, lowPriority) {
Chris@95 414 QListWidgetItem *item = new QListWidgetItem(file);
Chris@95 415 w->addItem(item);
Chris@95 416 item->setSelected(selectedFiles.contains(file));
Chris@95 417 }
Chris@95 418
Chris@100 419 setLabelFor(w, s, !highPriority.empty());
Chris@100 420
Chris@115 421 if (files.empty()) {
Chris@115 422 w->parentWidget()->hide();
Chris@115 423 } else {
Chris@115 424 w->parentWidget()->show();
Chris@115 425 haveAnything = true;
Chris@115 426 }
Chris@93 427 }
Chris@115 428
Chris@115 429 m_noModificationsLabel->setVisible(!haveAnything);
Chris@115 430
Chris@115 431 updateStateLabel();
Chris@88 432 }
Chris@88 433
Chris@100 434 void FileStatusWidget::setLabelFor(QWidget *w, FileStates::State s, bool addHighlight)
Chris@100 435 {
Chris@100 436 QString text = labelFor(s, addHighlight);
Chris@100 437 QWidget *p = w->parentWidget();
Chris@100 438 QList<QLabel *> ql = p->findChildren<QLabel *>();
Chris@100 439 if (!ql.empty()) ql[0]->setText(text);
Chris@100 440 }
Chris@115 441
Chris@115 442 void FileStatusWidget::updateStateLabel()
Chris@115 443 {
Chris@115 444 m_stateLabel->setText(m_state);
Chris@115 445 }