Version 2.6
[qgit4/redivivus.git] / src / treeview.cpp
blob5a72de0c88f7e34a6afd11e8b1d456f06d78ef3f
1 /*
2 Description: files tree view
4 Author: Marco Costalba (C) 2005-2007
6 Copyright: See COPYING file that comes with this distribution
8 */
9 #include <QApplication>
10 #include <QTreeWidgetItemIterator>
11 #include "git.h"
12 #include "domain.h"
13 #include "mainimpl.h"
14 #include "treeview.h"
16 QString FileItem::fullName() const {
18 QTreeWidgetItem* p = parent();
19 QString s(p ? text(0) : ""); // root directory has no fullName
20 while (p && p->parent()) {
21 s.prepend(p->text(0) + '/');
22 p = p->parent();
24 return s;
27 void FileItem::setBold(bool b) {
29 if (font(0).bold() == b)
30 return;
32 QFont fnt(font(0));
33 fnt.setBold(b);
34 setFont(0, fnt);
37 DirItem::DirItem(DirItem* p, SCRef ts, SCRef nm) : FileItem(p, nm), treeSha(ts) {}
38 DirItem::DirItem(QTreeWidget* p, SCRef ts, SCRef nm) : FileItem(p, nm), treeSha(ts) {}
40 void TreeView::setup(Domain* dm, Git* g) {
42 d = dm;
43 git = g;
44 st = &(d->st);
45 ignoreCurrentChanged = false;
46 isWorkingDir = false;
48 // set built-in pixmaps
49 folderClosed = QGit::mimePix(".#folder_closed");
50 folderOpen = QGit::mimePix(".#folder_open");
51 fileDefault = QGit::mimePix(".#default");
53 connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)),
54 this, SLOT(on_itemExpanded(QTreeWidgetItem*)));
56 connect(this, SIGNAL(itemCollapsed(QTreeWidgetItem*)),
57 this, SLOT(on_itemCollapsed(QTreeWidgetItem*)));
59 connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)),
60 this, SLOT(on_currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)));
62 connect(this, SIGNAL(customContextMenuRequested(const QPoint&)),
63 this, SLOT(on_customContextMenuRequested(const QPoint&)));
66 void TreeView::on_currentItemChanged(QTreeWidgetItem* item, QTreeWidgetItem*) {
68 if (item) {
69 SCRef fn = ((FileItem*)item)->fullName();
70 if (!ignoreCurrentChanged && fn != st->fileName()) {
71 st->setFileName(fn);
72 st->setSelectItem(true);
73 UPDATE_DOMAIN(d);
78 void TreeView::on_customContextMenuRequested(const QPoint& pos) {
80 QTreeWidgetItem* item = itemAt(pos);
81 if (item)
82 emit contextMenu(fullName(item), QGit::POPUP_TREE_EV);
85 void TreeView::clear() {
87 rootName = "";
88 QTreeWidget::clear();
91 bool TreeView::isModified(SCRef path, bool isDir) {
93 if (isDir)
94 return modifiedDirs.contains(path);
96 return modifiedFiles.contains(path);
99 bool TreeView::isDir(SCRef fileName) {
101 // if currentItem is NULL or is different from fileName
102 // return false, because treeview is not updated while
103 // not visible, so could be out of sync.
104 FileItem* item = static_cast<FileItem*>(currentItem());
105 if (item == NULL || item->fullName() != fileName)
106 return false;
108 return dynamic_cast<DirItem*>(item);
111 const QString TreeView::fullName(QTreeWidgetItem* item) {
113 FileItem* f = static_cast<FileItem*>(item);
114 return (item ? f->fullName() : "");
117 void TreeView::getTreeSelectedItems(QStringList& selectedItems) {
119 selectedItems.clear();
120 QList<QTreeWidgetItem*> ls = QTreeWidget::selectedItems();
121 FOREACH (QList<QTreeWidgetItem*>, it, ls) {
122 FileItem* f = static_cast<FileItem*>(*it);
123 selectedItems.append(f->fullName());
127 void TreeView::setTree(SCRef treeSha) {
129 if (topLevelItemCount() == 0)
130 // get working directory info only once after each TreeView::clear()
131 git->getWorkDirFiles(modifiedFiles, modifiedDirs, RevFile::ANY);
133 QTreeWidget::clear();
134 treeIsValid = true;
136 if (!treeSha.isEmpty()) {
137 // insert a new dir at the beginning of the list
138 DirItem* root = new DirItem(this, treeSha, rootName);
139 expandItem(root); // be interesting
143 bool TreeView::getTree(SCRef treeSha, Git::TreeInfo& ti, bool wd, SCRef treePath) {
145 // calls qApp->processEvents()
146 treeIsValid = git->getTree(treeSha, ti, wd, treePath);
147 return treeIsValid;
150 void TreeView::on_itemCollapsed(QTreeWidgetItem* item) {
152 item->setData(0, Qt::DecorationRole, *folderClosed);
155 void TreeView::on_itemExpanded(QTreeWidgetItem* itm) {
157 DirItem* item = dynamic_cast<DirItem*>(itm);
158 if (!item)
159 return;
161 item->setData(0, Qt::DecorationRole, *folderOpen);
163 bool alreadyWaiting = false;
164 if (QApplication::overrideCursor())
165 alreadyWaiting = (QApplication::overrideCursor()->shape() == Qt::WaitCursor);
167 if (!alreadyWaiting)
168 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
170 if (item->childCount() < 2) {
172 QTreeWidgetItem* dummy = item->child(0);
173 if (dummy && dummy->text(0).isEmpty())
174 delete dummy; // remove dummy child
176 Git::TreeInfo ti;
177 if (!getTree(item->treeSha, ti, isWorkingDir, item->fullName()))
178 return;
180 if (!ti.empty()) {
182 Git::TreeInfo::const_iterator it(ti.constBegin());
183 while (it != ti.constEnd()) {
185 const Git::TreeEntry& te = *it;
187 if (te.type == "tree") {
188 DirItem* dir = new DirItem(item, te.sha, te.name);
189 dir->setData(0, Qt::DecorationRole, *folderClosed);
190 dir->setBold(isModified(dir->fullName(), true));
191 new DirItem(dir, "", ""); // dummy child to show expand sign
192 } else {
193 FileItem* file = new FileItem(item, te.name);
194 file->setData(0, Qt::DecorationRole, *QGit::mimePix(te.name));
195 file->setBold(isModified(file->fullName()));
197 ++it;
201 if (!alreadyWaiting)
202 QApplication::restoreOverrideCursor();
205 void TreeView::updateTree() {
207 if (st->sha().isEmpty())
208 return;
210 // qt emits currentChanged() signal when populating
211 // the list view, so we should ignore while here
212 ignoreCurrentChanged = true;
213 QApplication::setOverrideCursor(Qt::WaitCursor);
215 isWorkingDir = (st->sha() == QGit::ZERO_SHA);
216 bool newTree = true;
217 DirItem* root = static_cast<DirItem*>(topLevelItem(0));
218 if (root && treeIsValid)
219 newTree = (root->treeSha != st->sha());
221 if ( newTree
222 && treeIsValid
223 && root
224 && st->sha() != QGit::ZERO_SHA
225 && root->treeSha != QGit::ZERO_SHA) {
226 // root->treeSha could reference a different sha from current
227 // one in case the tree is the same, i.e. has the same files.
228 // so we prefer to use the previous state sha to call isSameFiles()
229 // and benefit from the early skip logic.
230 // In case previous sha is the same of current it means an update
231 // call has been forced, in that case we use the 'real' root->treeSha
232 if (st->sha(true) != st->sha(false))
233 newTree = !git->isSameFiles(st->sha(false), st->sha(true));
234 else
235 newTree = !git->isSameFiles(root->treeSha, st->sha(true));
238 if (newTree) // ok, we really need to update the tree
239 setTree(st->sha());
240 else {
241 FileItem* f = static_cast<FileItem*>(currentItem());
242 if (f && f->fullName() == st->fileName()) {
244 restoreStuff();
245 return;
248 if (st->fileName().isEmpty()) {
249 restoreStuff();
250 return;
252 setUpdatesEnabled(false);
253 const QStringList lst(st->fileName().split("/"));
255 QTreeWidgetItemIterator item(this);
256 ++item; // first item is repository name
257 FOREACH_SL (it, lst) {
258 while (*item && treeIsValid) {
260 if ((*item)->text(0) == *it) {
262 // could be a different subdirectory with the
263 // same name that appears before in tree view
264 // to be sure we need to check the names
265 SCRef fn = ((FileItem*)*item)->fullName();
266 if (st->fileName().startsWith(fn)) {
268 if (dynamic_cast<DirItem*>(*item)) {
269 expandItem(*item);
270 ++item;
272 break; // from while loop only
275 ++item;
278 // check if st->fileName() has been deleted by a patch older than this tree
279 if (*item && treeIsValid) {
280 clearSelection();
281 setCurrentItem(*item); // calls on_currentChanged()
282 scrollToItem(*item);
284 setUpdatesEnabled(true);
285 QTreeWidget::update();
286 restoreStuff();
289 void TreeView::restoreStuff() {
291 ignoreCurrentChanged = false;
292 QApplication::restoreOverrideCursor();