FIX issue #19 where multiple widget values led to bad replacements in the command
[qgit4/redivivus.git] / src / smartbrowse.cpp
blobe92cec90f6a0b587a9314432ae7f18118245b36f
1 /*
2 Author: Marco Costalba (C) 2005-2007
4 Copyright: See COPYING file that comes with this distribution
6 */
7 #include <QContextMenuEvent>
8 #include <QMenu>
9 #include <QPainter>
10 #include <QScrollBar>
11 #include <QWheelEvent>
12 #include "revsview.h"
13 #include "smartbrowse.h"
15 #define GO_UP 1
16 #define GO_DOWN 2
17 #define GO_LOG 3
18 #define GO_DIFF 4
20 #define AT_TOP 1
21 #define AT_BTM 2
23 SmartLabel::SmartLabel(const QString& text, QWidget* par) : QLabel(text, par) {
24 this->setStyleSheet("SmartLabel { border: 1px solid LightGray;"
25 "padding: 0px 2px 2px 2px; }");
28 void SmartLabel::paintEvent(QPaintEvent* event) {
29 // our QPainter must be destroyed before QLabel's paintEvent is called
31 QPainter painter(this);
32 QColor backgroundColor = Qt::white;
34 // give label a semi-transparent background
35 backgroundColor.setAlpha(200);
36 painter.fillRect(this->rect(), backgroundColor);
39 // let QLabel do the rest
40 QLabel::paintEvent(event);
43 void SmartLabel::contextMenuEvent(QContextMenuEvent* e) {
45 if (text().count("href=") != 2)
46 return;
48 QMenu* menu = new QMenu(this);
49 menu->addAction("Switch links", this, SLOT(switchLinks()));
50 menu->exec(e->globalPos());
51 delete menu;
54 void SmartLabel::switchLinks() {
56 QString t(text());
57 QString link1(t.section("<a href=", 1). section("</a>", 0, 0));
58 QString link2(t.section("<a href=", 2). section("</a>", 0, 0));
59 t.replace(link1, "%1").replace(link2, "%2");
60 setText(t.arg(link2, link1));
61 adjustSize();
64 SmartBrowse::SmartBrowse(RevsView* par) : QObject(par) {
66 rv = par;
67 wheelCnt = 0;
68 lablesEnabled = QGit::testFlag(QGit::SMART_LBL_F);
70 QString txt("<p><img src=\":/icons/resources/%1\"> %2 %3</p>");
71 QString link("<a href=\"%1\">%2</a>");
72 QString linkUp(link.arg(QString::number(GO_UP), "Up"));
73 QString linkDown(link.arg(QString::number(GO_DOWN), "Down"));
74 QString linkLog(link.arg(QString::number(GO_LOG), "Log"));
75 QString linkDiff(link.arg(QString::number(GO_DIFF), "Diff"));
77 QTextEdit* log = static_cast<QTextEdit*>(rv->tab()->textBrowserDesc);
78 QTextEdit* diff = static_cast<QTextEdit*>(rv->tab()->textEditDiff);
80 logTopLbl = new SmartLabel(txt.arg("go-up.svg", linkUp, ""), log);
81 logBottomLbl = new SmartLabel(txt.arg("go-down.svg", linkDiff, linkDown), log);
82 diffTopLbl = new SmartLabel(txt.arg("go-up.svg", linkLog, linkUp), diff);
83 diffBottomLbl = new SmartLabel(txt.arg("go-down.svg", linkUp, linkDown), diff);
85 diffTopLbl->setFont(qApp->font()); // override parent's font to
86 diffBottomLbl->setFont(qApp->font()); // avoid QGit::TYPE_WRITER_FONT
88 setVisible(false);
90 log->installEventFilter(this);
91 diff->installEventFilter(this);
93 QScrollBar* vsbLog = log->verticalScrollBar();
94 QScrollBar* vsbDiff = diff->verticalScrollBar();
96 vsbLog->installEventFilter(this);
97 vsbDiff->installEventFilter(this);
99 log->horizontalScrollBar()->installEventFilter(this);
100 diff->horizontalScrollBar()->installEventFilter(this);
102 connect(vsbLog, SIGNAL(valueChanged(int)),
103 this, SLOT(updateVisibility()));
105 connect(vsbDiff, SIGNAL(valueChanged(int)),
106 this, SLOT(updateVisibility()));
108 connect(logTopLbl, SIGNAL(linkActivated(const QString&)),
109 this, SLOT(linkActivated(const QString&)));
111 connect(logBottomLbl, SIGNAL(linkActivated(const QString&)),
112 this, SLOT(linkActivated(const QString&)));
114 connect(diffTopLbl, SIGNAL(linkActivated(const QString&)),
115 this, SLOT(linkActivated(const QString&)));
117 connect(diffBottomLbl, SIGNAL(linkActivated(const QString&)),
118 this, SLOT(linkActivated(const QString&)));
121 void SmartBrowse::setVisible(bool b) {
123 b = b && lablesEnabled;
124 logTopLbl->setVisible(b);
125 logBottomLbl->setVisible(b);
126 diffTopLbl->setVisible(b);
127 diffBottomLbl->setVisible(b);
130 QTextEdit* SmartBrowse::curTextEdit(bool* isDiff) {
132 QTextEdit* log = static_cast<QTextEdit*>(rv->tab()->textBrowserDesc);
133 QTextEdit* diff = static_cast<QTextEdit*>(rv->tab()->textEditDiff);
135 if (isDiff)
136 *isDiff = diff->isVisible();
138 if (!diff->isVisible() && !log->isVisible())
139 return NULL;
141 return (diff->isVisible() ? diff : log);
144 int SmartBrowse::visibilityFlags(bool* isDiff) {
146 static int MIN = 5;
148 QTextEdit* te = curTextEdit(isDiff);
149 if (!te)
150 return 0;
152 QScrollBar* vsb = te->verticalScrollBar();
154 bool v = lablesEnabled && te->isEnabled();
155 bool top = v && (!vsb->isVisible() || (vsb->value() - vsb->minimum() < MIN));
156 bool btm = v && (!vsb->isVisible() || (vsb->maximum() - vsb->value() < MIN));
158 return AT_TOP * top + AT_BTM * btm;
161 void SmartBrowse::updateVisibility() {
163 bool isDiff;
164 int flags = visibilityFlags(&isDiff);
166 if (isDiff) {
167 diffTopLbl->setVisible(flags & AT_TOP);
168 diffBottomLbl->setVisible(flags & AT_BTM);
169 } else {
170 logTopLbl->setVisible(flags & AT_TOP);
171 logBottomLbl->setVisible(flags & AT_BTM);
175 void SmartBrowse::flagChanged(uint flag) {
177 if (flag == QGit::SMART_LBL_F && curTextEdit()) {
178 lablesEnabled = QGit::testFlag(QGit::SMART_LBL_F);
179 setVisible(curTextEdit()->isEnabled());
180 updatePosition();
182 if (flag == QGit::LOG_DIFF_TAB_F)
183 rv->setTabLogDiffVisible(QGit::testFlag(QGit::LOG_DIFF_TAB_F));
186 void SmartBrowse::linkActivated(const QString& text) {
188 int key = text.toInt();
189 switch (key) {
190 case GO_LOG:
191 case GO_DIFF:
192 rv->toggleDiffView();
193 break;
194 case GO_UP:
195 rv->tab()->listViewLog->on_keyUp();
196 break;
197 case GO_DOWN:
198 rv->tab()->listViewLog->on_keyDown();
199 break;
200 default:
201 dbp("ASSERT in SmartBrowse::linkActivated, key %1 not known", text);
205 bool SmartBrowse::eventFilter(QObject *obj, QEvent *event) {
207 if (!lablesEnabled)
208 return QObject::eventFilter(obj, event);
210 QTextEdit* te = dynamic_cast<QTextEdit*>(obj);
211 QScrollBar* sb = dynamic_cast<QScrollBar*>(obj);
213 QEvent::Type t = event->type();
214 if (te && t == QEvent::Resize)
215 updatePosition();
217 if (sb && (t == QEvent::Show || t == QEvent::Hide))
218 updatePosition();
220 if (te && t == QEvent::EnabledChange) {
221 setVisible(te->isEnabled());
222 updatePosition();
224 if (sb && t == QEvent::Wheel && sb->orientation() == Qt::Vertical) {
226 QWheelEvent* we = static_cast<QWheelEvent*>(event);
227 if (wheelRolled(we->angleDelta().y(), visibilityFlags()))
228 return true; // filter event out
230 return QObject::eventFilter(obj, event);
233 void SmartBrowse::updatePosition() {
235 QTextEdit* te = curTextEdit();
236 if (!te)
237 return;
239 QScrollBar* vb = te->verticalScrollBar();
240 QScrollBar* hb = te->horizontalScrollBar();
242 int w = te->width() - vb->width() * vb->isVisible();
243 int h = te->height() - hb->height() * hb->isVisible();
245 logTopLbl->move(w - logTopLbl->width() - 10, 10);
246 diffTopLbl->move(w - diffTopLbl->width() - 10, 10);
247 logBottomLbl->move(w - logBottomLbl->width() - 10, h - logBottomLbl->height() - 10);
248 diffBottomLbl->move(w - diffBottomLbl->width() - 10, h - diffBottomLbl->height() - 10);
250 updateVisibility();
252 // we are called also when user toggle view manually,
253 // so reset wheel counters to be sure we don't have alias
254 scrollTimer.restart();
255 wheelCnt = 0;
258 bool SmartBrowse::wheelRolled(int delta, int flags) {
260 bool justSwitched = (switchTimer.isValid() && switchTimer.elapsed() < 400);
261 if (justSwitched)
262 switchTimer.restart();
264 bool scrolling = (scrollTimer.isValid() && scrollTimer.elapsed() < 400);
265 bool directionChanged = (wheelCnt * delta < 0);
267 // we are called before the scroll bar is updated, so we need
268 // to take in account roll direction to avoid false positives
269 bool scrollingOut = ( ((flags & AT_TOP) && (delta > 0))
270 ||((flags & AT_BTM) && (delta < 0)));
272 // a scroll action have to start when in range
273 // but can continue also when goes out of range
274 if (!scrollingOut || scrolling)
275 scrollTimer.restart();
277 if (!scrollingOut || justSwitched)
278 return justSwitched; // filter wheels events just after a switch
280 // we want a quick rolling action to be considered valid
281 bool tooSlow = (timeoutTimer.isValid() && timeoutTimer.elapsed() > 300);
282 timeoutTimer.restart();
284 if (directionChanged || scrolling || tooSlow)
285 wheelCnt = 0;
287 // ok, we would be ready to switch, but we want to add some inertia
288 wheelCnt += (delta > 0 ? 1 : -1);
289 if (wheelCnt * wheelCnt < 9)
290 return false;
292 QLabel* l;
293 if (wheelCnt > 0)
294 l = logTopLbl->isVisible() ? logTopLbl : diffTopLbl;
295 else
296 l = logBottomLbl->isVisible() ? logBottomLbl : diffBottomLbl;
298 wheelCnt = 0;
299 switchTimer.restart();
300 linkActivated(l->text().section("href=", 1).section("\"", 1, 1));
301 return false;