xref: /glogg/src/abstractlogview.cpp (revision 045703da85a28b11b557ede4ab9f54834cd9b320)
1bb02e0acSNicolas Bonnefon /*
2bb02e0acSNicolas Bonnefon  * Copyright (C) 2009, 2010, 2011, 2012, 2013 Nicolas Bonnefon and other contributors
3bb02e0acSNicolas Bonnefon  *
4bb02e0acSNicolas Bonnefon  * This file is part of glogg.
5bb02e0acSNicolas Bonnefon  *
6bb02e0acSNicolas Bonnefon  * glogg is free software: you can redistribute it and/or modify
7bb02e0acSNicolas Bonnefon  * it under the terms of the GNU General Public License as published by
8bb02e0acSNicolas Bonnefon  * the Free Software Foundation, either version 3 of the License, or
9bb02e0acSNicolas Bonnefon  * (at your option) any later version.
10bb02e0acSNicolas Bonnefon  *
11bb02e0acSNicolas Bonnefon  * glogg is distributed in the hope that it will be useful,
12bb02e0acSNicolas Bonnefon  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13bb02e0acSNicolas Bonnefon  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14bb02e0acSNicolas Bonnefon  * GNU General Public License for more details.
15bb02e0acSNicolas Bonnefon  *
16bb02e0acSNicolas Bonnefon  * You should have received a copy of the GNU General Public License
17bb02e0acSNicolas Bonnefon  * along with glogg.  If not, see <http://www.gnu.org/licenses/>.
18bb02e0acSNicolas Bonnefon  */
19bb02e0acSNicolas Bonnefon 
20bb02e0acSNicolas Bonnefon // This file implements the AbstractLogView base class.
21bb02e0acSNicolas Bonnefon // Most of the actual drawing and event management common to the two views
22bb02e0acSNicolas Bonnefon // is implemented in this class.  The class only calls protected virtual
23bb02e0acSNicolas Bonnefon // functions when view specific behaviour is desired, using the template
24bb02e0acSNicolas Bonnefon // pattern.
25bb02e0acSNicolas Bonnefon 
26bb02e0acSNicolas Bonnefon #include <iostream>
27bb02e0acSNicolas Bonnefon #include <cassert>
28bb02e0acSNicolas Bonnefon 
29bb02e0acSNicolas Bonnefon #include <QApplication>
30bb02e0acSNicolas Bonnefon #include <QClipboard>
31bb02e0acSNicolas Bonnefon #include <QFile>
32bb02e0acSNicolas Bonnefon #include <QRect>
33bb02e0acSNicolas Bonnefon #include <QPaintEvent>
34bb02e0acSNicolas Bonnefon #include <QPainter>
35bb02e0acSNicolas Bonnefon #include <QFontMetrics>
36bb02e0acSNicolas Bonnefon #include <QScrollBar>
37bb02e0acSNicolas Bonnefon #include <QMenu>
38bb02e0acSNicolas Bonnefon #include <QAction>
39bb02e0acSNicolas Bonnefon #include <QtCore>
40bb02e0acSNicolas Bonnefon 
41bb02e0acSNicolas Bonnefon #include "log.h"
42bb02e0acSNicolas Bonnefon 
43bb02e0acSNicolas Bonnefon #include "persistentinfo.h"
44bb02e0acSNicolas Bonnefon #include "filterset.h"
45bb02e0acSNicolas Bonnefon #include "logmainview.h"
46bb02e0acSNicolas Bonnefon #include "quickfind.h"
47bb02e0acSNicolas Bonnefon #include "quickfindpattern.h"
48bb02e0acSNicolas Bonnefon #include "overview.h"
49bb02e0acSNicolas Bonnefon #include "configuration.h"
50bb02e0acSNicolas Bonnefon 
51bb02e0acSNicolas Bonnefon namespace {
52bb02e0acSNicolas Bonnefon 
53bb02e0acSNicolas Bonnefon int countDigits( quint64 n )
54bb02e0acSNicolas Bonnefon {
55bb02e0acSNicolas Bonnefon     if (n == 0)
56bb02e0acSNicolas Bonnefon         return 1;
57bb02e0acSNicolas Bonnefon 
58bb02e0acSNicolas Bonnefon     // We must force the compiler to not store intermediate results
59bb02e0acSNicolas Bonnefon     // in registers because this causes incorrect result on some
60bb02e0acSNicolas Bonnefon     // systems under optimizations level >0. For the skeptical:
61bb02e0acSNicolas Bonnefon     //
62bb02e0acSNicolas Bonnefon     // #include <math.h>
63bb02e0acSNicolas Bonnefon     // #include <stdlib.h>
64bb02e0acSNicolas Bonnefon     // int main(int argc, char **argv) {
65bb02e0acSNicolas Bonnefon     //     (void)argc;
66bb02e0acSNicolas Bonnefon     //     long long int n = atoll(argv[1]);
67bb02e0acSNicolas Bonnefon     //     return floor( log( n ) / log( 10 ) + 1 );
68bb02e0acSNicolas Bonnefon     // }
69bb02e0acSNicolas Bonnefon     //
70bb02e0acSNicolas Bonnefon     // This is on Thinkpad T60 (Genuine Intel(R) CPU T2300).
71bb02e0acSNicolas Bonnefon     // $ g++ --version
72bb02e0acSNicolas Bonnefon     // g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
73bb02e0acSNicolas Bonnefon     // $ g++ -O0 -Wall -W -o math math.cpp -lm; ./math 10; echo $?
74bb02e0acSNicolas Bonnefon     // 2
75bb02e0acSNicolas Bonnefon     // $ g++ -O1 -Wall -W -o math math.cpp -lm; ./math 10; echo $?
76bb02e0acSNicolas Bonnefon     // 1
77bb02e0acSNicolas Bonnefon     //
78bb02e0acSNicolas Bonnefon     // A fix is to (1) explicitly place intermediate results in
79bb02e0acSNicolas Bonnefon     // variables *and* (2) [A] mark them as 'volatile', or [B] pass
80bb02e0acSNicolas Bonnefon     // -ffloat-store to g++ (note that approach [A] is more portable).
81bb02e0acSNicolas Bonnefon 
82bb02e0acSNicolas Bonnefon     volatile qreal ln_n  = qLn( n );
83bb02e0acSNicolas Bonnefon     volatile qreal ln_10 = qLn( 10 );
84bb02e0acSNicolas Bonnefon     volatile qreal lg_n = ln_n / ln_10;
85bb02e0acSNicolas Bonnefon     volatile qreal lg_n_1 = lg_n + 1;
86bb02e0acSNicolas Bonnefon     volatile qreal fl_lg_n_1 = qFloor( lg_n_1 );
87bb02e0acSNicolas Bonnefon 
88bb02e0acSNicolas Bonnefon     return fl_lg_n_1;
89bb02e0acSNicolas Bonnefon }
90bb02e0acSNicolas Bonnefon 
91bb02e0acSNicolas Bonnefon } // anon namespace
92bb02e0acSNicolas Bonnefon 
93bb02e0acSNicolas Bonnefon 
94bb02e0acSNicolas Bonnefon LineChunk::LineChunk( int first_col, int last_col, ChunkType type )
95bb02e0acSNicolas Bonnefon {
96bb02e0acSNicolas Bonnefon     // LOG(logDEBUG) << "new LineChunk: " << first_col << " " << last_col;
97bb02e0acSNicolas Bonnefon 
98bb02e0acSNicolas Bonnefon     start_ = first_col;
99bb02e0acSNicolas Bonnefon     end_   = last_col;
100bb02e0acSNicolas Bonnefon     type_  = type;
101bb02e0acSNicolas Bonnefon }
102bb02e0acSNicolas Bonnefon 
103bb02e0acSNicolas Bonnefon QList<LineChunk> LineChunk::select( int sel_start, int sel_end ) const
104bb02e0acSNicolas Bonnefon {
105bb02e0acSNicolas Bonnefon     QList<LineChunk> list;
106bb02e0acSNicolas Bonnefon 
107bb02e0acSNicolas Bonnefon     if ( ( sel_start < start_ ) && ( sel_end < start_ ) ) {
108bb02e0acSNicolas Bonnefon         // Selection BEFORE this chunk: no change
109bb02e0acSNicolas Bonnefon         list << LineChunk( *this );
110bb02e0acSNicolas Bonnefon     }
111bb02e0acSNicolas Bonnefon     else if ( sel_start > end_ ) {
112bb02e0acSNicolas Bonnefon         // Selection AFTER this chunk: no change
113bb02e0acSNicolas Bonnefon         list << LineChunk( *this );
114bb02e0acSNicolas Bonnefon     }
115bb02e0acSNicolas Bonnefon     else /* if ( ( sel_start >= start_ ) && ( sel_end <= end_ ) ) */
116bb02e0acSNicolas Bonnefon     {
117bb02e0acSNicolas Bonnefon         // We only want to consider what's inside THIS chunk
118bb02e0acSNicolas Bonnefon         sel_start = qMax( sel_start, start_ );
119bb02e0acSNicolas Bonnefon         sel_end   = qMin( sel_end, end_ );
120bb02e0acSNicolas Bonnefon 
121bb02e0acSNicolas Bonnefon         if ( sel_start > start_ )
122bb02e0acSNicolas Bonnefon             list << LineChunk( start_, sel_start - 1, type_ );
123bb02e0acSNicolas Bonnefon         list << LineChunk( sel_start, sel_end, Selected );
124bb02e0acSNicolas Bonnefon         if ( sel_end < end_ )
125bb02e0acSNicolas Bonnefon             list << LineChunk( sel_end + 1, end_, type_ );
126bb02e0acSNicolas Bonnefon     }
127bb02e0acSNicolas Bonnefon 
128bb02e0acSNicolas Bonnefon     return list;
129bb02e0acSNicolas Bonnefon }
130bb02e0acSNicolas Bonnefon 
131bb02e0acSNicolas Bonnefon inline void LineDrawer::addChunk( int first_col, int last_col,
132bb02e0acSNicolas Bonnefon         QColor fore, QColor back )
133bb02e0acSNicolas Bonnefon {
134bb02e0acSNicolas Bonnefon     if ( first_col < 0 )
135bb02e0acSNicolas Bonnefon         first_col = 0;
136bb02e0acSNicolas Bonnefon     int length = last_col - first_col + 1;
137bb02e0acSNicolas Bonnefon     if ( length > 0 ) {
138bb02e0acSNicolas Bonnefon         list << Chunk ( first_col, length, fore, back );
139bb02e0acSNicolas Bonnefon     }
140bb02e0acSNicolas Bonnefon }
141bb02e0acSNicolas Bonnefon 
142bb02e0acSNicolas Bonnefon inline void LineDrawer::addChunk( const LineChunk& chunk,
143bb02e0acSNicolas Bonnefon         QColor fore, QColor back )
144bb02e0acSNicolas Bonnefon {
145bb02e0acSNicolas Bonnefon     int first_col = chunk.start();
146bb02e0acSNicolas Bonnefon     int last_col  = chunk.end();
147bb02e0acSNicolas Bonnefon 
148bb02e0acSNicolas Bonnefon     addChunk( first_col, last_col, fore, back );
149bb02e0acSNicolas Bonnefon }
150bb02e0acSNicolas Bonnefon 
151bb02e0acSNicolas Bonnefon inline void LineDrawer::draw( QPainter& painter,
152bb02e0acSNicolas Bonnefon         int initialXPos, int initialYPos,
153bb02e0acSNicolas Bonnefon         int line_width, const QString& line,
154bb02e0acSNicolas Bonnefon         int leftExtraBackgroundPx )
155bb02e0acSNicolas Bonnefon {
156bb02e0acSNicolas Bonnefon     QFontMetrics fm = painter.fontMetrics();
157bb02e0acSNicolas Bonnefon     const int fontHeight = fm.height();
158bb02e0acSNicolas Bonnefon     const int fontAscent = fm.ascent();
159bb02e0acSNicolas Bonnefon     // For some reason on Qt 4.8.2 for Win, maxWidth() is wrong but the
160bb02e0acSNicolas Bonnefon     // following give the right result, not sure why:
161bb02e0acSNicolas Bonnefon     const int fontWidth = fm.width( QChar('a') );
162bb02e0acSNicolas Bonnefon 
163bb02e0acSNicolas Bonnefon     int xPos = initialXPos;
164bb02e0acSNicolas Bonnefon     int yPos = initialYPos;
165bb02e0acSNicolas Bonnefon 
166bb02e0acSNicolas Bonnefon     foreach ( Chunk chunk, list ) {
167bb02e0acSNicolas Bonnefon         // Draw each chunk
168bb02e0acSNicolas Bonnefon         // LOG(logDEBUG) << "Chunk: " << chunk.start() << " " << chunk.length();
169bb02e0acSNicolas Bonnefon         QString cutline = line.mid( chunk.start(), chunk.length() );
170bb02e0acSNicolas Bonnefon         const int chunk_width = cutline.length() * fontWidth;
171bb02e0acSNicolas Bonnefon         if ( xPos == initialXPos ) {
172bb02e0acSNicolas Bonnefon             // First chunk, we extend the left background a bit,
173bb02e0acSNicolas Bonnefon             // it looks prettier.
174bb02e0acSNicolas Bonnefon             painter.fillRect( xPos - leftExtraBackgroundPx, yPos,
175bb02e0acSNicolas Bonnefon                     chunk_width + leftExtraBackgroundPx,
176bb02e0acSNicolas Bonnefon                     fontHeight, chunk.backColor() );
177bb02e0acSNicolas Bonnefon         }
178bb02e0acSNicolas Bonnefon         else {
179bb02e0acSNicolas Bonnefon             // other chunks...
180bb02e0acSNicolas Bonnefon             painter.fillRect( xPos, yPos, chunk_width,
181bb02e0acSNicolas Bonnefon                     fontHeight, chunk.backColor() );
182bb02e0acSNicolas Bonnefon         }
183bb02e0acSNicolas Bonnefon         painter.setPen( chunk.foreColor() );
184bb02e0acSNicolas Bonnefon         painter.drawText( xPos, yPos + fontAscent, cutline );
185bb02e0acSNicolas Bonnefon         xPos += chunk_width;
186bb02e0acSNicolas Bonnefon     }
187bb02e0acSNicolas Bonnefon 
188bb02e0acSNicolas Bonnefon     // Draw the empty block at the end of the line
189bb02e0acSNicolas Bonnefon     int blank_width = line_width - xPos;
190bb02e0acSNicolas Bonnefon 
191bb02e0acSNicolas Bonnefon     if ( blank_width > 0 )
192bb02e0acSNicolas Bonnefon         painter.fillRect( xPos, yPos, blank_width, fontHeight, backColor_ );
193bb02e0acSNicolas Bonnefon }
194bb02e0acSNicolas Bonnefon 
195bb02e0acSNicolas Bonnefon const int DigitsBuffer::timeout_ = 2000;
196bb02e0acSNicolas Bonnefon 
197bb02e0acSNicolas Bonnefon DigitsBuffer::DigitsBuffer() : QObject()
198bb02e0acSNicolas Bonnefon {
199bb02e0acSNicolas Bonnefon }
200bb02e0acSNicolas Bonnefon 
201bb02e0acSNicolas Bonnefon void DigitsBuffer::reset()
202bb02e0acSNicolas Bonnefon {
203bb02e0acSNicolas Bonnefon     LOG(logDEBUG) << "DigitsBuffer::reset()";
204bb02e0acSNicolas Bonnefon 
205bb02e0acSNicolas Bonnefon     timer_.stop();
206bb02e0acSNicolas Bonnefon     digits_.clear();
207bb02e0acSNicolas Bonnefon }
208bb02e0acSNicolas Bonnefon 
209bb02e0acSNicolas Bonnefon void DigitsBuffer::add( char character )
210bb02e0acSNicolas Bonnefon {
211bb02e0acSNicolas Bonnefon     LOG(logDEBUG) << "DigitsBuffer::add()";
212bb02e0acSNicolas Bonnefon 
213bb02e0acSNicolas Bonnefon     digits_.append( QChar( character ) );
214bb02e0acSNicolas Bonnefon     timer_.start( timeout_ , this );
215bb02e0acSNicolas Bonnefon }
216bb02e0acSNicolas Bonnefon 
217bb02e0acSNicolas Bonnefon int DigitsBuffer::content()
218bb02e0acSNicolas Bonnefon {
219bb02e0acSNicolas Bonnefon     int result = digits_.toInt();
220bb02e0acSNicolas Bonnefon     reset();
221bb02e0acSNicolas Bonnefon 
222bb02e0acSNicolas Bonnefon     return result;
223bb02e0acSNicolas Bonnefon }
224bb02e0acSNicolas Bonnefon 
225bb02e0acSNicolas Bonnefon void DigitsBuffer::timerEvent( QTimerEvent* event )
226bb02e0acSNicolas Bonnefon {
227bb02e0acSNicolas Bonnefon     if ( event->timerId() == timer_.timerId() ) {
228bb02e0acSNicolas Bonnefon         reset();
229bb02e0acSNicolas Bonnefon     }
230bb02e0acSNicolas Bonnefon     else {
231bb02e0acSNicolas Bonnefon         QObject::timerEvent( event );
232bb02e0acSNicolas Bonnefon     }
233bb02e0acSNicolas Bonnefon }
234bb02e0acSNicolas Bonnefon 
235bb02e0acSNicolas Bonnefon // Graphic parameters
236bb02e0acSNicolas Bonnefon const int AbstractLogView::OVERVIEW_WIDTH = 27;
237bb02e0acSNicolas Bonnefon 
238bb02e0acSNicolas Bonnefon AbstractLogView::AbstractLogView(const AbstractLogData* newLogData,
239bb02e0acSNicolas Bonnefon         const QuickFindPattern* const quickFindPattern, QWidget* parent) :
240bb02e0acSNicolas Bonnefon     QAbstractScrollArea( parent ),
241bb02e0acSNicolas Bonnefon     lineNumbersVisible_( false ),
242bb02e0acSNicolas Bonnefon     selectionStartPos_(),
243bb02e0acSNicolas Bonnefon     selectionCurrentEndPos_(),
244bb02e0acSNicolas Bonnefon     autoScrollTimer_(),
245bb02e0acSNicolas Bonnefon     selection_(),
246bb02e0acSNicolas Bonnefon     quickFindPattern_( quickFindPattern ),
247bb02e0acSNicolas Bonnefon     quickFind_( newLogData, &selection_, quickFindPattern )
248bb02e0acSNicolas Bonnefon {
249bb02e0acSNicolas Bonnefon     logData = newLogData;
250bb02e0acSNicolas Bonnefon 
251bb02e0acSNicolas Bonnefon     followMode_ = false;
252bb02e0acSNicolas Bonnefon 
253bb02e0acSNicolas Bonnefon     selectionStarted_ = false;
254bb02e0acSNicolas Bonnefon     markingClickInitiated_ = false;
255bb02e0acSNicolas Bonnefon 
256bb02e0acSNicolas Bonnefon     firstLine = 0;
257bb02e0acSNicolas Bonnefon     lastLine = 0;
258bb02e0acSNicolas Bonnefon     firstCol = 0;
259bb02e0acSNicolas Bonnefon 
260bb02e0acSNicolas Bonnefon     overview_ = NULL;
261bb02e0acSNicolas Bonnefon     overviewWidget_ = NULL;
262bb02e0acSNicolas Bonnefon 
263bb02e0acSNicolas Bonnefon     // Display
264bb02e0acSNicolas Bonnefon     nbDigitsInLineNumber_ = 0;
265bb02e0acSNicolas Bonnefon     leftMarginPx_ = 0;
266bb02e0acSNicolas Bonnefon 
267bb02e0acSNicolas Bonnefon     // Fonts
268bb02e0acSNicolas Bonnefon     charWidth_ = 1;
269bb02e0acSNicolas Bonnefon     charHeight_ = 1;
270bb02e0acSNicolas Bonnefon 
271bb02e0acSNicolas Bonnefon     // Create the viewport QWidget
272bb02e0acSNicolas Bonnefon     setViewport( 0 );
273bb02e0acSNicolas Bonnefon 
274bb02e0acSNicolas Bonnefon     setAttribute( Qt::WA_StaticContents );  // Does it work?
275bb02e0acSNicolas Bonnefon 
276bb02e0acSNicolas Bonnefon     // Hovering
277bb02e0acSNicolas Bonnefon     setMouseTracking( true );
278bb02e0acSNicolas Bonnefon     lastHoveredLine_ = -1;
279bb02e0acSNicolas Bonnefon 
280bb02e0acSNicolas Bonnefon     // Init the popup menu
281bb02e0acSNicolas Bonnefon     createMenu();
282bb02e0acSNicolas Bonnefon 
283bb02e0acSNicolas Bonnefon     // Signals
284bb02e0acSNicolas Bonnefon     connect( quickFindPattern_, SIGNAL( patternUpdated() ),
285bb02e0acSNicolas Bonnefon             this, SLOT ( handlePatternUpdated() ) );
286bb02e0acSNicolas Bonnefon     connect( verticalScrollBar(), SIGNAL( sliderMoved( int ) ),
287bb02e0acSNicolas Bonnefon             this, SIGNAL( followDisabled() ) );
288bb02e0acSNicolas Bonnefon     connect( &quickFind_, SIGNAL( notify( const QFNotification& ) ),
289bb02e0acSNicolas Bonnefon             this, SIGNAL( notifyQuickFind( const QFNotification& ) ) );
290bb02e0acSNicolas Bonnefon     connect( &quickFind_, SIGNAL( clearNotification() ),
291bb02e0acSNicolas Bonnefon             this, SIGNAL( clearQuickFindNotification() ) );
292bb02e0acSNicolas Bonnefon }
293bb02e0acSNicolas Bonnefon 
294bb02e0acSNicolas Bonnefon AbstractLogView::~AbstractLogView()
295bb02e0acSNicolas Bonnefon {
296bb02e0acSNicolas Bonnefon }
297bb02e0acSNicolas Bonnefon 
298bb02e0acSNicolas Bonnefon 
299bb02e0acSNicolas Bonnefon //
300bb02e0acSNicolas Bonnefon // Received events
301bb02e0acSNicolas Bonnefon //
302bb02e0acSNicolas Bonnefon 
303bb02e0acSNicolas Bonnefon void AbstractLogView::changeEvent( QEvent* changeEvent )
304bb02e0acSNicolas Bonnefon {
305bb02e0acSNicolas Bonnefon     QAbstractScrollArea::changeEvent( changeEvent );
306bb02e0acSNicolas Bonnefon 
307bb02e0acSNicolas Bonnefon     // Stop the timer if the widget becomes inactive
308bb02e0acSNicolas Bonnefon     if ( changeEvent->type() == QEvent::ActivationChange ) {
309bb02e0acSNicolas Bonnefon         if ( ! isActiveWindow() )
310bb02e0acSNicolas Bonnefon             autoScrollTimer_.stop();
311bb02e0acSNicolas Bonnefon     }
312bb02e0acSNicolas Bonnefon     viewport()->update();
313bb02e0acSNicolas Bonnefon }
314bb02e0acSNicolas Bonnefon 
315bb02e0acSNicolas Bonnefon void AbstractLogView::mousePressEvent( QMouseEvent* mouseEvent )
316bb02e0acSNicolas Bonnefon {
31711582726SNicolas Bonnefon     static std::shared_ptr<Configuration> config =
31811582726SNicolas Bonnefon         Persistent<Configuration>( "settings" );
319bb02e0acSNicolas Bonnefon 
320bb02e0acSNicolas Bonnefon     if ( mouseEvent->button() == Qt::LeftButton )
321bb02e0acSNicolas Bonnefon     {
322bb02e0acSNicolas Bonnefon         int line = convertCoordToLine( mouseEvent->y() );
323bb02e0acSNicolas Bonnefon 
324bb02e0acSNicolas Bonnefon         if ( mouseEvent->modifiers() & Qt::ShiftModifier )
325bb02e0acSNicolas Bonnefon         {
326bb02e0acSNicolas Bonnefon             selection_.selectRangeFromPrevious( line );
327bb02e0acSNicolas Bonnefon             emit updateLineNumber( line );
328bb02e0acSNicolas Bonnefon             update();
329bb02e0acSNicolas Bonnefon         }
330bb02e0acSNicolas Bonnefon         else
331bb02e0acSNicolas Bonnefon         {
332bb02e0acSNicolas Bonnefon             if ( mouseEvent->x() < bulletZoneWidthPx_ ) {
333bb02e0acSNicolas Bonnefon                 // Mark a line if it is clicked in the left margin
334bb02e0acSNicolas Bonnefon                 // (only if click and release in the same area)
335bb02e0acSNicolas Bonnefon                 markingClickInitiated_ = true;
336bb02e0acSNicolas Bonnefon                 markingClickLine_ = line;
337bb02e0acSNicolas Bonnefon             }
338bb02e0acSNicolas Bonnefon             else {
339bb02e0acSNicolas Bonnefon                 // Select the line, and start a selection
340bb02e0acSNicolas Bonnefon                 if ( line < logData->getNbLine() ) {
341bb02e0acSNicolas Bonnefon                     selection_.selectLine( line );
342bb02e0acSNicolas Bonnefon                     emit updateLineNumber( line );
343bb02e0acSNicolas Bonnefon                     emit newSelection( line );
344bb02e0acSNicolas Bonnefon                 }
345bb02e0acSNicolas Bonnefon 
346bb02e0acSNicolas Bonnefon                 // Remember the click in case we're starting a selection
347bb02e0acSNicolas Bonnefon                 selectionStarted_ = true;
348bb02e0acSNicolas Bonnefon                 selectionStartPos_ = convertCoordToFilePos( mouseEvent->pos() );
349bb02e0acSNicolas Bonnefon                 selectionCurrentEndPos_ = selectionStartPos_;
350bb02e0acSNicolas Bonnefon             }
351bb02e0acSNicolas Bonnefon         }
352bb02e0acSNicolas Bonnefon     }
353bb02e0acSNicolas Bonnefon     else if ( mouseEvent->button() == Qt::RightButton )
354bb02e0acSNicolas Bonnefon     {
355bb02e0acSNicolas Bonnefon         // Prepare the popup depending on selection type
356bb02e0acSNicolas Bonnefon         if ( selection_.isSingleLine() ) {
357bb02e0acSNicolas Bonnefon             copyAction_->setText( "&Copy this line" );
358bb02e0acSNicolas Bonnefon         }
359bb02e0acSNicolas Bonnefon         else {
360bb02e0acSNicolas Bonnefon             copyAction_->setText( "&Copy" );
361bb02e0acSNicolas Bonnefon             copyAction_->setStatusTip( tr("Copy the selection") );
362bb02e0acSNicolas Bonnefon         }
363bb02e0acSNicolas Bonnefon 
364bb02e0acSNicolas Bonnefon         if ( selection_.isPortion() ) {
365bb02e0acSNicolas Bonnefon             findNextAction_->setEnabled( true );
366bb02e0acSNicolas Bonnefon             findPreviousAction_->setEnabled( true );
367bb02e0acSNicolas Bonnefon             addToSearchAction_->setEnabled( true );
368bb02e0acSNicolas Bonnefon         }
369bb02e0acSNicolas Bonnefon         else {
370bb02e0acSNicolas Bonnefon             findNextAction_->setEnabled( false );
371bb02e0acSNicolas Bonnefon             findPreviousAction_->setEnabled( false );
372bb02e0acSNicolas Bonnefon             addToSearchAction_->setEnabled( false );
373bb02e0acSNicolas Bonnefon         }
374bb02e0acSNicolas Bonnefon 
375bb02e0acSNicolas Bonnefon         // "Add to search" only makes sense in regexp mode
37611582726SNicolas Bonnefon         if ( config->mainRegexpType() != ExtendedRegexp )
377bb02e0acSNicolas Bonnefon             addToSearchAction_->setEnabled( false );
378bb02e0acSNicolas Bonnefon 
379bb02e0acSNicolas Bonnefon         // Display the popup (blocking)
380bb02e0acSNicolas Bonnefon         popupMenu_->exec( QCursor::pos() );
381bb02e0acSNicolas Bonnefon     }
382bb02e0acSNicolas Bonnefon }
383bb02e0acSNicolas Bonnefon 
384bb02e0acSNicolas Bonnefon void AbstractLogView::mouseMoveEvent( QMouseEvent* mouseEvent )
385bb02e0acSNicolas Bonnefon {
386bb02e0acSNicolas Bonnefon     // Selection implementation
387bb02e0acSNicolas Bonnefon     if ( selectionStarted_ )
388bb02e0acSNicolas Bonnefon     {
389bb02e0acSNicolas Bonnefon         QPoint thisEndPos = convertCoordToFilePos( mouseEvent->pos() );
390bb02e0acSNicolas Bonnefon         if ( thisEndPos != selectionCurrentEndPos_ )
391bb02e0acSNicolas Bonnefon         {
392bb02e0acSNicolas Bonnefon             // Are we on a different line?
393bb02e0acSNicolas Bonnefon             if ( selectionStartPos_.y() != thisEndPos.y() )
394bb02e0acSNicolas Bonnefon             {
395bb02e0acSNicolas Bonnefon                 if ( thisEndPos.y() != selectionCurrentEndPos_.y() )
396bb02e0acSNicolas Bonnefon                 {
397bb02e0acSNicolas Bonnefon                     // This is a 'range' selection
398bb02e0acSNicolas Bonnefon                     selection_.selectRange( selectionStartPos_.y(),
399bb02e0acSNicolas Bonnefon                             thisEndPos.y() );
400bb02e0acSNicolas Bonnefon                     emit updateLineNumber( thisEndPos.y() );
401bb02e0acSNicolas Bonnefon                     update();
402bb02e0acSNicolas Bonnefon                 }
403bb02e0acSNicolas Bonnefon             }
404bb02e0acSNicolas Bonnefon             // So we are on the same line. Are we moving horizontaly?
405bb02e0acSNicolas Bonnefon             else if ( thisEndPos.x() != selectionCurrentEndPos_.x() )
406bb02e0acSNicolas Bonnefon             {
407bb02e0acSNicolas Bonnefon                 // This is a 'portion' selection
408bb02e0acSNicolas Bonnefon                 selection_.selectPortion( thisEndPos.y(),
409bb02e0acSNicolas Bonnefon                         selectionStartPos_.x(), thisEndPos.x() );
410bb02e0acSNicolas Bonnefon                 update();
411bb02e0acSNicolas Bonnefon             }
412bb02e0acSNicolas Bonnefon             // On the same line, and moving vertically then
413bb02e0acSNicolas Bonnefon             else
414bb02e0acSNicolas Bonnefon             {
415bb02e0acSNicolas Bonnefon                 // This is a 'line' selection
416bb02e0acSNicolas Bonnefon                 selection_.selectLine( thisEndPos.y() );
417bb02e0acSNicolas Bonnefon                 emit updateLineNumber( thisEndPos.y() );
418bb02e0acSNicolas Bonnefon                 update();
419bb02e0acSNicolas Bonnefon             }
420bb02e0acSNicolas Bonnefon             selectionCurrentEndPos_ = thisEndPos;
421bb02e0acSNicolas Bonnefon 
422bb02e0acSNicolas Bonnefon             // Do we need to scroll while extending the selection?
423bb02e0acSNicolas Bonnefon             QRect visible = viewport()->rect();
424bb02e0acSNicolas Bonnefon             if ( visible.contains( mouseEvent->pos() ) )
425bb02e0acSNicolas Bonnefon                 autoScrollTimer_.stop();
426bb02e0acSNicolas Bonnefon             else if ( ! autoScrollTimer_.isActive() )
427bb02e0acSNicolas Bonnefon                 autoScrollTimer_.start( 100, this );
428bb02e0acSNicolas Bonnefon         }
429bb02e0acSNicolas Bonnefon     }
430bb02e0acSNicolas Bonnefon     else {
431bb02e0acSNicolas Bonnefon         considerMouseHovering( mouseEvent->x(), mouseEvent->y() );
432bb02e0acSNicolas Bonnefon     }
433bb02e0acSNicolas Bonnefon }
434bb02e0acSNicolas Bonnefon 
435bb02e0acSNicolas Bonnefon void AbstractLogView::mouseReleaseEvent( QMouseEvent* mouseEvent )
436bb02e0acSNicolas Bonnefon {
437bb02e0acSNicolas Bonnefon     if ( markingClickInitiated_ ) {
438bb02e0acSNicolas Bonnefon         markingClickInitiated_ = false;
439bb02e0acSNicolas Bonnefon         int line = convertCoordToLine( mouseEvent->y() );
440bb02e0acSNicolas Bonnefon         if ( line == markingClickLine_ )
441bb02e0acSNicolas Bonnefon             emit markLine( line );
442bb02e0acSNicolas Bonnefon     }
443bb02e0acSNicolas Bonnefon     else {
444bb02e0acSNicolas Bonnefon         selectionStarted_ = false;
445bb02e0acSNicolas Bonnefon         if ( autoScrollTimer_.isActive() )
446bb02e0acSNicolas Bonnefon             autoScrollTimer_.stop();
447bb02e0acSNicolas Bonnefon         updateGlobalSelection();
448bb02e0acSNicolas Bonnefon     }
449bb02e0acSNicolas Bonnefon }
450bb02e0acSNicolas Bonnefon 
451bb02e0acSNicolas Bonnefon void AbstractLogView::mouseDoubleClickEvent( QMouseEvent* mouseEvent )
452bb02e0acSNicolas Bonnefon {
453bb02e0acSNicolas Bonnefon     if ( mouseEvent->button() == Qt::LeftButton )
454bb02e0acSNicolas Bonnefon     {
455bb02e0acSNicolas Bonnefon         const QPoint pos = convertCoordToFilePos( mouseEvent->pos() );
456bb02e0acSNicolas Bonnefon         selectWordAtPosition( pos );
457bb02e0acSNicolas Bonnefon     }
458bb02e0acSNicolas Bonnefon }
459bb02e0acSNicolas Bonnefon 
460bb02e0acSNicolas Bonnefon void AbstractLogView::timerEvent( QTimerEvent* timerEvent )
461bb02e0acSNicolas Bonnefon {
462bb02e0acSNicolas Bonnefon     if ( timerEvent->timerId() == autoScrollTimer_.timerId() ) {
463bb02e0acSNicolas Bonnefon         QRect visible = viewport()->rect();
464bb02e0acSNicolas Bonnefon         const QPoint globalPos = QCursor::pos();
465bb02e0acSNicolas Bonnefon         const QPoint pos = viewport()->mapFromGlobal( globalPos );
466bb02e0acSNicolas Bonnefon         QMouseEvent ev( QEvent::MouseMove, pos, globalPos, Qt::LeftButton,
467bb02e0acSNicolas Bonnefon                 Qt::LeftButton, Qt::NoModifier );
468bb02e0acSNicolas Bonnefon         mouseMoveEvent( &ev );
469bb02e0acSNicolas Bonnefon         int deltaX = qMax( pos.x() - visible.left(),
470bb02e0acSNicolas Bonnefon                 visible.right() - pos.x() ) - visible.width();
471bb02e0acSNicolas Bonnefon         int deltaY = qMax( pos.y() - visible.top(),
472bb02e0acSNicolas Bonnefon                 visible.bottom() - pos.y() ) - visible.height();
473bb02e0acSNicolas Bonnefon         int delta = qMax( deltaX, deltaY );
474bb02e0acSNicolas Bonnefon 
475bb02e0acSNicolas Bonnefon         if ( delta >= 0 ) {
476bb02e0acSNicolas Bonnefon             if ( delta < 7 )
477bb02e0acSNicolas Bonnefon                 delta = 7;
478bb02e0acSNicolas Bonnefon             int timeout = 4900 / ( delta * delta );
479bb02e0acSNicolas Bonnefon             autoScrollTimer_.start( timeout, this );
480bb02e0acSNicolas Bonnefon 
481bb02e0acSNicolas Bonnefon             if ( deltaX > 0 )
482bb02e0acSNicolas Bonnefon                 horizontalScrollBar()->triggerAction(
483bb02e0acSNicolas Bonnefon                         pos.x() <visible.center().x() ?
484bb02e0acSNicolas Bonnefon                         QAbstractSlider::SliderSingleStepSub :
485bb02e0acSNicolas Bonnefon                         QAbstractSlider::SliderSingleStepAdd );
486bb02e0acSNicolas Bonnefon 
487bb02e0acSNicolas Bonnefon             if ( deltaY > 0 )
488bb02e0acSNicolas Bonnefon                 verticalScrollBar()->triggerAction(
489bb02e0acSNicolas Bonnefon                         pos.y() <visible.center().y() ?
490bb02e0acSNicolas Bonnefon                         QAbstractSlider::SliderSingleStepSub :
491bb02e0acSNicolas Bonnefon                         QAbstractSlider::SliderSingleStepAdd );
492bb02e0acSNicolas Bonnefon         }
493bb02e0acSNicolas Bonnefon     }
494bb02e0acSNicolas Bonnefon     QAbstractScrollArea::timerEvent( timerEvent );
495bb02e0acSNicolas Bonnefon }
496bb02e0acSNicolas Bonnefon 
497bb02e0acSNicolas Bonnefon void AbstractLogView::keyPressEvent( QKeyEvent* keyEvent )
498bb02e0acSNicolas Bonnefon {
499bb02e0acSNicolas Bonnefon     LOG(logDEBUG4) << "keyPressEvent received";
500bb02e0acSNicolas Bonnefon     bool controlModifier = (keyEvent->modifiers() & Qt::ControlModifier) == Qt::ControlModifier;
501bb02e0acSNicolas Bonnefon     bool shiftModifier = (keyEvent->modifiers() & Qt::ShiftModifier) == Qt::ShiftModifier;
502bb02e0acSNicolas Bonnefon 
503bb02e0acSNicolas Bonnefon     if ( keyEvent->key() == Qt::Key_Left )
504bb02e0acSNicolas Bonnefon         horizontalScrollBar()->triggerAction(QScrollBar::SliderPageStepSub);
505bb02e0acSNicolas Bonnefon     else if ( keyEvent->key() == Qt::Key_Right )
506bb02e0acSNicolas Bonnefon         horizontalScrollBar()->triggerAction(QScrollBar::SliderPageStepAdd);
507bb02e0acSNicolas Bonnefon     else if ( keyEvent->key() == Qt::Key_Home && !controlModifier)
508bb02e0acSNicolas Bonnefon         jumpToStartOfLine();
509bb02e0acSNicolas Bonnefon     else if ( keyEvent->key() == Qt::Key_End  && !controlModifier)
510bb02e0acSNicolas Bonnefon         jumpToRightOfScreen();
511bb02e0acSNicolas Bonnefon     else if ( (keyEvent->key() == Qt::Key_PageDown && controlModifier)
512bb02e0acSNicolas Bonnefon            || (keyEvent->key() == Qt::Key_End && controlModifier) )
513bb02e0acSNicolas Bonnefon     {
514bb02e0acSNicolas Bonnefon         emit followDisabled(); // duplicate of 'G' action.
515bb02e0acSNicolas Bonnefon         selection_.selectLine( logData->getNbLine() - 1 );
516bb02e0acSNicolas Bonnefon         emit updateLineNumber( logData->getNbLine() - 1 );
517bb02e0acSNicolas Bonnefon         jumpToBottom();
518bb02e0acSNicolas Bonnefon     }
519bb02e0acSNicolas Bonnefon     else if ( (keyEvent->key() == Qt::Key_PageUp && controlModifier)
520bb02e0acSNicolas Bonnefon            || (keyEvent->key() == Qt::Key_Home && controlModifier) )
521bb02e0acSNicolas Bonnefon     {
522bb02e0acSNicolas Bonnefon         emit followDisabled(); // like 'g' but 0 input first line action.
523bb02e0acSNicolas Bonnefon         selectAndDisplayLine( 0 );
524bb02e0acSNicolas Bonnefon         emit updateLineNumber( 0 );
525bb02e0acSNicolas Bonnefon     }
526bb02e0acSNicolas Bonnefon     else if ( keyEvent->key() == Qt::Key_F3 && !shiftModifier )
527bb02e0acSNicolas Bonnefon         searchNext(); // duplicate of 'n' action.
528bb02e0acSNicolas Bonnefon     else if ( keyEvent->key() == Qt::Key_F3 && shiftModifier )
529bb02e0acSNicolas Bonnefon         searchPrevious(); // duplicate of 'N' action.
530bb02e0acSNicolas Bonnefon     else {
531eb6a8f99SNicolas Bonnefon         const char character = (keyEvent->text())[0].toLatin1();
532bb02e0acSNicolas Bonnefon 
533f88e59a4SNicolas Bonnefon         if ( keyEvent->modifiers() == Qt::NoModifier &&
534f88e59a4SNicolas Bonnefon                 ( character >= '0' ) && ( character <= '9' ) ) {
535bb02e0acSNicolas Bonnefon             // Adds the digit to the timed buffer
536bb02e0acSNicolas Bonnefon             digitsBuffer_.add( character );
537bb02e0acSNicolas Bonnefon         }
538bb02e0acSNicolas Bonnefon         else {
539eb6a8f99SNicolas Bonnefon             switch ( (keyEvent->text())[0].toLatin1() ) {
540bb02e0acSNicolas Bonnefon                 case 'j':
541bb02e0acSNicolas Bonnefon                     {
542bb02e0acSNicolas Bonnefon                         int delta = qMax( 1, digitsBuffer_.content() );
543bb02e0acSNicolas Bonnefon                         emit followDisabled();
544bb02e0acSNicolas Bonnefon                         //verticalScrollBar()->triggerAction(
545bb02e0acSNicolas Bonnefon                         //QScrollBar::SliderSingleStepAdd);
546bb02e0acSNicolas Bonnefon                         moveSelection( delta );
547bb02e0acSNicolas Bonnefon                         break;
548bb02e0acSNicolas Bonnefon                     }
549bb02e0acSNicolas Bonnefon                 case 'k':
550bb02e0acSNicolas Bonnefon                     {
551bb02e0acSNicolas Bonnefon                         int delta = qMin( -1, - digitsBuffer_.content() );
552bb02e0acSNicolas Bonnefon                         emit followDisabled();
553bb02e0acSNicolas Bonnefon                         //verticalScrollBar()->triggerAction(
554bb02e0acSNicolas Bonnefon                         //QScrollBar::SliderSingleStepSub);
555bb02e0acSNicolas Bonnefon                         moveSelection( delta );
556bb02e0acSNicolas Bonnefon                         break;
557bb02e0acSNicolas Bonnefon                     }
558bb02e0acSNicolas Bonnefon                 case 'h':
559bb02e0acSNicolas Bonnefon                     horizontalScrollBar()->triggerAction(
560bb02e0acSNicolas Bonnefon                             QScrollBar::SliderSingleStepSub);
561bb02e0acSNicolas Bonnefon                     break;
562bb02e0acSNicolas Bonnefon                 case 'l':
563bb02e0acSNicolas Bonnefon                     horizontalScrollBar()->triggerAction(
564bb02e0acSNicolas Bonnefon                             QScrollBar::SliderSingleStepAdd);
565bb02e0acSNicolas Bonnefon                     break;
566bb02e0acSNicolas Bonnefon                 case '0':
567bb02e0acSNicolas Bonnefon                     jumpToStartOfLine();
568bb02e0acSNicolas Bonnefon                     break;
569bb02e0acSNicolas Bonnefon                 case '$':
570bb02e0acSNicolas Bonnefon                     jumpToEndOfLine();
571bb02e0acSNicolas Bonnefon                     break;
572bb02e0acSNicolas Bonnefon                 case 'g':
573bb02e0acSNicolas Bonnefon                     {
574bb02e0acSNicolas Bonnefon                         int newLine = qMax( 0, digitsBuffer_.content() - 1 );
575bb02e0acSNicolas Bonnefon                         if ( newLine >= logData->getNbLine() )
576bb02e0acSNicolas Bonnefon                             newLine = logData->getNbLine() - 1;
577bb02e0acSNicolas Bonnefon                         emit followDisabled();
578bb02e0acSNicolas Bonnefon                         selectAndDisplayLine( newLine );
579bb02e0acSNicolas Bonnefon                         emit updateLineNumber( newLine );
580bb02e0acSNicolas Bonnefon                         break;
581bb02e0acSNicolas Bonnefon                     }
582bb02e0acSNicolas Bonnefon                 case 'G':
583bb02e0acSNicolas Bonnefon                     emit followDisabled();
584bb02e0acSNicolas Bonnefon                     selection_.selectLine( logData->getNbLine() - 1 );
585bb02e0acSNicolas Bonnefon                     emit updateLineNumber( logData->getNbLine() - 1 );
586bb02e0acSNicolas Bonnefon                     jumpToBottom();
587bb02e0acSNicolas Bonnefon                     break;
588bb02e0acSNicolas Bonnefon                 case 'n':
589bb02e0acSNicolas Bonnefon                     emit searchNext();
590bb02e0acSNicolas Bonnefon                     break;
591bb02e0acSNicolas Bonnefon                 case 'N':
592bb02e0acSNicolas Bonnefon                     emit searchPrevious();
593bb02e0acSNicolas Bonnefon                     break;
594bb02e0acSNicolas Bonnefon                 case '*':
595bb02e0acSNicolas Bonnefon                     // Use the selected 'word' and search forward
596bb02e0acSNicolas Bonnefon                     findNextSelected();
597bb02e0acSNicolas Bonnefon                     break;
598bb02e0acSNicolas Bonnefon                 case '#':
599bb02e0acSNicolas Bonnefon                     // Use the selected 'word' and search backward
600bb02e0acSNicolas Bonnefon                     findPreviousSelected();
601bb02e0acSNicolas Bonnefon                     break;
602bb02e0acSNicolas Bonnefon                 default:
603bb02e0acSNicolas Bonnefon                     keyEvent->ignore();
604bb02e0acSNicolas Bonnefon             }
605bb02e0acSNicolas Bonnefon         }
606bb02e0acSNicolas Bonnefon     }
607bb02e0acSNicolas Bonnefon 
608bb02e0acSNicolas Bonnefon     if ( !keyEvent->isAccepted() )
609bb02e0acSNicolas Bonnefon         QAbstractScrollArea::keyPressEvent( keyEvent );
610bb02e0acSNicolas Bonnefon }
611bb02e0acSNicolas Bonnefon 
612bb02e0acSNicolas Bonnefon void AbstractLogView::wheelEvent( QWheelEvent* wheelEvent )
613bb02e0acSNicolas Bonnefon {
614bb02e0acSNicolas Bonnefon     emit followDisabled();
615bb02e0acSNicolas Bonnefon 
616bb02e0acSNicolas Bonnefon     QAbstractScrollArea::wheelEvent( wheelEvent );
617bb02e0acSNicolas Bonnefon }
618bb02e0acSNicolas Bonnefon 
619bb02e0acSNicolas Bonnefon void AbstractLogView::resizeEvent( QResizeEvent* )
620bb02e0acSNicolas Bonnefon {
621bb02e0acSNicolas Bonnefon     if ( logData == NULL )
622bb02e0acSNicolas Bonnefon         return;
623bb02e0acSNicolas Bonnefon 
624bb02e0acSNicolas Bonnefon     LOG(logDEBUG) << "resizeEvent received";
625bb02e0acSNicolas Bonnefon 
626bb02e0acSNicolas Bonnefon     updateDisplaySize();
627bb02e0acSNicolas Bonnefon }
628bb02e0acSNicolas Bonnefon 
629bb02e0acSNicolas Bonnefon void AbstractLogView::scrollContentsBy( int dx, int dy )
630bb02e0acSNicolas Bonnefon {
631bb02e0acSNicolas Bonnefon     LOG(logDEBUG4) << "scrollContentsBy received";
632bb02e0acSNicolas Bonnefon 
633bb02e0acSNicolas Bonnefon     firstLine = (firstLine - dy) > 0 ? firstLine - dy : 0;
634bb02e0acSNicolas Bonnefon     firstCol  = (firstCol - dx) > 0 ? firstCol - dx : 0;
635bb02e0acSNicolas Bonnefon     lastLine = qMin( logData->getNbLine(), firstLine + getNbVisibleLines() );
636bb02e0acSNicolas Bonnefon 
637bb02e0acSNicolas Bonnefon     // Update the overview if we have one
638bb02e0acSNicolas Bonnefon     if ( overview_ != NULL )
639bb02e0acSNicolas Bonnefon         overview_->updateCurrentPosition( firstLine, lastLine );
640bb02e0acSNicolas Bonnefon 
641bb02e0acSNicolas Bonnefon     // Are we hovering over a new line?
642bb02e0acSNicolas Bonnefon     const QPoint mouse_pos = mapFromGlobal( QCursor::pos() );
643bb02e0acSNicolas Bonnefon     considerMouseHovering( mouse_pos.x(), mouse_pos.y() );
644bb02e0acSNicolas Bonnefon 
645bb02e0acSNicolas Bonnefon     // Redraw
646bb02e0acSNicolas Bonnefon     update();
647bb02e0acSNicolas Bonnefon }
648bb02e0acSNicolas Bonnefon 
649bb02e0acSNicolas Bonnefon void AbstractLogView::paintEvent( QPaintEvent* paintEvent )
650bb02e0acSNicolas Bonnefon {
651bb02e0acSNicolas Bonnefon     QRect invalidRect = paintEvent->rect();
652bb02e0acSNicolas Bonnefon     if ( (invalidRect.isEmpty()) || (logData == NULL) )
653bb02e0acSNicolas Bonnefon         return;
654bb02e0acSNicolas Bonnefon 
655bb02e0acSNicolas Bonnefon     LOG(logDEBUG4) << "paintEvent received, firstLine=" << firstLine
656bb02e0acSNicolas Bonnefon         << " lastLine=" << lastLine <<
657bb02e0acSNicolas Bonnefon         " rect: " << invalidRect.topLeft().x() <<
658bb02e0acSNicolas Bonnefon         ", " << invalidRect.topLeft().y() <<
659bb02e0acSNicolas Bonnefon         ", " << invalidRect.bottomRight().x() <<
660bb02e0acSNicolas Bonnefon         ", " << invalidRect.bottomRight().y();
661bb02e0acSNicolas Bonnefon 
662*045703daSNicolas Bonnefon     if ( ! perfCounter_.addEvent() ) {
663*045703daSNicolas Bonnefon         LOG(logWARNING) << "Redraw per second: " << perfCounter_.readAndReset()
664*045703daSNicolas Bonnefon             << " lines: " << logData->getNbLine();
665*045703daSNicolas Bonnefon         perfCounter_.addEvent();
666*045703daSNicolas Bonnefon     }
667*045703daSNicolas Bonnefon 
668bb02e0acSNicolas Bonnefon     {
669bb02e0acSNicolas Bonnefon         // Repaint the viewport
670bb02e0acSNicolas Bonnefon         QPainter painter( viewport() );
671bb02e0acSNicolas Bonnefon         const int fontHeight = charHeight_;
672bb02e0acSNicolas Bonnefon         const int fontAscent = painter.fontMetrics().ascent();
673bb02e0acSNicolas Bonnefon         const int nbCols = getNbVisibleCols();
674bb02e0acSNicolas Bonnefon         const QPalette& palette = viewport()->palette();
67511582726SNicolas Bonnefon         std::shared_ptr<const FilterSet> filterSet =
67611582726SNicolas Bonnefon             Persistent<FilterSet>( "filterSet" );
677bb02e0acSNicolas Bonnefon         QColor foreColor, backColor;
678bb02e0acSNicolas Bonnefon 
679bb02e0acSNicolas Bonnefon         static const QBrush normalBulletBrush = QBrush( Qt::white );
680bb02e0acSNicolas Bonnefon         static const QBrush matchBulletBrush = QBrush( Qt::red );
681bb02e0acSNicolas Bonnefon         static const QBrush markBrush = QBrush( "dodgerblue" );
682bb02e0acSNicolas Bonnefon 
683bb02e0acSNicolas Bonnefon         static const int SEPARATOR_WIDTH = 1;
684bb02e0acSNicolas Bonnefon         static const int BULLET_AREA_WIDTH = 11;
685bb02e0acSNicolas Bonnefon         static const int CONTENT_MARGIN_WIDTH = 1;
686bb02e0acSNicolas Bonnefon         static const int LINE_NUMBER_PADDING = 3;
687bb02e0acSNicolas Bonnefon 
688bb02e0acSNicolas Bonnefon         // First check the lines to be drawn are within range (might not be the case if
689bb02e0acSNicolas Bonnefon         // the file has just changed)
690bb02e0acSNicolas Bonnefon         const int nbLines = logData->getNbLine();
691bb02e0acSNicolas Bonnefon         if ( nbLines == 0 ) {
692bb02e0acSNicolas Bonnefon             return;
693bb02e0acSNicolas Bonnefon         }
694bb02e0acSNicolas Bonnefon         else {
695bb02e0acSNicolas Bonnefon             if ( firstLine >= nbLines )
696bb02e0acSNicolas Bonnefon                 firstLine = nbLines - 1;
697bb02e0acSNicolas Bonnefon             if ( lastLine >= nbLines )
698bb02e0acSNicolas Bonnefon                 lastLine =  nbLines - 1;
699bb02e0acSNicolas Bonnefon         }
700bb02e0acSNicolas Bonnefon 
701bb02e0acSNicolas Bonnefon         // Lines to write
702bb02e0acSNicolas Bonnefon         const QStringList lines = logData->getExpandedLines( firstLine, lastLine - firstLine + 1 );
703bb02e0acSNicolas Bonnefon 
704bb02e0acSNicolas Bonnefon         // First draw the bullet left margin
705bb02e0acSNicolas Bonnefon         painter.setPen(palette.color(QPalette::Text));
706bb02e0acSNicolas Bonnefon         painter.drawLine( BULLET_AREA_WIDTH, 0,
707bb02e0acSNicolas Bonnefon                           BULLET_AREA_WIDTH, viewport()->height() );
708bb02e0acSNicolas Bonnefon         painter.fillRect( 0, 0,
709bb02e0acSNicolas Bonnefon                           BULLET_AREA_WIDTH, viewport()->height(),
710bb02e0acSNicolas Bonnefon                           Qt::darkGray );
711bb02e0acSNicolas Bonnefon 
712bb02e0acSNicolas Bonnefon         // Column at which the content should start (pixels)
713bb02e0acSNicolas Bonnefon         int contentStartPosX = BULLET_AREA_WIDTH + SEPARATOR_WIDTH;
714bb02e0acSNicolas Bonnefon 
715bb02e0acSNicolas Bonnefon         // This is also the bullet zone width, used for marking clicks
716bb02e0acSNicolas Bonnefon         bulletZoneWidthPx_ = contentStartPosX;
717bb02e0acSNicolas Bonnefon 
718bb02e0acSNicolas Bonnefon         // Draw the line numbers area
719bb02e0acSNicolas Bonnefon         int lineNumberAreaStartX = 0;
720bb02e0acSNicolas Bonnefon         if ( lineNumbersVisible_ ) {
721bb02e0acSNicolas Bonnefon             int lineNumberWidth = charWidth_ * nbDigitsInLineNumber_;
722bb02e0acSNicolas Bonnefon             int lineNumberAreaWidth =
723bb02e0acSNicolas Bonnefon                 2 * LINE_NUMBER_PADDING + lineNumberWidth;
724bb02e0acSNicolas Bonnefon             lineNumberAreaStartX = contentStartPosX;
725bb02e0acSNicolas Bonnefon 
726bb02e0acSNicolas Bonnefon             painter.setPen(palette.color(QPalette::Text));
727bb02e0acSNicolas Bonnefon             /* Not sure if it looks good...
728bb02e0acSNicolas Bonnefon             painter.drawLine( contentStartPosX + lineNumberAreaWidth,
729bb02e0acSNicolas Bonnefon                               0,
730bb02e0acSNicolas Bonnefon                               contentStartPosX + lineNumberAreaWidth,
731bb02e0acSNicolas Bonnefon                               viewport()->height() );
732bb02e0acSNicolas Bonnefon             */
733bb02e0acSNicolas Bonnefon             painter.fillRect( contentStartPosX, 0,
734bb02e0acSNicolas Bonnefon                               lineNumberAreaWidth, viewport()->height(),
735bb02e0acSNicolas Bonnefon                               Qt::lightGray );
736bb02e0acSNicolas Bonnefon 
737bb02e0acSNicolas Bonnefon             // Update for drawing the actual text
738bb02e0acSNicolas Bonnefon             contentStartPosX += lineNumberAreaWidth;
739bb02e0acSNicolas Bonnefon         }
740bb02e0acSNicolas Bonnefon         else {
741bb02e0acSNicolas Bonnefon             contentStartPosX += SEPARATOR_WIDTH;
742bb02e0acSNicolas Bonnefon         }
743bb02e0acSNicolas Bonnefon 
744bb02e0acSNicolas Bonnefon         // This is the total width of the 'margin' (including line number if any)
745bb02e0acSNicolas Bonnefon         // used for mouse calculation etc...
746bb02e0acSNicolas Bonnefon         leftMarginPx_ = contentStartPosX;
747bb02e0acSNicolas Bonnefon 
748bb02e0acSNicolas Bonnefon         // Then draw each line
749bb02e0acSNicolas Bonnefon         for (int i = firstLine; i <= lastLine; i++) {
750bb02e0acSNicolas Bonnefon             // Position in pixel of the base line of the line to print
751bb02e0acSNicolas Bonnefon             const int yPos = (i-firstLine) * fontHeight;
752bb02e0acSNicolas Bonnefon             const int xPos = contentStartPosX + CONTENT_MARGIN_WIDTH;
753bb02e0acSNicolas Bonnefon 
754bb02e0acSNicolas Bonnefon             // string to print, cut to fit the length and position of the view
755bb02e0acSNicolas Bonnefon             const QString line = lines[i - firstLine];
756bb02e0acSNicolas Bonnefon             const QString cutLine = line.mid( firstCol, nbCols );
757bb02e0acSNicolas Bonnefon 
758bb02e0acSNicolas Bonnefon             if ( selection_.isLineSelected( i ) ) {
759bb02e0acSNicolas Bonnefon                 // Reverse the selected line
760bb02e0acSNicolas Bonnefon                 foreColor = palette.color( QPalette::HighlightedText );
761bb02e0acSNicolas Bonnefon                 backColor = palette.color( QPalette::Highlight );
762bb02e0acSNicolas Bonnefon                 painter.setPen(palette.color(QPalette::Text));
763bb02e0acSNicolas Bonnefon             }
76411582726SNicolas Bonnefon             else if ( filterSet->matchLine( logData->getLineString( i ),
765bb02e0acSNicolas Bonnefon                         &foreColor, &backColor ) ) {
766bb02e0acSNicolas Bonnefon                 // Apply a filter to the line
767bb02e0acSNicolas Bonnefon             }
768bb02e0acSNicolas Bonnefon             else {
769bb02e0acSNicolas Bonnefon                 // Use the default colors
770bb02e0acSNicolas Bonnefon                 foreColor = palette.color( QPalette::Text );
771bb02e0acSNicolas Bonnefon                 backColor = palette.color( QPalette::Base );
772bb02e0acSNicolas Bonnefon             }
773bb02e0acSNicolas Bonnefon 
774bb02e0acSNicolas Bonnefon             // Is there something selected in the line?
775bb02e0acSNicolas Bonnefon             int sel_start, sel_end;
776bb02e0acSNicolas Bonnefon             bool isSelection =
777bb02e0acSNicolas Bonnefon                 selection_.getPortionForLine( i, &sel_start, &sel_end );
778bb02e0acSNicolas Bonnefon             // Has the line got elements to be highlighted
779bb02e0acSNicolas Bonnefon             QList<QuickFindMatch> qfMatchList;
780bb02e0acSNicolas Bonnefon             bool isMatch =
781bb02e0acSNicolas Bonnefon                 quickFindPattern_->matchLine( line, qfMatchList );
782bb02e0acSNicolas Bonnefon 
783bb02e0acSNicolas Bonnefon             if ( isSelection || isMatch ) {
784bb02e0acSNicolas Bonnefon                 // We use the LineDrawer and its chunks because the
785bb02e0acSNicolas Bonnefon                 // line has to be somehow highlighted
786bb02e0acSNicolas Bonnefon                 LineDrawer lineDrawer( backColor );
787bb02e0acSNicolas Bonnefon 
788bb02e0acSNicolas Bonnefon                 // First we create a list of chunks with the highlights
789bb02e0acSNicolas Bonnefon                 QList<LineChunk> chunkList;
790bb02e0acSNicolas Bonnefon                 int column = 0; // Current column in line space
791bb02e0acSNicolas Bonnefon                 foreach( const QuickFindMatch match, qfMatchList ) {
792bb02e0acSNicolas Bonnefon                     int start = match.startColumn() - firstCol;
793bb02e0acSNicolas Bonnefon                     int end = start + match.length();
794bb02e0acSNicolas Bonnefon                     // Ignore matches that are *completely* outside view area
795bb02e0acSNicolas Bonnefon                     if ( ( start < 0 && end < 0 ) || start >= nbCols )
796bb02e0acSNicolas Bonnefon                         continue;
797bb02e0acSNicolas Bonnefon                     if ( start > column )
798bb02e0acSNicolas Bonnefon                         chunkList << LineChunk( column, start - 1, LineChunk::Normal );
799bb02e0acSNicolas Bonnefon                     column = qMin( start + match.length() - 1, nbCols );
800bb02e0acSNicolas Bonnefon                     chunkList << LineChunk( qMax( start, 0 ), column,
801bb02e0acSNicolas Bonnefon                                             LineChunk::Highlighted );
802bb02e0acSNicolas Bonnefon                     column++;
803bb02e0acSNicolas Bonnefon                 }
804bb02e0acSNicolas Bonnefon                 if ( column <= cutLine.length() - 1 )
805bb02e0acSNicolas Bonnefon                     chunkList << LineChunk( column, cutLine.length() - 1, LineChunk::Normal );
806bb02e0acSNicolas Bonnefon 
807bb02e0acSNicolas Bonnefon                 // Then we add the selection if needed
808bb02e0acSNicolas Bonnefon                 QList<LineChunk> newChunkList;
809bb02e0acSNicolas Bonnefon                 if ( isSelection ) {
810bb02e0acSNicolas Bonnefon                     sel_start -= firstCol; // coord in line space
811bb02e0acSNicolas Bonnefon                     sel_end   -= firstCol;
812bb02e0acSNicolas Bonnefon 
813bb02e0acSNicolas Bonnefon                     foreach ( const LineChunk chunk, chunkList ) {
814bb02e0acSNicolas Bonnefon                         newChunkList << chunk.select( sel_start, sel_end );
815bb02e0acSNicolas Bonnefon                     }
816bb02e0acSNicolas Bonnefon                 }
817bb02e0acSNicolas Bonnefon                 else
818bb02e0acSNicolas Bonnefon                     newChunkList = chunkList;
819bb02e0acSNicolas Bonnefon 
820bb02e0acSNicolas Bonnefon                 foreach ( const LineChunk chunk, newChunkList ) {
821bb02e0acSNicolas Bonnefon                     // Select the colours
822bb02e0acSNicolas Bonnefon                     QColor fore;
823bb02e0acSNicolas Bonnefon                     QColor back;
824bb02e0acSNicolas Bonnefon                     switch ( chunk.type() ) {
825bb02e0acSNicolas Bonnefon                         case LineChunk::Normal:
826bb02e0acSNicolas Bonnefon                             fore = foreColor;
827bb02e0acSNicolas Bonnefon                             back = backColor;
828bb02e0acSNicolas Bonnefon                             break;
829bb02e0acSNicolas Bonnefon                         case LineChunk::Highlighted:
830bb02e0acSNicolas Bonnefon                             fore = QColor( "black" );
831bb02e0acSNicolas Bonnefon                             back = QColor( "yellow" );
832bb02e0acSNicolas Bonnefon                             // fore = highlightForeColor;
833bb02e0acSNicolas Bonnefon                             // back = highlightBackColor;
834bb02e0acSNicolas Bonnefon                             break;
835bb02e0acSNicolas Bonnefon                         case LineChunk::Selected:
836bb02e0acSNicolas Bonnefon                             fore = palette.color( QPalette::HighlightedText ),
837bb02e0acSNicolas Bonnefon                             back = palette.color( QPalette::Highlight );
838bb02e0acSNicolas Bonnefon                             break;
839bb02e0acSNicolas Bonnefon                     }
840bb02e0acSNicolas Bonnefon                     lineDrawer.addChunk ( chunk, fore, back );
841bb02e0acSNicolas Bonnefon                 }
842bb02e0acSNicolas Bonnefon 
843bb02e0acSNicolas Bonnefon                 lineDrawer.draw( painter, xPos, yPos,
844bb02e0acSNicolas Bonnefon                                  viewport()->width(), cutLine,
845bb02e0acSNicolas Bonnefon                                  CONTENT_MARGIN_WIDTH );
846bb02e0acSNicolas Bonnefon             }
847bb02e0acSNicolas Bonnefon             else {
848bb02e0acSNicolas Bonnefon                 // Nothing to be highlighted, we print the whole line!
849bb02e0acSNicolas Bonnefon                 painter.fillRect( xPos - CONTENT_MARGIN_WIDTH, yPos,
850bb02e0acSNicolas Bonnefon                         viewport()->width(), fontHeight, backColor );
851bb02e0acSNicolas Bonnefon                 // (the rectangle is extended on the left to cover the small
852bb02e0acSNicolas Bonnefon                 // margin, it looks better (LineDrawer does the same) )
853bb02e0acSNicolas Bonnefon                 painter.setPen( foreColor );
854bb02e0acSNicolas Bonnefon                 painter.drawText( xPos, yPos + fontAscent, cutLine );
855bb02e0acSNicolas Bonnefon             }
856bb02e0acSNicolas Bonnefon 
857bb02e0acSNicolas Bonnefon             // Then draw the bullet
858bb02e0acSNicolas Bonnefon             painter.setPen( palette.color( QPalette::Text ) );
859bb02e0acSNicolas Bonnefon             const int circleSize = 3;
860bb02e0acSNicolas Bonnefon             const int arrowHeight = 4;
861bb02e0acSNicolas Bonnefon             const int middleXLine = BULLET_AREA_WIDTH / 2;
862bb02e0acSNicolas Bonnefon             const int middleYLine = yPos + (fontHeight / 2);
863bb02e0acSNicolas Bonnefon 
864bb02e0acSNicolas Bonnefon             const LineType line_type = lineType( i );
865bb02e0acSNicolas Bonnefon             if ( line_type == Marked ) {
866bb02e0acSNicolas Bonnefon                 // A pretty arrow if the line is marked
867bb02e0acSNicolas Bonnefon                 const QPoint points[7] = {
868bb02e0acSNicolas Bonnefon                     QPoint(1, middleYLine - 2),
869bb02e0acSNicolas Bonnefon                     QPoint(middleXLine, middleYLine - 2),
870bb02e0acSNicolas Bonnefon                     QPoint(middleXLine, middleYLine - arrowHeight),
871bb02e0acSNicolas Bonnefon                     QPoint(BULLET_AREA_WIDTH - 2, middleYLine),
872bb02e0acSNicolas Bonnefon                     QPoint(middleXLine, middleYLine + arrowHeight),
873bb02e0acSNicolas Bonnefon                     QPoint(middleXLine, middleYLine + 2),
874bb02e0acSNicolas Bonnefon                     QPoint(1, middleYLine + 2 ),
875bb02e0acSNicolas Bonnefon                 };
876bb02e0acSNicolas Bonnefon 
877bb02e0acSNicolas Bonnefon                 painter.setBrush( markBrush );
878bb02e0acSNicolas Bonnefon                 painter.drawPolygon( points, 7 );
879bb02e0acSNicolas Bonnefon             }
880bb02e0acSNicolas Bonnefon             else {
881bb02e0acSNicolas Bonnefon                 if ( lineType( i ) == Match )
882bb02e0acSNicolas Bonnefon                     painter.setBrush( matchBulletBrush );
883bb02e0acSNicolas Bonnefon                 else
884bb02e0acSNicolas Bonnefon                     painter.setBrush( normalBulletBrush );
885bb02e0acSNicolas Bonnefon                 painter.drawEllipse( middleXLine - circleSize,
886bb02e0acSNicolas Bonnefon                         middleYLine - circleSize,
887bb02e0acSNicolas Bonnefon                         circleSize * 2, circleSize * 2 );
888bb02e0acSNicolas Bonnefon             }
889bb02e0acSNicolas Bonnefon 
890bb02e0acSNicolas Bonnefon             // Draw the line number
891bb02e0acSNicolas Bonnefon             if ( lineNumbersVisible_ ) {
892bb02e0acSNicolas Bonnefon                 static const QString lineNumberFormat( "%1" );
893bb02e0acSNicolas Bonnefon                 const QString& lineNumberStr =
894bb02e0acSNicolas Bonnefon                     lineNumberFormat.arg( displayLineNumber( i ),
895bb02e0acSNicolas Bonnefon                                           nbDigitsInLineNumber_ );
896bb02e0acSNicolas Bonnefon                 painter.setPen( palette.color( QPalette::Text ) );
897bb02e0acSNicolas Bonnefon                 painter.drawText( lineNumberAreaStartX + LINE_NUMBER_PADDING,
898bb02e0acSNicolas Bonnefon                                   yPos + fontAscent, lineNumberStr );
899bb02e0acSNicolas Bonnefon             }
900bb02e0acSNicolas Bonnefon 
901bb02e0acSNicolas Bonnefon         } // For each line
902bb02e0acSNicolas Bonnefon     }
903bb02e0acSNicolas Bonnefon     LOG(logDEBUG4) << "End of repaint";
904bb02e0acSNicolas Bonnefon }
905bb02e0acSNicolas Bonnefon 
906bb02e0acSNicolas Bonnefon // These two functions are virtual and this implementation is clearly
907bb02e0acSNicolas Bonnefon // only valid for a non-filtered display.
908bb02e0acSNicolas Bonnefon // We count on the 'filtered' derived classes to override them.
909bb02e0acSNicolas Bonnefon qint64 AbstractLogView::displayLineNumber( int lineNumber ) const
910bb02e0acSNicolas Bonnefon {
911bb02e0acSNicolas Bonnefon     return lineNumber + 1; // show a 1-based index
912bb02e0acSNicolas Bonnefon }
913bb02e0acSNicolas Bonnefon 
914bb02e0acSNicolas Bonnefon qint64 AbstractLogView::maxDisplayLineNumber() const
915bb02e0acSNicolas Bonnefon {
916bb02e0acSNicolas Bonnefon     return logData->getNbLine();
917bb02e0acSNicolas Bonnefon }
918bb02e0acSNicolas Bonnefon 
919bb02e0acSNicolas Bonnefon void AbstractLogView::setOverview( Overview* overview,
920bb02e0acSNicolas Bonnefon        OverviewWidget* overview_widget )
921bb02e0acSNicolas Bonnefon {
922bb02e0acSNicolas Bonnefon     overview_ = overview;
923bb02e0acSNicolas Bonnefon     overviewWidget_ = overview_widget;
924bb02e0acSNicolas Bonnefon 
925bb02e0acSNicolas Bonnefon     if ( overviewWidget_ ) {
926bb02e0acSNicolas Bonnefon         connect( overviewWidget_, SIGNAL( lineClicked ( int ) ),
927bb02e0acSNicolas Bonnefon                 this, SIGNAL( followDisabled() ) );
928bb02e0acSNicolas Bonnefon         connect( overviewWidget_, SIGNAL( lineClicked ( int ) ),
929bb02e0acSNicolas Bonnefon                 this, SLOT( jumpToLine( int ) ) );
930bb02e0acSNicolas Bonnefon     }
931bb02e0acSNicolas Bonnefon     refreshOverview();
932bb02e0acSNicolas Bonnefon }
933bb02e0acSNicolas Bonnefon 
934bb02e0acSNicolas Bonnefon void AbstractLogView::searchUsingFunction(
935bb02e0acSNicolas Bonnefon         qint64 (QuickFind::*search_function)() )
936bb02e0acSNicolas Bonnefon {
937bb02e0acSNicolas Bonnefon     emit followDisabled();
938bb02e0acSNicolas Bonnefon 
939bb02e0acSNicolas Bonnefon     int line = (quickFind_.*search_function)();
940bb02e0acSNicolas Bonnefon     if ( line >= 0 ) {
941bb02e0acSNicolas Bonnefon         LOG(logDEBUG) << "search " << line;
942bb02e0acSNicolas Bonnefon         displayLine( line );
943bb02e0acSNicolas Bonnefon         emit updateLineNumber( line );
944bb02e0acSNicolas Bonnefon     }
945bb02e0acSNicolas Bonnefon }
946bb02e0acSNicolas Bonnefon 
947bb02e0acSNicolas Bonnefon void AbstractLogView::searchForward()
948bb02e0acSNicolas Bonnefon {
949bb02e0acSNicolas Bonnefon     searchUsingFunction( &QuickFind::searchForward );
950bb02e0acSNicolas Bonnefon }
951bb02e0acSNicolas Bonnefon 
952bb02e0acSNicolas Bonnefon void AbstractLogView::searchBackward()
953bb02e0acSNicolas Bonnefon {
954bb02e0acSNicolas Bonnefon     searchUsingFunction( &QuickFind::searchBackward );
955bb02e0acSNicolas Bonnefon }
956bb02e0acSNicolas Bonnefon 
957bb02e0acSNicolas Bonnefon void AbstractLogView::incrementallySearchForward()
958bb02e0acSNicolas Bonnefon {
959bb02e0acSNicolas Bonnefon     searchUsingFunction( &QuickFind::incrementallySearchForward );
960bb02e0acSNicolas Bonnefon }
961bb02e0acSNicolas Bonnefon 
962bb02e0acSNicolas Bonnefon void AbstractLogView::incrementallySearchBackward()
963bb02e0acSNicolas Bonnefon {
964bb02e0acSNicolas Bonnefon     searchUsingFunction( &QuickFind::incrementallySearchBackward );
965bb02e0acSNicolas Bonnefon }
966bb02e0acSNicolas Bonnefon 
967bb02e0acSNicolas Bonnefon void AbstractLogView::incrementalSearchAbort()
968bb02e0acSNicolas Bonnefon {
969bb02e0acSNicolas Bonnefon     quickFind_.incrementalSearchAbort();
970bb02e0acSNicolas Bonnefon     emit changeQuickFind(
971bb02e0acSNicolas Bonnefon             "",
972bb02e0acSNicolas Bonnefon             QuickFindMux::Forward );
973bb02e0acSNicolas Bonnefon }
974bb02e0acSNicolas Bonnefon 
975bb02e0acSNicolas Bonnefon void AbstractLogView::incrementalSearchStop()
976bb02e0acSNicolas Bonnefon {
977bb02e0acSNicolas Bonnefon     quickFind_.incrementalSearchStop();
978bb02e0acSNicolas Bonnefon }
979bb02e0acSNicolas Bonnefon 
980bb02e0acSNicolas Bonnefon void AbstractLogView::followSet( bool checked )
981bb02e0acSNicolas Bonnefon {
982bb02e0acSNicolas Bonnefon     followMode_ = checked;
983bb02e0acSNicolas Bonnefon     if ( checked )
984bb02e0acSNicolas Bonnefon         jumpToBottom();
985bb02e0acSNicolas Bonnefon }
986bb02e0acSNicolas Bonnefon 
987bb02e0acSNicolas Bonnefon void AbstractLogView::refreshOverview()
988bb02e0acSNicolas Bonnefon {
989bb02e0acSNicolas Bonnefon     assert( overviewWidget_ );
990bb02e0acSNicolas Bonnefon 
991bb02e0acSNicolas Bonnefon     // Create space for the Overview if needed
992bb02e0acSNicolas Bonnefon     if ( ( getOverview() != NULL ) && getOverview()->isVisible() ) {
993bb02e0acSNicolas Bonnefon         setViewportMargins( 0, 0, OVERVIEW_WIDTH, 0 );
994bb02e0acSNicolas Bonnefon         overviewWidget_->show();
995bb02e0acSNicolas Bonnefon     }
996bb02e0acSNicolas Bonnefon     else {
997bb02e0acSNicolas Bonnefon         setViewportMargins( 0, 0, 0, 0 );
998bb02e0acSNicolas Bonnefon         overviewWidget_->hide();
999bb02e0acSNicolas Bonnefon     }
1000bb02e0acSNicolas Bonnefon }
1001bb02e0acSNicolas Bonnefon 
1002bb02e0acSNicolas Bonnefon // Reset the QuickFind when the pattern is changed.
1003bb02e0acSNicolas Bonnefon void AbstractLogView::handlePatternUpdated()
1004bb02e0acSNicolas Bonnefon {
1005bb02e0acSNicolas Bonnefon     LOG(logDEBUG) << "AbstractLogView::handlePatternUpdated()";
1006bb02e0acSNicolas Bonnefon 
1007bb02e0acSNicolas Bonnefon     quickFind_.resetLimits();
1008bb02e0acSNicolas Bonnefon     update();
1009bb02e0acSNicolas Bonnefon }
1010bb02e0acSNicolas Bonnefon 
1011bb02e0acSNicolas Bonnefon // OR the current with the current search expression
1012bb02e0acSNicolas Bonnefon void AbstractLogView::addToSearch()
1013bb02e0acSNicolas Bonnefon {
1014bb02e0acSNicolas Bonnefon     if ( selection_.isPortion() ) {
1015bb02e0acSNicolas Bonnefon         LOG(logDEBUG) << "AbstractLogView::addToSearch()";
1016bb02e0acSNicolas Bonnefon         emit addToSearch( selection_.getSelectedText( logData ) );
1017bb02e0acSNicolas Bonnefon     }
1018bb02e0acSNicolas Bonnefon     else {
1019bb02e0acSNicolas Bonnefon         LOG(logERROR) << "AbstractLogView::addToSearch called for a wrong type of selection";
1020bb02e0acSNicolas Bonnefon     }
1021bb02e0acSNicolas Bonnefon }
1022bb02e0acSNicolas Bonnefon 
1023bb02e0acSNicolas Bonnefon // Find next occurence of the selected text (*)
1024bb02e0acSNicolas Bonnefon void AbstractLogView::findNextSelected()
1025bb02e0acSNicolas Bonnefon {
1026bb02e0acSNicolas Bonnefon     // Use the selected 'word' and search forward
1027bb02e0acSNicolas Bonnefon     if ( selection_.isPortion() ) {
1028bb02e0acSNicolas Bonnefon         emit changeQuickFind(
1029bb02e0acSNicolas Bonnefon                 selection_.getSelectedText( logData ),
1030bb02e0acSNicolas Bonnefon                 QuickFindMux::Forward );
1031bb02e0acSNicolas Bonnefon         emit searchNext();
1032bb02e0acSNicolas Bonnefon     }
1033bb02e0acSNicolas Bonnefon }
1034bb02e0acSNicolas Bonnefon 
1035bb02e0acSNicolas Bonnefon // Find next previous of the selected text (#)
1036bb02e0acSNicolas Bonnefon void AbstractLogView::findPreviousSelected()
1037bb02e0acSNicolas Bonnefon {
1038bb02e0acSNicolas Bonnefon     if ( selection_.isPortion() ) {
1039bb02e0acSNicolas Bonnefon         emit changeQuickFind(
1040bb02e0acSNicolas Bonnefon                 selection_.getSelectedText( logData ),
1041bb02e0acSNicolas Bonnefon                 QuickFindMux::Backward );
1042bb02e0acSNicolas Bonnefon         emit searchNext();
1043bb02e0acSNicolas Bonnefon     }
1044bb02e0acSNicolas Bonnefon }
1045bb02e0acSNicolas Bonnefon 
1046bb02e0acSNicolas Bonnefon // Copy the selection to the clipboard
1047bb02e0acSNicolas Bonnefon void AbstractLogView::copy()
1048bb02e0acSNicolas Bonnefon {
1049bb02e0acSNicolas Bonnefon     static QClipboard* clipboard = QApplication::clipboard();
1050bb02e0acSNicolas Bonnefon 
1051bb02e0acSNicolas Bonnefon     clipboard->setText( selection_.getSelectedText( logData ) );
1052bb02e0acSNicolas Bonnefon }
1053bb02e0acSNicolas Bonnefon 
1054bb02e0acSNicolas Bonnefon //
1055bb02e0acSNicolas Bonnefon // Public functions
1056bb02e0acSNicolas Bonnefon //
1057bb02e0acSNicolas Bonnefon 
1058bb02e0acSNicolas Bonnefon void AbstractLogView::updateData()
1059bb02e0acSNicolas Bonnefon {
1060bb02e0acSNicolas Bonnefon     LOG(logDEBUG) << "AbstractLogView::updateData";
1061bb02e0acSNicolas Bonnefon 
1062bb02e0acSNicolas Bonnefon     // Check the top Line is within range
1063bb02e0acSNicolas Bonnefon     if ( firstLine >= logData->getNbLine() ) {
1064bb02e0acSNicolas Bonnefon         firstLine = 0;
1065bb02e0acSNicolas Bonnefon         firstCol = 0;
1066bb02e0acSNicolas Bonnefon         verticalScrollBar()->setValue( 0 );
1067bb02e0acSNicolas Bonnefon         horizontalScrollBar()->setValue( 0 );
1068bb02e0acSNicolas Bonnefon     }
1069bb02e0acSNicolas Bonnefon 
1070bb02e0acSNicolas Bonnefon     // Crop selection if it become out of range
1071bb02e0acSNicolas Bonnefon     selection_.crop( logData->getNbLine() - 1 );
1072bb02e0acSNicolas Bonnefon 
1073bb02e0acSNicolas Bonnefon     // Adapt the scroll bars to the new content
1074bb02e0acSNicolas Bonnefon     verticalScrollBar()->setRange( 0, logData->getNbLine()-1 );
1075bb02e0acSNicolas Bonnefon     const int hScrollMaxValue = ( logData->getMaxLength() - getNbVisibleCols() + 1 ) > 0 ?
1076bb02e0acSNicolas Bonnefon         ( logData->getMaxLength() - getNbVisibleCols() + 1 ) : 0;
1077bb02e0acSNicolas Bonnefon     horizontalScrollBar()->setRange( 0, hScrollMaxValue );
1078bb02e0acSNicolas Bonnefon 
1079bb02e0acSNicolas Bonnefon     lastLine = qMin( logData->getNbLine(), firstLine + getNbVisibleLines() );
1080bb02e0acSNicolas Bonnefon 
1081bb02e0acSNicolas Bonnefon     // Reset the QuickFind in case we have new stuff to search into
1082bb02e0acSNicolas Bonnefon     quickFind_.resetLimits();
1083bb02e0acSNicolas Bonnefon 
1084bb02e0acSNicolas Bonnefon     if ( followMode_ )
1085bb02e0acSNicolas Bonnefon         jumpToBottom();
1086bb02e0acSNicolas Bonnefon 
1087bb02e0acSNicolas Bonnefon     // Update the overview if we have one
1088bb02e0acSNicolas Bonnefon     if ( overview_ != NULL )
1089bb02e0acSNicolas Bonnefon         overview_->updateCurrentPosition( firstLine, lastLine );
1090bb02e0acSNicolas Bonnefon 
1091bb02e0acSNicolas Bonnefon     // Update the length of line numbers
1092bb02e0acSNicolas Bonnefon     nbDigitsInLineNumber_ = countDigits( maxDisplayLineNumber() );
1093bb02e0acSNicolas Bonnefon 
1094bb02e0acSNicolas Bonnefon     // Repaint!
1095bb02e0acSNicolas Bonnefon     update();
1096bb02e0acSNicolas Bonnefon }
1097bb02e0acSNicolas Bonnefon 
1098bb02e0acSNicolas Bonnefon void AbstractLogView::updateDisplaySize()
1099bb02e0acSNicolas Bonnefon {
1100bb02e0acSNicolas Bonnefon     // Font is assumed to be mono-space (is restricted by options dialog)
1101bb02e0acSNicolas Bonnefon     QFontMetrics fm = fontMetrics();
1102bb02e0acSNicolas Bonnefon     charHeight_ = fm.height();
1103bb02e0acSNicolas Bonnefon     // For some reason on Qt 4.8.2 for Win, maxWidth() is wrong but the
1104bb02e0acSNicolas Bonnefon     // following give the right result, not sure why:
1105bb02e0acSNicolas Bonnefon     charWidth_ = fm.width( QChar('a') );
1106bb02e0acSNicolas Bonnefon 
1107bb02e0acSNicolas Bonnefon     // Calculate the index of the last line shown
1108bb02e0acSNicolas Bonnefon     lastLine = qMin( logData->getNbLine(), firstLine + getNbVisibleLines() );
1109bb02e0acSNicolas Bonnefon 
1110bb02e0acSNicolas Bonnefon     // Update the scroll bars
1111bb02e0acSNicolas Bonnefon     verticalScrollBar()->setPageStep( getNbVisibleLines() );
1112bb02e0acSNicolas Bonnefon 
1113bb02e0acSNicolas Bonnefon     const int hScrollMaxValue = ( logData->getMaxLength() - getNbVisibleCols() + 1 ) > 0 ?
1114bb02e0acSNicolas Bonnefon         ( logData->getMaxLength() - getNbVisibleCols() + 1 ) : 0;
1115bb02e0acSNicolas Bonnefon     horizontalScrollBar()->setRange( 0, hScrollMaxValue );
1116bb02e0acSNicolas Bonnefon 
1117bb02e0acSNicolas Bonnefon     LOG(logDEBUG) << "viewport.width()=" << viewport()->width();
1118bb02e0acSNicolas Bonnefon     LOG(logDEBUG) << "viewport.height()=" << viewport()->height();
1119bb02e0acSNicolas Bonnefon     LOG(logDEBUG) << "width()=" << width();
1120bb02e0acSNicolas Bonnefon     LOG(logDEBUG) << "height()=" << height();
1121bb02e0acSNicolas Bonnefon 
1122bb02e0acSNicolas Bonnefon     if ( overviewWidget_ )
1123bb02e0acSNicolas Bonnefon         overviewWidget_->setGeometry( viewport()->width() + 2, 1,
1124bb02e0acSNicolas Bonnefon                 OVERVIEW_WIDTH - 1, viewport()->height() );
1125bb02e0acSNicolas Bonnefon }
1126bb02e0acSNicolas Bonnefon 
1127bb02e0acSNicolas Bonnefon int AbstractLogView::getTopLine() const
1128bb02e0acSNicolas Bonnefon {
1129bb02e0acSNicolas Bonnefon     return firstLine;
1130bb02e0acSNicolas Bonnefon }
1131bb02e0acSNicolas Bonnefon 
1132bb02e0acSNicolas Bonnefon QString AbstractLogView::getSelection() const
1133bb02e0acSNicolas Bonnefon {
1134bb02e0acSNicolas Bonnefon     return selection_.getSelectedText( logData );
1135bb02e0acSNicolas Bonnefon }
1136bb02e0acSNicolas Bonnefon 
1137bb02e0acSNicolas Bonnefon void AbstractLogView::selectAll()
1138bb02e0acSNicolas Bonnefon {
1139bb02e0acSNicolas Bonnefon     selection_.selectRange( 0, logData->getNbLine() - 1 );
1140bb02e0acSNicolas Bonnefon     update();
1141bb02e0acSNicolas Bonnefon }
1142bb02e0acSNicolas Bonnefon 
1143bb02e0acSNicolas Bonnefon void AbstractLogView::selectAndDisplayLine( int line )
1144bb02e0acSNicolas Bonnefon {
1145bb02e0acSNicolas Bonnefon     emit followDisabled();
1146bb02e0acSNicolas Bonnefon     selection_.selectLine( line );
1147bb02e0acSNicolas Bonnefon     displayLine( line );
1148bb02e0acSNicolas Bonnefon     emit updateLineNumber( line );
1149bb02e0acSNicolas Bonnefon }
1150bb02e0acSNicolas Bonnefon 
1151bb02e0acSNicolas Bonnefon // The difference between this function and displayLine() is quite
1152bb02e0acSNicolas Bonnefon // subtle: this one always jump, even if the line passed is visible.
1153bb02e0acSNicolas Bonnefon void AbstractLogView::jumpToLine( int line )
1154bb02e0acSNicolas Bonnefon {
1155bb02e0acSNicolas Bonnefon     // Put the selected line in the middle if possible
1156bb02e0acSNicolas Bonnefon     int newTopLine = line - ( getNbVisibleLines() / 2 );
1157bb02e0acSNicolas Bonnefon     if ( newTopLine < 0 )
1158bb02e0acSNicolas Bonnefon         newTopLine = 0;
1159bb02e0acSNicolas Bonnefon 
1160bb02e0acSNicolas Bonnefon     // This will also trigger a scrollContents event
1161bb02e0acSNicolas Bonnefon     verticalScrollBar()->setValue( newTopLine );
1162bb02e0acSNicolas Bonnefon }
1163bb02e0acSNicolas Bonnefon 
1164bb02e0acSNicolas Bonnefon void AbstractLogView::setLineNumbersVisible( bool lineNumbersVisible )
1165bb02e0acSNicolas Bonnefon {
1166bb02e0acSNicolas Bonnefon     lineNumbersVisible_ = lineNumbersVisible;
1167bb02e0acSNicolas Bonnefon }
1168bb02e0acSNicolas Bonnefon 
1169bb02e0acSNicolas Bonnefon //
1170bb02e0acSNicolas Bonnefon // Private functions
1171bb02e0acSNicolas Bonnefon //
1172bb02e0acSNicolas Bonnefon 
1173bb02e0acSNicolas Bonnefon // Returns the number of lines visible in the viewport
1174bb02e0acSNicolas Bonnefon int AbstractLogView::getNbVisibleLines() const
1175bb02e0acSNicolas Bonnefon {
1176bb02e0acSNicolas Bonnefon     return viewport()->height() / charHeight_ + 1;
1177bb02e0acSNicolas Bonnefon }
1178bb02e0acSNicolas Bonnefon 
1179bb02e0acSNicolas Bonnefon // Returns the number of columns visible in the viewport
1180bb02e0acSNicolas Bonnefon int AbstractLogView::getNbVisibleCols() const
1181bb02e0acSNicolas Bonnefon {
1182bb02e0acSNicolas Bonnefon     return ( viewport()->width() - leftMarginPx_ ) / charWidth_ + 1;
1183bb02e0acSNicolas Bonnefon }
1184bb02e0acSNicolas Bonnefon 
1185bb02e0acSNicolas Bonnefon // Converts the mouse x, y coordinates to the line number in the file
1186bb02e0acSNicolas Bonnefon int AbstractLogView::convertCoordToLine(int yPos) const
1187bb02e0acSNicolas Bonnefon {
1188bb02e0acSNicolas Bonnefon     int line = firstLine + yPos / charHeight_;
1189bb02e0acSNicolas Bonnefon 
1190bb02e0acSNicolas Bonnefon     return line;
1191bb02e0acSNicolas Bonnefon }
1192bb02e0acSNicolas Bonnefon 
1193bb02e0acSNicolas Bonnefon // Converts the mouse x, y coordinates to the char coordinates (in the file)
1194bb02e0acSNicolas Bonnefon // This function ensure the pos exists in the file.
1195bb02e0acSNicolas Bonnefon QPoint AbstractLogView::convertCoordToFilePos( const QPoint& pos ) const
1196bb02e0acSNicolas Bonnefon {
1197bb02e0acSNicolas Bonnefon     int line = firstLine + pos.y() / charHeight_;
1198bb02e0acSNicolas Bonnefon     if ( line >= logData->getNbLine() )
1199bb02e0acSNicolas Bonnefon         line = logData->getNbLine() - 1;
1200bb02e0acSNicolas Bonnefon     if ( line < 0 )
1201bb02e0acSNicolas Bonnefon         line = 0;
1202bb02e0acSNicolas Bonnefon 
1203bb02e0acSNicolas Bonnefon     // Determine column in screen space and convert it to file space
1204bb02e0acSNicolas Bonnefon     int column = firstCol + ( pos.x() - leftMarginPx_ ) / charWidth_;
1205bb02e0acSNicolas Bonnefon 
1206bb02e0acSNicolas Bonnefon     QString this_line = logData->getExpandedLineString( line );
1207bb02e0acSNicolas Bonnefon     const int length = this_line.length();
1208bb02e0acSNicolas Bonnefon 
1209bb02e0acSNicolas Bonnefon     if ( column >= length )
1210bb02e0acSNicolas Bonnefon         column = length - 1;
1211bb02e0acSNicolas Bonnefon     if ( column < 0 )
1212bb02e0acSNicolas Bonnefon         column = 0;
1213bb02e0acSNicolas Bonnefon 
1214bb02e0acSNicolas Bonnefon     LOG(logDEBUG4) << "AbstractLogView::convertCoordToFilePos col="
1215bb02e0acSNicolas Bonnefon         << column << " line=" << line;
1216bb02e0acSNicolas Bonnefon     QPoint point( column, line );
1217bb02e0acSNicolas Bonnefon 
1218bb02e0acSNicolas Bonnefon     return point;
1219bb02e0acSNicolas Bonnefon }
1220bb02e0acSNicolas Bonnefon 
1221bb02e0acSNicolas Bonnefon // Makes the widget adjust itself to display the passed line.
1222bb02e0acSNicolas Bonnefon // Doing so, it will throw itself a scrollContents event.
1223bb02e0acSNicolas Bonnefon void AbstractLogView::displayLine( int line )
1224bb02e0acSNicolas Bonnefon {
1225bb02e0acSNicolas Bonnefon     // If the line is already the screen
1226bb02e0acSNicolas Bonnefon     if ( ( line >= firstLine ) &&
1227bb02e0acSNicolas Bonnefon          ( line < ( firstLine + getNbVisibleLines() ) ) ) {
1228bb02e0acSNicolas Bonnefon         // ... don't scroll and just repaint
1229bb02e0acSNicolas Bonnefon         update();
1230bb02e0acSNicolas Bonnefon     } else {
1231bb02e0acSNicolas Bonnefon         jumpToLine( line );
1232bb02e0acSNicolas Bonnefon     }
1233bb02e0acSNicolas Bonnefon }
1234bb02e0acSNicolas Bonnefon 
1235bb02e0acSNicolas Bonnefon // Move the selection up and down by the passed number of lines
1236bb02e0acSNicolas Bonnefon void AbstractLogView::moveSelection( int delta )
1237bb02e0acSNicolas Bonnefon {
1238bb02e0acSNicolas Bonnefon     LOG(logDEBUG) << "AbstractLogView::moveSelection delta=" << delta;
1239bb02e0acSNicolas Bonnefon 
1240bb02e0acSNicolas Bonnefon     QList<int> selection = selection_.getLines();
1241bb02e0acSNicolas Bonnefon     int new_line;
1242bb02e0acSNicolas Bonnefon 
1243bb02e0acSNicolas Bonnefon     // If nothing is selected, do as if line -1 was.
1244bb02e0acSNicolas Bonnefon     if ( selection.isEmpty() )
1245bb02e0acSNicolas Bonnefon         selection.append( -1 );
1246bb02e0acSNicolas Bonnefon 
1247bb02e0acSNicolas Bonnefon     if ( delta < 0 )
1248bb02e0acSNicolas Bonnefon         new_line = selection.first() + delta;
1249bb02e0acSNicolas Bonnefon     else
1250bb02e0acSNicolas Bonnefon         new_line = selection.last() + delta;
1251bb02e0acSNicolas Bonnefon 
1252bb02e0acSNicolas Bonnefon     if ( new_line < 0 )
1253bb02e0acSNicolas Bonnefon         new_line = 0;
1254bb02e0acSNicolas Bonnefon     else if ( new_line >= logData->getNbLine() )
1255bb02e0acSNicolas Bonnefon         new_line = logData->getNbLine() - 1;
1256bb02e0acSNicolas Bonnefon 
1257bb02e0acSNicolas Bonnefon     // Select and display the new line
1258bb02e0acSNicolas Bonnefon     selection_.selectLine( new_line );
1259bb02e0acSNicolas Bonnefon     displayLine( new_line );
1260bb02e0acSNicolas Bonnefon     emit updateLineNumber( new_line );
1261bb02e0acSNicolas Bonnefon }
1262bb02e0acSNicolas Bonnefon 
1263bb02e0acSNicolas Bonnefon // Make the start of the lines visible
1264bb02e0acSNicolas Bonnefon void AbstractLogView::jumpToStartOfLine()
1265bb02e0acSNicolas Bonnefon {
1266bb02e0acSNicolas Bonnefon     horizontalScrollBar()->setValue( 0 );
1267bb02e0acSNicolas Bonnefon }
1268bb02e0acSNicolas Bonnefon 
1269bb02e0acSNicolas Bonnefon // Make the end of the lines in the selection visible
1270bb02e0acSNicolas Bonnefon void AbstractLogView::jumpToEndOfLine()
1271bb02e0acSNicolas Bonnefon {
1272bb02e0acSNicolas Bonnefon     QList<int> selection = selection_.getLines();
1273bb02e0acSNicolas Bonnefon 
1274bb02e0acSNicolas Bonnefon     // Search the longest line in the selection
1275bb02e0acSNicolas Bonnefon     int max_length = 0;
1276bb02e0acSNicolas Bonnefon     foreach ( int line, selection ) {
1277bb02e0acSNicolas Bonnefon         int length = logData->getLineLength( line );
1278bb02e0acSNicolas Bonnefon         if ( length > max_length )
1279bb02e0acSNicolas Bonnefon             max_length = length;
1280bb02e0acSNicolas Bonnefon     }
1281bb02e0acSNicolas Bonnefon 
1282bb02e0acSNicolas Bonnefon     horizontalScrollBar()->setValue( max_length - getNbVisibleCols() );
1283bb02e0acSNicolas Bonnefon }
1284bb02e0acSNicolas Bonnefon 
1285bb02e0acSNicolas Bonnefon // Make the end of the lines on the screen visible
1286bb02e0acSNicolas Bonnefon void AbstractLogView::jumpToRightOfScreen()
1287bb02e0acSNicolas Bonnefon {
1288bb02e0acSNicolas Bonnefon     QList<int> selection = selection_.getLines();
1289bb02e0acSNicolas Bonnefon 
1290bb02e0acSNicolas Bonnefon     // Search the longest line on screen
1291bb02e0acSNicolas Bonnefon     int max_length = 0;
1292bb02e0acSNicolas Bonnefon     for ( int i = firstLine; i <= ( firstLine + getNbVisibleLines() ); i++ ) {
1293bb02e0acSNicolas Bonnefon         int length = logData->getLineLength( i );
1294bb02e0acSNicolas Bonnefon         if ( length > max_length )
1295bb02e0acSNicolas Bonnefon             max_length = length;
1296bb02e0acSNicolas Bonnefon     }
1297bb02e0acSNicolas Bonnefon 
1298bb02e0acSNicolas Bonnefon     horizontalScrollBar()->setValue( max_length - getNbVisibleCols() );
1299bb02e0acSNicolas Bonnefon }
1300bb02e0acSNicolas Bonnefon 
1301bb02e0acSNicolas Bonnefon // Jump to the first line
1302bb02e0acSNicolas Bonnefon void AbstractLogView::jumpToTop()
1303bb02e0acSNicolas Bonnefon {
1304bb02e0acSNicolas Bonnefon     // This will also trigger a scrollContents event
1305bb02e0acSNicolas Bonnefon     verticalScrollBar()->setValue( 0 );
1306bb02e0acSNicolas Bonnefon     update();       // in case the screen hasn't moved
1307bb02e0acSNicolas Bonnefon }
1308bb02e0acSNicolas Bonnefon 
1309bb02e0acSNicolas Bonnefon // Jump to the last line
1310bb02e0acSNicolas Bonnefon void AbstractLogView::jumpToBottom()
1311bb02e0acSNicolas Bonnefon {
1312bb02e0acSNicolas Bonnefon     const int new_top_line =
1313bb02e0acSNicolas Bonnefon         qMax( logData->getNbLine() - getNbVisibleLines() + 1, 0LL );
1314bb02e0acSNicolas Bonnefon 
1315bb02e0acSNicolas Bonnefon     // This will also trigger a scrollContents event
1316bb02e0acSNicolas Bonnefon     verticalScrollBar()->setValue( new_top_line );
1317bb02e0acSNicolas Bonnefon     update();       // in case the screen hasn't moved
1318bb02e0acSNicolas Bonnefon }
1319bb02e0acSNicolas Bonnefon 
1320bb02e0acSNicolas Bonnefon // Returns whether the character passed is a 'word' character
1321bb02e0acSNicolas Bonnefon inline bool AbstractLogView::isCharWord( char c )
1322bb02e0acSNicolas Bonnefon {
1323bb02e0acSNicolas Bonnefon     if ( ( ( c >= 'A' ) && ( c <= 'Z' ) ) ||
1324bb02e0acSNicolas Bonnefon          ( ( c >= 'a' ) && ( c <= 'z' ) ) ||
1325bb02e0acSNicolas Bonnefon          ( ( c >= '0' ) && ( c <= '9' ) ) ||
1326bb02e0acSNicolas Bonnefon          ( ( c == '_' ) ) )
1327bb02e0acSNicolas Bonnefon         return true;
1328bb02e0acSNicolas Bonnefon     else
1329bb02e0acSNicolas Bonnefon         return false;
1330bb02e0acSNicolas Bonnefon }
1331bb02e0acSNicolas Bonnefon 
1332bb02e0acSNicolas Bonnefon // Select the word under the given position
1333bb02e0acSNicolas Bonnefon void AbstractLogView::selectWordAtPosition( const QPoint& pos )
1334bb02e0acSNicolas Bonnefon {
1335bb02e0acSNicolas Bonnefon     const int x = pos.x();
1336bb02e0acSNicolas Bonnefon     const QString line = logData->getExpandedLineString( pos.y() );
1337bb02e0acSNicolas Bonnefon 
1338bb02e0acSNicolas Bonnefon     if ( isCharWord( line[x].toLatin1() ) ) {
1339bb02e0acSNicolas Bonnefon         // Search backward for the first character in the word
1340bb02e0acSNicolas Bonnefon         int currentPos = x;
1341bb02e0acSNicolas Bonnefon         for ( ; currentPos > 0; currentPos-- )
1342bb02e0acSNicolas Bonnefon             if ( ! isCharWord( line[currentPos].toLatin1() ) )
1343bb02e0acSNicolas Bonnefon                 break;
1344bb02e0acSNicolas Bonnefon         // Exclude the first char of the line if needed
1345bb02e0acSNicolas Bonnefon         if ( ! isCharWord( line[currentPos].toLatin1() ) )
1346bb02e0acSNicolas Bonnefon             currentPos++;
1347bb02e0acSNicolas Bonnefon         int start = currentPos;
1348bb02e0acSNicolas Bonnefon 
1349bb02e0acSNicolas Bonnefon         // Now search for the end
1350bb02e0acSNicolas Bonnefon         currentPos = x;
1351bb02e0acSNicolas Bonnefon         for ( ; currentPos < line.length() - 1; currentPos++ )
1352bb02e0acSNicolas Bonnefon             if ( ! isCharWord( line[currentPos].toLatin1() ) )
1353bb02e0acSNicolas Bonnefon                 break;
1354bb02e0acSNicolas Bonnefon         // Exclude the last char of the line if needed
1355bb02e0acSNicolas Bonnefon         if ( ! isCharWord( line[currentPos].toLatin1() ) )
1356bb02e0acSNicolas Bonnefon             currentPos--;
1357bb02e0acSNicolas Bonnefon         int end = currentPos;
1358bb02e0acSNicolas Bonnefon 
1359bb02e0acSNicolas Bonnefon         selection_.selectPortion( pos.y(), start, end );
1360bb02e0acSNicolas Bonnefon         updateGlobalSelection();
1361bb02e0acSNicolas Bonnefon         update();
1362bb02e0acSNicolas Bonnefon     }
1363bb02e0acSNicolas Bonnefon }
1364bb02e0acSNicolas Bonnefon 
1365bb02e0acSNicolas Bonnefon // Update the system global (middle click) selection (X11 only)
1366bb02e0acSNicolas Bonnefon void AbstractLogView::updateGlobalSelection()
1367bb02e0acSNicolas Bonnefon {
1368bb02e0acSNicolas Bonnefon     static QClipboard* const clipboard = QApplication::clipboard();
1369bb02e0acSNicolas Bonnefon 
1370bb02e0acSNicolas Bonnefon     // Updating it only for "non-trivial" (range or portion) selections
1371bb02e0acSNicolas Bonnefon     if ( ! selection_.isSingleLine() )
1372bb02e0acSNicolas Bonnefon         clipboard->setText( selection_.getSelectedText( logData ),
1373bb02e0acSNicolas Bonnefon                 QClipboard::Selection );
1374bb02e0acSNicolas Bonnefon }
1375bb02e0acSNicolas Bonnefon 
1376bb02e0acSNicolas Bonnefon // Create the pop-up menu
1377bb02e0acSNicolas Bonnefon void AbstractLogView::createMenu()
1378bb02e0acSNicolas Bonnefon {
1379bb02e0acSNicolas Bonnefon     copyAction_ = new QAction( tr("&Copy"), this );
1380bb02e0acSNicolas Bonnefon     // No text as this action title depends on the type of selection
1381bb02e0acSNicolas Bonnefon     connect( copyAction_, SIGNAL(triggered()), this, SLOT(copy()) );
1382bb02e0acSNicolas Bonnefon 
1383bb02e0acSNicolas Bonnefon     // For '#' and '*', shortcuts doesn't seem to work but
1384bb02e0acSNicolas Bonnefon     // at least it displays them in the menu, we manually handle those keys
1385bb02e0acSNicolas Bonnefon     // as keys event anyway (in keyPressEvent).
1386bb02e0acSNicolas Bonnefon     findNextAction_ = new QAction(tr("Find &next"), this);
1387bb02e0acSNicolas Bonnefon     findNextAction_->setShortcut( Qt::Key_Asterisk );
1388bb02e0acSNicolas Bonnefon     findNextAction_->setStatusTip( tr("Find the next occurence") );
1389bb02e0acSNicolas Bonnefon     connect( findNextAction_, SIGNAL(triggered()),
1390bb02e0acSNicolas Bonnefon             this, SLOT( findNextSelected() ) );
1391bb02e0acSNicolas Bonnefon 
1392bb02e0acSNicolas Bonnefon     findPreviousAction_ = new QAction( tr("Find &previous"), this );
1393bb02e0acSNicolas Bonnefon     findPreviousAction_->setShortcut( tr("#")  );
1394bb02e0acSNicolas Bonnefon     findPreviousAction_->setStatusTip( tr("Find the previous occurence") );
1395bb02e0acSNicolas Bonnefon     connect( findPreviousAction_, SIGNAL(triggered()),
1396bb02e0acSNicolas Bonnefon             this, SLOT( findPreviousSelected() ) );
1397bb02e0acSNicolas Bonnefon 
1398bb02e0acSNicolas Bonnefon     addToSearchAction_ = new QAction( tr("&Add to search"), this );
1399bb02e0acSNicolas Bonnefon     addToSearchAction_->setStatusTip(
1400bb02e0acSNicolas Bonnefon             tr("Add the selection to the current search") );
1401bb02e0acSNicolas Bonnefon     connect( addToSearchAction_, SIGNAL( triggered() ),
1402bb02e0acSNicolas Bonnefon             this, SLOT( addToSearch() ) );
1403bb02e0acSNicolas Bonnefon 
1404bb02e0acSNicolas Bonnefon     popupMenu_ = new QMenu( this );
1405bb02e0acSNicolas Bonnefon     popupMenu_->addAction( copyAction_ );
1406bb02e0acSNicolas Bonnefon     popupMenu_->addSeparator();
1407bb02e0acSNicolas Bonnefon     popupMenu_->addAction( findNextAction_ );
1408bb02e0acSNicolas Bonnefon     popupMenu_->addAction( findPreviousAction_ );
1409bb02e0acSNicolas Bonnefon     popupMenu_->addAction( addToSearchAction_ );
1410bb02e0acSNicolas Bonnefon }
1411bb02e0acSNicolas Bonnefon 
1412bb02e0acSNicolas Bonnefon void AbstractLogView::considerMouseHovering( int x_pos, int y_pos )
1413bb02e0acSNicolas Bonnefon {
1414bb02e0acSNicolas Bonnefon     int line = convertCoordToLine( y_pos );
1415bb02e0acSNicolas Bonnefon     if ( ( x_pos < leftMarginPx_ )
1416bb02e0acSNicolas Bonnefon             && ( line >= 0 )
1417bb02e0acSNicolas Bonnefon             && ( line < logData->getNbLine() ) ) {
1418bb02e0acSNicolas Bonnefon         // Mouse moved in the margin, send event up
1419bb02e0acSNicolas Bonnefon         // (possibly to highlight the overview)
1420bb02e0acSNicolas Bonnefon         if ( line != lastHoveredLine_ ) {
1421bb02e0acSNicolas Bonnefon             LOG(logDEBUG) << "Mouse moved in margin line: " << line;
1422bb02e0acSNicolas Bonnefon             emit mouseHoveredOverLine( line );
1423bb02e0acSNicolas Bonnefon             lastHoveredLine_ = line;
1424bb02e0acSNicolas Bonnefon         }
1425bb02e0acSNicolas Bonnefon     }
1426bb02e0acSNicolas Bonnefon     else {
1427bb02e0acSNicolas Bonnefon         if ( lastHoveredLine_ != -1 ) {
1428bb02e0acSNicolas Bonnefon             emit mouseLeftHoveringZone();
1429bb02e0acSNicolas Bonnefon             lastHoveredLine_ = -1;
1430bb02e0acSNicolas Bonnefon         }
1431bb02e0acSNicolas Bonnefon     }
1432bb02e0acSNicolas Bonnefon }
1433