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