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