xref: /glogg/src/overviewwidget.cpp (revision c633ced33b4f2c2cf77c0c80a15fa73a7f13ad9f)
1 /*
2  * Copyright (C) 2011, 2012, 2013 Nicolas Bonnefon and other contributors
3  *
4  * This file is part of glogg.
5  *
6  * glogg is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * glogg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with glogg.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 // This file implements OverviewWidget.  This class is responsable for
21 // managing and painting the matches overview widget.
22 
23 #include <QPainter>
24 #include <QMouseEvent>
25 #include <cassert>
26 
27 #include "log.h"
28 
29 #include "overviewwidget.h"
30 
31 #include "overview.h"
32 
33 // Graphic parameters
34 const int OverviewWidget::LINE_MARGIN = 4;
35 const int OverviewWidget::STEP_DURATION_MS = 30;
36 const int OverviewWidget::INITIAL_TTL_VALUE = 5;
37 
38 #define HIGHLIGHT_XPM_WIDTH 27
39 #define HIGHLIGHT_XPM_HEIGHT 9
40 
41 #define S(x) #x
42 #define SX(x) S(x)
43 
44     // width height colours char/pixel
45     // Colours
46 #define HIGHLIGHT_XPM_LEAD_LINE SX(HIGHLIGHT_XPM_WIDTH) " " SX(HIGHLIGHT_XPM_HEIGHT) " 2 1",\
47     "  s mask c none",\
48     "x c #572F80"
49 
50 const char* const highlight_xpm[][14] = {
51     {
52     HIGHLIGHT_XPM_LEAD_LINE,
53     "                           ",
54     "                           ",
55     "   xxxxxxxxxxxxxxxxxxxxx   ",
56     "   xxxxxxxxxxxxxxxxxxxxx   ",
57     "   xx                 xx   ",
58     "   xx                 xx   ",
59     "   xx                 xx   ",
60     "   xxxxxxxxxxxxxxxxxxxxx   ",
61     "   xxxxxxxxxxxxxxxxxxxxx   ",
62     "                           ",
63     "                           ",
64     },
65     {
66     HIGHLIGHT_XPM_LEAD_LINE,
67     "                           ",
68     "  xxxxxxxxxxxxxxxxxxxxxxx  ",
69     "  xxxxxxxxxxxxxxxxxxxxxxx  ",
70     "  xxxxxxxxxxxxxxxxxxxxxxx  ",
71     "  xxx                 xxx  ",
72     "  xxx                 xxx  ",
73     "  xxx                 xxx  ",
74     "  xxxxxxxxxxxxxxxxxxxxxxx  ",
75     "  xxxxxxxxxxxxxxxxxxxxxxx  ",
76     "  xxxxxxxxxxxxxxxxxxxxxxx  ",
77     "                           ",
78     },
79     {
80     HIGHLIGHT_XPM_LEAD_LINE,
81     " xxxxxxxxxxxxxxxxxxxxxxxxx ",
82     " xxxxxxxxxxxxxxxxxxxxxxxxx ",
83     " xxxxxxxxxxxxxxxxxxxxxxxxx ",
84     " xxxxxxxxxxxxxxxxxxxxxxxxx ",
85     " xxxx                 xxxx ",
86     " xxxx                 xxxx ",
87     " xxxx                 xxxx ",
88     " xxxxxxxxxxxxxxxxxxxxxxxxx ",
89     " xxxxxxxxxxxxxxxxxxxxxxxxx ",
90     " xxxxxxxxxxxxxxxxxxxxxxxxx ",
91     " xxxxxxxxxxxxxxxxxxxxxxxxx ",
92     },
93     {
94     HIGHLIGHT_XPM_LEAD_LINE,
95     "                           ",
96     "  xxxxxxxxxxxxxxxxxxxxxxx  ",
97     "  xxxxxxxxxxxxxxxxxxxxxxx  ",
98     "  xxxxxxxxxxxxxxxxxxxxxxx  ",
99     "  xxx                 xxx  ",
100     "  xxx                 xxx  ",
101     "  xxx                 xxx  ",
102     "  xxxxxxxxxxxxxxxxxxxxxxx  ",
103     "  xxxxxxxxxxxxxxxxxxxxxxx  ",
104     "  xxxxxxxxxxxxxxxxxxxxxxx  ",
105     "                           ",
106     },
107     {
108     HIGHLIGHT_XPM_LEAD_LINE,
109     "                           ",
110     "                           ",
111     "   xxxxxxxxxxxxxxxxxxxxx   ",
112     "   xxxxxxxxxxxxxxxxxxxxx   ",
113     "   xx                 xx   ",
114     "   xx                 xx   ",
115     "   xx                 xx   ",
116     "   xxxxxxxxxxxxxxxxxxxxx   ",
117     "   xxxxxxxxxxxxxxxxxxxxx   ",
118     "                           ",
119     "                           ",
120     },
121     {
122     HIGHLIGHT_XPM_LEAD_LINE,
123     "                           ",
124     "                           ",
125     "                           ",
126     "    xxxxxxxxxxxxxxxxxxx    ",
127     "    x                 x    ",
128     "    x                 x    ",
129     "    x                 x    ",
130     "    xxxxxxxxxxxxxxxxxxx    ",
131     "                           ",
132     "                           ",
133     "                           ",
134     },
135 };
136 
137 OverviewWidget::OverviewWidget( QWidget* parent ) :
138     QWidget( parent ), highlightTimer_()
139 {
140     overview_ = NULL;
141 
142     setBackgroundRole( QPalette::Window );
143 
144     // Highlight
145     highlightedLine_ = -1;
146     highlightedTTL_  = 0;
147 
148     // We should be hidden by default (e.g. for the FilteredView)
149     hide();
150 }
151 
152 void OverviewWidget::paintEvent( QPaintEvent* /* paintEvent */ )
153 {
154     static const QColor match_color("red");
155     static const QColor mark_color("dodgerblue");
156 
157     static const QPixmap highlight_pixmap[] = {
158         QPixmap( highlight_xpm[0] ),
159         QPixmap( highlight_xpm[1] ),
160         QPixmap( highlight_xpm[2] ),
161         QPixmap( highlight_xpm[3] ),
162         QPixmap( highlight_xpm[4] ),
163         QPixmap( highlight_xpm[5] ), };
164 
165     // We must be hidden until we have an Overview
166     assert( overview_ != NULL );
167 
168     overview_->updateView( height() );
169 
170     {
171         QPainter painter( this );
172 
173         painter.fillRect( painter.viewport(), painter.background() );
174 
175         // The line separating from the main view
176         painter.setPen( palette().color(QPalette::Text) );
177         painter.drawLine( 0, 0, 0, height() );
178 
179         // The 'match' lines
180         painter.setPen( match_color );
181         foreach (Overview::WeightedLine line, *(overview_->getMatchLines()) ) {
182             painter.setOpacity( ( 1.0 / Overview::WeightedLine::WEIGHT_STEPS )
183                    * ( line.weight() + 1 ) );
184             // (allow multiple matches to look 'darker' than a single one.)
185             painter.drawLine( 1 + LINE_MARGIN,
186                     line.position(), width() - LINE_MARGIN - 1, line.position() );
187         }
188 
189         // The 'mark' lines
190         painter.setPen( mark_color );
191         foreach (Overview::WeightedLine line, *(overview_->getMarkLines()) ) {
192             painter.setOpacity( ( 1.0 / Overview::WeightedLine::WEIGHT_STEPS )
193                    * ( line.weight() + 1 ) );
194             // (allow multiple matches to look 'darker' than a single one.)
195             painter.drawLine( 1 + LINE_MARGIN,
196                     line.position(), width() - LINE_MARGIN - 1, line.position() );
197         }
198 
199         // The 'view' lines
200         painter.setOpacity( 1 );
201         painter.setPen( palette().color(QPalette::Text) );
202         std::pair<int,int> view_lines = overview_->getViewLines();
203         painter.drawLine( 1, view_lines.first, width(), view_lines.first );
204         painter.drawLine( 1, view_lines.second, width(), view_lines.second );
205 
206         // The highlight
207         if ( highlightedLine_ >= 0 ) {
208             /*
209             QPen highlight_pen( palette().color(QPalette::Text) );
210             highlight_pen.setWidth( 4 - highlightedTTL_ );
211             painter.setOpacity( 1 );
212             painter.setPen( highlight_pen );
213             painter.drawRect( 2, position - 2, width() - 2 - 2, 4 );
214             */
215             int position = overview_->yFromFileLine( highlightedLine_ );
216             painter.drawPixmap(
217                    ( width() - HIGHLIGHT_XPM_WIDTH ) / 2,
218                    position - ( HIGHLIGHT_XPM_HEIGHT / 2 ),
219                    highlight_pixmap[ INITIAL_TTL_VALUE - highlightedTTL_ ] );
220         }
221     }
222 }
223 
224 void OverviewWidget::mousePressEvent( QMouseEvent* mouseEvent )
225 {
226     if ( mouseEvent->button() == Qt::LeftButton )
227         handleMousePress( mouseEvent->y() );
228 }
229 
230 void OverviewWidget::mouseMoveEvent( QMouseEvent* mouseEvent )
231 {
232     if ( mouseEvent->buttons() |= Qt::LeftButton )
233         handleMousePress( mouseEvent->y() );
234 }
235 
236 void OverviewWidget::handleMousePress( int position )
237 {
238     int line = overview_->fileLineFromY( position );
239     LOG(logDEBUG) << "OverviewWidget::handleMousePress y=" << position << " line=" << line;
240     emit lineClicked( line );
241 }
242 
243 void OverviewWidget::highlightLine( qint64 line )
244 {
245     highlightTimer_.stop();
246 
247     highlightedLine_ = line;
248     highlightedTTL_  = INITIAL_TTL_VALUE;
249 
250     update();
251     highlightTimer_.start( STEP_DURATION_MS, this );
252 }
253 
254 void OverviewWidget::removeHighlight()
255 {
256     highlightTimer_.stop();
257 
258     highlightedLine_ = -1;
259     update();
260 }
261 
262 void OverviewWidget::timerEvent( QTimerEvent* event )
263 {
264     if ( event->timerId() == highlightTimer_.timerId() ) {
265         LOG(logDEBUG) << "OverviewWidget::timerEvent";
266         if ( highlightedTTL_ > 0 ) {
267             --highlightedTTL_;
268             update();
269         }
270         else {
271             highlightTimer_.stop();
272         }
273     }
274     else {
275         QObject::timerEvent( event );
276     }
277 }
278