1bb02e0acSNicolas Bonnefon /*
2ac6602a5SNicolas Bonnefon * Copyright (C) 2009, 2010, 2011, 2012, 2013, 2015 Nicolas Bonnefon
3ac6602a5SNicolas Bonnefon * and other contributors
4bb02e0acSNicolas Bonnefon *
5bb02e0acSNicolas Bonnefon * This file is part of glogg.
6bb02e0acSNicolas Bonnefon *
7bb02e0acSNicolas Bonnefon * glogg is free software: you can redistribute it and/or modify
8bb02e0acSNicolas Bonnefon * it under the terms of the GNU General Public License as published by
9bb02e0acSNicolas Bonnefon * the Free Software Foundation, either version 3 of the License, or
10bb02e0acSNicolas Bonnefon * (at your option) any later version.
11bb02e0acSNicolas Bonnefon *
12bb02e0acSNicolas Bonnefon * glogg is distributed in the hope that it will be useful,
13bb02e0acSNicolas Bonnefon * but WITHOUT ANY WARRANTY; without even the implied warranty of
14bb02e0acSNicolas Bonnefon * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15bb02e0acSNicolas Bonnefon * GNU General Public License for more details.
16bb02e0acSNicolas Bonnefon *
17bb02e0acSNicolas Bonnefon * You should have received a copy of the GNU General Public License
18bb02e0acSNicolas Bonnefon * along with glogg. If not, see <http://www.gnu.org/licenses/>.
19bb02e0acSNicolas Bonnefon */
20bb02e0acSNicolas Bonnefon
21bb02e0acSNicolas Bonnefon // This file implements the AbstractLogView base class.
22bb02e0acSNicolas Bonnefon // Most of the actual drawing and event management common to the two views
23bb02e0acSNicolas Bonnefon // is implemented in this class. The class only calls protected virtual
24bb02e0acSNicolas Bonnefon // functions when view specific behaviour is desired, using the template
25bb02e0acSNicolas Bonnefon // pattern.
26bb02e0acSNicolas Bonnefon
27bb02e0acSNicolas Bonnefon #include <iostream>
28bb02e0acSNicolas Bonnefon #include <cassert>
29bb02e0acSNicolas Bonnefon
30bb02e0acSNicolas Bonnefon #include <QApplication>
31bb02e0acSNicolas Bonnefon #include <QClipboard>
32bb02e0acSNicolas Bonnefon #include <QFile>
33bb02e0acSNicolas Bonnefon #include <QRect>
34bb02e0acSNicolas Bonnefon #include <QPaintEvent>
35bb02e0acSNicolas Bonnefon #include <QPainter>
36bb02e0acSNicolas Bonnefon #include <QFontMetrics>
37bb02e0acSNicolas Bonnefon #include <QScrollBar>
38bb02e0acSNicolas Bonnefon #include <QMenu>
39bb02e0acSNicolas Bonnefon #include <QAction>
40bb02e0acSNicolas Bonnefon #include <QtCore>
411853a1fdSNicolas Bonnefon #include <QGestureEvent>
42bb02e0acSNicolas Bonnefon
43bb02e0acSNicolas Bonnefon #include "log.h"
44bb02e0acSNicolas Bonnefon
45bb02e0acSNicolas Bonnefon #include "persistentinfo.h"
46bb02e0acSNicolas Bonnefon #include "filterset.h"
47bb02e0acSNicolas Bonnefon #include "logmainview.h"
48bb02e0acSNicolas Bonnefon #include "quickfind.h"
49bb02e0acSNicolas Bonnefon #include "quickfindpattern.h"
50bb02e0acSNicolas Bonnefon #include "overview.h"
51bb02e0acSNicolas Bonnefon #include "configuration.h"
52bb02e0acSNicolas Bonnefon
53bb02e0acSNicolas Bonnefon namespace {
540b05c6eaSNicolas Bonnefon int mapPullToFollowLength( int length );
550b05c6eaSNicolas Bonnefon };
560b05c6eaSNicolas Bonnefon
570b05c6eaSNicolas Bonnefon namespace {
58bb02e0acSNicolas Bonnefon
countDigits(quint64 n)59bb02e0acSNicolas Bonnefon int countDigits( quint64 n )
60bb02e0acSNicolas Bonnefon {
61bb02e0acSNicolas Bonnefon if (n == 0)
62bb02e0acSNicolas Bonnefon return 1;
63bb02e0acSNicolas Bonnefon
64bb02e0acSNicolas Bonnefon // We must force the compiler to not store intermediate results
65bb02e0acSNicolas Bonnefon // in registers because this causes incorrect result on some
66bb02e0acSNicolas Bonnefon // systems under optimizations level >0. For the skeptical:
67bb02e0acSNicolas Bonnefon //
68bb02e0acSNicolas Bonnefon // #include <math.h>
69bb02e0acSNicolas Bonnefon // #include <stdlib.h>
70bb02e0acSNicolas Bonnefon // int main(int argc, char **argv) {
71bb02e0acSNicolas Bonnefon // (void)argc;
72bb02e0acSNicolas Bonnefon // long long int n = atoll(argv[1]);
73bb02e0acSNicolas Bonnefon // return floor( log( n ) / log( 10 ) + 1 );
74bb02e0acSNicolas Bonnefon // }
75bb02e0acSNicolas Bonnefon //
76bb02e0acSNicolas Bonnefon // This is on Thinkpad T60 (Genuine Intel(R) CPU T2300).
77bb02e0acSNicolas Bonnefon // $ g++ --version
78bb02e0acSNicolas Bonnefon // g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
79bb02e0acSNicolas Bonnefon // $ g++ -O0 -Wall -W -o math math.cpp -lm; ./math 10; echo $?
80bb02e0acSNicolas Bonnefon // 2
81bb02e0acSNicolas Bonnefon // $ g++ -O1 -Wall -W -o math math.cpp -lm; ./math 10; echo $?
82bb02e0acSNicolas Bonnefon // 1
83bb02e0acSNicolas Bonnefon //
84bb02e0acSNicolas Bonnefon // A fix is to (1) explicitly place intermediate results in
85bb02e0acSNicolas Bonnefon // variables *and* (2) [A] mark them as 'volatile', or [B] pass
86bb02e0acSNicolas Bonnefon // -ffloat-store to g++ (note that approach [A] is more portable).
87bb02e0acSNicolas Bonnefon
88bb02e0acSNicolas Bonnefon volatile qreal ln_n = qLn( n );
89bb02e0acSNicolas Bonnefon volatile qreal ln_10 = qLn( 10 );
90bb02e0acSNicolas Bonnefon volatile qreal lg_n = ln_n / ln_10;
91bb02e0acSNicolas Bonnefon volatile qreal lg_n_1 = lg_n + 1;
92bb02e0acSNicolas Bonnefon volatile qreal fl_lg_n_1 = qFloor( lg_n_1 );
93bb02e0acSNicolas Bonnefon
94bb02e0acSNicolas Bonnefon return fl_lg_n_1;
95bb02e0acSNicolas Bonnefon }
96bb02e0acSNicolas Bonnefon
97bb02e0acSNicolas Bonnefon } // anon namespace
98bb02e0acSNicolas Bonnefon
99bb02e0acSNicolas Bonnefon
LineChunk(int first_col,int last_col,ChunkType type)100bb02e0acSNicolas Bonnefon LineChunk::LineChunk( int first_col, int last_col, ChunkType type )
101bb02e0acSNicolas Bonnefon {
102bb02e0acSNicolas Bonnefon // LOG(logDEBUG) << "new LineChunk: " << first_col << " " << last_col;
103bb02e0acSNicolas Bonnefon
104bb02e0acSNicolas Bonnefon start_ = first_col;
105bb02e0acSNicolas Bonnefon end_ = last_col;
106bb02e0acSNicolas Bonnefon type_ = type;
107bb02e0acSNicolas Bonnefon }
108bb02e0acSNicolas Bonnefon
select(int sel_start,int sel_end) const109bb02e0acSNicolas Bonnefon QList<LineChunk> LineChunk::select( int sel_start, int sel_end ) const
110bb02e0acSNicolas Bonnefon {
111bb02e0acSNicolas Bonnefon QList<LineChunk> list;
112bb02e0acSNicolas Bonnefon
113bb02e0acSNicolas Bonnefon if ( ( sel_start < start_ ) && ( sel_end < start_ ) ) {
114bb02e0acSNicolas Bonnefon // Selection BEFORE this chunk: no change
115bb02e0acSNicolas Bonnefon list << LineChunk( *this );
116bb02e0acSNicolas Bonnefon }
117bb02e0acSNicolas Bonnefon else if ( sel_start > end_ ) {
118bb02e0acSNicolas Bonnefon // Selection AFTER this chunk: no change
119bb02e0acSNicolas Bonnefon list << LineChunk( *this );
120bb02e0acSNicolas Bonnefon }
121bb02e0acSNicolas Bonnefon else /* if ( ( sel_start >= start_ ) && ( sel_end <= end_ ) ) */
122bb02e0acSNicolas Bonnefon {
123bb02e0acSNicolas Bonnefon // We only want to consider what's inside THIS chunk
124bb02e0acSNicolas Bonnefon sel_start = qMax( sel_start, start_ );
125bb02e0acSNicolas Bonnefon sel_end = qMin( sel_end, end_ );
126bb02e0acSNicolas Bonnefon
127bb02e0acSNicolas Bonnefon if ( sel_start > start_ )
128bb02e0acSNicolas Bonnefon list << LineChunk( start_, sel_start - 1, type_ );
129bb02e0acSNicolas Bonnefon list << LineChunk( sel_start, sel_end, Selected );
130bb02e0acSNicolas Bonnefon if ( sel_end < end_ )
131bb02e0acSNicolas Bonnefon list << LineChunk( sel_end + 1, end_, type_ );
132bb02e0acSNicolas Bonnefon }
133bb02e0acSNicolas Bonnefon
134bb02e0acSNicolas Bonnefon return list;
135bb02e0acSNicolas Bonnefon }
136bb02e0acSNicolas Bonnefon
addChunk(int first_col,int last_col,QColor fore,QColor back)137bb02e0acSNicolas Bonnefon inline void LineDrawer::addChunk( int first_col, int last_col,
138bb02e0acSNicolas Bonnefon QColor fore, QColor back )
139bb02e0acSNicolas Bonnefon {
140bb02e0acSNicolas Bonnefon if ( first_col < 0 )
141bb02e0acSNicolas Bonnefon first_col = 0;
142bb02e0acSNicolas Bonnefon int length = last_col - first_col + 1;
143bb02e0acSNicolas Bonnefon if ( length > 0 ) {
144bb02e0acSNicolas Bonnefon list << Chunk ( first_col, length, fore, back );
145bb02e0acSNicolas Bonnefon }
146bb02e0acSNicolas Bonnefon }
147bb02e0acSNicolas Bonnefon
addChunk(const LineChunk & chunk,QColor fore,QColor back)148bb02e0acSNicolas Bonnefon inline void LineDrawer::addChunk( const LineChunk& chunk,
149bb02e0acSNicolas Bonnefon QColor fore, QColor back )
150bb02e0acSNicolas Bonnefon {
151bb02e0acSNicolas Bonnefon int first_col = chunk.start();
152bb02e0acSNicolas Bonnefon int last_col = chunk.end();
153bb02e0acSNicolas Bonnefon
154bb02e0acSNicolas Bonnefon addChunk( first_col, last_col, fore, back );
155bb02e0acSNicolas Bonnefon }
156bb02e0acSNicolas Bonnefon
draw(QPainter & painter,int initialXPos,int initialYPos,int line_width,const QString & line,int leftExtraBackgroundPx)157bb02e0acSNicolas Bonnefon inline void LineDrawer::draw( QPainter& painter,
158bb02e0acSNicolas Bonnefon int initialXPos, int initialYPos,
159bb02e0acSNicolas Bonnefon int line_width, const QString& line,
160bb02e0acSNicolas Bonnefon int leftExtraBackgroundPx )
161bb02e0acSNicolas Bonnefon {
162bb02e0acSNicolas Bonnefon QFontMetrics fm = painter.fontMetrics();
163bb02e0acSNicolas Bonnefon const int fontHeight = fm.height();
164bb02e0acSNicolas Bonnefon const int fontAscent = fm.ascent();
165bb02e0acSNicolas Bonnefon // For some reason on Qt 4.8.2 for Win, maxWidth() is wrong but the
166bb02e0acSNicolas Bonnefon // following give the right result, not sure why:
167bb02e0acSNicolas Bonnefon const int fontWidth = fm.width( QChar('a') );
168bb02e0acSNicolas Bonnefon
169bb02e0acSNicolas Bonnefon int xPos = initialXPos;
170bb02e0acSNicolas Bonnefon int yPos = initialYPos;
171bb02e0acSNicolas Bonnefon
172bb02e0acSNicolas Bonnefon foreach ( Chunk chunk, list ) {
173bb02e0acSNicolas Bonnefon // Draw each chunk
174bb02e0acSNicolas Bonnefon // LOG(logDEBUG) << "Chunk: " << chunk.start() << " " << chunk.length();
175bb02e0acSNicolas Bonnefon QString cutline = line.mid( chunk.start(), chunk.length() );
176bb02e0acSNicolas Bonnefon const int chunk_width = cutline.length() * fontWidth;
177bb02e0acSNicolas Bonnefon if ( xPos == initialXPos ) {
178bb02e0acSNicolas Bonnefon // First chunk, we extend the left background a bit,
179bb02e0acSNicolas Bonnefon // it looks prettier.
180bb02e0acSNicolas Bonnefon painter.fillRect( xPos - leftExtraBackgroundPx, yPos,
181bb02e0acSNicolas Bonnefon chunk_width + leftExtraBackgroundPx,
182bb02e0acSNicolas Bonnefon fontHeight, chunk.backColor() );
183bb02e0acSNicolas Bonnefon }
184bb02e0acSNicolas Bonnefon else {
185bb02e0acSNicolas Bonnefon // other chunks...
186bb02e0acSNicolas Bonnefon painter.fillRect( xPos, yPos, chunk_width,
187bb02e0acSNicolas Bonnefon fontHeight, chunk.backColor() );
188bb02e0acSNicolas Bonnefon }
189bb02e0acSNicolas Bonnefon painter.setPen( chunk.foreColor() );
190bb02e0acSNicolas Bonnefon painter.drawText( xPos, yPos + fontAscent, cutline );
191bb02e0acSNicolas Bonnefon xPos += chunk_width;
192bb02e0acSNicolas Bonnefon }
193bb02e0acSNicolas Bonnefon
194bb02e0acSNicolas Bonnefon // Draw the empty block at the end of the line
195bb02e0acSNicolas Bonnefon int blank_width = line_width - xPos;
196bb02e0acSNicolas Bonnefon
197bb02e0acSNicolas Bonnefon if ( blank_width > 0 )
198bb02e0acSNicolas Bonnefon painter.fillRect( xPos, yPos, blank_width, fontHeight, backColor_ );
199bb02e0acSNicolas Bonnefon }
200bb02e0acSNicolas Bonnefon
201bb02e0acSNicolas Bonnefon const int DigitsBuffer::timeout_ = 2000;
202bb02e0acSNicolas Bonnefon
DigitsBuffer()203bb02e0acSNicolas Bonnefon DigitsBuffer::DigitsBuffer() : QObject()
204bb02e0acSNicolas Bonnefon {
205bb02e0acSNicolas Bonnefon }
206bb02e0acSNicolas Bonnefon
reset()207bb02e0acSNicolas Bonnefon void DigitsBuffer::reset()
208bb02e0acSNicolas Bonnefon {
209bb02e0acSNicolas Bonnefon LOG(logDEBUG) << "DigitsBuffer::reset()";
210bb02e0acSNicolas Bonnefon
211bb02e0acSNicolas Bonnefon timer_.stop();
212bb02e0acSNicolas Bonnefon digits_.clear();
213bb02e0acSNicolas Bonnefon }
214bb02e0acSNicolas Bonnefon
add(char character)215bb02e0acSNicolas Bonnefon void DigitsBuffer::add( char character )
216bb02e0acSNicolas Bonnefon {
217bb02e0acSNicolas Bonnefon LOG(logDEBUG) << "DigitsBuffer::add()";
218bb02e0acSNicolas Bonnefon
219bb02e0acSNicolas Bonnefon digits_.append( QChar( character ) );
220bb02e0acSNicolas Bonnefon timer_.start( timeout_ , this );
221bb02e0acSNicolas Bonnefon }
222bb02e0acSNicolas Bonnefon
content()223bb02e0acSNicolas Bonnefon int DigitsBuffer::content()
224bb02e0acSNicolas Bonnefon {
225bb02e0acSNicolas Bonnefon int result = digits_.toInt();
226bb02e0acSNicolas Bonnefon reset();
227bb02e0acSNicolas Bonnefon
228bb02e0acSNicolas Bonnefon return result;
229bb02e0acSNicolas Bonnefon }
230bb02e0acSNicolas Bonnefon
timerEvent(QTimerEvent * event)231bb02e0acSNicolas Bonnefon void DigitsBuffer::timerEvent( QTimerEvent* event )
232bb02e0acSNicolas Bonnefon {
233bb02e0acSNicolas Bonnefon if ( event->timerId() == timer_.timerId() ) {
234bb02e0acSNicolas Bonnefon reset();
235bb02e0acSNicolas Bonnefon }
236bb02e0acSNicolas Bonnefon else {
237bb02e0acSNicolas Bonnefon QObject::timerEvent( event );
238bb02e0acSNicolas Bonnefon }
239bb02e0acSNicolas Bonnefon }
240bb02e0acSNicolas Bonnefon
AbstractLogView(const AbstractLogData * newLogData,const QuickFindPattern * const quickFindPattern,QWidget * parent)241bb02e0acSNicolas Bonnefon AbstractLogView::AbstractLogView(const AbstractLogData* newLogData,
242bb02e0acSNicolas Bonnefon const QuickFindPattern* const quickFindPattern, QWidget* parent) :
243bb02e0acSNicolas Bonnefon QAbstractScrollArea( parent ),
2442493b508SNicolas Bonnefon followElasticHook_( HOOK_THRESHOLD ),
245bb02e0acSNicolas Bonnefon lineNumbersVisible_( false ),
246bb02e0acSNicolas Bonnefon selectionStartPos_(),
247bb02e0acSNicolas Bonnefon selectionCurrentEndPos_(),
248bb02e0acSNicolas Bonnefon autoScrollTimer_(),
249bb02e0acSNicolas Bonnefon selection_(),
250bb02e0acSNicolas Bonnefon quickFindPattern_( quickFindPattern ),
2512493b508SNicolas Bonnefon quickFind_( newLogData, &selection_, quickFindPattern )
252bb02e0acSNicolas Bonnefon {
253bb02e0acSNicolas Bonnefon logData = newLogData;
254bb02e0acSNicolas Bonnefon
255bb02e0acSNicolas Bonnefon followMode_ = false;
256bb02e0acSNicolas Bonnefon
257bb02e0acSNicolas Bonnefon selectionStarted_ = false;
258bb02e0acSNicolas Bonnefon markingClickInitiated_ = false;
259bb02e0acSNicolas Bonnefon
260bb02e0acSNicolas Bonnefon firstLine = 0;
2619f0249b2SNicolas Bonnefon lastLineAligned = false;
262bb02e0acSNicolas Bonnefon firstCol = 0;
263bb02e0acSNicolas Bonnefon
264bb02e0acSNicolas Bonnefon overview_ = NULL;
265bb02e0acSNicolas Bonnefon overviewWidget_ = NULL;
266bb02e0acSNicolas Bonnefon
267bb02e0acSNicolas Bonnefon // Display
268bb02e0acSNicolas Bonnefon leftMarginPx_ = 0;
269bb02e0acSNicolas Bonnefon
270bc929c39SNicolas Bonnefon // Fonts (sensible default for overview widget)
271bb02e0acSNicolas Bonnefon charWidth_ = 1;
272bc929c39SNicolas Bonnefon charHeight_ = 10;
273bb02e0acSNicolas Bonnefon
274bb02e0acSNicolas Bonnefon // Create the viewport QWidget
275bb02e0acSNicolas Bonnefon setViewport( 0 );
276bb02e0acSNicolas Bonnefon
277bb02e0acSNicolas Bonnefon // Hovering
278bb02e0acSNicolas Bonnefon setMouseTracking( true );
279bb02e0acSNicolas Bonnefon lastHoveredLine_ = -1;
280bb02e0acSNicolas Bonnefon
281bb02e0acSNicolas Bonnefon // Init the popup menu
282bb02e0acSNicolas Bonnefon createMenu();
283bb02e0acSNicolas Bonnefon
284bb02e0acSNicolas Bonnefon // Signals
285bb02e0acSNicolas Bonnefon connect( quickFindPattern_, SIGNAL( patternUpdated() ),
286bb02e0acSNicolas Bonnefon this, SLOT ( handlePatternUpdated() ) );
287bb02e0acSNicolas Bonnefon connect( &quickFind_, SIGNAL( notify( const QFNotification& ) ),
288bb02e0acSNicolas Bonnefon this, SIGNAL( notifyQuickFind( const QFNotification& ) ) );
289bb02e0acSNicolas Bonnefon connect( &quickFind_, SIGNAL( clearNotification() ),
290bb02e0acSNicolas Bonnefon this, SIGNAL( clearQuickFindNotification() ) );
2910b05c6eaSNicolas Bonnefon connect( &followElasticHook_, SIGNAL( lengthChanged() ),
2920b05c6eaSNicolas Bonnefon this, SLOT( repaint() ) );
293b297d2f4SNicolas Bonnefon connect( &followElasticHook_, SIGNAL( hooked( bool ) ),
294b297d2f4SNicolas Bonnefon this, SIGNAL( followModeChanged( bool ) ) );
295bb02e0acSNicolas Bonnefon }
296bb02e0acSNicolas Bonnefon
~AbstractLogView()297bb02e0acSNicolas Bonnefon AbstractLogView::~AbstractLogView()
298bb02e0acSNicolas Bonnefon {
299bb02e0acSNicolas Bonnefon }
300bb02e0acSNicolas Bonnefon
301bb02e0acSNicolas Bonnefon
302bb02e0acSNicolas Bonnefon //
303bb02e0acSNicolas Bonnefon // Received events
304bb02e0acSNicolas Bonnefon //
305bb02e0acSNicolas Bonnefon
changeEvent(QEvent * changeEvent)306bb02e0acSNicolas Bonnefon void AbstractLogView::changeEvent( QEvent* changeEvent )
307bb02e0acSNicolas Bonnefon {
308bb02e0acSNicolas Bonnefon QAbstractScrollArea::changeEvent( changeEvent );
309bb02e0acSNicolas Bonnefon
310bb02e0acSNicolas Bonnefon // Stop the timer if the widget becomes inactive
311bb02e0acSNicolas Bonnefon if ( changeEvent->type() == QEvent::ActivationChange ) {
312bb02e0acSNicolas Bonnefon if ( ! isActiveWindow() )
313bb02e0acSNicolas Bonnefon autoScrollTimer_.stop();
314bb02e0acSNicolas Bonnefon }
315bb02e0acSNicolas Bonnefon viewport()->update();
316bb02e0acSNicolas Bonnefon }
317bb02e0acSNicolas Bonnefon
mousePressEvent(QMouseEvent * mouseEvent)318bb02e0acSNicolas Bonnefon void AbstractLogView::mousePressEvent( QMouseEvent* mouseEvent )
319bb02e0acSNicolas Bonnefon {
32011582726SNicolas Bonnefon static std::shared_ptr<Configuration> config =
32111582726SNicolas Bonnefon Persistent<Configuration>( "settings" );
322bb02e0acSNicolas Bonnefon
323bb02e0acSNicolas Bonnefon if ( mouseEvent->button() == Qt::LeftButton )
324bb02e0acSNicolas Bonnefon {
325bb02e0acSNicolas Bonnefon int line = convertCoordToLine( mouseEvent->y() );
326bb02e0acSNicolas Bonnefon
327bb02e0acSNicolas Bonnefon if ( mouseEvent->modifiers() & Qt::ShiftModifier )
328bb02e0acSNicolas Bonnefon {
329bb02e0acSNicolas Bonnefon selection_.selectRangeFromPrevious( line );
330bb02e0acSNicolas Bonnefon emit updateLineNumber( line );
331bb02e0acSNicolas Bonnefon update();
332bb02e0acSNicolas Bonnefon }
333bb02e0acSNicolas Bonnefon else
334bb02e0acSNicolas Bonnefon {
335bb02e0acSNicolas Bonnefon if ( mouseEvent->x() < bulletZoneWidthPx_ ) {
336bb02e0acSNicolas Bonnefon // Mark a line if it is clicked in the left margin
337bb02e0acSNicolas Bonnefon // (only if click and release in the same area)
338bb02e0acSNicolas Bonnefon markingClickInitiated_ = true;
339bb02e0acSNicolas Bonnefon markingClickLine_ = line;
340bb02e0acSNicolas Bonnefon }
341bb02e0acSNicolas Bonnefon else {
342bb02e0acSNicolas Bonnefon // Select the line, and start a selection
343bb02e0acSNicolas Bonnefon if ( line < logData->getNbLine() ) {
344bb02e0acSNicolas Bonnefon selection_.selectLine( line );
345bb02e0acSNicolas Bonnefon emit updateLineNumber( line );
346bb02e0acSNicolas Bonnefon emit newSelection( line );
347bb02e0acSNicolas Bonnefon }
348bb02e0acSNicolas Bonnefon
349bb02e0acSNicolas Bonnefon // Remember the click in case we're starting a selection
350bb02e0acSNicolas Bonnefon selectionStarted_ = true;
351bb02e0acSNicolas Bonnefon selectionStartPos_ = convertCoordToFilePos( mouseEvent->pos() );
352bb02e0acSNicolas Bonnefon selectionCurrentEndPos_ = selectionStartPos_;
353bb02e0acSNicolas Bonnefon }
354bb02e0acSNicolas Bonnefon }
355a9448ba0SNicolas Bonnefon
356a9448ba0SNicolas Bonnefon // Invalidate our cache
357a9448ba0SNicolas Bonnefon textAreaCache_.invalid_ = true;
358bb02e0acSNicolas Bonnefon }
359bb02e0acSNicolas Bonnefon else if ( mouseEvent->button() == Qt::RightButton )
360bb02e0acSNicolas Bonnefon {
361bb02e0acSNicolas Bonnefon // Prepare the popup depending on selection type
362bb02e0acSNicolas Bonnefon if ( selection_.isSingleLine() ) {
363bb02e0acSNicolas Bonnefon copyAction_->setText( "&Copy this line" );
364bb02e0acSNicolas Bonnefon }
365bb02e0acSNicolas Bonnefon else {
366bb02e0acSNicolas Bonnefon copyAction_->setText( "&Copy" );
367bb02e0acSNicolas Bonnefon copyAction_->setStatusTip( tr("Copy the selection") );
368bb02e0acSNicolas Bonnefon }
369bb02e0acSNicolas Bonnefon
370bb02e0acSNicolas Bonnefon if ( selection_.isPortion() ) {
371bb02e0acSNicolas Bonnefon findNextAction_->setEnabled( true );
372bb02e0acSNicolas Bonnefon findPreviousAction_->setEnabled( true );
373bb02e0acSNicolas Bonnefon addToSearchAction_->setEnabled( true );
374bb02e0acSNicolas Bonnefon }
375bb02e0acSNicolas Bonnefon else {
376bb02e0acSNicolas Bonnefon findNextAction_->setEnabled( false );
377bb02e0acSNicolas Bonnefon findPreviousAction_->setEnabled( false );
378bb02e0acSNicolas Bonnefon addToSearchAction_->setEnabled( false );
379bb02e0acSNicolas Bonnefon }
380bb02e0acSNicolas Bonnefon
381bb02e0acSNicolas Bonnefon // "Add to search" only makes sense in regexp mode
38211582726SNicolas Bonnefon if ( config->mainRegexpType() != ExtendedRegexp )
383bb02e0acSNicolas Bonnefon addToSearchAction_->setEnabled( false );
384bb02e0acSNicolas Bonnefon
385bb02e0acSNicolas Bonnefon // Display the popup (blocking)
386bb02e0acSNicolas Bonnefon popupMenu_->exec( QCursor::pos() );
387bb02e0acSNicolas Bonnefon }
38845ef183cSNicolas Bonnefon
38945ef183cSNicolas Bonnefon emit activity();
390bb02e0acSNicolas Bonnefon }
391bb02e0acSNicolas Bonnefon
mouseMoveEvent(QMouseEvent * mouseEvent)392bb02e0acSNicolas Bonnefon void AbstractLogView::mouseMoveEvent( QMouseEvent* mouseEvent )
393bb02e0acSNicolas Bonnefon {
394bb02e0acSNicolas Bonnefon // Selection implementation
395bb02e0acSNicolas Bonnefon if ( selectionStarted_ )
396bb02e0acSNicolas Bonnefon {
397a9448ba0SNicolas Bonnefon // Invalidate our cache
398a9448ba0SNicolas Bonnefon textAreaCache_.invalid_ = true;
399a9448ba0SNicolas Bonnefon
400bb02e0acSNicolas Bonnefon QPoint thisEndPos = convertCoordToFilePos( mouseEvent->pos() );
401bb02e0acSNicolas Bonnefon if ( thisEndPos != selectionCurrentEndPos_ )
402bb02e0acSNicolas Bonnefon {
403bb02e0acSNicolas Bonnefon // Are we on a different line?
404bb02e0acSNicolas Bonnefon if ( selectionStartPos_.y() != thisEndPos.y() )
405bb02e0acSNicolas Bonnefon {
406bb02e0acSNicolas Bonnefon if ( thisEndPos.y() != selectionCurrentEndPos_.y() )
407bb02e0acSNicolas Bonnefon {
408bb02e0acSNicolas Bonnefon // This is a 'range' selection
409bb02e0acSNicolas Bonnefon selection_.selectRange( selectionStartPos_.y(),
410bb02e0acSNicolas Bonnefon thisEndPos.y() );
411bb02e0acSNicolas Bonnefon emit updateLineNumber( thisEndPos.y() );
412bb02e0acSNicolas Bonnefon update();
413bb02e0acSNicolas Bonnefon }
414bb02e0acSNicolas Bonnefon }
415bb02e0acSNicolas Bonnefon // So we are on the same line. Are we moving horizontaly?
416bb02e0acSNicolas Bonnefon else if ( thisEndPos.x() != selectionCurrentEndPos_.x() )
417bb02e0acSNicolas Bonnefon {
418bb02e0acSNicolas Bonnefon // This is a 'portion' selection
419bb02e0acSNicolas Bonnefon selection_.selectPortion( thisEndPos.y(),
420bb02e0acSNicolas Bonnefon selectionStartPos_.x(), thisEndPos.x() );
421bb02e0acSNicolas Bonnefon update();
422bb02e0acSNicolas Bonnefon }
423bb02e0acSNicolas Bonnefon // On the same line, and moving vertically then
424bb02e0acSNicolas Bonnefon else
425bb02e0acSNicolas Bonnefon {
426bb02e0acSNicolas Bonnefon // This is a 'line' selection
427bb02e0acSNicolas Bonnefon selection_.selectLine( thisEndPos.y() );
428bb02e0acSNicolas Bonnefon emit updateLineNumber( thisEndPos.y() );
429bb02e0acSNicolas Bonnefon update();
430bb02e0acSNicolas Bonnefon }
431bb02e0acSNicolas Bonnefon selectionCurrentEndPos_ = thisEndPos;
432bb02e0acSNicolas Bonnefon
433bb02e0acSNicolas Bonnefon // Do we need to scroll while extending the selection?
434bb02e0acSNicolas Bonnefon QRect visible = viewport()->rect();
435bb02e0acSNicolas Bonnefon if ( visible.contains( mouseEvent->pos() ) )
436bb02e0acSNicolas Bonnefon autoScrollTimer_.stop();
437bb02e0acSNicolas Bonnefon else if ( ! autoScrollTimer_.isActive() )
438bb02e0acSNicolas Bonnefon autoScrollTimer_.start( 100, this );
439bb02e0acSNicolas Bonnefon }
440bb02e0acSNicolas Bonnefon }
441bb02e0acSNicolas Bonnefon else {
442bb02e0acSNicolas Bonnefon considerMouseHovering( mouseEvent->x(), mouseEvent->y() );
443bb02e0acSNicolas Bonnefon }
444bb02e0acSNicolas Bonnefon }
445bb02e0acSNicolas Bonnefon
mouseReleaseEvent(QMouseEvent * mouseEvent)446bb02e0acSNicolas Bonnefon void AbstractLogView::mouseReleaseEvent( QMouseEvent* mouseEvent )
447bb02e0acSNicolas Bonnefon {
448bb02e0acSNicolas Bonnefon if ( markingClickInitiated_ ) {
449bb02e0acSNicolas Bonnefon markingClickInitiated_ = false;
450bb02e0acSNicolas Bonnefon int line = convertCoordToLine( mouseEvent->y() );
451a9448ba0SNicolas Bonnefon if ( line == markingClickLine_ ) {
452a9448ba0SNicolas Bonnefon // Invalidate our cache
453a9448ba0SNicolas Bonnefon textAreaCache_.invalid_ = true;
454a9448ba0SNicolas Bonnefon
455bb02e0acSNicolas Bonnefon emit markLine( line );
456bb02e0acSNicolas Bonnefon }
457a9448ba0SNicolas Bonnefon }
458bb02e0acSNicolas Bonnefon else {
459bb02e0acSNicolas Bonnefon selectionStarted_ = false;
460bb02e0acSNicolas Bonnefon if ( autoScrollTimer_.isActive() )
461bb02e0acSNicolas Bonnefon autoScrollTimer_.stop();
462bb02e0acSNicolas Bonnefon updateGlobalSelection();
463bb02e0acSNicolas Bonnefon }
464bb02e0acSNicolas Bonnefon }
465bb02e0acSNicolas Bonnefon
mouseDoubleClickEvent(QMouseEvent * mouseEvent)466bb02e0acSNicolas Bonnefon void AbstractLogView::mouseDoubleClickEvent( QMouseEvent* mouseEvent )
467bb02e0acSNicolas Bonnefon {
468bb02e0acSNicolas Bonnefon if ( mouseEvent->button() == Qt::LeftButton )
469bb02e0acSNicolas Bonnefon {
470a9448ba0SNicolas Bonnefon // Invalidate our cache
471a9448ba0SNicolas Bonnefon textAreaCache_.invalid_ = true;
472a9448ba0SNicolas Bonnefon
473bb02e0acSNicolas Bonnefon const QPoint pos = convertCoordToFilePos( mouseEvent->pos() );
474bb02e0acSNicolas Bonnefon selectWordAtPosition( pos );
475bb02e0acSNicolas Bonnefon }
47645ef183cSNicolas Bonnefon
47745ef183cSNicolas Bonnefon emit activity();
478bb02e0acSNicolas Bonnefon }
479bb02e0acSNicolas Bonnefon
timerEvent(QTimerEvent * timerEvent)480bb02e0acSNicolas Bonnefon void AbstractLogView::timerEvent( QTimerEvent* timerEvent )
481bb02e0acSNicolas Bonnefon {
482bb02e0acSNicolas Bonnefon if ( timerEvent->timerId() == autoScrollTimer_.timerId() ) {
483bb02e0acSNicolas Bonnefon QRect visible = viewport()->rect();
484bb02e0acSNicolas Bonnefon const QPoint globalPos = QCursor::pos();
485bb02e0acSNicolas Bonnefon const QPoint pos = viewport()->mapFromGlobal( globalPos );
486bb02e0acSNicolas Bonnefon QMouseEvent ev( QEvent::MouseMove, pos, globalPos, Qt::LeftButton,
487bb02e0acSNicolas Bonnefon Qt::LeftButton, Qt::NoModifier );
488bb02e0acSNicolas Bonnefon mouseMoveEvent( &ev );
489bb02e0acSNicolas Bonnefon int deltaX = qMax( pos.x() - visible.left(),
490bb02e0acSNicolas Bonnefon visible.right() - pos.x() ) - visible.width();
491bb02e0acSNicolas Bonnefon int deltaY = qMax( pos.y() - visible.top(),
492bb02e0acSNicolas Bonnefon visible.bottom() - pos.y() ) - visible.height();
493bb02e0acSNicolas Bonnefon int delta = qMax( deltaX, deltaY );
494bb02e0acSNicolas Bonnefon
495bb02e0acSNicolas Bonnefon if ( delta >= 0 ) {
496bb02e0acSNicolas Bonnefon if ( delta < 7 )
497bb02e0acSNicolas Bonnefon delta = 7;
498bb02e0acSNicolas Bonnefon int timeout = 4900 / ( delta * delta );
499bb02e0acSNicolas Bonnefon autoScrollTimer_.start( timeout, this );
500bb02e0acSNicolas Bonnefon
501bb02e0acSNicolas Bonnefon if ( deltaX > 0 )
502bb02e0acSNicolas Bonnefon horizontalScrollBar()->triggerAction(
503bb02e0acSNicolas Bonnefon pos.x() <visible.center().x() ?
504bb02e0acSNicolas Bonnefon QAbstractSlider::SliderSingleStepSub :
505bb02e0acSNicolas Bonnefon QAbstractSlider::SliderSingleStepAdd );
506bb02e0acSNicolas Bonnefon
507bb02e0acSNicolas Bonnefon if ( deltaY > 0 )
508bb02e0acSNicolas Bonnefon verticalScrollBar()->triggerAction(
509bb02e0acSNicolas Bonnefon pos.y() <visible.center().y() ?
510bb02e0acSNicolas Bonnefon QAbstractSlider::SliderSingleStepSub :
511bb02e0acSNicolas Bonnefon QAbstractSlider::SliderSingleStepAdd );
512bb02e0acSNicolas Bonnefon }
513bb02e0acSNicolas Bonnefon }
514bb02e0acSNicolas Bonnefon QAbstractScrollArea::timerEvent( timerEvent );
515bb02e0acSNicolas Bonnefon }
516bb02e0acSNicolas Bonnefon
keyPressEvent(QKeyEvent * keyEvent)517bb02e0acSNicolas Bonnefon void AbstractLogView::keyPressEvent( QKeyEvent* keyEvent )
518bb02e0acSNicolas Bonnefon {
519bb02e0acSNicolas Bonnefon LOG(logDEBUG4) << "keyPressEvent received";
520a9518197SNicolas Bonnefon
521bb02e0acSNicolas Bonnefon bool controlModifier = (keyEvent->modifiers() & Qt::ControlModifier) == Qt::ControlModifier;
522bb02e0acSNicolas Bonnefon bool shiftModifier = (keyEvent->modifiers() & Qt::ShiftModifier) == Qt::ShiftModifier;
523a9518197SNicolas Bonnefon bool noModifier = keyEvent->modifiers() == Qt::NoModifier;
524bb02e0acSNicolas Bonnefon
525a9518197SNicolas Bonnefon if ( keyEvent->key() == Qt::Key_Left && noModifier )
526bb02e0acSNicolas Bonnefon horizontalScrollBar()->triggerAction(QScrollBar::SliderPageStepSub);
527a9518197SNicolas Bonnefon else if ( keyEvent->key() == Qt::Key_Right && noModifier )
528bb02e0acSNicolas Bonnefon horizontalScrollBar()->triggerAction(QScrollBar::SliderPageStepAdd);
529bb02e0acSNicolas Bonnefon else if ( keyEvent->key() == Qt::Key_Home && !controlModifier)
530bb02e0acSNicolas Bonnefon jumpToStartOfLine();
531bb02e0acSNicolas Bonnefon else if ( keyEvent->key() == Qt::Key_End && !controlModifier)
532bb02e0acSNicolas Bonnefon jumpToRightOfScreen();
533bb02e0acSNicolas Bonnefon else if ( (keyEvent->key() == Qt::Key_PageDown && controlModifier)
534bb02e0acSNicolas Bonnefon || (keyEvent->key() == Qt::Key_End && controlModifier) )
535bb02e0acSNicolas Bonnefon {
536b297d2f4SNicolas Bonnefon disableFollow(); // duplicate of 'G' action.
537bb02e0acSNicolas Bonnefon selection_.selectLine( logData->getNbLine() - 1 );
538bb02e0acSNicolas Bonnefon emit updateLineNumber( logData->getNbLine() - 1 );
539bb02e0acSNicolas Bonnefon jumpToBottom();
540bb02e0acSNicolas Bonnefon }
541bb02e0acSNicolas Bonnefon else if ( (keyEvent->key() == Qt::Key_PageUp && controlModifier)
542bb02e0acSNicolas Bonnefon || (keyEvent->key() == Qt::Key_Home && controlModifier) )
543bb02e0acSNicolas Bonnefon selectAndDisplayLine( 0 );
544bb02e0acSNicolas Bonnefon else if ( keyEvent->key() == Qt::Key_F3 && !shiftModifier )
545bb02e0acSNicolas Bonnefon searchNext(); // duplicate of 'n' action.
546bb02e0acSNicolas Bonnefon else if ( keyEvent->key() == Qt::Key_F3 && shiftModifier )
547bb02e0acSNicolas Bonnefon searchPrevious(); // duplicate of 'N' action.
5487552c461SNicolas Bonnefon else if ( keyEvent->key() == Qt::Key_Space && noModifier )
5497552c461SNicolas Bonnefon emit exitView();
550bb02e0acSNicolas Bonnefon else {
551eb6a8f99SNicolas Bonnefon const char character = (keyEvent->text())[0].toLatin1();
552bb02e0acSNicolas Bonnefon
553f88e59a4SNicolas Bonnefon if ( keyEvent->modifiers() == Qt::NoModifier &&
554f88e59a4SNicolas Bonnefon ( character >= '0' ) && ( character <= '9' ) ) {
555bb02e0acSNicolas Bonnefon // Adds the digit to the timed buffer
556bb02e0acSNicolas Bonnefon digitsBuffer_.add( character );
557bb02e0acSNicolas Bonnefon }
558bb02e0acSNicolas Bonnefon else {
559eb6a8f99SNicolas Bonnefon switch ( (keyEvent->text())[0].toLatin1() ) {
560bb02e0acSNicolas Bonnefon case 'j':
561bb02e0acSNicolas Bonnefon {
562bb02e0acSNicolas Bonnefon int delta = qMax( 1, digitsBuffer_.content() );
563b297d2f4SNicolas Bonnefon disableFollow();
564bb02e0acSNicolas Bonnefon //verticalScrollBar()->triggerAction(
565bb02e0acSNicolas Bonnefon //QScrollBar::SliderSingleStepAdd);
566bb02e0acSNicolas Bonnefon moveSelection( delta );
567bb02e0acSNicolas Bonnefon break;
568bb02e0acSNicolas Bonnefon }
569bb02e0acSNicolas Bonnefon case 'k':
570bb02e0acSNicolas Bonnefon {
571bb02e0acSNicolas Bonnefon int delta = qMin( -1, - digitsBuffer_.content() );
572b297d2f4SNicolas Bonnefon disableFollow();
573bb02e0acSNicolas Bonnefon //verticalScrollBar()->triggerAction(
574bb02e0acSNicolas Bonnefon //QScrollBar::SliderSingleStepSub);
575bb02e0acSNicolas Bonnefon moveSelection( delta );
576bb02e0acSNicolas Bonnefon break;
577bb02e0acSNicolas Bonnefon }
578bb02e0acSNicolas Bonnefon case 'h':
579bb02e0acSNicolas Bonnefon horizontalScrollBar()->triggerAction(
580bb02e0acSNicolas Bonnefon QScrollBar::SliderSingleStepSub);
581bb02e0acSNicolas Bonnefon break;
582bb02e0acSNicolas Bonnefon case 'l':
583bb02e0acSNicolas Bonnefon horizontalScrollBar()->triggerAction(
584bb02e0acSNicolas Bonnefon QScrollBar::SliderSingleStepAdd);
585bb02e0acSNicolas Bonnefon break;
586bb02e0acSNicolas Bonnefon case '0':
587bb02e0acSNicolas Bonnefon jumpToStartOfLine();
588bb02e0acSNicolas Bonnefon break;
589bb02e0acSNicolas Bonnefon case '$':
590bb02e0acSNicolas Bonnefon jumpToEndOfLine();
591bb02e0acSNicolas Bonnefon break;
592bb02e0acSNicolas Bonnefon case 'g':
593bb02e0acSNicolas Bonnefon {
594bb02e0acSNicolas Bonnefon int newLine = qMax( 0, digitsBuffer_.content() - 1 );
595bb02e0acSNicolas Bonnefon if ( newLine >= logData->getNbLine() )
596bb02e0acSNicolas Bonnefon newLine = logData->getNbLine() - 1;
597bb02e0acSNicolas Bonnefon selectAndDisplayLine( newLine );
598bb02e0acSNicolas Bonnefon break;
599bb02e0acSNicolas Bonnefon }
600bb02e0acSNicolas Bonnefon case 'G':
601b297d2f4SNicolas Bonnefon disableFollow();
602bb02e0acSNicolas Bonnefon selection_.selectLine( logData->getNbLine() - 1 );
603bb02e0acSNicolas Bonnefon emit updateLineNumber( logData->getNbLine() - 1 );
6048e788202SNicolas Bonnefon emit newSelection( logData->getNbLine() - 1 );
605bb02e0acSNicolas Bonnefon jumpToBottom();
606bb02e0acSNicolas Bonnefon break;
607bb02e0acSNicolas Bonnefon case 'n':
608bb02e0acSNicolas Bonnefon emit searchNext();
609bb02e0acSNicolas Bonnefon break;
610bb02e0acSNicolas Bonnefon case 'N':
611bb02e0acSNicolas Bonnefon emit searchPrevious();
612bb02e0acSNicolas Bonnefon break;
613bb02e0acSNicolas Bonnefon case '*':
614bb02e0acSNicolas Bonnefon // Use the selected 'word' and search forward
615bb02e0acSNicolas Bonnefon findNextSelected();
616bb02e0acSNicolas Bonnefon break;
617bb02e0acSNicolas Bonnefon case '#':
618bb02e0acSNicolas Bonnefon // Use the selected 'word' and search backward
619bb02e0acSNicolas Bonnefon findPreviousSelected();
620bb02e0acSNicolas Bonnefon break;
6217552c461SNicolas Bonnefon case 'm':
6227552c461SNicolas Bonnefon {
6237552c461SNicolas Bonnefon qint64 line = selection_.selectedLine();
6247552c461SNicolas Bonnefon if ( line >= 0 )
6257552c461SNicolas Bonnefon emit markLine( line );
6267552c461SNicolas Bonnefon break;
6277552c461SNicolas Bonnefon }
628bb02e0acSNicolas Bonnefon default:
629bb02e0acSNicolas Bonnefon keyEvent->ignore();
630bb02e0acSNicolas Bonnefon }
631bb02e0acSNicolas Bonnefon }
632bb02e0acSNicolas Bonnefon }
633bb02e0acSNicolas Bonnefon
63445ef183cSNicolas Bonnefon if ( keyEvent->isAccepted() ) {
63545ef183cSNicolas Bonnefon emit activity();
63645ef183cSNicolas Bonnefon }
63745ef183cSNicolas Bonnefon else {
638a9518197SNicolas Bonnefon // Only pass bare keys to the superclass this is so that
639a9518197SNicolas Bonnefon // shortcuts such as Ctrl+Alt+Arrow are handled by the parent.
640a9518197SNicolas Bonnefon LOG(logDEBUG) << std::hex << keyEvent->modifiers();
641a9518197SNicolas Bonnefon if ( keyEvent->modifiers() == Qt::NoModifier ||
642a9518197SNicolas Bonnefon keyEvent->modifiers() == Qt::KeypadModifier ) {
643bb02e0acSNicolas Bonnefon QAbstractScrollArea::keyPressEvent( keyEvent );
644bb02e0acSNicolas Bonnefon }
64545ef183cSNicolas Bonnefon }
646a9518197SNicolas Bonnefon }
647bb02e0acSNicolas Bonnefon
wheelEvent(QWheelEvent * wheelEvent)648bb02e0acSNicolas Bonnefon void AbstractLogView::wheelEvent( QWheelEvent* wheelEvent )
649bb02e0acSNicolas Bonnefon {
65045ef183cSNicolas Bonnefon emit activity();
651bb02e0acSNicolas Bonnefon
652a9448ba0SNicolas Bonnefon // LOG(logDEBUG) << "wheelEvent";
6530b05c6eaSNicolas Bonnefon
6542493b508SNicolas Bonnefon // This is to handle the case where follow mode is on, but the user
6552493b508SNicolas Bonnefon // has moved using the scroll bar. We take them back to the bottom.
6562493b508SNicolas Bonnefon if ( followMode_ )
6572493b508SNicolas Bonnefon jumpToBottom();
6582493b508SNicolas Bonnefon
65968d95677SNicolas Bonnefon int y_delta = 0;
6600b05c6eaSNicolas Bonnefon if ( verticalScrollBar()->value() == verticalScrollBar()->maximum() ) {
661819e4a32SNicolas Bonnefon // First see if we need to block the elastic (on Mac)
662819e4a32SNicolas Bonnefon if ( wheelEvent->phase() == Qt::ScrollBegin )
663819e4a32SNicolas Bonnefon followElasticHook_.hold();
664819e4a32SNicolas Bonnefon else if ( wheelEvent->phase() == Qt::ScrollEnd )
665819e4a32SNicolas Bonnefon followElasticHook_.release();
666819e4a32SNicolas Bonnefon
66768d95677SNicolas Bonnefon auto pixel_delta = wheelEvent->pixelDelta();
66868d95677SNicolas Bonnefon
66968d95677SNicolas Bonnefon if ( pixel_delta.isNull() ) {
67006d5d36cSNicolas Bonnefon y_delta = wheelEvent->angleDelta().y() / 0.7;
67168d95677SNicolas Bonnefon }
67268d95677SNicolas Bonnefon else {
67368d95677SNicolas Bonnefon y_delta = pixel_delta.y();
67468d95677SNicolas Bonnefon }
67568d95677SNicolas Bonnefon
67668d95677SNicolas Bonnefon // LOG(logDEBUG) << "Elastic " << y_delta;
67768d95677SNicolas Bonnefon followElasticHook_.move( - y_delta );
6780b05c6eaSNicolas Bonnefon }
6790b05c6eaSNicolas Bonnefon
680a9448ba0SNicolas Bonnefon // LOG(logDEBUG) << "Length = " << followElasticHook_.length();
681ac6602a5SNicolas Bonnefon if ( followElasticHook_.length() == 0 && !followElasticHook_.isHooked() ) {
682bb02e0acSNicolas Bonnefon QAbstractScrollArea::wheelEvent( wheelEvent );
683bb02e0acSNicolas Bonnefon }
6840b05c6eaSNicolas Bonnefon }
685bb02e0acSNicolas Bonnefon
resizeEvent(QResizeEvent *)686bb02e0acSNicolas Bonnefon void AbstractLogView::resizeEvent( QResizeEvent* )
687bb02e0acSNicolas Bonnefon {
688bb02e0acSNicolas Bonnefon if ( logData == NULL )
689bb02e0acSNicolas Bonnefon return;
690bb02e0acSNicolas Bonnefon
691bb02e0acSNicolas Bonnefon LOG(logDEBUG) << "resizeEvent received";
692bb02e0acSNicolas Bonnefon
693bb02e0acSNicolas Bonnefon updateDisplaySize();
694bb02e0acSNicolas Bonnefon }
695bb02e0acSNicolas Bonnefon
event(QEvent * e)6961853a1fdSNicolas Bonnefon bool AbstractLogView::event( QEvent* e )
6971853a1fdSNicolas Bonnefon {
6981853a1fdSNicolas Bonnefon LOG(logDEBUG4) << "Event! Type: " << e->type();
6991853a1fdSNicolas Bonnefon
7001853a1fdSNicolas Bonnefon // Make sure we ignore the gesture events as
7011853a1fdSNicolas Bonnefon // they seem to be accepted by default.
7021853a1fdSNicolas Bonnefon if ( e->type() == QEvent::Gesture ) {
7031853a1fdSNicolas Bonnefon auto gesture_event = dynamic_cast<QGestureEvent*>( e );
7041853a1fdSNicolas Bonnefon if ( gesture_event ) {
7051853a1fdSNicolas Bonnefon foreach( QGesture* gesture, gesture_event->gestures() ) {
7062493b508SNicolas Bonnefon LOG(logDEBUG4) << "Gesture: " << gesture->gestureType();
7071853a1fdSNicolas Bonnefon gesture_event->ignore( gesture );
7081853a1fdSNicolas Bonnefon }
7091853a1fdSNicolas Bonnefon
7101853a1fdSNicolas Bonnefon // Ensure the event is sent up to parents who might care
7111853a1fdSNicolas Bonnefon return false;
7121853a1fdSNicolas Bonnefon }
7131853a1fdSNicolas Bonnefon }
7141853a1fdSNicolas Bonnefon
7151853a1fdSNicolas Bonnefon return QAbstractScrollArea::event( e );
7161853a1fdSNicolas Bonnefon }
7171853a1fdSNicolas Bonnefon
scrollContentsBy(int dx,int dy)718bb02e0acSNicolas Bonnefon void AbstractLogView::scrollContentsBy( int dx, int dy )
719bb02e0acSNicolas Bonnefon {
7209f0249b2SNicolas Bonnefon LOG(logDEBUG) << "scrollContentsBy received " << dy
7219f0249b2SNicolas Bonnefon << "position " << verticalScrollBar()->value();
722bb02e0acSNicolas Bonnefon
7239f0249b2SNicolas Bonnefon int32_t last_top_line = ( logData->getNbLine() - getNbVisibleLines() );
7249f0249b2SNicolas Bonnefon if ( ( last_top_line > 0 ) && verticalScrollBar()->value() > last_top_line ) {
7259f0249b2SNicolas Bonnefon // The user is going further than the last line, we need to lock the last line at the bottom
7269f0249b2SNicolas Bonnefon LOG(logDEBUG) << "scrollContentsBy beyond!";
7279f0249b2SNicolas Bonnefon firstLine = last_top_line;
7289f0249b2SNicolas Bonnefon lastLineAligned = true;
7299f0249b2SNicolas Bonnefon }
7309f0249b2SNicolas Bonnefon else {
7319f0249b2SNicolas Bonnefon firstLine = verticalScrollBar()->value();
7329f0249b2SNicolas Bonnefon lastLineAligned = false;
7339f0249b2SNicolas Bonnefon }
7349f0249b2SNicolas Bonnefon
735bb02e0acSNicolas Bonnefon firstCol = (firstCol - dx) > 0 ? firstCol - dx : 0;
7362493b508SNicolas Bonnefon LineNumber last_line = firstLine + getNbVisibleLines();
737bb02e0acSNicolas Bonnefon
738bb02e0acSNicolas Bonnefon // Update the overview if we have one
739bb02e0acSNicolas Bonnefon if ( overview_ != NULL )
7402493b508SNicolas Bonnefon overview_->updateCurrentPosition( firstLine, last_line );
741bb02e0acSNicolas Bonnefon
742bb02e0acSNicolas Bonnefon // Are we hovering over a new line?
743bb02e0acSNicolas Bonnefon const QPoint mouse_pos = mapFromGlobal( QCursor::pos() );
744bb02e0acSNicolas Bonnefon considerMouseHovering( mouse_pos.x(), mouse_pos.y() );
745bb02e0acSNicolas Bonnefon
746bb02e0acSNicolas Bonnefon // Redraw
747bb02e0acSNicolas Bonnefon update();
748bb02e0acSNicolas Bonnefon }
749bb02e0acSNicolas Bonnefon
paintEvent(QPaintEvent * paintEvent)750bb02e0acSNicolas Bonnefon void AbstractLogView::paintEvent( QPaintEvent* paintEvent )
751bb02e0acSNicolas Bonnefon {
7522493b508SNicolas Bonnefon const QRect invalidRect = paintEvent->rect();
753bb02e0acSNicolas Bonnefon if ( (invalidRect.isEmpty()) || (logData == NULL) )
754bb02e0acSNicolas Bonnefon return;
755bb02e0acSNicolas Bonnefon
756bb02e0acSNicolas Bonnefon LOG(logDEBUG4) << "paintEvent received, firstLine=" << firstLine
7579f0249b2SNicolas Bonnefon << " lastLineAligned=" << lastLineAligned
7582493b508SNicolas Bonnefon << " rect: " << invalidRect.topLeft().x() <<
759bb02e0acSNicolas Bonnefon ", " << invalidRect.topLeft().y() <<
760bb02e0acSNicolas Bonnefon ", " << invalidRect.bottomRight().x() <<
761bb02e0acSNicolas Bonnefon ", " << invalidRect.bottomRight().y();
762bb02e0acSNicolas Bonnefon
7638bda3285SNicolas Bonnefon #ifdef GLOGG_PERF_MEASURE_FPS
7648bda3285SNicolas Bonnefon static uint32_t maxline = logData->getNbLine();
7658bda3285SNicolas Bonnefon if ( ! perfCounter_.addEvent() && logData->getNbLine() > maxline ) {
766045703daSNicolas Bonnefon LOG(logWARNING) << "Redraw per second: " << perfCounter_.readAndReset()
767045703daSNicolas Bonnefon << " lines: " << logData->getNbLine();
768045703daSNicolas Bonnefon perfCounter_.addEvent();
7698bda3285SNicolas Bonnefon maxline = logData->getNbLine();
770045703daSNicolas Bonnefon }
7718bda3285SNicolas Bonnefon #endif
772045703daSNicolas Bonnefon
773a9448ba0SNicolas Bonnefon auto start = std::chrono::system_clock::now();
774a9448ba0SNicolas Bonnefon
775a9448ba0SNicolas Bonnefon // Can we use our cache?
776ac6602a5SNicolas Bonnefon int32_t delta_y = textAreaCache_.first_line_ - firstLine;
777ac6602a5SNicolas Bonnefon
778ac6602a5SNicolas Bonnefon if ( textAreaCache_.invalid_ || ( textAreaCache_.first_column_ != firstCol ) ) {
779ac6602a5SNicolas Bonnefon // Force a full redraw
780ac6602a5SNicolas Bonnefon delta_y = INT32_MAX;
781a9448ba0SNicolas Bonnefon }
782ac6602a5SNicolas Bonnefon
783ac6602a5SNicolas Bonnefon if ( delta_y != 0 ) {
784ac6602a5SNicolas Bonnefon // Full or partial redraw
785ac6602a5SNicolas Bonnefon drawTextArea( &textAreaCache_.pixmap_, delta_y );
786a9448ba0SNicolas Bonnefon
787a9448ba0SNicolas Bonnefon textAreaCache_.invalid_ = false;
788a9448ba0SNicolas Bonnefon textAreaCache_.first_line_ = firstLine;
789ac6602a5SNicolas Bonnefon textAreaCache_.first_column_ = firstCol;
790a9448ba0SNicolas Bonnefon
791a9448ba0SNicolas Bonnefon LOG(logDEBUG) << "End of writing " <<
792a9448ba0SNicolas Bonnefon std::chrono::duration_cast<std::chrono::microseconds>
793a9448ba0SNicolas Bonnefon ( std::chrono::system_clock::now() - start ).count();
794a9448ba0SNicolas Bonnefon }
795ac6602a5SNicolas Bonnefon else {
796ac6602a5SNicolas Bonnefon // Use the cache as is: nothing to do!
797ac6602a5SNicolas Bonnefon }
798a9448ba0SNicolas Bonnefon
799b297d2f4SNicolas Bonnefon // Height including the potentially invisible last line
8009f0249b2SNicolas Bonnefon const int whole_height = getNbVisibleLines() * charHeight_;
8010b05c6eaSNicolas Bonnefon // Height in pixels of the "pull to follow" bottom bar.
802257b1c50SNicolas Bonnefon int pullToFollowHeight = mapPullToFollowLength( followElasticHook_.length() )
803b297d2f4SNicolas Bonnefon + ( followElasticHook_.isHooked() ?
8049f0249b2SNicolas Bonnefon ( whole_height - viewport()->height() ) + PULL_TO_FOLLOW_HOOKED_HEIGHT : 0 );
805ac6602a5SNicolas Bonnefon
806257b1c50SNicolas Bonnefon if ( pullToFollowHeight
807ac6602a5SNicolas Bonnefon && ( pullToFollowCache_.nb_columns_ != getNbVisibleCols() ) ) {
808ac6602a5SNicolas Bonnefon LOG(logDEBUG) << "Drawing pull to follow bar";
809ac6602a5SNicolas Bonnefon pullToFollowCache_.pixmap_ = drawPullToFollowBar(
810ac6602a5SNicolas Bonnefon viewport()->width(), viewport()->devicePixelRatio() );
811ac6602a5SNicolas Bonnefon pullToFollowCache_.nb_columns_ = getNbVisibleCols();
812ac6602a5SNicolas Bonnefon }
813bb02e0acSNicolas Bonnefon
814a9448ba0SNicolas Bonnefon QPainter devicePainter( viewport() );
815257b1c50SNicolas Bonnefon int drawingTopPosition = - pullToFollowHeight;
8169f0249b2SNicolas Bonnefon int drawingPullToFollowTopPosition = drawingTopPosition + whole_height;
8172493b508SNicolas Bonnefon
8182493b508SNicolas Bonnefon // This is to cover the special case where there is less than a screenful
8192493b508SNicolas Bonnefon // worth of data, we want to see the document from the top, rather than
8202493b508SNicolas Bonnefon // pushing the first couple of lines above the viewport.
8219f0249b2SNicolas Bonnefon if ( followElasticHook_.isHooked() && ( logData->getNbLine() < getNbVisibleLines() ) ) {
822257b1c50SNicolas Bonnefon drawingTopOffset_ = 0;
8239f0249b2SNicolas Bonnefon drawingTopPosition += ( whole_height - viewport()->height() ) + PULL_TO_FOLLOW_HOOKED_HEIGHT;
8249f0249b2SNicolas Bonnefon drawingPullToFollowTopPosition = drawingTopPosition + viewport()->height() - PULL_TO_FOLLOW_HOOKED_HEIGHT;
8259f0249b2SNicolas Bonnefon }
8269f0249b2SNicolas Bonnefon // This is the case where the user is on the 'extra' slot at the end
8279f0249b2SNicolas Bonnefon // and is aligned on the last line (but no elastic shown)
828257b1c50SNicolas Bonnefon else if ( lastLineAligned && !followElasticHook_.isHooked() ) {
829257b1c50SNicolas Bonnefon drawingTopOffset_ = - ( whole_height - viewport()->height() );
830257b1c50SNicolas Bonnefon drawingTopPosition += drawingTopOffset_;
8319f0249b2SNicolas Bonnefon drawingPullToFollowTopPosition = drawingTopPosition + whole_height;
8329f0249b2SNicolas Bonnefon }
833257b1c50SNicolas Bonnefon else {
834257b1c50SNicolas Bonnefon drawingTopOffset_ = - pullToFollowHeight;
835257b1c50SNicolas Bonnefon }
8362493b508SNicolas Bonnefon
8372493b508SNicolas Bonnefon devicePainter.drawPixmap( 0, drawingTopPosition, textAreaCache_.pixmap_ );
8380b05c6eaSNicolas Bonnefon
8390b05c6eaSNicolas Bonnefon // Draw the "pull to follow" zone if needed
840257b1c50SNicolas Bonnefon if ( pullToFollowHeight ) {
841b297d2f4SNicolas Bonnefon devicePainter.drawPixmap( 0,
8429f0249b2SNicolas Bonnefon drawingPullToFollowTopPosition,
843ac6602a5SNicolas Bonnefon pullToFollowCache_.pixmap_ );
8440b05c6eaSNicolas Bonnefon }
8450b05c6eaSNicolas Bonnefon
846a9448ba0SNicolas Bonnefon LOG(logDEBUG) << "End of repaint " <<
847a9448ba0SNicolas Bonnefon std::chrono::duration_cast<std::chrono::microseconds>
848a9448ba0SNicolas Bonnefon ( std::chrono::system_clock::now() - start ).count();
849bb02e0acSNicolas Bonnefon }
850bb02e0acSNicolas Bonnefon
851bb02e0acSNicolas Bonnefon // These two functions are virtual and this implementation is clearly
852bb02e0acSNicolas Bonnefon // only valid for a non-filtered display.
853bb02e0acSNicolas Bonnefon // We count on the 'filtered' derived classes to override them.
displayLineNumber(int lineNumber) const854bb02e0acSNicolas Bonnefon qint64 AbstractLogView::displayLineNumber( int lineNumber ) const
855bb02e0acSNicolas Bonnefon {
856bb02e0acSNicolas Bonnefon return lineNumber + 1; // show a 1-based index
857bb02e0acSNicolas Bonnefon }
858bb02e0acSNicolas Bonnefon
maxDisplayLineNumber() const859bb02e0acSNicolas Bonnefon qint64 AbstractLogView::maxDisplayLineNumber() const
860bb02e0acSNicolas Bonnefon {
861bb02e0acSNicolas Bonnefon return logData->getNbLine();
862bb02e0acSNicolas Bonnefon }
863bb02e0acSNicolas Bonnefon
setOverview(Overview * overview,OverviewWidget * overview_widget)864bb02e0acSNicolas Bonnefon void AbstractLogView::setOverview( Overview* overview,
865bb02e0acSNicolas Bonnefon OverviewWidget* overview_widget )
866bb02e0acSNicolas Bonnefon {
867bb02e0acSNicolas Bonnefon overview_ = overview;
868bb02e0acSNicolas Bonnefon overviewWidget_ = overview_widget;
869bb02e0acSNicolas Bonnefon
870bb02e0acSNicolas Bonnefon if ( overviewWidget_ ) {
871bb02e0acSNicolas Bonnefon connect( overviewWidget_, SIGNAL( lineClicked ( int ) ),
872bb02e0acSNicolas Bonnefon this, SLOT( jumpToLine( int ) ) );
873bb02e0acSNicolas Bonnefon }
874bb02e0acSNicolas Bonnefon refreshOverview();
875bb02e0acSNicolas Bonnefon }
876bb02e0acSNicolas Bonnefon
getViewPosition() const8778e788202SNicolas Bonnefon LineNumber AbstractLogView::getViewPosition() const
8788e788202SNicolas Bonnefon {
8798e788202SNicolas Bonnefon LineNumber line;
8808e788202SNicolas Bonnefon
8818e788202SNicolas Bonnefon qint64 m_line = selection_.selectedLine();
8828e788202SNicolas Bonnefon if ( m_line >= 0 ) {
8838e788202SNicolas Bonnefon line = m_line;
8848e788202SNicolas Bonnefon }
8858e788202SNicolas Bonnefon else {
8868e788202SNicolas Bonnefon // Middle of the view
8878e788202SNicolas Bonnefon line = firstLine + getNbVisibleLines() / 2;
8888e788202SNicolas Bonnefon }
8898e788202SNicolas Bonnefon
8908e788202SNicolas Bonnefon return line;
8918e788202SNicolas Bonnefon }
8928e788202SNicolas Bonnefon
searchUsingFunction(qint64 (QuickFind::* search_function)())893bb02e0acSNicolas Bonnefon void AbstractLogView::searchUsingFunction(
894bb02e0acSNicolas Bonnefon qint64 (QuickFind::*search_function)() )
895bb02e0acSNicolas Bonnefon {
896b297d2f4SNicolas Bonnefon disableFollow();
897bb02e0acSNicolas Bonnefon
898bb02e0acSNicolas Bonnefon int line = (quickFind_.*search_function)();
899bb02e0acSNicolas Bonnefon if ( line >= 0 ) {
900*e85b29ccSNicolas Bonnefon LOG(logDEBUG) << "Found line " << line;
901bb02e0acSNicolas Bonnefon displayLine( line );
902bb02e0acSNicolas Bonnefon emit updateLineNumber( line );
903bb02e0acSNicolas Bonnefon }
904bb02e0acSNicolas Bonnefon }
905bb02e0acSNicolas Bonnefon
searchForward()906bb02e0acSNicolas Bonnefon void AbstractLogView::searchForward()
907bb02e0acSNicolas Bonnefon {
908bb02e0acSNicolas Bonnefon searchUsingFunction( &QuickFind::searchForward );
909bb02e0acSNicolas Bonnefon }
910bb02e0acSNicolas Bonnefon
searchBackward()911bb02e0acSNicolas Bonnefon void AbstractLogView::searchBackward()
912bb02e0acSNicolas Bonnefon {
913bb02e0acSNicolas Bonnefon searchUsingFunction( &QuickFind::searchBackward );
914bb02e0acSNicolas Bonnefon }
915bb02e0acSNicolas Bonnefon
incrementallySearchForward()916bb02e0acSNicolas Bonnefon void AbstractLogView::incrementallySearchForward()
917bb02e0acSNicolas Bonnefon {
918bb02e0acSNicolas Bonnefon searchUsingFunction( &QuickFind::incrementallySearchForward );
919bb02e0acSNicolas Bonnefon }
920bb02e0acSNicolas Bonnefon
incrementallySearchBackward()921bb02e0acSNicolas Bonnefon void AbstractLogView::incrementallySearchBackward()
922bb02e0acSNicolas Bonnefon {
923bb02e0acSNicolas Bonnefon searchUsingFunction( &QuickFind::incrementallySearchBackward );
924bb02e0acSNicolas Bonnefon }
925bb02e0acSNicolas Bonnefon
incrementalSearchAbort()926bb02e0acSNicolas Bonnefon void AbstractLogView::incrementalSearchAbort()
927bb02e0acSNicolas Bonnefon {
928bb02e0acSNicolas Bonnefon quickFind_.incrementalSearchAbort();
929bb02e0acSNicolas Bonnefon emit changeQuickFind(
930bb02e0acSNicolas Bonnefon "",
931bb02e0acSNicolas Bonnefon QuickFindMux::Forward );
932bb02e0acSNicolas Bonnefon }
933bb02e0acSNicolas Bonnefon
incrementalSearchStop()934bb02e0acSNicolas Bonnefon void AbstractLogView::incrementalSearchStop()
935bb02e0acSNicolas Bonnefon {
936bb02e0acSNicolas Bonnefon quickFind_.incrementalSearchStop();
937bb02e0acSNicolas Bonnefon }
938bb02e0acSNicolas Bonnefon
followSet(bool checked)939bb02e0acSNicolas Bonnefon void AbstractLogView::followSet( bool checked )
940bb02e0acSNicolas Bonnefon {
941bb02e0acSNicolas Bonnefon followMode_ = checked;
942b297d2f4SNicolas Bonnefon followElasticHook_.hook( checked );
943b297d2f4SNicolas Bonnefon update();
944bb02e0acSNicolas Bonnefon if ( checked )
945bb02e0acSNicolas Bonnefon jumpToBottom();
946bb02e0acSNicolas Bonnefon }
947bb02e0acSNicolas Bonnefon
refreshOverview()948bb02e0acSNicolas Bonnefon void AbstractLogView::refreshOverview()
949bb02e0acSNicolas Bonnefon {
950bb02e0acSNicolas Bonnefon assert( overviewWidget_ );
951bb02e0acSNicolas Bonnefon
952bb02e0acSNicolas Bonnefon // Create space for the Overview if needed
953bb02e0acSNicolas Bonnefon if ( ( getOverview() != NULL ) && getOverview()->isVisible() ) {
954bb02e0acSNicolas Bonnefon setViewportMargins( 0, 0, OVERVIEW_WIDTH, 0 );
955bb02e0acSNicolas Bonnefon overviewWidget_->show();
956bb02e0acSNicolas Bonnefon }
957bb02e0acSNicolas Bonnefon else {
958bb02e0acSNicolas Bonnefon setViewportMargins( 0, 0, 0, 0 );
959bb02e0acSNicolas Bonnefon overviewWidget_->hide();
960bb02e0acSNicolas Bonnefon }
961bb02e0acSNicolas Bonnefon }
962bb02e0acSNicolas Bonnefon
963bb02e0acSNicolas Bonnefon // Reset the QuickFind when the pattern is changed.
handlePatternUpdated()964bb02e0acSNicolas Bonnefon void AbstractLogView::handlePatternUpdated()
965bb02e0acSNicolas Bonnefon {
966bb02e0acSNicolas Bonnefon LOG(logDEBUG) << "AbstractLogView::handlePatternUpdated()";
967bb02e0acSNicolas Bonnefon
968bb02e0acSNicolas Bonnefon quickFind_.resetLimits();
969bb02e0acSNicolas Bonnefon update();
970bb02e0acSNicolas Bonnefon }
971bb02e0acSNicolas Bonnefon
972bb02e0acSNicolas Bonnefon // OR the current with the current search expression
addToSearch()973bb02e0acSNicolas Bonnefon void AbstractLogView::addToSearch()
974bb02e0acSNicolas Bonnefon {
975bb02e0acSNicolas Bonnefon if ( selection_.isPortion() ) {
976bb02e0acSNicolas Bonnefon LOG(logDEBUG) << "AbstractLogView::addToSearch()";
977bb02e0acSNicolas Bonnefon emit addToSearch( selection_.getSelectedText( logData ) );
978bb02e0acSNicolas Bonnefon }
979bb02e0acSNicolas Bonnefon else {
980bb02e0acSNicolas Bonnefon LOG(logERROR) << "AbstractLogView::addToSearch called for a wrong type of selection";
981bb02e0acSNicolas Bonnefon }
982bb02e0acSNicolas Bonnefon }
983bb02e0acSNicolas Bonnefon
984bb02e0acSNicolas Bonnefon // Find next occurence of the selected text (*)
findNextSelected()985bb02e0acSNicolas Bonnefon void AbstractLogView::findNextSelected()
986bb02e0acSNicolas Bonnefon {
987bb02e0acSNicolas Bonnefon // Use the selected 'word' and search forward
988bb02e0acSNicolas Bonnefon if ( selection_.isPortion() ) {
989bb02e0acSNicolas Bonnefon emit changeQuickFind(
990bb02e0acSNicolas Bonnefon selection_.getSelectedText( logData ),
991bb02e0acSNicolas Bonnefon QuickFindMux::Forward );
992bb02e0acSNicolas Bonnefon emit searchNext();
993bb02e0acSNicolas Bonnefon }
994bb02e0acSNicolas Bonnefon }
995bb02e0acSNicolas Bonnefon
996bb02e0acSNicolas Bonnefon // Find next previous of the selected text (#)
findPreviousSelected()997bb02e0acSNicolas Bonnefon void AbstractLogView::findPreviousSelected()
998bb02e0acSNicolas Bonnefon {
999bb02e0acSNicolas Bonnefon if ( selection_.isPortion() ) {
1000bb02e0acSNicolas Bonnefon emit changeQuickFind(
1001bb02e0acSNicolas Bonnefon selection_.getSelectedText( logData ),
1002bb02e0acSNicolas Bonnefon QuickFindMux::Backward );
1003bb02e0acSNicolas Bonnefon emit searchNext();
1004bb02e0acSNicolas Bonnefon }
1005bb02e0acSNicolas Bonnefon }
1006bb02e0acSNicolas Bonnefon
1007bb02e0acSNicolas Bonnefon // Copy the selection to the clipboard
copy()1008bb02e0acSNicolas Bonnefon void AbstractLogView::copy()
1009bb02e0acSNicolas Bonnefon {
1010bb02e0acSNicolas Bonnefon static QClipboard* clipboard = QApplication::clipboard();
1011bb02e0acSNicolas Bonnefon
1012bb02e0acSNicolas Bonnefon clipboard->setText( selection_.getSelectedText( logData ) );
1013bb02e0acSNicolas Bonnefon }
1014bb02e0acSNicolas Bonnefon
1015bb02e0acSNicolas Bonnefon //
1016bb02e0acSNicolas Bonnefon // Public functions
1017bb02e0acSNicolas Bonnefon //
1018bb02e0acSNicolas Bonnefon
updateData()1019bb02e0acSNicolas Bonnefon void AbstractLogView::updateData()
1020bb02e0acSNicolas Bonnefon {
1021bb02e0acSNicolas Bonnefon LOG(logDEBUG) << "AbstractLogView::updateData";
1022bb02e0acSNicolas Bonnefon
1023bb02e0acSNicolas Bonnefon // Check the top Line is within range
1024bb02e0acSNicolas Bonnefon if ( firstLine >= logData->getNbLine() ) {
1025bb02e0acSNicolas Bonnefon firstLine = 0;
1026bb02e0acSNicolas Bonnefon firstCol = 0;
1027bb02e0acSNicolas Bonnefon verticalScrollBar()->setValue( 0 );
1028bb02e0acSNicolas Bonnefon horizontalScrollBar()->setValue( 0 );
1029bb02e0acSNicolas Bonnefon }
1030bb02e0acSNicolas Bonnefon
1031bb02e0acSNicolas Bonnefon // Crop selection if it become out of range
1032bb02e0acSNicolas Bonnefon selection_.crop( logData->getNbLine() - 1 );
1033bb02e0acSNicolas Bonnefon
1034bb02e0acSNicolas Bonnefon // Adapt the scroll bars to the new content
10350b05c6eaSNicolas Bonnefon updateScrollBars();
1036bb02e0acSNicolas Bonnefon
10372493b508SNicolas Bonnefon // Calculate the index of the last line shown
1038c7ef2b85SNicolas Bonnefon LineNumber last_line = std::min( static_cast<int64_t>( logData->getNbLine() ),
10392493b508SNicolas Bonnefon static_cast<int64_t>( firstLine + getNbVisibleLines() ) );
1040bb02e0acSNicolas Bonnefon
1041bb02e0acSNicolas Bonnefon // Reset the QuickFind in case we have new stuff to search into
1042bb02e0acSNicolas Bonnefon quickFind_.resetLimits();
1043bb02e0acSNicolas Bonnefon
1044bb02e0acSNicolas Bonnefon if ( followMode_ )
1045bb02e0acSNicolas Bonnefon jumpToBottom();
1046bb02e0acSNicolas Bonnefon
1047bb02e0acSNicolas Bonnefon // Update the overview if we have one
1048bb02e0acSNicolas Bonnefon if ( overview_ != NULL )
10492493b508SNicolas Bonnefon overview_->updateCurrentPosition( firstLine, last_line );
1050bb02e0acSNicolas Bonnefon
10512493b508SNicolas Bonnefon // Invalidate our cache
10522493b508SNicolas Bonnefon textAreaCache_.invalid_ = true;
1053bb02e0acSNicolas Bonnefon
1054bb02e0acSNicolas Bonnefon // Repaint!
1055bb02e0acSNicolas Bonnefon update();
1056bb02e0acSNicolas Bonnefon }
1057bb02e0acSNicolas Bonnefon
updateDisplaySize()1058bb02e0acSNicolas Bonnefon void AbstractLogView::updateDisplaySize()
1059bb02e0acSNicolas Bonnefon {
1060bb02e0acSNicolas Bonnefon // Font is assumed to be mono-space (is restricted by options dialog)
1061bb02e0acSNicolas Bonnefon QFontMetrics fm = fontMetrics();
1062bb02e0acSNicolas Bonnefon charHeight_ = fm.height();
1063bb02e0acSNicolas Bonnefon // For some reason on Qt 4.8.2 for Win, maxWidth() is wrong but the
1064bb02e0acSNicolas Bonnefon // following give the right result, not sure why:
1065bb02e0acSNicolas Bonnefon charWidth_ = fm.width( QChar('a') );
1066bb02e0acSNicolas Bonnefon
1067bb02e0acSNicolas Bonnefon // Update the scroll bars
10680b05c6eaSNicolas Bonnefon updateScrollBars();
1069bb02e0acSNicolas Bonnefon verticalScrollBar()->setPageStep( getNbVisibleLines() );
1070bb02e0acSNicolas Bonnefon
1071b297d2f4SNicolas Bonnefon if ( followMode_ )
1072b297d2f4SNicolas Bonnefon jumpToBottom();
1073b297d2f4SNicolas Bonnefon
1074bb02e0acSNicolas Bonnefon LOG(logDEBUG) << "viewport.width()=" << viewport()->width();
1075bb02e0acSNicolas Bonnefon LOG(logDEBUG) << "viewport.height()=" << viewport()->height();
1076bb02e0acSNicolas Bonnefon LOG(logDEBUG) << "width()=" << width();
1077bb02e0acSNicolas Bonnefon LOG(logDEBUG) << "height()=" << height();
1078bb02e0acSNicolas Bonnefon
1079bb02e0acSNicolas Bonnefon if ( overviewWidget_ )
1080bb02e0acSNicolas Bonnefon overviewWidget_->setGeometry( viewport()->width() + 2, 1,
1081bb02e0acSNicolas Bonnefon OVERVIEW_WIDTH - 1, viewport()->height() );
1082a9448ba0SNicolas Bonnefon
1083a9448ba0SNicolas Bonnefon // Our text area cache is now invalid
1084a9448ba0SNicolas Bonnefon textAreaCache_.invalid_ = true;
1085a9448ba0SNicolas Bonnefon textAreaCache_.pixmap_ = QPixmap {
1086b297d2f4SNicolas Bonnefon viewport()->width() * viewport()->devicePixelRatio(),
10872493b508SNicolas Bonnefon static_cast<int32_t>( getNbVisibleLines() ) * charHeight_ * viewport()->devicePixelRatio() };
1088a9448ba0SNicolas Bonnefon textAreaCache_.pixmap_.setDevicePixelRatio( viewport()->devicePixelRatio() );
1089bb02e0acSNicolas Bonnefon }
1090bb02e0acSNicolas Bonnefon
getTopLine() const1091bb02e0acSNicolas Bonnefon int AbstractLogView::getTopLine() const
1092bb02e0acSNicolas Bonnefon {
1093bb02e0acSNicolas Bonnefon return firstLine;
1094bb02e0acSNicolas Bonnefon }
1095bb02e0acSNicolas Bonnefon
getSelection() const1096bb02e0acSNicolas Bonnefon QString AbstractLogView::getSelection() const
1097bb02e0acSNicolas Bonnefon {
1098bb02e0acSNicolas Bonnefon return selection_.getSelectedText( logData );
1099bb02e0acSNicolas Bonnefon }
1100bb02e0acSNicolas Bonnefon
selectAll()1101bb02e0acSNicolas Bonnefon void AbstractLogView::selectAll()
1102bb02e0acSNicolas Bonnefon {
1103bb02e0acSNicolas Bonnefon selection_.selectRange( 0, logData->getNbLine() - 1 );
11041cd16a46SAnton Filimonov textAreaCache_.invalid_ = true;
1105bb02e0acSNicolas Bonnefon update();
1106bb02e0acSNicolas Bonnefon }
1107bb02e0acSNicolas Bonnefon
selectAndDisplayLine(int line)1108bb02e0acSNicolas Bonnefon void AbstractLogView::selectAndDisplayLine( int line )
1109bb02e0acSNicolas Bonnefon {
1110b297d2f4SNicolas Bonnefon disableFollow();
1111bb02e0acSNicolas Bonnefon selection_.selectLine( line );
1112bb02e0acSNicolas Bonnefon displayLine( line );
1113bb02e0acSNicolas Bonnefon emit updateLineNumber( line );
11148e788202SNicolas Bonnefon emit newSelection( line );
1115bb02e0acSNicolas Bonnefon }
1116bb02e0acSNicolas Bonnefon
1117bb02e0acSNicolas Bonnefon // The difference between this function and displayLine() is quite
1118bb02e0acSNicolas Bonnefon // subtle: this one always jump, even if the line passed is visible.
jumpToLine(int line)1119bb02e0acSNicolas Bonnefon void AbstractLogView::jumpToLine( int line )
1120bb02e0acSNicolas Bonnefon {
1121bb02e0acSNicolas Bonnefon // Put the selected line in the middle if possible
1122bb02e0acSNicolas Bonnefon int newTopLine = line - ( getNbVisibleLines() / 2 );
1123bb02e0acSNicolas Bonnefon if ( newTopLine < 0 )
1124bb02e0acSNicolas Bonnefon newTopLine = 0;
1125bb02e0acSNicolas Bonnefon
1126bb02e0acSNicolas Bonnefon // This will also trigger a scrollContents event
1127bb02e0acSNicolas Bonnefon verticalScrollBar()->setValue( newTopLine );
1128bb02e0acSNicolas Bonnefon }
1129bb02e0acSNicolas Bonnefon
setLineNumbersVisible(bool lineNumbersVisible)1130bb02e0acSNicolas Bonnefon void AbstractLogView::setLineNumbersVisible( bool lineNumbersVisible )
1131bb02e0acSNicolas Bonnefon {
1132bb02e0acSNicolas Bonnefon lineNumbersVisible_ = lineNumbersVisible;
1133bb02e0acSNicolas Bonnefon }
1134bb02e0acSNicolas Bonnefon
forceRefresh()11355fa25391SNicolas Bonnefon void AbstractLogView::forceRefresh()
11365fa25391SNicolas Bonnefon {
11375fa25391SNicolas Bonnefon // Invalidate our cache
11385fa25391SNicolas Bonnefon textAreaCache_.invalid_ = true;
11395fa25391SNicolas Bonnefon }
11405fa25391SNicolas Bonnefon
1141bb02e0acSNicolas Bonnefon //
1142bb02e0acSNicolas Bonnefon // Private functions
1143bb02e0acSNicolas Bonnefon //
1144bb02e0acSNicolas Bonnefon
1145bb02e0acSNicolas Bonnefon // Returns the number of lines visible in the viewport
getNbVisibleLines() const11462493b508SNicolas Bonnefon LineNumber AbstractLogView::getNbVisibleLines() const
1147bb02e0acSNicolas Bonnefon {
11482493b508SNicolas Bonnefon return static_cast<LineNumber>( viewport()->height() / charHeight_ + 1 );
1149bb02e0acSNicolas Bonnefon }
1150bb02e0acSNicolas Bonnefon
1151bb02e0acSNicolas Bonnefon // Returns the number of columns visible in the viewport
getNbVisibleCols() const1152bb02e0acSNicolas Bonnefon int AbstractLogView::getNbVisibleCols() const
1153bb02e0acSNicolas Bonnefon {
1154bb02e0acSNicolas Bonnefon return ( viewport()->width() - leftMarginPx_ ) / charWidth_ + 1;
1155bb02e0acSNicolas Bonnefon }
1156bb02e0acSNicolas Bonnefon
1157bb02e0acSNicolas Bonnefon // Converts the mouse x, y coordinates to the line number in the file
convertCoordToLine(int yPos) const1158bb02e0acSNicolas Bonnefon int AbstractLogView::convertCoordToLine(int yPos) const
1159bb02e0acSNicolas Bonnefon {
1160257b1c50SNicolas Bonnefon int line = firstLine + ( yPos - drawingTopOffset_ ) / charHeight_;
1161bb02e0acSNicolas Bonnefon
1162bb02e0acSNicolas Bonnefon return line;
1163bb02e0acSNicolas Bonnefon }
1164bb02e0acSNicolas Bonnefon
1165bb02e0acSNicolas Bonnefon // Converts the mouse x, y coordinates to the char coordinates (in the file)
1166bb02e0acSNicolas Bonnefon // This function ensure the pos exists in the file.
convertCoordToFilePos(const QPoint & pos) const1167bb02e0acSNicolas Bonnefon QPoint AbstractLogView::convertCoordToFilePos( const QPoint& pos ) const
1168bb02e0acSNicolas Bonnefon {
1169257b1c50SNicolas Bonnefon int line = convertCoordToLine( pos.y() );
1170bb02e0acSNicolas Bonnefon if ( line >= logData->getNbLine() )
1171bb02e0acSNicolas Bonnefon line = logData->getNbLine() - 1;
1172bb02e0acSNicolas Bonnefon if ( line < 0 )
1173bb02e0acSNicolas Bonnefon line = 0;
1174bb02e0acSNicolas Bonnefon
1175bb02e0acSNicolas Bonnefon // Determine column in screen space and convert it to file space
1176bb02e0acSNicolas Bonnefon int column = firstCol + ( pos.x() - leftMarginPx_ ) / charWidth_;
1177bb02e0acSNicolas Bonnefon
1178bb02e0acSNicolas Bonnefon QString this_line = logData->getExpandedLineString( line );
1179bb02e0acSNicolas Bonnefon const int length = this_line.length();
1180bb02e0acSNicolas Bonnefon
1181bb02e0acSNicolas Bonnefon if ( column >= length )
1182bb02e0acSNicolas Bonnefon column = length - 1;
1183bb02e0acSNicolas Bonnefon if ( column < 0 )
1184bb02e0acSNicolas Bonnefon column = 0;
1185bb02e0acSNicolas Bonnefon
1186bb02e0acSNicolas Bonnefon LOG(logDEBUG4) << "AbstractLogView::convertCoordToFilePos col="
1187bb02e0acSNicolas Bonnefon << column << " line=" << line;
1188bb02e0acSNicolas Bonnefon QPoint point( column, line );
1189bb02e0acSNicolas Bonnefon
1190bb02e0acSNicolas Bonnefon return point;
1191bb02e0acSNicolas Bonnefon }
1192bb02e0acSNicolas Bonnefon
1193bb02e0acSNicolas Bonnefon // Makes the widget adjust itself to display the passed line.
1194bb02e0acSNicolas Bonnefon // Doing so, it will throw itself a scrollContents event.
displayLine(LineNumber line)11952493b508SNicolas Bonnefon void AbstractLogView::displayLine( LineNumber line )
1196bb02e0acSNicolas Bonnefon {
1197bb02e0acSNicolas Bonnefon // If the line is already the screen
1198bb02e0acSNicolas Bonnefon if ( ( line >= firstLine ) &&
1199bb02e0acSNicolas Bonnefon ( line < ( firstLine + getNbVisibleLines() ) ) ) {
120000a57d05SNicolas Bonnefon // Invalidate our cache
120100a57d05SNicolas Bonnefon textAreaCache_.invalid_ = true;
120200a57d05SNicolas Bonnefon
1203bb02e0acSNicolas Bonnefon // ... don't scroll and just repaint
1204bb02e0acSNicolas Bonnefon update();
1205bb02e0acSNicolas Bonnefon } else {
1206bb02e0acSNicolas Bonnefon jumpToLine( line );
1207bb02e0acSNicolas Bonnefon }
1208bb02e0acSNicolas Bonnefon }
1209bb02e0acSNicolas Bonnefon
1210bb02e0acSNicolas Bonnefon // Move the selection up and down by the passed number of lines
moveSelection(int delta)1211bb02e0acSNicolas Bonnefon void AbstractLogView::moveSelection( int delta )
1212bb02e0acSNicolas Bonnefon {
1213bb02e0acSNicolas Bonnefon LOG(logDEBUG) << "AbstractLogView::moveSelection delta=" << delta;
1214bb02e0acSNicolas Bonnefon
1215bb02e0acSNicolas Bonnefon QList<int> selection = selection_.getLines();
1216bb02e0acSNicolas Bonnefon int new_line;
1217bb02e0acSNicolas Bonnefon
1218bb02e0acSNicolas Bonnefon // If nothing is selected, do as if line -1 was.
1219bb02e0acSNicolas Bonnefon if ( selection.isEmpty() )
1220bb02e0acSNicolas Bonnefon selection.append( -1 );
1221bb02e0acSNicolas Bonnefon
1222bb02e0acSNicolas Bonnefon if ( delta < 0 )
1223bb02e0acSNicolas Bonnefon new_line = selection.first() + delta;
1224bb02e0acSNicolas Bonnefon else
1225bb02e0acSNicolas Bonnefon new_line = selection.last() + delta;
1226bb02e0acSNicolas Bonnefon
1227bb02e0acSNicolas Bonnefon if ( new_line < 0 )
1228bb02e0acSNicolas Bonnefon new_line = 0;
1229bb02e0acSNicolas Bonnefon else if ( new_line >= logData->getNbLine() )
1230bb02e0acSNicolas Bonnefon new_line = logData->getNbLine() - 1;
1231bb02e0acSNicolas Bonnefon
1232bb02e0acSNicolas Bonnefon // Select and display the new line
1233bb02e0acSNicolas Bonnefon selection_.selectLine( new_line );
1234bb02e0acSNicolas Bonnefon displayLine( new_line );
1235bb02e0acSNicolas Bonnefon emit updateLineNumber( new_line );
12365c2998ccSNicolas Bonnefon emit newSelection( new_line );
1237bb02e0acSNicolas Bonnefon }
1238bb02e0acSNicolas Bonnefon
1239bb02e0acSNicolas Bonnefon // Make the start of the lines visible
jumpToStartOfLine()1240bb02e0acSNicolas Bonnefon void AbstractLogView::jumpToStartOfLine()
1241bb02e0acSNicolas Bonnefon {
1242bb02e0acSNicolas Bonnefon horizontalScrollBar()->setValue( 0 );
1243bb02e0acSNicolas Bonnefon }
1244bb02e0acSNicolas Bonnefon
1245bb02e0acSNicolas Bonnefon // Make the end of the lines in the selection visible
jumpToEndOfLine()1246bb02e0acSNicolas Bonnefon void AbstractLogView::jumpToEndOfLine()
1247bb02e0acSNicolas Bonnefon {
1248bb02e0acSNicolas Bonnefon QList<int> selection = selection_.getLines();
1249bb02e0acSNicolas Bonnefon
1250bb02e0acSNicolas Bonnefon // Search the longest line in the selection
1251bb02e0acSNicolas Bonnefon int max_length = 0;
1252bb02e0acSNicolas Bonnefon foreach ( int line, selection ) {
1253bb02e0acSNicolas Bonnefon int length = logData->getLineLength( line );
1254bb02e0acSNicolas Bonnefon if ( length > max_length )
1255bb02e0acSNicolas Bonnefon max_length = length;
1256bb02e0acSNicolas Bonnefon }
1257bb02e0acSNicolas Bonnefon
1258bb02e0acSNicolas Bonnefon horizontalScrollBar()->setValue( max_length - getNbVisibleCols() );
1259bb02e0acSNicolas Bonnefon }
1260bb02e0acSNicolas Bonnefon
1261bb02e0acSNicolas Bonnefon // Make the end of the lines on the screen visible
jumpToRightOfScreen()1262bb02e0acSNicolas Bonnefon void AbstractLogView::jumpToRightOfScreen()
1263bb02e0acSNicolas Bonnefon {
1264bb02e0acSNicolas Bonnefon QList<int> selection = selection_.getLines();
1265bb02e0acSNicolas Bonnefon
1266bb02e0acSNicolas Bonnefon // Search the longest line on screen
1267bb02e0acSNicolas Bonnefon int max_length = 0;
12682493b508SNicolas Bonnefon for ( auto i = firstLine; i <= ( firstLine + getNbVisibleLines() ); i++ ) {
1269bb02e0acSNicolas Bonnefon int length = logData->getLineLength( i );
1270bb02e0acSNicolas Bonnefon if ( length > max_length )
1271bb02e0acSNicolas Bonnefon max_length = length;
1272bb02e0acSNicolas Bonnefon }
1273bb02e0acSNicolas Bonnefon
1274bb02e0acSNicolas Bonnefon horizontalScrollBar()->setValue( max_length - getNbVisibleCols() );
1275bb02e0acSNicolas Bonnefon }
1276bb02e0acSNicolas Bonnefon
1277bb02e0acSNicolas Bonnefon // Jump to the first line
jumpToTop()1278bb02e0acSNicolas Bonnefon void AbstractLogView::jumpToTop()
1279bb02e0acSNicolas Bonnefon {
1280bb02e0acSNicolas Bonnefon // This will also trigger a scrollContents event
1281bb02e0acSNicolas Bonnefon verticalScrollBar()->setValue( 0 );
1282bb02e0acSNicolas Bonnefon update(); // in case the screen hasn't moved
1283bb02e0acSNicolas Bonnefon }
1284bb02e0acSNicolas Bonnefon
1285bb02e0acSNicolas Bonnefon // Jump to the last line
jumpToBottom()1286bb02e0acSNicolas Bonnefon void AbstractLogView::jumpToBottom()
1287bb02e0acSNicolas Bonnefon {
1288bb02e0acSNicolas Bonnefon const int new_top_line =
1289bb02e0acSNicolas Bonnefon qMax( logData->getNbLine() - getNbVisibleLines() + 1, 0LL );
1290bb02e0acSNicolas Bonnefon
1291bb02e0acSNicolas Bonnefon // This will also trigger a scrollContents event
1292bb02e0acSNicolas Bonnefon verticalScrollBar()->setValue( new_top_line );
1293bb02e0acSNicolas Bonnefon update(); // in case the screen hasn't moved
1294bb02e0acSNicolas Bonnefon }
1295bb02e0acSNicolas Bonnefon
1296bb02e0acSNicolas Bonnefon // Returns whether the character passed is a 'word' character
isCharWord(char c)1297bb02e0acSNicolas Bonnefon inline bool AbstractLogView::isCharWord( char c )
1298bb02e0acSNicolas Bonnefon {
1299bb02e0acSNicolas Bonnefon if ( ( ( c >= 'A' ) && ( c <= 'Z' ) ) ||
1300bb02e0acSNicolas Bonnefon ( ( c >= 'a' ) && ( c <= 'z' ) ) ||
1301bb02e0acSNicolas Bonnefon ( ( c >= '0' ) && ( c <= '9' ) ) ||
1302bb02e0acSNicolas Bonnefon ( ( c == '_' ) ) )
1303bb02e0acSNicolas Bonnefon return true;
1304bb02e0acSNicolas Bonnefon else
1305bb02e0acSNicolas Bonnefon return false;
1306bb02e0acSNicolas Bonnefon }
1307bb02e0acSNicolas Bonnefon
1308bb02e0acSNicolas Bonnefon // Select the word under the given position
selectWordAtPosition(const QPoint & pos)1309bb02e0acSNicolas Bonnefon void AbstractLogView::selectWordAtPosition( const QPoint& pos )
1310bb02e0acSNicolas Bonnefon {
1311bb02e0acSNicolas Bonnefon const int x = pos.x();
1312bb02e0acSNicolas Bonnefon const QString line = logData->getExpandedLineString( pos.y() );
1313bb02e0acSNicolas Bonnefon
1314bb02e0acSNicolas Bonnefon if ( isCharWord( line[x].toLatin1() ) ) {
1315bb02e0acSNicolas Bonnefon // Search backward for the first character in the word
1316bb02e0acSNicolas Bonnefon int currentPos = x;
1317bb02e0acSNicolas Bonnefon for ( ; currentPos > 0; currentPos-- )
1318bb02e0acSNicolas Bonnefon if ( ! isCharWord( line[currentPos].toLatin1() ) )
1319bb02e0acSNicolas Bonnefon break;
1320bb02e0acSNicolas Bonnefon // Exclude the first char of the line if needed
1321bb02e0acSNicolas Bonnefon if ( ! isCharWord( line[currentPos].toLatin1() ) )
1322bb02e0acSNicolas Bonnefon currentPos++;
1323bb02e0acSNicolas Bonnefon int start = currentPos;
1324bb02e0acSNicolas Bonnefon
1325bb02e0acSNicolas Bonnefon // Now search for the end
1326bb02e0acSNicolas Bonnefon currentPos = x;
1327bb02e0acSNicolas Bonnefon for ( ; currentPos < line.length() - 1; currentPos++ )
1328bb02e0acSNicolas Bonnefon if ( ! isCharWord( line[currentPos].toLatin1() ) )
1329bb02e0acSNicolas Bonnefon break;
1330bb02e0acSNicolas Bonnefon // Exclude the last char of the line if needed
1331bb02e0acSNicolas Bonnefon if ( ! isCharWord( line[currentPos].toLatin1() ) )
1332bb02e0acSNicolas Bonnefon currentPos--;
1333bb02e0acSNicolas Bonnefon int end = currentPos;
1334bb02e0acSNicolas Bonnefon
1335bb02e0acSNicolas Bonnefon selection_.selectPortion( pos.y(), start, end );
1336bb02e0acSNicolas Bonnefon updateGlobalSelection();
1337bb02e0acSNicolas Bonnefon update();
1338bb02e0acSNicolas Bonnefon }
1339bb02e0acSNicolas Bonnefon }
1340bb02e0acSNicolas Bonnefon
1341bb02e0acSNicolas Bonnefon // Update the system global (middle click) selection (X11 only)
updateGlobalSelection()1342bb02e0acSNicolas Bonnefon void AbstractLogView::updateGlobalSelection()
1343bb02e0acSNicolas Bonnefon {
1344bb02e0acSNicolas Bonnefon static QClipboard* const clipboard = QApplication::clipboard();
1345bb02e0acSNicolas Bonnefon
1346bb02e0acSNicolas Bonnefon // Updating it only for "non-trivial" (range or portion) selections
1347bb02e0acSNicolas Bonnefon if ( ! selection_.isSingleLine() )
1348bb02e0acSNicolas Bonnefon clipboard->setText( selection_.getSelectedText( logData ),
1349bb02e0acSNicolas Bonnefon QClipboard::Selection );
1350bb02e0acSNicolas Bonnefon }
1351bb02e0acSNicolas Bonnefon
1352bb02e0acSNicolas Bonnefon // Create the pop-up menu
createMenu()1353bb02e0acSNicolas Bonnefon void AbstractLogView::createMenu()
1354bb02e0acSNicolas Bonnefon {
1355bb02e0acSNicolas Bonnefon copyAction_ = new QAction( tr("&Copy"), this );
1356bb02e0acSNicolas Bonnefon // No text as this action title depends on the type of selection
1357bb02e0acSNicolas Bonnefon connect( copyAction_, SIGNAL(triggered()), this, SLOT(copy()) );
1358bb02e0acSNicolas Bonnefon
1359bb02e0acSNicolas Bonnefon // For '#' and '*', shortcuts doesn't seem to work but
1360bb02e0acSNicolas Bonnefon // at least it displays them in the menu, we manually handle those keys
1361bb02e0acSNicolas Bonnefon // as keys event anyway (in keyPressEvent).
1362bb02e0acSNicolas Bonnefon findNextAction_ = new QAction(tr("Find &next"), this);
1363bb02e0acSNicolas Bonnefon findNextAction_->setShortcut( Qt::Key_Asterisk );
1364bb02e0acSNicolas Bonnefon findNextAction_->setStatusTip( tr("Find the next occurence") );
1365bb02e0acSNicolas Bonnefon connect( findNextAction_, SIGNAL(triggered()),
1366bb02e0acSNicolas Bonnefon this, SLOT( findNextSelected() ) );
1367bb02e0acSNicolas Bonnefon
1368bb02e0acSNicolas Bonnefon findPreviousAction_ = new QAction( tr("Find &previous"), this );
1369bb02e0acSNicolas Bonnefon findPreviousAction_->setShortcut( tr("#") );
1370bb02e0acSNicolas Bonnefon findPreviousAction_->setStatusTip( tr("Find the previous occurence") );
1371bb02e0acSNicolas Bonnefon connect( findPreviousAction_, SIGNAL(triggered()),
1372bb02e0acSNicolas Bonnefon this, SLOT( findPreviousSelected() ) );
1373bb02e0acSNicolas Bonnefon
1374bb02e0acSNicolas Bonnefon addToSearchAction_ = new QAction( tr("&Add to search"), this );
1375bb02e0acSNicolas Bonnefon addToSearchAction_->setStatusTip(
1376bb02e0acSNicolas Bonnefon tr("Add the selection to the current search") );
1377bb02e0acSNicolas Bonnefon connect( addToSearchAction_, SIGNAL( triggered() ),
1378bb02e0acSNicolas Bonnefon this, SLOT( addToSearch() ) );
1379bb02e0acSNicolas Bonnefon
1380bb02e0acSNicolas Bonnefon popupMenu_ = new QMenu( this );
1381bb02e0acSNicolas Bonnefon popupMenu_->addAction( copyAction_ );
1382bb02e0acSNicolas Bonnefon popupMenu_->addSeparator();
1383bb02e0acSNicolas Bonnefon popupMenu_->addAction( findNextAction_ );
1384bb02e0acSNicolas Bonnefon popupMenu_->addAction( findPreviousAction_ );
1385bb02e0acSNicolas Bonnefon popupMenu_->addAction( addToSearchAction_ );
1386bb02e0acSNicolas Bonnefon }
1387bb02e0acSNicolas Bonnefon
considerMouseHovering(int x_pos,int y_pos)1388bb02e0acSNicolas Bonnefon void AbstractLogView::considerMouseHovering( int x_pos, int y_pos )
1389bb02e0acSNicolas Bonnefon {
1390bb02e0acSNicolas Bonnefon int line = convertCoordToLine( y_pos );
1391bb02e0acSNicolas Bonnefon if ( ( x_pos < leftMarginPx_ )
1392bb02e0acSNicolas Bonnefon && ( line >= 0 )
1393bb02e0acSNicolas Bonnefon && ( line < logData->getNbLine() ) ) {
1394bb02e0acSNicolas Bonnefon // Mouse moved in the margin, send event up
1395bb02e0acSNicolas Bonnefon // (possibly to highlight the overview)
1396bb02e0acSNicolas Bonnefon if ( line != lastHoveredLine_ ) {
1397bb02e0acSNicolas Bonnefon LOG(logDEBUG) << "Mouse moved in margin line: " << line;
1398bb02e0acSNicolas Bonnefon emit mouseHoveredOverLine( line );
1399bb02e0acSNicolas Bonnefon lastHoveredLine_ = line;
1400bb02e0acSNicolas Bonnefon }
1401bb02e0acSNicolas Bonnefon }
1402bb02e0acSNicolas Bonnefon else {
1403bb02e0acSNicolas Bonnefon if ( lastHoveredLine_ != -1 ) {
1404bb02e0acSNicolas Bonnefon emit mouseLeftHoveringZone();
1405bb02e0acSNicolas Bonnefon lastHoveredLine_ = -1;
1406bb02e0acSNicolas Bonnefon }
1407bb02e0acSNicolas Bonnefon }
1408bb02e0acSNicolas Bonnefon }
14090b05c6eaSNicolas Bonnefon
updateScrollBars()14100b05c6eaSNicolas Bonnefon void AbstractLogView::updateScrollBars()
14110b05c6eaSNicolas Bonnefon {
14120b05c6eaSNicolas Bonnefon verticalScrollBar()->setRange( 0, std::max( 0LL,
14139f0249b2SNicolas Bonnefon logData->getNbLine() - getNbVisibleLines() + 1 ) );
14140b05c6eaSNicolas Bonnefon
14152493b508SNicolas Bonnefon const int hScrollMaxValue = std::max( 0,
14162493b508SNicolas Bonnefon logData->getMaxLength() - getNbVisibleCols() + 1 );
14170b05c6eaSNicolas Bonnefon horizontalScrollBar()->setRange( 0, hScrollMaxValue );
14180b05c6eaSNicolas Bonnefon }
14190b05c6eaSNicolas Bonnefon
drawTextArea(QPaintDevice * paint_device,int32_t)1420481c483cSSergei Dyshel void AbstractLogView::drawTextArea( QPaintDevice* paint_device, int32_t )
1421a9448ba0SNicolas Bonnefon {
1422a9448ba0SNicolas Bonnefon // LOG( logDEBUG ) << "devicePixelRatio: " << viewport()->devicePixelRatio();
1423a9448ba0SNicolas Bonnefon // LOG( logDEBUG ) << "viewport size: " << viewport()->size().width();
1424a9448ba0SNicolas Bonnefon // LOG( logDEBUG ) << "pixmap size: " << textPixmap.width();
1425a9448ba0SNicolas Bonnefon // Repaint the viewport
1426a9448ba0SNicolas Bonnefon QPainter painter( paint_device );
1427a9448ba0SNicolas Bonnefon // LOG( logDEBUG ) << "font: " << viewport()->font().family().toStdString();
1428a9448ba0SNicolas Bonnefon // LOG( logDEBUG ) << "font painter: " << painter.font().family().toStdString();
1429a9448ba0SNicolas Bonnefon
1430a9448ba0SNicolas Bonnefon painter.setFont( this->font() );
1431a9448ba0SNicolas Bonnefon
1432a9448ba0SNicolas Bonnefon const int fontHeight = charHeight_;
1433a9448ba0SNicolas Bonnefon const int fontAscent = painter.fontMetrics().ascent();
1434a9448ba0SNicolas Bonnefon const int nbCols = getNbVisibleCols();
14352493b508SNicolas Bonnefon const int paintDeviceHeight = paint_device->height() / viewport()->devicePixelRatio();
14362493b508SNicolas Bonnefon const int paintDeviceWidth = paint_device->width() / viewport()->devicePixelRatio();
1437a9448ba0SNicolas Bonnefon const QPalette& palette = viewport()->palette();
1438a9448ba0SNicolas Bonnefon std::shared_ptr<const FilterSet> filterSet =
1439a9448ba0SNicolas Bonnefon Persistent<FilterSet>( "filterSet" );
1440a9448ba0SNicolas Bonnefon QColor foreColor, backColor;
1441a9448ba0SNicolas Bonnefon
1442a9448ba0SNicolas Bonnefon static const QBrush normalBulletBrush = QBrush( Qt::white );
1443a9448ba0SNicolas Bonnefon static const QBrush matchBulletBrush = QBrush( Qt::red );
1444a9448ba0SNicolas Bonnefon static const QBrush markBrush = QBrush( "dodgerblue" );
1445a9448ba0SNicolas Bonnefon
1446a9448ba0SNicolas Bonnefon static const int SEPARATOR_WIDTH = 1;
1447f01b5ae6SNicolas Bonnefon static const qreal BULLET_AREA_WIDTH = 11;
1448a9448ba0SNicolas Bonnefon static const int CONTENT_MARGIN_WIDTH = 1;
1449a9448ba0SNicolas Bonnefon static const int LINE_NUMBER_PADDING = 3;
1450a9448ba0SNicolas Bonnefon
1451a9448ba0SNicolas Bonnefon // First check the lines to be drawn are within range (might not be the case if
1452a9448ba0SNicolas Bonnefon // the file has just changed)
14532493b508SNicolas Bonnefon const int64_t lines_in_file = logData->getNbLine();
14542493b508SNicolas Bonnefon
14552493b508SNicolas Bonnefon if ( firstLine > lines_in_file )
14568ea5bc4cSNicolas Bonnefon firstLine = lines_in_file ? lines_in_file - 1 : 0;
14572493b508SNicolas Bonnefon
14582493b508SNicolas Bonnefon const int64_t nbLines = std::min(
14592493b508SNicolas Bonnefon static_cast<int64_t>( getNbVisibleLines() ), lines_in_file - firstLine );
14602493b508SNicolas Bonnefon
14612493b508SNicolas Bonnefon const int bottomOfTextPx = nbLines * fontHeight;
14622493b508SNicolas Bonnefon
14632493b508SNicolas Bonnefon LOG(logDEBUG) << "drawing lines from " << firstLine << " (" << nbLines << " lines)";
14642493b508SNicolas Bonnefon LOG(logDEBUG) << "bottomOfTextPx: " << bottomOfTextPx;
14652493b508SNicolas Bonnefon LOG(logDEBUG) << "Height: " << paintDeviceHeight;
1466a9448ba0SNicolas Bonnefon
1467a9448ba0SNicolas Bonnefon // Lines to write
14682493b508SNicolas Bonnefon const QStringList lines = logData->getExpandedLines( firstLine, nbLines );
1469a9448ba0SNicolas Bonnefon
1470a9448ba0SNicolas Bonnefon // First draw the bullet left margin
1471a9448ba0SNicolas Bonnefon painter.setPen(palette.color(QPalette::Text));
1472a9448ba0SNicolas Bonnefon painter.fillRect( 0, 0,
14732493b508SNicolas Bonnefon BULLET_AREA_WIDTH, paintDeviceHeight,
1474a9448ba0SNicolas Bonnefon Qt::darkGray );
1475a9448ba0SNicolas Bonnefon
1476a9448ba0SNicolas Bonnefon // Column at which the content should start (pixels)
1477f01b5ae6SNicolas Bonnefon qreal contentStartPosX = BULLET_AREA_WIDTH + SEPARATOR_WIDTH;
1478a9448ba0SNicolas Bonnefon
1479a9448ba0SNicolas Bonnefon // This is also the bullet zone width, used for marking clicks
1480a9448ba0SNicolas Bonnefon bulletZoneWidthPx_ = contentStartPosX;
1481a9448ba0SNicolas Bonnefon
14822493b508SNicolas Bonnefon // Update the length of line numbers
14832493b508SNicolas Bonnefon const int nbDigitsInLineNumber = countDigits( maxDisplayLineNumber() );
14842493b508SNicolas Bonnefon
1485a9448ba0SNicolas Bonnefon // Draw the line numbers area
1486a9448ba0SNicolas Bonnefon int lineNumberAreaStartX = 0;
1487a9448ba0SNicolas Bonnefon if ( lineNumbersVisible_ ) {
14882493b508SNicolas Bonnefon int lineNumberWidth = charWidth_ * nbDigitsInLineNumber;
1489a9448ba0SNicolas Bonnefon int lineNumberAreaWidth =
1490a9448ba0SNicolas Bonnefon 2 * LINE_NUMBER_PADDING + lineNumberWidth;
1491a9448ba0SNicolas Bonnefon lineNumberAreaStartX = contentStartPosX;
1492a9448ba0SNicolas Bonnefon
1493a9448ba0SNicolas Bonnefon painter.setPen(palette.color(QPalette::Text));
1494a9448ba0SNicolas Bonnefon /* Not sure if it looks good...
1495a9448ba0SNicolas Bonnefon painter.drawLine( contentStartPosX + lineNumberAreaWidth,
1496a9448ba0SNicolas Bonnefon 0,
1497a9448ba0SNicolas Bonnefon contentStartPosX + lineNumberAreaWidth,
1498a9448ba0SNicolas Bonnefon viewport()->height() );
1499a9448ba0SNicolas Bonnefon */
1500f01b5ae6SNicolas Bonnefon painter.fillRect( contentStartPosX - SEPARATOR_WIDTH, 0,
1501f01b5ae6SNicolas Bonnefon lineNumberAreaWidth + SEPARATOR_WIDTH, paintDeviceHeight,
1502a9448ba0SNicolas Bonnefon Qt::lightGray );
1503a9448ba0SNicolas Bonnefon
1504a9448ba0SNicolas Bonnefon // Update for drawing the actual text
1505a9448ba0SNicolas Bonnefon contentStartPosX += lineNumberAreaWidth;
1506a9448ba0SNicolas Bonnefon }
1507a9448ba0SNicolas Bonnefon else {
1508f01b5ae6SNicolas Bonnefon painter.fillRect( contentStartPosX - SEPARATOR_WIDTH, 0,
1509f01b5ae6SNicolas Bonnefon SEPARATOR_WIDTH + 1, paintDeviceHeight,
1510f01b5ae6SNicolas Bonnefon Qt::lightGray );
1511f01b5ae6SNicolas Bonnefon // contentStartPosX += SEPARATOR_WIDTH;
1512a9448ba0SNicolas Bonnefon }
1513a9448ba0SNicolas Bonnefon
1514f01b5ae6SNicolas Bonnefon painter.drawLine( BULLET_AREA_WIDTH, 0,
1515f01b5ae6SNicolas Bonnefon BULLET_AREA_WIDTH, paintDeviceHeight - 1 );
1516f01b5ae6SNicolas Bonnefon
1517a9448ba0SNicolas Bonnefon // This is the total width of the 'margin' (including line number if any)
1518a9448ba0SNicolas Bonnefon // used for mouse calculation etc...
1519f01b5ae6SNicolas Bonnefon leftMarginPx_ = contentStartPosX + SEPARATOR_WIDTH;
1520a9448ba0SNicolas Bonnefon
1521a9448ba0SNicolas Bonnefon // Then draw each line
15222493b508SNicolas Bonnefon for (int i = 0; i < nbLines; i++) {
15232493b508SNicolas Bonnefon const LineNumber line_index = i + firstLine;
15242493b508SNicolas Bonnefon
1525a9448ba0SNicolas Bonnefon // Position in pixel of the base line of the line to print
15262493b508SNicolas Bonnefon const int yPos = i * fontHeight;
1527a9448ba0SNicolas Bonnefon const int xPos = contentStartPosX + CONTENT_MARGIN_WIDTH;
1528a9448ba0SNicolas Bonnefon
1529a9448ba0SNicolas Bonnefon // string to print, cut to fit the length and position of the view
15302493b508SNicolas Bonnefon const QString line = lines[i];
1531a9448ba0SNicolas Bonnefon const QString cutLine = line.mid( firstCol, nbCols );
1532a9448ba0SNicolas Bonnefon
15332493b508SNicolas Bonnefon if ( selection_.isLineSelected( line_index ) ) {
1534a9448ba0SNicolas Bonnefon // Reverse the selected line
1535a9448ba0SNicolas Bonnefon foreColor = palette.color( QPalette::HighlightedText );
1536a9448ba0SNicolas Bonnefon backColor = palette.color( QPalette::Highlight );
1537a9448ba0SNicolas Bonnefon painter.setPen(palette.color(QPalette::Text));
1538a9448ba0SNicolas Bonnefon }
15392493b508SNicolas Bonnefon else if ( filterSet->matchLine( logData->getLineString( line_index ),
1540a9448ba0SNicolas Bonnefon &foreColor, &backColor ) ) {
1541a9448ba0SNicolas Bonnefon // Apply a filter to the line
1542a9448ba0SNicolas Bonnefon }
1543a9448ba0SNicolas Bonnefon else {
1544a9448ba0SNicolas Bonnefon // Use the default colors
1545a9448ba0SNicolas Bonnefon foreColor = palette.color( QPalette::Text );
1546a9448ba0SNicolas Bonnefon backColor = palette.color( QPalette::Base );
1547a9448ba0SNicolas Bonnefon }
1548a9448ba0SNicolas Bonnefon
1549a9448ba0SNicolas Bonnefon // Is there something selected in the line?
1550a9448ba0SNicolas Bonnefon int sel_start, sel_end;
1551a9448ba0SNicolas Bonnefon bool isSelection =
15522493b508SNicolas Bonnefon selection_.getPortionForLine( line_index, &sel_start, &sel_end );
1553a9448ba0SNicolas Bonnefon // Has the line got elements to be highlighted
1554a9448ba0SNicolas Bonnefon QList<QuickFindMatch> qfMatchList;
1555a9448ba0SNicolas Bonnefon bool isMatch =
1556a9448ba0SNicolas Bonnefon quickFindPattern_->matchLine( line, qfMatchList );
1557a9448ba0SNicolas Bonnefon
1558a9448ba0SNicolas Bonnefon if ( isSelection || isMatch ) {
1559a9448ba0SNicolas Bonnefon // We use the LineDrawer and its chunks because the
1560a9448ba0SNicolas Bonnefon // line has to be somehow highlighted
1561a9448ba0SNicolas Bonnefon LineDrawer lineDrawer( backColor );
1562a9448ba0SNicolas Bonnefon
1563a9448ba0SNicolas Bonnefon // First we create a list of chunks with the highlights
1564a9448ba0SNicolas Bonnefon QList<LineChunk> chunkList;
1565a9448ba0SNicolas Bonnefon int column = 0; // Current column in line space
1566a9448ba0SNicolas Bonnefon foreach( const QuickFindMatch match, qfMatchList ) {
1567a9448ba0SNicolas Bonnefon int start = match.startColumn() - firstCol;
1568a9448ba0SNicolas Bonnefon int end = start + match.length();
1569a9448ba0SNicolas Bonnefon // Ignore matches that are *completely* outside view area
1570a9448ba0SNicolas Bonnefon if ( ( start < 0 && end < 0 ) || start >= nbCols )
1571a9448ba0SNicolas Bonnefon continue;
1572a9448ba0SNicolas Bonnefon if ( start > column )
1573a9448ba0SNicolas Bonnefon chunkList << LineChunk( column, start - 1, LineChunk::Normal );
1574a9448ba0SNicolas Bonnefon column = qMin( start + match.length() - 1, nbCols );
1575a9448ba0SNicolas Bonnefon chunkList << LineChunk( qMax( start, 0 ), column,
1576a9448ba0SNicolas Bonnefon LineChunk::Highlighted );
1577a9448ba0SNicolas Bonnefon column++;
1578a9448ba0SNicolas Bonnefon }
1579a9448ba0SNicolas Bonnefon if ( column <= cutLine.length() - 1 )
1580a9448ba0SNicolas Bonnefon chunkList << LineChunk( column, cutLine.length() - 1, LineChunk::Normal );
1581a9448ba0SNicolas Bonnefon
1582a9448ba0SNicolas Bonnefon // Then we add the selection if needed
1583a9448ba0SNicolas Bonnefon QList<LineChunk> newChunkList;
1584a9448ba0SNicolas Bonnefon if ( isSelection ) {
1585a9448ba0SNicolas Bonnefon sel_start -= firstCol; // coord in line space
1586a9448ba0SNicolas Bonnefon sel_end -= firstCol;
1587a9448ba0SNicolas Bonnefon
1588a9448ba0SNicolas Bonnefon foreach ( const LineChunk chunk, chunkList ) {
1589a9448ba0SNicolas Bonnefon newChunkList << chunk.select( sel_start, sel_end );
1590a9448ba0SNicolas Bonnefon }
1591a9448ba0SNicolas Bonnefon }
1592a9448ba0SNicolas Bonnefon else
1593a9448ba0SNicolas Bonnefon newChunkList = chunkList;
1594a9448ba0SNicolas Bonnefon
1595a9448ba0SNicolas Bonnefon foreach ( const LineChunk chunk, newChunkList ) {
1596a9448ba0SNicolas Bonnefon // Select the colours
1597a9448ba0SNicolas Bonnefon QColor fore;
1598a9448ba0SNicolas Bonnefon QColor back;
1599a9448ba0SNicolas Bonnefon switch ( chunk.type() ) {
1600a9448ba0SNicolas Bonnefon case LineChunk::Normal:
1601a9448ba0SNicolas Bonnefon fore = foreColor;
1602a9448ba0SNicolas Bonnefon back = backColor;
1603a9448ba0SNicolas Bonnefon break;
1604a9448ba0SNicolas Bonnefon case LineChunk::Highlighted:
1605a9448ba0SNicolas Bonnefon fore = QColor( "black" );
1606a9448ba0SNicolas Bonnefon back = QColor( "yellow" );
1607a9448ba0SNicolas Bonnefon // fore = highlightForeColor;
1608a9448ba0SNicolas Bonnefon // back = highlightBackColor;
1609a9448ba0SNicolas Bonnefon break;
1610a9448ba0SNicolas Bonnefon case LineChunk::Selected:
1611a9448ba0SNicolas Bonnefon fore = palette.color( QPalette::HighlightedText ),
1612a9448ba0SNicolas Bonnefon back = palette.color( QPalette::Highlight );
1613a9448ba0SNicolas Bonnefon break;
1614a9448ba0SNicolas Bonnefon }
1615a9448ba0SNicolas Bonnefon lineDrawer.addChunk ( chunk, fore, back );
1616a9448ba0SNicolas Bonnefon }
1617a9448ba0SNicolas Bonnefon
1618a9448ba0SNicolas Bonnefon lineDrawer.draw( painter, xPos, yPos,
1619a9448ba0SNicolas Bonnefon viewport()->width(), cutLine,
1620a9448ba0SNicolas Bonnefon CONTENT_MARGIN_WIDTH );
1621a9448ba0SNicolas Bonnefon }
1622a9448ba0SNicolas Bonnefon else {
1623a9448ba0SNicolas Bonnefon // Nothing to be highlighted, we print the whole line!
1624a9448ba0SNicolas Bonnefon painter.fillRect( xPos - CONTENT_MARGIN_WIDTH, yPos,
1625a9448ba0SNicolas Bonnefon viewport()->width(), fontHeight, backColor );
1626a9448ba0SNicolas Bonnefon // (the rectangle is extended on the left to cover the small
1627a9448ba0SNicolas Bonnefon // margin, it looks better (LineDrawer does the same) )
1628a9448ba0SNicolas Bonnefon painter.setPen( foreColor );
1629a9448ba0SNicolas Bonnefon painter.drawText( xPos, yPos + fontAscent, cutLine );
1630a9448ba0SNicolas Bonnefon }
1631a9448ba0SNicolas Bonnefon
1632a9448ba0SNicolas Bonnefon // Then draw the bullet
1633a9448ba0SNicolas Bonnefon painter.setPen( palette.color( QPalette::Text ) );
1634739c6a66SNicolas Bonnefon const qreal circleSize = 3;
1635739c6a66SNicolas Bonnefon const qreal arrowHeight = 4;
1636739c6a66SNicolas Bonnefon const qreal middleXLine = BULLET_AREA_WIDTH / 2;
1637739c6a66SNicolas Bonnefon const qreal middleYLine = yPos + (fontHeight / 2);
1638a9448ba0SNicolas Bonnefon
16392493b508SNicolas Bonnefon const LineType line_type = lineType( line_index );
1640a9448ba0SNicolas Bonnefon if ( line_type == Marked ) {
1641a9448ba0SNicolas Bonnefon // A pretty arrow if the line is marked
1642739c6a66SNicolas Bonnefon const QPointF points[7] = {
1643739c6a66SNicolas Bonnefon QPointF(1, middleYLine - 2),
1644739c6a66SNicolas Bonnefon QPointF(middleXLine, middleYLine - 2),
1645739c6a66SNicolas Bonnefon QPointF(middleXLine, middleYLine - arrowHeight),
1646739c6a66SNicolas Bonnefon QPointF(BULLET_AREA_WIDTH - 1, middleYLine),
1647739c6a66SNicolas Bonnefon QPointF(middleXLine, middleYLine + arrowHeight),
1648739c6a66SNicolas Bonnefon QPointF(middleXLine, middleYLine + 2),
1649739c6a66SNicolas Bonnefon QPointF(1, middleYLine + 2 ),
1650a9448ba0SNicolas Bonnefon };
1651a9448ba0SNicolas Bonnefon
1652a9448ba0SNicolas Bonnefon painter.setBrush( markBrush );
1653a9448ba0SNicolas Bonnefon painter.drawPolygon( points, 7 );
1654a9448ba0SNicolas Bonnefon }
1655a9448ba0SNicolas Bonnefon else {
1656739c6a66SNicolas Bonnefon // For pretty circles
1657739c6a66SNicolas Bonnefon painter.setRenderHint( QPainter::Antialiasing );
1658739c6a66SNicolas Bonnefon
16592493b508SNicolas Bonnefon if ( lineType( line_index ) == Match )
1660a9448ba0SNicolas Bonnefon painter.setBrush( matchBulletBrush );
1661a9448ba0SNicolas Bonnefon else
1662a9448ba0SNicolas Bonnefon painter.setBrush( normalBulletBrush );
1663a9448ba0SNicolas Bonnefon painter.drawEllipse( middleXLine - circleSize,
1664a9448ba0SNicolas Bonnefon middleYLine - circleSize,
1665a9448ba0SNicolas Bonnefon circleSize * 2, circleSize * 2 );
1666a9448ba0SNicolas Bonnefon }
1667a9448ba0SNicolas Bonnefon
1668a9448ba0SNicolas Bonnefon // Draw the line number
1669a9448ba0SNicolas Bonnefon if ( lineNumbersVisible_ ) {
1670a9448ba0SNicolas Bonnefon static const QString lineNumberFormat( "%1" );
1671a9448ba0SNicolas Bonnefon const QString& lineNumberStr =
16722493b508SNicolas Bonnefon lineNumberFormat.arg( displayLineNumber( line_index ),
16732493b508SNicolas Bonnefon nbDigitsInLineNumber );
1674a9448ba0SNicolas Bonnefon painter.setPen( palette.color( QPalette::Text ) );
1675a9448ba0SNicolas Bonnefon painter.drawText( lineNumberAreaStartX + LINE_NUMBER_PADDING,
1676a9448ba0SNicolas Bonnefon yPos + fontAscent, lineNumberStr );
1677a9448ba0SNicolas Bonnefon }
1678a9448ba0SNicolas Bonnefon } // For each line
16792493b508SNicolas Bonnefon
16802493b508SNicolas Bonnefon if ( bottomOfTextPx < paintDeviceHeight ) {
16812493b508SNicolas Bonnefon // The lines don't cover the whole device
16822493b508SNicolas Bonnefon painter.fillRect( contentStartPosX, bottomOfTextPx,
16832493b508SNicolas Bonnefon paintDeviceWidth - contentStartPosX,
16842493b508SNicolas Bonnefon paintDeviceHeight, palette.color( QPalette::Window ) );
16852493b508SNicolas Bonnefon }
1686ac6602a5SNicolas Bonnefon }
1687a9448ba0SNicolas Bonnefon
1688ac6602a5SNicolas Bonnefon // Draw the "pull to follow" bar and return a pixmap.
1689ac6602a5SNicolas Bonnefon // The width is passed in "logic" pixels.
drawPullToFollowBar(int width,float pixel_ratio)1690ac6602a5SNicolas Bonnefon QPixmap AbstractLogView::drawPullToFollowBar( int width, float pixel_ratio )
1691ac6602a5SNicolas Bonnefon {
1692ac6602a5SNicolas Bonnefon static constexpr int barWidth = 40;
1693ac6602a5SNicolas Bonnefon QPixmap pixmap ( static_cast<float>( width ) * pixel_ratio, barWidth * 6.0 );
1694ac6602a5SNicolas Bonnefon pixmap.setDevicePixelRatio( pixel_ratio );
1695ac6602a5SNicolas Bonnefon pixmap.fill( this->palette().color( this->backgroundRole() ) );
1696ac6602a5SNicolas Bonnefon const int nbBars = width / (barWidth * 2) + 1;
1697ac6602a5SNicolas Bonnefon
1698ac6602a5SNicolas Bonnefon QPainter painter( &pixmap );
1699ac6602a5SNicolas Bonnefon painter.setPen( QPen( QColor( 0, 0, 0, 0 ) ) );
1700ac6602a5SNicolas Bonnefon painter.setBrush( QBrush( QColor( "lightyellow" ) ) );
1701ac6602a5SNicolas Bonnefon
1702ac6602a5SNicolas Bonnefon for ( int i = 0; i < nbBars; ++i ) {
1703ac6602a5SNicolas Bonnefon QPoint points[4] = {
1704ac6602a5SNicolas Bonnefon { (i*2+1)*barWidth, 0 },
1705ac6602a5SNicolas Bonnefon { 0, (i*2+1)*barWidth },
1706ac6602a5SNicolas Bonnefon { 0, (i+1)*2*barWidth },
1707ac6602a5SNicolas Bonnefon { (i+1)*2*barWidth, 0 }
1708ac6602a5SNicolas Bonnefon };
1709ac6602a5SNicolas Bonnefon painter.drawConvexPolygon( points, 4 );
1710ac6602a5SNicolas Bonnefon }
1711ac6602a5SNicolas Bonnefon
1712ac6602a5SNicolas Bonnefon return pixmap;
1713a9448ba0SNicolas Bonnefon }
1714a9448ba0SNicolas Bonnefon
disableFollow()1715b297d2f4SNicolas Bonnefon void AbstractLogView::disableFollow()
1716b297d2f4SNicolas Bonnefon {
1717b297d2f4SNicolas Bonnefon emit followModeChanged( false );
1718b297d2f4SNicolas Bonnefon followElasticHook_.hook( false );
1719b297d2f4SNicolas Bonnefon }
1720b297d2f4SNicolas Bonnefon
17210b05c6eaSNicolas Bonnefon namespace {
17220b05c6eaSNicolas Bonnefon
17230b05c6eaSNicolas Bonnefon // Convert the length of the pull to follow bar to pixels
mapPullToFollowLength(int length)17240b05c6eaSNicolas Bonnefon int mapPullToFollowLength( int length )
17250b05c6eaSNicolas Bonnefon {
17260b05c6eaSNicolas Bonnefon return length / 14;
17270b05c6eaSNicolas Bonnefon }
17280b05c6eaSNicolas Bonnefon
17290b05c6eaSNicolas Bonnefon };
1730