annotate filestatuswidget.cpp @ 131:2550aaa09240

* Add connecting line to uncommitted item; turn Revert icon blue; cut down menus to things that actually work (well, mostly)
author Chris Cannam
date Tue, 30 Nov 2010 11:17:30 +0000
parents 9734fb0d6fff
children 908a1fdeed6a
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@115 65 (tr("You have made no 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@100 73 m_simpleLabels[FileStates::Unknown] = tr("Untracked:");
Chris@99 74
Chris@100 75 m_descriptions[FileStates::Clean] = tr("You have not changed these files.");
Chris@100 76 m_descriptions[FileStates::Modified] = tr("You have changed these files since you last committed them.");
Chris@100 77 m_descriptions[FileStates::Added] = tr("These files will be added to version control next time you commit.");
Chris@100 78 m_descriptions[FileStates::Removed] = tr("These files will be removed from version control next time you commit.<br>"
Chris@100 79 "They will not be deleted from the local folder.");
Chris@109 80 m_descriptions[FileStates::Missing] = tr("These files are recorded in the version control, but absent from your working folder.<br>"
Chris@109 81 "If you deleted them by accident, select them here and use Revert to restore their previous contents.<br>"
Chris@109 82 "If you deleted them intentionally, select them here and use Remove to tell the version control system about it.");
Chris@100 83 m_descriptions[FileStates::Unknown] = tr("These files are in your working folder but are not under version control.<br>"
Chris@100 84 "Select a file and use Add to place it under version control or Ignore to remove it from this list.");
Chris@100 85
Chris@118 86 m_highlightExplanation = tr("Files highlighted <font color=#d40000>in red</font> "
Chris@100 87 "have appeared since your most recent commit or update.");
Chris@89 88
Chris@94 89 for (int i = int(FileStates::FirstState);
Chris@94 90 i <= int(FileStates::LastState); ++i) {
Chris@89 91
Chris@94 92 FileStates::State s = FileStates::State(i);
Chris@89 93
Chris@89 94 QWidget *box = new QWidget;
Chris@89 95 QGridLayout *boxlayout = new QGridLayout;
Chris@99 96 boxlayout->setMargin(0);
Chris@89 97 box->setLayout(boxlayout);
Chris@89 98
Chris@106 99 boxlayout->addItem(new QSpacerItem(5, 5), 0, 0);
Chris@101 100
Chris@101 101 boxlayout->addWidget(new QLabel(labelFor(s)), 1, 0);
Chris@89 102
Chris@94 103 QListWidget *w = new QListWidget;
Chris@94 104 m_stateListMap[s] = w;
Chris@94 105 w->setSelectionMode(QListWidget::ExtendedSelection);
Chris@101 106 boxlayout->addWidget(w, 2, 0);
Chris@89 107
Chris@95 108 connect(w, SIGNAL(itemSelectionChanged()),
Chris@95 109 this, SLOT(itemSelectionChanged()));
Chris@95 110
Chris@115 111 layout->addWidget(box, ++row, 0, 1, 3);
Chris@89 112 box->hide();
Chris@89 113 }
Chris@89 114
Chris@89 115 layout->setRowStretch(++row, 20);
Chris@88 116 }
Chris@88 117
Chris@93 118 FileStatusWidget::~FileStatusWidget()
Chris@93 119 {
Chris@93 120 delete m_dateReference;
Chris@93 121 }
Chris@93 122
Chris@100 123 QString FileStatusWidget::labelFor(FileStates::State s, bool addHighlightExplanation)
Chris@100 124 {
Chris@100 125 if (addHighlightExplanation) {
Chris@100 126 return QString("<qt><b>%1</b><br>%2<br>%3</qt>")
Chris@100 127 .arg(m_simpleLabels[s])
Chris@100 128 .arg(m_descriptions[s])
Chris@100 129 .arg(m_highlightExplanation);
Chris@100 130 } else {
Chris@100 131 return QString("<qt><b>%1</b><br>%2</qt>")
Chris@100 132 .arg(m_simpleLabels[s])
Chris@100 133 .arg(m_descriptions[s]);
Chris@100 134 }
Chris@100 135 }
Chris@100 136
Chris@95 137 void FileStatusWidget::itemSelectionChanged()
Chris@95 138 {
Chris@95 139 m_selectedFiles.clear();
Chris@95 140
Chris@95 141 DEBUG << "FileStatusWidget::itemSelectionChanged" << endl;
Chris@95 142
Chris@95 143 foreach (QListWidget *w, m_stateListMap) {
Chris@95 144 QList<QListWidgetItem *> sel = w->selectedItems();
Chris@95 145 foreach (QListWidgetItem *i, sel) {
Chris@95 146 m_selectedFiles.push_back(i->text());
Chris@95 147 DEBUG << "file " << i->text() << " is selected" << endl;
Chris@95 148 }
Chris@95 149 }
Chris@95 150
Chris@95 151 emit selectionChanged();
Chris@95 152 }
Chris@95 153
Chris@94 154 void FileStatusWidget::clearSelections()
Chris@94 155 {
Chris@95 156 m_selectedFiles.clear();
Chris@94 157 foreach (QListWidget *w, m_stateListMap) {
Chris@94 158 w->clearSelection();
Chris@94 159 }
Chris@94 160 }
Chris@94 161
Chris@95 162 bool FileStatusWidget::haveChangesToCommit() const
Chris@95 163 {
Chris@95 164 return !m_fileStates.added().empty() ||
Chris@95 165 !m_fileStates.removed().empty() ||
Chris@95 166 !m_fileStates.modified().empty();
Chris@95 167 }
Chris@95 168
Chris@95 169 bool FileStatusWidget::haveSelection() const
Chris@95 170 {
Chris@95 171 return !m_selectedFiles.empty();
Chris@95 172 }
Chris@95 173
Chris@95 174 QStringList FileStatusWidget::getAllSelectedFiles() const
Chris@95 175 {
Chris@95 176 return m_selectedFiles;
Chris@95 177 }
Chris@95 178
Chris@95 179 QStringList FileStatusWidget::getSelectedCommittableFiles() const
Chris@95 180 {
Chris@95 181 QStringList files;
Chris@95 182 foreach (QString f, m_selectedFiles) {
Chris@95 183 switch (m_fileStates.getStateOfFile(f)) {
Chris@95 184 case FileStates::Added:
Chris@95 185 case FileStates::Modified:
Chris@95 186 case FileStates::Removed:
Chris@95 187 files.push_back(f);
Chris@95 188 break;
Chris@95 189 default: break;
Chris@95 190 }
Chris@95 191 }
Chris@95 192 return files;
Chris@95 193 }
Chris@95 194
Chris@103 195 QStringList FileStatusWidget::getAllCommittableFiles() const
Chris@103 196 {
Chris@103 197 QStringList files;
Chris@103 198 files << m_fileStates.getFilesInState(FileStates::Modified);
Chris@103 199 files << m_fileStates.getFilesInState(FileStates::Added);
Chris@103 200 files << m_fileStates.getFilesInState(FileStates::Removed);
Chris@103 201 return files;
Chris@103 202 }
Chris@103 203
Chris@109 204 QStringList FileStatusWidget::getSelectedRevertableFiles() const
Chris@109 205 {
Chris@109 206 QStringList files;
Chris@109 207 foreach (QString f, m_selectedFiles) {
Chris@109 208 switch (m_fileStates.getStateOfFile(f)) {
Chris@109 209 case FileStates::Added:
Chris@109 210 case FileStates::Modified:
Chris@109 211 case FileStates::Removed:
Chris@109 212 case FileStates::Missing:
Chris@109 213 files.push_back(f);
Chris@109 214 break;
Chris@109 215 default: break;
Chris@109 216 }
Chris@109 217 }
Chris@109 218 return files;
Chris@109 219 }
Chris@109 220
Chris@109 221 QStringList FileStatusWidget::getAllRevertableFiles() const
Chris@109 222 {
Chris@109 223 QStringList files;
Chris@109 224 files << m_fileStates.getFilesInState(FileStates::Modified);
Chris@109 225 files << m_fileStates.getFilesInState(FileStates::Added);
Chris@109 226 files << m_fileStates.getFilesInState(FileStates::Removed);
Chris@109 227 files << m_fileStates.getFilesInState(FileStates::Missing);
Chris@109 228 return files;
Chris@109 229 }
Chris@109 230
Chris@95 231 QStringList FileStatusWidget::getSelectedAddableFiles() const
Chris@95 232 {
Chris@95 233 QStringList files;
Chris@95 234 foreach (QString f, m_selectedFiles) {
Chris@95 235 switch (m_fileStates.getStateOfFile(f)) {
Chris@95 236 case FileStates::Unknown:
Chris@95 237 case FileStates::Removed:
Chris@95 238 files.push_back(f);
Chris@95 239 break;
Chris@95 240 default: break;
Chris@95 241 }
Chris@95 242 }
Chris@95 243 return files;
Chris@95 244 }
Chris@95 245
Chris@103 246 QStringList FileStatusWidget::getAllAddableFiles() const
Chris@103 247 {
Chris@103 248 QStringList files;
Chris@103 249 files << m_fileStates.getFilesInState(FileStates::Removed);
Chris@103 250 files << m_fileStates.getFilesInState(FileStates::Unknown);
Chris@103 251 return files;
Chris@103 252 }
Chris@103 253
Chris@95 254 QStringList FileStatusWidget::getSelectedRemovableFiles() const
Chris@95 255 {
Chris@95 256 QStringList files;
Chris@95 257 foreach (QString f, m_selectedFiles) {
Chris@95 258 switch (m_fileStates.getStateOfFile(f)) {
Chris@95 259 case FileStates::Clean:
Chris@95 260 case FileStates::Added:
Chris@95 261 case FileStates::Modified:
Chris@95 262 case FileStates::Missing:
Chris@95 263 files.push_back(f);
Chris@95 264 break;
Chris@95 265 default: break;
Chris@95 266 }
Chris@95 267 }
Chris@95 268 return files;
Chris@95 269 }
Chris@95 270
Chris@103 271 QStringList FileStatusWidget::getAllRemovableFiles() const
Chris@103 272 {
Chris@103 273 QStringList files;
Chris@103 274 files << m_fileStates.getFilesInState(FileStates::Clean);
Chris@103 275 files << m_fileStates.getFilesInState(FileStates::Added);
Chris@103 276 files << m_fileStates.getFilesInState(FileStates::Modified);
Chris@103 277 files << m_fileStates.getFilesInState(FileStates::Missing);
Chris@103 278 return files;
Chris@103 279 }
Chris@103 280
Chris@88 281 void
Chris@88 282 FileStatusWidget::setLocalPath(QString p)
Chris@88 283 {
Chris@88 284 m_localPath = p;
Chris@88 285 m_localPathLabel->setText(p);
Chris@93 286 delete m_dateReference;
Chris@93 287 m_dateReference = new QFileInfo(p + "/.hg/dirstate");
Chris@93 288 if (!m_dateReference->exists() ||
Chris@93 289 !m_dateReference->isFile() ||
Chris@93 290 !m_dateReference->isReadable()) {
Chris@93 291 DEBUG << "FileStatusWidget::setLocalPath: date reference file "
Chris@93 292 << m_dateReference->absoluteFilePath()
Chris@93 293 << " does not exist, is not a file, or cannot be read"
Chris@93 294 << endl;
Chris@93 295 delete m_dateReference;
Chris@93 296 m_dateReference = 0;
Chris@93 297 }
Chris@88 298 }
Chris@88 299
Chris@88 300 void
Chris@88 301 FileStatusWidget::setRemoteURL(QString r)
Chris@88 302 {
Chris@88 303 m_remoteURL = r;
Chris@88 304 m_remoteURLLabel->setText(r);
Chris@88 305 }
Chris@88 306
Chris@88 307 void
Chris@92 308 FileStatusWidget::setFileStates(FileStates p)
Chris@88 309 {
Chris@92 310 m_fileStates = p;
Chris@88 311 updateWidgets();
Chris@88 312 }
Chris@88 313
Chris@88 314 void
Chris@115 315 FileStatusWidget::setState(QString b)
Chris@106 316 {
Chris@115 317 m_state = b;
Chris@115 318 updateStateLabel();
Chris@106 319 }
Chris@106 320
Chris@106 321 void
Chris@88 322 FileStatusWidget::updateWidgets()
Chris@88 323 {
Chris@95 324 QDateTime lastInteractionTime;
Chris@95 325 if (m_dateReference) {
Chris@95 326 lastInteractionTime = m_dateReference->lastModified();
Chris@95 327 DEBUG << "reference time: " << lastInteractionTime << endl;
Chris@95 328 }
Chris@95 329
Chris@95 330 QSet<QString> selectedFiles;
Chris@95 331 foreach (QString f, m_selectedFiles) selectedFiles.insert(f);
Chris@95 332
Chris@115 333 bool haveAnything = false;
Chris@115 334
Chris@94 335 foreach (FileStates::State s, m_stateListMap.keys()) {
Chris@95 336
Chris@94 337 QListWidget *w = m_stateListMap[s];
Chris@94 338 w->clear();
Chris@95 339 QStringList files = m_fileStates.getFilesInState(s);
Chris@93 340
Chris@95 341 QStringList highPriority, lowPriority;
Chris@95 342
Chris@95 343 foreach (QString file, files) {
Chris@95 344
Chris@95 345 bool highlighted = false;
Chris@95 346
Chris@95 347 if (s == FileStates::Unknown) {
Chris@95 348 // We want to highlight untracked files that have appeared
Chris@95 349 // since the last interaction with the repo
Chris@95 350 QString fn(m_localPath + "/" + file);
Chris@95 351 DEBUG << "comparing with " << fn << endl;
Chris@95 352 QFileInfo fi(fn);
Chris@100 353 if (fi.exists() && fi.created() > lastInteractionTime) {
Chris@95 354 DEBUG << "file " << fn << " is newer (" << fi.lastModified()
Chris@95 355 << ") than reference" << endl;
Chris@95 356 highlighted = true;
Chris@95 357 }
Chris@95 358 }
Chris@95 359
Chris@95 360 if (highlighted) {
Chris@95 361 highPriority.push_back(file);
Chris@95 362 } else {
Chris@95 363 lowPriority.push_back(file);
Chris@93 364 }
Chris@93 365 }
Chris@95 366
Chris@95 367 foreach (QString file, highPriority) {
Chris@95 368 QListWidgetItem *item = new QListWidgetItem(file);
Chris@95 369 w->addItem(item);
Chris@118 370 item->setForeground(QColor("#d40000")); //!!! and a nice gold star
Chris@95 371 item->setSelected(selectedFiles.contains(file));
Chris@95 372 }
Chris@95 373
Chris@95 374 foreach (QString file, lowPriority) {
Chris@95 375 QListWidgetItem *item = new QListWidgetItem(file);
Chris@95 376 w->addItem(item);
Chris@95 377 item->setSelected(selectedFiles.contains(file));
Chris@95 378 }
Chris@95 379
Chris@100 380 setLabelFor(w, s, !highPriority.empty());
Chris@100 381
Chris@115 382 if (files.empty()) {
Chris@115 383 w->parentWidget()->hide();
Chris@115 384 } else {
Chris@115 385 w->parentWidget()->show();
Chris@115 386 haveAnything = true;
Chris@115 387 }
Chris@93 388 }
Chris@115 389
Chris@115 390 m_noModificationsLabel->setVisible(!haveAnything);
Chris@115 391
Chris@115 392 updateStateLabel();
Chris@88 393 }
Chris@88 394
Chris@100 395 void FileStatusWidget::setLabelFor(QWidget *w, FileStates::State s, bool addHighlight)
Chris@100 396 {
Chris@100 397 QString text = labelFor(s, addHighlight);
Chris@100 398 QWidget *p = w->parentWidget();
Chris@100 399 QList<QLabel *> ql = p->findChildren<QLabel *>();
Chris@100 400 if (!ql.empty()) ql[0]->setText(text);
Chris@100 401 }
Chris@115 402
Chris@115 403 void FileStatusWidget::updateStateLabel()
Chris@115 404 {
Chris@115 405 m_stateLabel->setText(m_state);
Chris@115 406 }