Version 2.6
[qgit4/redivivus.git] / src / FileHistory.cc
blob35124ea3072ee657e8fa394a78c921f46b577674
1 /*
2 Description: interface to git programs
4 Author: Marco Costalba (C) 2005-2007
6 Copyright: See COPYING file that comes with this distribution
8 */
10 #include "FileHistory.h"
12 #include <QApplication>
13 #include <QDateTime>
14 #include <QFontMetrics>
16 #include "lanes.h"
18 #include "git.h"
20 using namespace QGit;
22 FileHistory::FileHistory(QObject* p, Git* g) : QAbstractItemModel(p), git(g) {
24 headerInfo << "Graph" << "Id" << "Short Log" << "Author" << "Author Date";
25 lns = new Lanes();
26 revs.reserve(QGit::MAX_DICT_SIZE);
27 clear(); // after _headerInfo is set
29 connect(git, SIGNAL(newRevsAdded(const FileHistory*, const QVector<ShaString>&)),
30 this, SLOT(on_newRevsAdded(const FileHistory*, const QVector<ShaString>&)));
32 connect(git, SIGNAL(loadCompleted(const FileHistory*, const QString&)),
33 this, SLOT(on_loadCompleted(const FileHistory*, const QString&)));
35 connect(git, SIGNAL(changeFont(const QFont&)), this, SLOT(on_changeFont(const QFont&)));
38 FileHistory::~FileHistory() {
40 clear();
41 delete lns;
44 void FileHistory::resetFileNames(SCRef fn) {
46 fNames.clear();
47 fNames.append(fn);
48 curFNames = fNames;
51 int FileHistory::rowCount(const QModelIndex& parent) const {
53 return (!parent.isValid() ? rowCnt : 0);
56 bool FileHistory::hasChildren(const QModelIndex& parent) const {
58 return !parent.isValid();
61 int FileHistory::row(SCRef sha) const {
63 const Rev* r = git->revLookup(sha, this);
64 return (r ? r->orderIdx : -1);
67 const QString FileHistory::sha(int row) const {
69 return (row < 0 || row >= rowCnt ? "" : QString(revOrder.at(row)));
72 void FileHistory::flushTail() {
74 if (earlyOutputCnt < 0 || earlyOutputCnt >= revOrder.count()) {
75 dbp("ASSERT in FileHistory::flushTail(), earlyOutputCnt is %1", earlyOutputCnt);
76 return;
78 int cnt = revOrder.count() - earlyOutputCnt + 1;
79 beginResetModel();
80 while (cnt > 0) {
81 const ShaString& sha = revOrder.last();
82 const Rev* c = revs[sha];
83 delete c;
84 revs.remove(sha);
85 revOrder.pop_back();
86 cnt--;
88 // reset all lanes, will be redrawn
89 for (int i = earlyOutputCntBase; i < revOrder.count(); i++) {
90 Rev* c = const_cast<Rev*>(revs[revOrder[i]]);
91 c->lanes.clear();
93 firstFreeLane = earlyOutputCntBase;
94 lns->clear();
95 rowCnt = revOrder.count();
96 endResetModel();
99 void FileHistory::clear(bool complete) {
101 if (!complete) {
102 if (revOrder.count() > 0)
103 flushTail();
104 return;
106 git->cancelDataLoading(this);
108 beginResetModel();
109 qDeleteAll(revs);
110 revs.clear();
111 revOrder.clear();
112 firstFreeLane = loadTime = earlyOutputCntBase = 0;
113 setEarlyOutputState(false);
114 lns->clear();
115 fNames.clear();
116 curFNames.clear();
117 qDeleteAll(rowData);
118 rowData.clear();
120 if (testFlag(REL_DATE_F)) {
121 secs = QDateTime::currentDateTime().toTime_t();
122 headerInfo[4] = "Last Change";
123 } else {
124 secs = 0;
125 headerInfo[4] = "Author Date";
127 rowCnt = revOrder.count();
128 annIdValid = false;
129 endResetModel();
130 emit headerDataChanged(Qt::Horizontal, 0, 4);
133 void FileHistory::on_newRevsAdded(const FileHistory* fh, const QVector<ShaString>& shaVec) {
135 if (fh != this) // signal newRevsAdded() is broadcast
136 return;
138 // do not process revisions if there are possible renamed points
139 // or pending renamed patch to apply
140 if (!renamedRevs.isEmpty() || !renamedPatches.isEmpty())
141 return;
143 // do not attempt to insert 0 rows since the inclusive range would be invalid
144 if (rowCnt == shaVec.count())
145 return;
147 beginInsertRows(QModelIndex(), rowCnt, shaVec.count()-1);
148 rowCnt = shaVec.count();
149 endInsertRows();
152 void FileHistory::on_loadCompleted(const FileHistory* fh, const QString&) {
154 if (fh != this || rowCnt >= revOrder.count())
155 return;
157 // now we can process last revision
158 rowCnt = revOrder.count();
159 beginResetModel(); // force a reset to avoid artifacts in file history graph under Windows
160 endResetModel();
162 // adjust Id column width according to the numbers of revisions we have
163 if (!git->isMainHistory(this))
164 on_changeFont(QGit::STD_FONT);
167 void FileHistory::on_changeFont(const QFont& f) {
169 QString maxStr(QString::number(rowCnt).length() + 1, '8');
170 QFontMetrics fmRows(f);
171 int neededWidth = fmRows.boundingRect(maxStr).width();
173 QString id("Id");
174 QFontMetrics fmId(qApp->font());
176 while (fmId.boundingRect(id).width() < neededWidth)
177 id += ' ';
179 headerInfo[1] = id;
180 emit headerDataChanged(Qt::Horizontal, 1, 1);
183 Qt::ItemFlags FileHistory::flags(const QModelIndex&) const {
185 return Qt::ItemIsEnabled | Qt::ItemIsSelectable; // read only
188 QVariant FileHistory::headerData(int section, Qt::Orientation orientation, int role) const {
190 if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
191 return headerInfo.at(section);
193 return QVariant();
196 QModelIndex FileHistory::index(int row, int column, const QModelIndex&) const {
198 index() is called much more then data(), also by a 100X factor on
199 big archives, so we use just the row number as QModelIndex payload
200 and defer the revision lookup later, inside data().
201 Because row and column info are stored anyway in QModelIndex we
202 don't need to add any additional data.
204 if (row < 0 || row >= rowCnt)
205 return QModelIndex();
207 return createIndex(row, column, (void*)0);
210 QModelIndex FileHistory::parent(const QModelIndex&) const {
212 static const QModelIndex no_parent;
213 return no_parent;
216 const QString FileHistory::timeDiff(unsigned long secs) const {
218 ulong days = secs / (3600 * 24);
219 ulong hours = (secs - days * 3600 * 24) / 3600;
220 ulong min = (secs - days * 3600 * 24 - hours * 3600) / 60;
221 ulong sec = secs - days * 3600 * 24 - hours * 3600 - min * 60;
222 QString tmp;
223 if (days > 0)
224 tmp.append(QString::number(days) + "d ");
226 if (hours > 0 || !tmp.isEmpty())
227 tmp.append(QString::number(hours) + "h ");
229 if (min > 0 || !tmp.isEmpty())
230 tmp.append(QString::number(min) + "m ");
232 tmp.append(QString::number(sec) + "s");
233 return tmp;
236 QVariant FileHistory::data(const QModelIndex& index, int role) const {
238 static const QVariant no_value;
240 if (!index.isValid() || role != Qt::DisplayRole)
241 return no_value; // fast path, 90% of calls ends here!
243 const Rev* r = git->revLookup(revOrder.at(index.row()), this);
244 if (!r)
245 return no_value;
247 int col = index.column();
249 // calculate lanes
250 if (r->lanes.count() == 0)
251 git->setLane(r->sha(), const_cast<FileHistory*>(this));
253 if (col == QGit::ANN_ID_COL)
254 return (annIdValid ? rowCnt - index.row() : QVariant());
256 if (col == QGit::LOG_COL)
257 return r->shortLog();
259 if (col == QGit::AUTH_COL)
260 return r->author();
262 if (col == QGit::TIME_COL && r->sha() != QGit::ZERO_SHA_RAW) {
264 if (secs != 0) // secs is 0 for absolute date
265 return timeDiff(secs - r->authorDate().toULong());
266 else
267 return git->getLocalDate(r->authorDate());
269 return no_value;