Chris@88: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@88: Chris@88: /* Chris@88: EasyMercurial Chris@88: Chris@88: Based on HgExplorer by Jari Korhonen Chris@88: Copyright (c) 2010 Jari Korhonen Chris@88: Copyright (c) 2010 Chris Cannam Chris@88: Copyright (c) 2010 Queen Mary, University of London Chris@88: Chris@88: This program is free software; you can redistribute it and/or Chris@88: modify it under the terms of the GNU General Public License as Chris@88: published by the Free Software Foundation; either version 2 of the Chris@88: License, or (at your option) any later version. See the file Chris@88: COPYING included with this distribution for more information. Chris@88: */ Chris@88: Chris@88: #include "filestatuswidget.h" Chris@88: Chris@88: #include Chris@88: #include Chris@88: #include Chris@93: #include Chris@93: #include Chris@93: #include Chris@93: Chris@93: #include "debug.h" Chris@88: Chris@88: FileStatusWidget::FileStatusWidget(QWidget *parent) : Chris@93: QWidget(parent), Chris@93: m_dateReference(0) Chris@88: { Chris@88: QGridLayout *layout = new QGridLayout; Chris@88: setLayout(layout); Chris@88: Chris@88: int row = 0; Chris@88: Chris@88: layout->addWidget(new QLabel(tr("Local:")), row, 0); Chris@88: m_localPathLabel = new QLabel; Chris@89: QFont f(m_localPathLabel->font()); Chris@89: f.setBold(true); Chris@89: m_localPathLabel->setFont(f); Chris@88: layout->addWidget(m_localPathLabel, row, 1); Chris@88: Chris@88: ++row; Chris@88: layout->addWidget(new QLabel(tr("Remote:")), row, 0); Chris@88: m_remoteURLLabel = new QLabel; Chris@88: layout->addWidget(m_remoteURLLabel, row, 1); Chris@88: Chris@106: ++row; Chris@106: layout->addWidget(new QLabel(tr("Branch:")), row, 0); Chris@106: m_branchLabel = new QLabel; Chris@106: layout->addWidget(m_branchLabel, row, 1); Chris@106: Chris@89: layout->setColumnStretch(1, 20); Chris@88: Chris@106: layout->addWidget(new QLabel("
"), ++row, 0, 1, 2); Chris@106: Chris@100: m_simpleLabels[FileStates::Clean] = tr("Unmodified:"); Chris@100: m_simpleLabels[FileStates::Modified] = tr("Modified:"); Chris@100: m_simpleLabels[FileStates::Added] = tr("Added:"); Chris@100: m_simpleLabels[FileStates::Removed] = tr("Removed:"); Chris@100: m_simpleLabels[FileStates::Missing] = tr("Missing:"); Chris@100: m_simpleLabels[FileStates::Unknown] = tr("Untracked:"); Chris@99: Chris@100: m_descriptions[FileStates::Clean] = tr("You have not changed these files."); Chris@100: m_descriptions[FileStates::Modified] = tr("You have changed these files since you last committed them."); Chris@100: m_descriptions[FileStates::Added] = tr("These files will be added to version control next time you commit."); Chris@100: m_descriptions[FileStates::Removed] = tr("These files will be removed from version control next time you commit.
" Chris@100: "They will not be deleted from the local folder."); Chris@109: m_descriptions[FileStates::Missing] = tr("These files are recorded in the version control, but absent from your working folder.
" Chris@109: "If you deleted them by accident, select them here and use Revert to restore their previous contents.
" Chris@109: "If you deleted them intentionally, select them here and use Remove to tell the version control system about it."); Chris@100: m_descriptions[FileStates::Unknown] = tr("These files are in your working folder but are not under version control.
" Chris@100: "Select a file and use Add to place it under version control or Ignore to remove it from this list."); Chris@100: Chris@102: m_highlightExplanation = tr("Files highlighted in red " Chris@100: "have appeared since your most recent commit or update."); Chris@89: Chris@94: for (int i = int(FileStates::FirstState); Chris@94: i <= int(FileStates::LastState); ++i) { Chris@89: Chris@94: FileStates::State s = FileStates::State(i); Chris@89: Chris@89: QWidget *box = new QWidget; Chris@89: QGridLayout *boxlayout = new QGridLayout; Chris@99: boxlayout->setMargin(0); Chris@89: box->setLayout(boxlayout); Chris@89: Chris@106: boxlayout->addItem(new QSpacerItem(5, 5), 0, 0); Chris@101: Chris@101: boxlayout->addWidget(new QLabel(labelFor(s)), 1, 0); Chris@89: Chris@94: QListWidget *w = new QListWidget; Chris@94: m_stateListMap[s] = w; Chris@94: w->setSelectionMode(QListWidget::ExtendedSelection); Chris@101: boxlayout->addWidget(w, 2, 0); Chris@89: Chris@95: connect(w, SIGNAL(itemSelectionChanged()), Chris@95: this, SLOT(itemSelectionChanged())); Chris@95: Chris@89: layout->addWidget(box, ++row, 0, 1, 2); Chris@89: box->hide(); Chris@89: } Chris@89: Chris@89: layout->setRowStretch(++row, 20); Chris@88: } Chris@88: Chris@93: FileStatusWidget::~FileStatusWidget() Chris@93: { Chris@93: delete m_dateReference; Chris@93: } Chris@93: Chris@100: QString FileStatusWidget::labelFor(FileStates::State s, bool addHighlightExplanation) Chris@100: { Chris@100: if (addHighlightExplanation) { Chris@100: return QString("%1
%2
%3
") Chris@100: .arg(m_simpleLabels[s]) Chris@100: .arg(m_descriptions[s]) Chris@100: .arg(m_highlightExplanation); Chris@100: } else { Chris@100: return QString("%1
%2
") Chris@100: .arg(m_simpleLabels[s]) Chris@100: .arg(m_descriptions[s]); Chris@100: } Chris@100: } Chris@100: Chris@95: void FileStatusWidget::itemSelectionChanged() Chris@95: { Chris@95: m_selectedFiles.clear(); Chris@95: Chris@95: DEBUG << "FileStatusWidget::itemSelectionChanged" << endl; Chris@95: Chris@95: foreach (QListWidget *w, m_stateListMap) { Chris@95: QList sel = w->selectedItems(); Chris@95: foreach (QListWidgetItem *i, sel) { Chris@95: m_selectedFiles.push_back(i->text()); Chris@95: DEBUG << "file " << i->text() << " is selected" << endl; Chris@95: } Chris@95: } Chris@95: Chris@95: emit selectionChanged(); Chris@95: } Chris@95: Chris@94: void FileStatusWidget::clearSelections() Chris@94: { Chris@95: m_selectedFiles.clear(); Chris@94: foreach (QListWidget *w, m_stateListMap) { Chris@94: w->clearSelection(); Chris@94: } Chris@94: } Chris@94: Chris@95: bool FileStatusWidget::haveChangesToCommit() const Chris@95: { Chris@95: return !m_fileStates.added().empty() || Chris@95: !m_fileStates.removed().empty() || Chris@95: !m_fileStates.modified().empty(); Chris@95: } Chris@95: Chris@95: bool FileStatusWidget::haveSelection() const Chris@95: { Chris@95: return !m_selectedFiles.empty(); Chris@95: } Chris@95: Chris@95: QStringList FileStatusWidget::getAllSelectedFiles() const Chris@95: { Chris@95: return m_selectedFiles; Chris@95: } Chris@95: Chris@95: QStringList FileStatusWidget::getSelectedCommittableFiles() const Chris@95: { Chris@95: QStringList files; Chris@95: foreach (QString f, m_selectedFiles) { Chris@95: switch (m_fileStates.getStateOfFile(f)) { Chris@95: case FileStates::Added: Chris@95: case FileStates::Modified: Chris@95: case FileStates::Removed: Chris@95: files.push_back(f); Chris@95: break; Chris@95: default: break; Chris@95: } Chris@95: } Chris@95: return files; Chris@95: } Chris@95: Chris@103: QStringList FileStatusWidget::getAllCommittableFiles() const Chris@103: { Chris@103: QStringList files; Chris@103: files << m_fileStates.getFilesInState(FileStates::Modified); Chris@103: files << m_fileStates.getFilesInState(FileStates::Added); Chris@103: files << m_fileStates.getFilesInState(FileStates::Removed); Chris@103: return files; Chris@103: } Chris@103: Chris@109: QStringList FileStatusWidget::getSelectedRevertableFiles() const Chris@109: { Chris@109: QStringList files; Chris@109: foreach (QString f, m_selectedFiles) { Chris@109: switch (m_fileStates.getStateOfFile(f)) { Chris@109: case FileStates::Added: Chris@109: case FileStates::Modified: Chris@109: case FileStates::Removed: Chris@109: case FileStates::Missing: Chris@109: files.push_back(f); Chris@109: break; Chris@109: default: break; Chris@109: } Chris@109: } Chris@109: return files; Chris@109: } Chris@109: Chris@109: QStringList FileStatusWidget::getAllRevertableFiles() const Chris@109: { Chris@109: QStringList files; Chris@109: files << m_fileStates.getFilesInState(FileStates::Modified); Chris@109: files << m_fileStates.getFilesInState(FileStates::Added); Chris@109: files << m_fileStates.getFilesInState(FileStates::Removed); Chris@109: files << m_fileStates.getFilesInState(FileStates::Missing); Chris@109: return files; Chris@109: } Chris@109: Chris@95: QStringList FileStatusWidget::getSelectedAddableFiles() const Chris@95: { Chris@95: QStringList files; Chris@95: foreach (QString f, m_selectedFiles) { Chris@95: switch (m_fileStates.getStateOfFile(f)) { Chris@95: case FileStates::Unknown: Chris@95: case FileStates::Removed: Chris@95: files.push_back(f); Chris@95: break; Chris@95: default: break; Chris@95: } Chris@95: } Chris@95: return files; Chris@95: } Chris@95: Chris@103: QStringList FileStatusWidget::getAllAddableFiles() const Chris@103: { Chris@103: QStringList files; Chris@103: files << m_fileStates.getFilesInState(FileStates::Removed); Chris@103: files << m_fileStates.getFilesInState(FileStates::Unknown); Chris@103: return files; Chris@103: } Chris@103: Chris@95: QStringList FileStatusWidget::getSelectedRemovableFiles() const Chris@95: { Chris@95: QStringList files; Chris@95: foreach (QString f, m_selectedFiles) { Chris@95: switch (m_fileStates.getStateOfFile(f)) { Chris@95: case FileStates::Clean: Chris@95: case FileStates::Added: Chris@95: case FileStates::Modified: Chris@95: case FileStates::Missing: Chris@95: files.push_back(f); Chris@95: break; Chris@95: default: break; Chris@95: } Chris@95: } Chris@95: return files; Chris@95: } Chris@95: Chris@103: QStringList FileStatusWidget::getAllRemovableFiles() const Chris@103: { Chris@103: QStringList files; Chris@103: files << m_fileStates.getFilesInState(FileStates::Clean); Chris@103: files << m_fileStates.getFilesInState(FileStates::Added); Chris@103: files << m_fileStates.getFilesInState(FileStates::Modified); Chris@103: files << m_fileStates.getFilesInState(FileStates::Missing); Chris@103: return files; Chris@103: } Chris@103: Chris@88: void Chris@88: FileStatusWidget::setLocalPath(QString p) Chris@88: { Chris@88: m_localPath = p; Chris@88: m_localPathLabel->setText(p); Chris@93: delete m_dateReference; Chris@93: m_dateReference = new QFileInfo(p + "/.hg/dirstate"); Chris@93: if (!m_dateReference->exists() || Chris@93: !m_dateReference->isFile() || Chris@93: !m_dateReference->isReadable()) { Chris@93: DEBUG << "FileStatusWidget::setLocalPath: date reference file " Chris@93: << m_dateReference->absoluteFilePath() Chris@93: << " does not exist, is not a file, or cannot be read" Chris@93: << endl; Chris@93: delete m_dateReference; Chris@93: m_dateReference = 0; Chris@93: } Chris@88: } Chris@88: Chris@88: void Chris@88: FileStatusWidget::setRemoteURL(QString r) Chris@88: { Chris@88: m_remoteURL = r; Chris@88: m_remoteURLLabel->setText(r); Chris@88: } Chris@88: Chris@88: void Chris@92: FileStatusWidget::setFileStates(FileStates p) Chris@88: { Chris@92: m_fileStates = p; Chris@88: updateWidgets(); Chris@88: } Chris@88: Chris@88: void Chris@106: FileStatusWidget::setBranch(QString b) Chris@106: { Chris@106: m_branch = b; Chris@106: m_branchLabel->setText(b); Chris@106: } Chris@106: Chris@106: void Chris@88: FileStatusWidget::updateWidgets() Chris@88: { Chris@95: QDateTime lastInteractionTime; Chris@95: if (m_dateReference) { Chris@95: lastInteractionTime = m_dateReference->lastModified(); Chris@95: DEBUG << "reference time: " << lastInteractionTime << endl; Chris@95: } Chris@95: Chris@95: QSet selectedFiles; Chris@95: foreach (QString f, m_selectedFiles) selectedFiles.insert(f); Chris@95: Chris@94: foreach (FileStates::State s, m_stateListMap.keys()) { Chris@95: Chris@94: QListWidget *w = m_stateListMap[s]; Chris@94: w->clear(); Chris@95: QStringList files = m_fileStates.getFilesInState(s); Chris@93: Chris@95: QStringList highPriority, lowPriority; Chris@95: Chris@95: foreach (QString file, files) { Chris@95: Chris@95: bool highlighted = false; Chris@95: Chris@95: if (s == FileStates::Unknown) { Chris@95: // We want to highlight untracked files that have appeared Chris@95: // since the last interaction with the repo Chris@95: QString fn(m_localPath + "/" + file); Chris@95: DEBUG << "comparing with " << fn << endl; Chris@95: QFileInfo fi(fn); Chris@100: if (fi.exists() && fi.created() > lastInteractionTime) { Chris@95: DEBUG << "file " << fn << " is newer (" << fi.lastModified() Chris@95: << ") than reference" << endl; Chris@95: highlighted = true; Chris@95: } Chris@95: } Chris@95: Chris@95: if (highlighted) { Chris@95: highPriority.push_back(file); Chris@95: } else { Chris@95: lowPriority.push_back(file); Chris@93: } Chris@93: } Chris@95: Chris@95: foreach (QString file, highPriority) { Chris@95: QListWidgetItem *item = new QListWidgetItem(file); Chris@95: w->addItem(item); Chris@95: item->setForeground(Qt::red); //!!! and a nice gold star Chris@95: item->setSelected(selectedFiles.contains(file)); Chris@95: } Chris@95: Chris@95: foreach (QString file, lowPriority) { Chris@95: QListWidgetItem *item = new QListWidgetItem(file); Chris@95: w->addItem(item); Chris@95: item->setSelected(selectedFiles.contains(file)); Chris@95: } Chris@95: Chris@100: setLabelFor(w, s, !highPriority.empty()); Chris@100: Chris@95: w->parentWidget()->setVisible(!files.empty()); Chris@93: } Chris@88: } Chris@88: Chris@100: void FileStatusWidget::setLabelFor(QWidget *w, FileStates::State s, bool addHighlight) Chris@100: { Chris@100: QString text = labelFor(s, addHighlight); Chris@100: QWidget *p = w->parentWidget(); Chris@100: QList ql = p->findChildren(); Chris@100: if (!ql.empty()) ql[0]->setText(text); Chris@100: }