fix a compiler warning
[qgit4/redivivus.git] / src / listview.cpp
blob7a1f3fbb6787c71825327a79e05aedd05aacbffa
1 /*
2 Description: qgit revision list view
4 Author: Marco Costalba (C) 2005-2007
6 Copyright: See COPYING file that comes with this distribution
8 */
9 #include <QApplication>
10 #include <QHeaderView>
11 #include <QMouseEvent>
12 #include <QPainter>
13 #include <QPixmap>
14 #include <QShortcut>
15 #include "domain.h"
16 #include "git.h"
17 #include "listview.h"
19 using namespace QGit;
21 ListView::ListView(QWidget* parent) : QTreeView(parent), d(NULL), git(NULL), fh(NULL), lp(NULL) {}
23 void ListView::setup(Domain* dm, Git* g) {
25 d = dm;
26 git = g;
27 fh = d->model();
28 st = &(d->st);
29 filterNextContextMenuRequest = false;
31 setFont(QGit::STD_FONT);
33 // create ListViewProxy unplugged, will be plug
34 // to the model only when filtering is needed
35 lp = new ListViewProxy(this, d, git);
36 setModel(fh);
38 ListViewDelegate* lvd = new ListViewDelegate(git, lp, this);
39 lvd->setLaneHeight(fontMetrics().height() + 2);
40 setItemDelegate(lvd);
42 setupGeometry(); // after setting delegate
44 // shortcuts are activated only if widget is visible, this is good
45 new QShortcut(Qt::Key_Up, this, SLOT(on_keyUp()));
46 new QShortcut(Qt::Key_Down, this, SLOT(on_keyDown()));
48 connect(lvd, SIGNAL(updateView()), viewport(), SLOT(update()));
50 connect(this, SIGNAL(diffTargetChanged(int)), lvd, SLOT(diffTargetChanged(int)));
52 connect(this, SIGNAL(customContextMenuRequested(const QPoint&)),
53 this, SLOT(on_customContextMenuRequested(const QPoint&)));
56 ListView::~ListView() {
58 git->cancelDataLoading(fh); // non blocking
61 const QString ListView::sha(int row) const {
63 if (!lp->sourceModel()) // unplugged
64 return fh->sha(row);
66 QModelIndex idx = lp->mapToSource(lp->index(row, 0));
67 return fh->sha(idx.row());
70 int ListView::row(SCRef sha) const {
72 if (!lp->sourceModel()) // unplugged
73 return fh->row(sha);
75 int row = fh->row(sha);
76 QModelIndex idx = fh->index(row, 0);
77 return lp->mapFromSource(idx).row();
80 void ListView::setupGeometry() {
82 QPalette pl = palette();
83 pl.setColor(QPalette::Base, ODD_LINE_COL);
84 pl.setColor(QPalette::AlternateBase, EVEN_LINE_COL);
85 setPalette(pl); // does not seem to inherit application paletteAnnotate
87 QHeaderView* hv = header();
88 hv->setStretchLastSection(true);
89 hv->setResizeMode(LOG_COL, QHeaderView::Interactive);
90 hv->setResizeMode(TIME_COL, QHeaderView::Interactive);
91 hv->setResizeMode(ANN_ID_COL, QHeaderView::ResizeToContents);
92 hv->resizeSection(GRAPH_COL, DEF_GRAPH_COL_WIDTH);
93 hv->resizeSection(LOG_COL, DEF_LOG_COL_WIDTH);
94 hv->resizeSection(AUTH_COL, DEF_AUTH_COL_WIDTH);
95 hv->resizeSection(TIME_COL, DEF_TIME_COL_WIDTH);
97 if (git->isMainHistory(fh))
98 hideColumn(ANN_ID_COL);
101 void ListView::scrollToNextHighlighted(int direction) {
103 // Depending on the value of direction, scroll to:
104 // -1 = the next highlighted item above the current one (i.e. newer in history)
105 // 1 = the next highlighted item below the current one (i.e. older in history)
106 // 0 = the first highlighted item from the top of the list
108 QModelIndex idx = currentIndex();
110 if (!direction) {
111 idx = idx.sibling(0,0);
112 if (lp->isHighlighted(idx.row())) {
113 setCurrentIndex(idx);
114 return;
118 do {
119 idx = (direction >= 0 ? indexBelow(idx) : indexAbove(idx));
120 if (!idx.isValid())
121 return;
123 } while (!lp->isHighlighted(idx.row()));
125 setCurrentIndex(idx);
128 void ListView::scrollToCurrent(ScrollHint hint) {
130 if (currentIndex().isValid())
131 scrollTo(currentIndex(), hint);
134 void ListView::on_keyUp() {
136 QModelIndex idx = indexAbove(currentIndex());
137 if (idx.isValid())
138 setCurrentIndex(idx);
141 void ListView::on_keyDown() {
143 QModelIndex idx = indexBelow(currentIndex());
144 if (idx.isValid())
145 setCurrentIndex(idx);
148 void ListView::on_changeFont(const QFont& f) {
150 setFont(f);
151 ListViewDelegate* lvd = static_cast<ListViewDelegate*>(itemDelegate());
152 lvd->setLaneHeight(fontMetrics().height());
153 scrollToCurrent();
156 const QString ListView::currentText(int column) {
158 QModelIndex idx = model()->index(currentIndex().row(), column);
159 return (idx.isValid() ? idx.data().toString() : "");
162 int ListView::getLaneType(SCRef sha, int pos) const {
164 const Rev* r = git->revLookup(sha, fh);
165 return (r && pos < r->lanes.count() && pos >= 0 ? r->lanes.at(pos) : -1);
168 void ListView::showIdValues() {
170 fh->setAnnIdValid();
171 viewport()->update();
174 void ListView::getSelectedItems(QStringList& selectedItems) {
176 selectedItems.clear();
177 QModelIndexList ml = selectionModel()->selectedRows();
178 FOREACH (QModelIndexList, it, ml)
179 selectedItems.append(sha((*it).row()));
181 // selectedRows() returns the items in an unspecified order,
182 // so be sure rows are ordered from newest to oldest.
183 selectedItems = git->sortShaListByIndex(selectedItems);
186 const QString ListView::shaFromAnnId(uint id) {
188 if (git->isMainHistory(fh))
189 return "";
191 return sha(model()->rowCount() - id);
194 int ListView::filterRows(bool isOn, bool highlight, SCRef filter, int colNum, ShaSet* set) {
196 setUpdatesEnabled(false);
197 int matchedNum = lp->setFilter(isOn, highlight, filter, colNum, set);
198 viewport()->update();
199 setUpdatesEnabled(true);
200 UPDATE_DOMAIN(d);
201 return matchedNum;
204 bool ListView::update() {
206 int stRow = row(st->sha());
207 if (stRow == -1)
208 return false; // main/tree view asked us a sha not in history
210 QModelIndex index = currentIndex();
211 QItemSelectionModel* sel = selectionModel();
213 if (index.isValid() && (index.row() == stRow)) {
215 if (sel->isSelected(index) != st->selectItem())
216 sel->select(index, QItemSelectionModel::Toggle);
218 scrollTo(index);
219 } else {
220 // setCurrentIndex() does not clear previous
221 // selections in a multi selection QListView
222 clearSelection();
224 QModelIndex newIndex = model()->index(stRow, 0);
225 if (newIndex.isValid()) {
227 // emits QItemSelectionModel::currentChanged()
228 setCurrentIndex(newIndex);
229 scrollTo(newIndex);
230 if (!st->selectItem())
231 sel->select(newIndex, QItemSelectionModel::Deselect);
234 if (git->isMainHistory(fh))
235 emit diffTargetChanged(row(st->diffToSha()));
237 return currentIndex().isValid();
240 void ListView::currentChanged(const QModelIndex& index, const QModelIndex&) {
242 SCRef selRev = sha(index.row());
243 if (st->sha() != selRev) { // to avoid looping
244 st->setSha(selRev);
245 st->setSelectItem(true);
246 UPDATE_DOMAIN(d);
250 bool ListView::filterRightButtonPressed(QMouseEvent* e) {
252 QModelIndex index = indexAt(e->pos());
253 SCRef selSha = sha(index.row());
254 if (selSha.isEmpty())
255 return false;
257 if (e->modifiers() == Qt::ControlModifier) { // check for 'diff to' function
259 if (selSha != ZERO_SHA && st->sha() != ZERO_SHA) {
261 if (selSha != st->diffToSha())
262 st->setDiffToSha(selSha);
263 else
264 st->setDiffToSha(""); // restore std view
266 filterNextContextMenuRequest = true;
267 UPDATE_DOMAIN(d);
268 return true; // filter event out
271 // check for 'children & parents' function, i.e. if mouse is on the graph
272 if (index.column() == GRAPH_COL) {
274 filterNextContextMenuRequest = true;
275 QStringList parents, children;
276 if (getLaneParentsChildren(selSha, e->pos().x(), parents, children))
277 emit lanesContextMenuRequested(parents, children);
279 return true; // filter event out
281 return false;
284 void ListView::mousePressEvent(QMouseEvent* e) {
286 if (currentIndex().isValid() && e->button() == Qt::LeftButton)
287 d->setReadyToDrag(true);
289 if (e->button() == Qt::RightButton && filterRightButtonPressed(e))
290 return; // filtered out
292 QTreeView::mousePressEvent(e);
295 void ListView::mouseReleaseEvent(QMouseEvent* e) {
297 d->setReadyToDrag(false); // in case of just click without moving
298 QTreeView::mouseReleaseEvent(e);
301 void ListView::mouseMoveEvent(QMouseEvent* e) {
303 if (d->isReadyToDrag()) {
305 if (indexAt(e->pos()).row() == currentIndex().row())
306 return; // move at least by one line to activate drag
308 if (!d->setDragging(true))
309 return;
311 QStringList selRevs;
312 getSelectedItems(selRevs);
313 selRevs.removeAll(ZERO_SHA);
314 if (!selRevs.empty())
315 emit revisionsDragged(selRevs); // blocking until drop event
317 d->setDragging(false);
319 QTreeView::mouseMoveEvent(e);
322 void ListView::dragEnterEvent(QDragEnterEvent* e) {
324 if (e->mimeData()->hasFormat("text/plain"))
325 e->accept();
328 void ListView::dragMoveEvent(QDragMoveEvent* e) {
330 // already checked by dragEnterEvent()
331 e->accept();
334 void ListView::dropEvent(QDropEvent *e) {
336 SCList remoteRevs(e->mimeData()->text().split('\n', QString::SkipEmptyParts));
337 if (!remoteRevs.isEmpty()) {
338 // some sanity check on dropped data
339 SCRef sha(remoteRevs.first().section('@', 0, 0));
340 SCRef remoteRepo(remoteRevs.first().section('@', 1));
341 if (sha.length() == 40 && !remoteRepo.isEmpty())
342 emit revisionsDropped(remoteRevs);
346 void ListView::on_customContextMenuRequested(const QPoint& pos) {
348 QModelIndex index = indexAt(pos);
349 if (!index.isValid())
350 return;
352 if (filterNextContextMenuRequest) {
353 // event filter does not work on them
354 filterNextContextMenuRequest = false;
355 return;
357 emit contextMenu(sha(index.row()), POPUP_LIST_EV);
360 bool ListView::getLaneParentsChildren(SCRef sha, int x, SList p, SList c) {
362 ListViewDelegate* lvd = static_cast<ListViewDelegate*>(itemDelegate());
363 uint lane = x / lvd->laneWidth();
364 int t = getLaneType(sha, lane);
365 if (t == EMPTY || t == -1)
366 return false;
368 // first find the parents
369 p.clear();
370 QString root;
371 if (!isFreeLane(t)) {
372 p = git->revLookup(sha, fh)->parents(); // pointer cannot be NULL
373 root = sha;
374 } else {
375 SCRef par(git->getLaneParent(sha, lane));
376 if (par.isEmpty()) {
377 dbs("ASSERT getLaneParentsChildren: parent not found");
378 return false;
380 p.append(par);
381 root = p.first();
383 // then find children
384 c = git->getChildren(root);
385 return true;
388 // *****************************************************************************
390 ListViewDelegate::ListViewDelegate(Git* g, ListViewProxy* px, QObject* p) : QItemDelegate(p) {
392 git = g;
393 lp = px;
394 laneHeight = 0;
395 diffTargetRow = -1;
398 QSize ListViewDelegate::sizeHint(const QStyleOptionViewItem&, const QModelIndex&) const {
400 return QSize(laneWidth(), laneHeight);
403 void ListViewDelegate::diffTargetChanged(int row) {
405 if (diffTargetRow != row) {
406 diffTargetRow = row;
407 emit updateView();
411 const Rev* ListViewDelegate::revLookup(int row, FileHistory** fhPtr) const {
413 ListView* lv = static_cast<ListView*>(parent());
414 FileHistory* fh = static_cast<FileHistory*>(lv->model());
416 if (lp->sourceModel())
417 fh = static_cast<FileHistory*>(lp->sourceModel());
419 if (fhPtr)
420 *fhPtr = fh;
422 return git->revLookup(lv->sha(row), fh);
425 static QColor blend(const QColor& col1, const QColor& col2, int amount = 128) {
427 // Returns ((256 - amount)*col1 + amount*col2) / 256;
428 return QColor(((256 - amount)*col1.red() + amount*col2.red() ) / 256,
429 ((256 - amount)*col1.green() + amount*col2.green()) / 256,
430 ((256 - amount)*col1.blue() + amount*col2.blue() ) / 256);
433 void ListViewDelegate::paintGraphLane(QPainter* p, int type, int x1, int x2,
434 const QColor& col, const QColor& activeCol, const QBrush& back) const {
436 const int padding = 2;
437 x1 += padding;
438 x2 += padding;
440 int h = laneHeight / 2;
441 int m = (x1 + x2) / 2;
442 int r = (x2 - x1) * 1 / 3;
443 int d = 2 * r;
445 #define P_CENTER m , h
446 #define P_0 x2, h // >
447 #define P_90 m , 0 // ^
448 #define P_180 x1, h // <
449 #define P_270 m , 2 * h // v
450 #define DELTA_UR 2*(x1 - m), 2*h , 0*16, 90*16 // -,
451 #define DELTA_DR 2*(x1 - m), 2*-h, 270*16, 90*16 // -'
452 #define DELTA_UL 2*(x2 - m), 2*h , 90*16, 90*16 // ,-
453 #define DELTA_DL 2*(x2 - m), 2*-h, 180*16, 90*16 // '-
454 #define CENTER_UR x1, 2*h, 225
455 #define CENTER_DR x1, 0 , 135
456 #define CENTER_UL x2, 2*h, 315
457 #define CENTER_DL x2, 0 , 45
458 #define R_CENTER m - r, h - r, d, d
460 static QPen lanePen(Qt::black, 2); // fast path here
462 // arc
463 switch (type) {
464 case JOIN:
465 case JOIN_R:
466 case HEAD:
467 case HEAD_R: {
468 QConicalGradient gradient(CENTER_UR);
469 gradient.setColorAt(0.375, col);
470 gradient.setColorAt(0.625, activeCol);
471 lanePen.setBrush(gradient);
472 p->setPen(lanePen);
473 p->drawArc(P_CENTER, DELTA_UR);
474 break;
476 case JOIN_L: {
477 QConicalGradient gradient(CENTER_UL);
478 gradient.setColorAt(0.375, activeCol);
479 gradient.setColorAt(0.625, col);
480 lanePen.setBrush(gradient);
481 p->setPen(lanePen);
482 p->drawArc(P_CENTER, DELTA_UL);
483 break;
485 case TAIL:
486 case TAIL_R: {
487 QConicalGradient gradient(CENTER_DR);
488 gradient.setColorAt(0.375, activeCol);
489 gradient.setColorAt(0.625, col);
490 lanePen.setBrush(gradient);
491 p->setPen(lanePen);
492 p->drawArc(P_CENTER, DELTA_DR);
493 break;
495 default:
496 break;
499 lanePen.setColor(col);
500 p->setPen(lanePen);
502 // vertical line
503 switch (type) {
504 case ACTIVE:
505 case NOT_ACTIVE:
506 case MERGE_FORK:
507 case MERGE_FORK_R:
508 case MERGE_FORK_L:
509 case JOIN:
510 case JOIN_R:
511 case JOIN_L:
512 case CROSS:
513 p->drawLine(P_90, P_270);
514 break;
515 case HEAD_L:
516 case BRANCH:
517 p->drawLine(P_CENTER, P_270);
518 break;
519 case TAIL_L:
520 case INITIAL:
521 case BOUNDARY:
522 case BOUNDARY_C:
523 case BOUNDARY_R:
524 case BOUNDARY_L:
525 p->drawLine(P_90, P_CENTER);
526 break;
527 default:
528 break;
531 lanePen.setColor(activeCol);
532 p->setPen(lanePen);
534 // horizontal line
535 switch (type) {
536 case MERGE_FORK:
537 case JOIN:
538 case HEAD:
539 case TAIL:
540 case CROSS:
541 case CROSS_EMPTY:
542 case BOUNDARY_C:
543 p->drawLine(P_180, P_0);
544 break;
545 case MERGE_FORK_R:
546 case BOUNDARY_R:
547 p->drawLine(P_180, P_CENTER);
548 break;
549 case MERGE_FORK_L:
550 case HEAD_L:
551 case TAIL_L:
552 case BOUNDARY_L:
553 p->drawLine(P_CENTER, P_0);
554 break;
555 default:
556 break;
559 // center symbol, e.g. rect or ellipse
560 switch (type) {
561 case ACTIVE:
562 case INITIAL:
563 case BRANCH:
564 p->setPen(Qt::black);
565 p->setBrush(col);
566 p->drawEllipse(R_CENTER);
567 break;
568 case MERGE_FORK:
569 case MERGE_FORK_R:
570 case MERGE_FORK_L:
571 p->setPen(Qt::black);
572 p->setBrush(col);
573 p->drawRect(R_CENTER);
574 break;
575 case UNAPPLIED:
576 // Red minus sign
577 p->setPen(Qt::NoPen);
578 p->setBrush(Qt::red);
579 p->drawRect(m - r, h - 1, d, 2);
580 break;
581 case APPLIED:
582 // Green plus sign
583 p->setPen(Qt::NoPen);
584 p->setBrush(DARK_GREEN);
585 p->drawRect(m - r, h - 1, d, 2);
586 p->drawRect(m - 1, h - r, 2, d);
587 break;
588 case BOUNDARY:
589 p->setPen(Qt::black);
590 p->setBrush(back);
591 p->drawEllipse(R_CENTER);
592 break;
593 case BOUNDARY_C:
594 case BOUNDARY_R:
595 case BOUNDARY_L:
596 p->setPen(Qt::black);
597 p->setBrush(back);
598 p->drawRect(R_CENTER);
599 break;
600 default:
601 break;
603 #undef P_CENTER
604 #undef P_0
605 #undef P_90
606 #undef P_180
607 #undef P_270
608 #undef DELTA_UR
609 #undef DELTA_DR
610 #undef DELTA_UL
611 #undef DELTA_DL
612 #undef CENTER_UR
613 #undef CENTER_DR
614 #undef CENTER_UL
615 #undef CENTER_DL
616 #undef R_CENTER
619 void ListViewDelegate::paintGraph(QPainter* p, const QStyleOptionViewItem& opt,
620 const QModelIndex& i) const {
622 static const QColor colors[COLORS_NUM] = { Qt::red, DARK_GREEN,
623 Qt::blue, Qt::darkGray, BROWN,
624 Qt::magenta, ORANGE };
625 if (opt.state & QStyle::State_Selected)
626 p->fillRect(opt.rect, opt.palette.highlight());
627 else if (i.row() & 1)
628 p->fillRect(opt.rect, opt.palette.alternateBase());
629 else
630 p->fillRect(opt.rect, opt.palette.base());
632 FileHistory* fh;
633 const Rev* r = revLookup(i.row(), &fh);
634 if (!r)
635 return;
637 p->save();
638 p->setClipRect(opt.rect, Qt::IntersectClip);
639 p->translate(opt.rect.topLeft());
641 // calculate lanes
642 if (r->lanes.count() == 0)
643 git->setLane(r->sha(), fh);
645 QBrush back = opt.palette.base();
646 const QVector<int>& lanes(r->lanes);
647 uint laneNum = lanes.count();
648 uint activeLane = 0;
649 for (uint i = 0; i < laneNum; i++)
650 if (isActive(lanes[i])) {
651 activeLane = i;
652 break;
655 int x1 = 0, x2 = 0;
656 int maxWidth = opt.rect.width();
657 int lw = laneWidth();
658 QColor activeColor = colors[activeLane % COLORS_NUM];
659 if (opt.state & QStyle::State_Selected)
660 activeColor = blend(activeColor, opt.palette.highlightedText().color(), 208);
661 for (uint i = 0; i < laneNum && x2 < maxWidth; i++) {
663 x1 = x2;
664 x2 += lw;
666 int ln = lanes[i];
667 if (ln == EMPTY)
668 continue;
670 QColor color = i == activeLane ? activeColor : colors[i % COLORS_NUM];
671 paintGraphLane(p, ln, x1, x2, color, activeColor, back);
673 p->restore();
676 void ListViewDelegate::paintLog(QPainter* p, const QStyleOptionViewItem& opt,
677 const QModelIndex& index) const {
679 int row = index.row();
680 const Rev* r = revLookup(row);
681 if (!r)
682 return;
684 if (r->isDiffCache)
685 p->fillRect(opt.rect, changedFiles(ZERO_SHA) ? ORANGE : DARK_ORANGE);
687 if (diffTargetRow == row)
688 p->fillRect(opt.rect, LIGHT_BLUE);
690 bool isHighlighted = lp->isHighlighted(row);
691 QPixmap* pm = getTagMarks(r->sha(), opt);
693 if (!pm && !isHighlighted) { // fast path in common case
694 QItemDelegate::paint(p, opt, index);
695 return;
697 QStyleOptionViewItem newOpt(opt); // we need a copy
698 if (pm) {
699 p->drawPixmap(newOpt.rect.x(), newOpt.rect.y() + 1, *pm); // +1 means leave a pixel spacing above the pixmap
700 newOpt.rect.adjust(pm->width(), 0, 0, 0);
701 delete pm;
703 if (isHighlighted)
704 newOpt.font.setBold(true);
706 QItemDelegate::paint(p, newOpt, index);
709 void ListViewDelegate::paint(QPainter* p, const QStyleOptionViewItem& opt,
710 const QModelIndex& index) const {
712 p->setRenderHints(QPainter::Antialiasing);
714 if (index.column() == GRAPH_COL)
715 return paintGraph(p, opt, index);
717 if (index.column() == LOG_COL)
718 return paintLog(p, opt, index);
720 return QItemDelegate::paint(p, opt, index);
723 bool ListViewDelegate::changedFiles(SCRef sha) const {
725 const RevFile* f = git->getFiles(sha);
726 if (f)
727 for (int i = 0; i < f->count(); i++)
728 if (!f->statusCmp(i, RevFile::UNKNOWN))
729 return true;
730 return false;
733 QPixmap* ListViewDelegate::getTagMarks(SCRef sha, const QStyleOptionViewItem& opt) const {
735 uint rt = git->checkRef(sha);
736 if (rt == 0)
737 return NULL; // common case
739 QPixmap* pm = new QPixmap(); // must be deleted by caller
741 if (rt & Git::BRANCH)
742 addRefPixmap(&pm, sha, Git::BRANCH, opt);
744 if (rt & Git::RMT_BRANCH)
745 addRefPixmap(&pm, sha, Git::RMT_BRANCH, opt);
747 if (rt & Git::TAG)
748 addRefPixmap(&pm, sha, Git::TAG, opt);
750 if (rt & Git::REF)
751 addRefPixmap(&pm, sha, Git::REF, opt);
753 return pm;
756 void ListViewDelegate::addRefPixmap(QPixmap** pp, SCRef sha, int type, QStyleOptionViewItem opt) const {
758 QString curBranch;
759 SCList refs = git->getRefName(sha, (Git::RefType)type, &curBranch);
760 FOREACH_SL (it, refs) {
762 bool isCur = (curBranch == *it);
763 opt.font.setBold(isCur);
765 QColor clr;
766 if (type == Git::BRANCH)
767 clr = (isCur ? Qt::green : DARK_GREEN);
769 else if (type == Git::RMT_BRANCH)
770 clr = LIGHT_ORANGE;
772 else if (type == Git::TAG)
773 clr = Qt::yellow;
775 else if (type == Git::REF)
776 clr = PURPLE;
778 opt.palette.setColor(QPalette::Window, clr);
779 addTextPixmap(pp, *it, opt);
783 void ListViewDelegate::addTextPixmap(QPixmap** pp, SCRef txt, const QStyleOptionViewItem& opt) const {
785 QPixmap* pm = *pp;
786 int ofs = pm->isNull() ? 0 : pm->width() + 2;
787 int spacing = 4;
788 QFontMetrics fm(opt.font);
789 int pw = fm.boundingRect(txt).width() + 2 * spacing;
790 int ph = fm.height();
792 QPixmap* newPm = new QPixmap(ofs + pw, ph);
793 QPainter p;
794 p.begin(newPm);
795 if (!pm->isNull()) {
796 newPm->fill(opt.palette.base().color());
797 p.drawPixmap(0, 0, *pm);
799 p.setPen(opt.palette.color(QPalette::WindowText));
800 p.setBrush(opt.palette.color(QPalette::Window));
801 p.setFont(opt.font);
802 p.drawRect(ofs, 0, pw - 1, ph - 1);
803 p.drawText(ofs + spacing, fm.ascent(), txt);
804 p.end();
806 delete pm;
807 *pp = newPm;
810 // *****************************************************************************
812 ListViewProxy::ListViewProxy(QObject* p, Domain * dm, Git * g) : QSortFilterProxyModel(p) {
814 d = dm;
815 git = g;
816 colNum = 0;
817 isHighLight = false;
818 setDynamicSortFilter(false);
821 bool ListViewProxy::isMatch(SCRef sha) const {
823 if (colNum == SHA_MAP_COL)
824 // in this case shaMap contains all good sha to search for
825 return shaSet.contains(sha);
827 const Rev* r = git->revLookup(sha);
828 if (!r) {
829 dbp("ASSERT in ListViewFilter::isMatch, sha <%1> not found", sha);
830 return false;
832 QString target;
833 if (colNum == LOG_COL)
834 target = r->shortLog();
835 else if (colNum == AUTH_COL)
836 target = r->author();
837 else if (colNum == LOG_MSG_COL)
838 target = r->longLog();
839 else if (colNum == COMMIT_COL)
840 target = sha;
842 // wildcard search, case insensitive
843 return (target.contains(filter));
846 bool ListViewProxy::isMatch(int source_row) const {
848 FileHistory* fh = d->model();
849 if (fh->rowCount() <= source_row) // FIXME required to avoid an ASSERT in d->isMatch()
850 return false;
852 bool extFilter = (colNum == -1);
853 return ((!extFilter && isMatch(fh->sha(source_row)))
854 ||( extFilter && d->isMatch(fh->sha(source_row))));
857 bool ListViewProxy::isHighlighted(int row) const {
859 // FIXME row == source_row only because when
860 // higlights the rows are not hidden
861 return (isHighLight && isMatch(row));
864 bool ListViewProxy::filterAcceptsRow(int source_row, const QModelIndex&) const {
866 return (isHighLight || isMatch(source_row));
869 int ListViewProxy::setFilter(bool isOn, bool h, SCRef fl, int cn, ShaSet* s) {
871 filter = QRegExp(fl, Qt::CaseInsensitive, QRegExp::Wildcard);
872 colNum = cn;
873 if (s)
874 shaSet = *s;
876 // isHighlighted() is called also when filter is off,
877 // so reset 'isHighLight' flag in that case
878 isHighLight = h && isOn;
880 ListView* lv = static_cast<ListView*>(parent());
881 FileHistory* fh = d->model();
883 if (!isOn && sourceModel()){
884 lv->setModel(fh);
885 setSourceModel(NULL);
887 } else if (isOn && !isHighLight) {
888 setSourceModel(fh); // trigger a rows scanning
889 lv->setModel(this);
891 return (sourceModel() ? rowCount() : 0);