2 Description: interface to git programs
4 Author: Marco Costalba (C) 2005-2007
6 Copyright: See COPYING file that comes with this distribution
10 #include "FileHistory.h"
12 #include <QApplication>
14 #include <QFontMetrics>
22 FileHistory::FileHistory(QObject
* p
, Git
* g
) : QAbstractItemModel(p
), git(g
) {
24 headerInfo
<< "Graph" << "Id" << "Short Log" << "Author" << "Author Date";
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() {
44 void FileHistory::resetFileNames(SCRef fn
) {
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
);
78 int cnt
= revOrder
.count() - earlyOutputCnt
+ 1;
81 const ShaString
& sha
= revOrder
.last();
82 const Rev
* c
= revs
[sha
];
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
]]);
93 firstFreeLane
= earlyOutputCntBase
;
95 rowCnt
= revOrder
.count();
99 void FileHistory::clear(bool complete
) {
102 if (revOrder
.count() > 0)
106 git
->cancelDataLoading(this);
112 firstFreeLane
= loadTime
= earlyOutputCntBase
= 0;
113 setEarlyOutputState(false);
120 if (testFlag(REL_DATE_F
)) {
121 secs
= QDateTime::currentDateTime().toTime_t();
122 headerInfo
[4] = "Last Change";
125 headerInfo
[4] = "Author Date";
127 rowCnt
= revOrder
.count();
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
138 // do not process revisions if there are possible renamed points
139 // or pending renamed patch to apply
140 if (!renamedRevs
.isEmpty() || !renamedPatches
.isEmpty())
143 // do not attempt to insert 0 rows since the inclusive range would be invalid
144 if (rowCnt
== shaVec
.count())
147 beginInsertRows(QModelIndex(), rowCnt
, shaVec
.count()-1);
148 rowCnt
= shaVec
.count();
152 void FileHistory::on_loadCompleted(const FileHistory
* fh
, const QString
&) {
154 if (fh
!= this || rowCnt
>= revOrder
.count())
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
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();
174 QFontMetrics
fmId(qApp
->font());
176 while (fmId
.boundingRect(id
).width() < neededWidth
)
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
);
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
;
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;
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");
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);
247 int col
= index
.column();
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
)
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());
267 return git
->getLocalDate(r
->authorDate());