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