xref: /glogg/src/abstractlogview.h (revision 05467f52d72e282658c219cc889ed6bc08e5dc01)
1 /*
2  * Copyright (C) 2009, 2010, 2011, 2012, 2013, 2017 Nicolas Bonnefon
3  * and other contributors
4  *
5  * This file is part of glogg.
6  *
7  * glogg is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * glogg is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with glogg.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #ifndef ABSTRACTLOGVIEW_H
22 #define ABSTRACTLOGVIEW_H
23 
24 #include <QAbstractScrollArea>
25 #include <QBasicTimer>
26 
27 #ifdef GLOGG_PERF_MEASURE_FPS
28 #  include "perfcounter.h"
29 #endif
30 
31 #include "selection.h"
32 #include "quickfind.h"
33 #include "overviewwidget.h"
34 #include "quickfindmux.h"
35 #include "viewtools.h"
36 
37 class QMenu;
38 class QAction;
39 class AbstractLogData;
40 
41 class LineChunk
42 {
43   public:
44     enum ChunkType {
45         Normal,
46         Highlighted,
47         Selected,
48     };
49 
50     LineChunk( int first_col, int end_col, ChunkType type );
51 
52     int start() const { return start_; }
53     int end() const { return end_; }
54     ChunkType type() const { return type_; }
55 
56     // Returns 'true' if the selection is part of this chunk
57     // (at least partially), if so, it should be replaced by the list returned
58     QList<LineChunk> select( int selection_start, int selection_end ) const;
59 
60   private:
61     int start_;
62     int end_;
63     ChunkType type_;
64 };
65 
66 // Utility class for syntax colouring.
67 // It stores the chunks of line to draw
68 // each chunk having a different colour
69 class LineDrawer
70 {
71   public:
72     LineDrawer( const QColor& back_color) :
73         list(), backColor_( back_color ) { };
74 
75     // Add a chunk of line using the given colours.
76     // Both first_col and last_col are included
77     // An empty chunk will be ignored.
78     // the first column will be set to 0 if negative
79     // The column are relative to the screen
80     void addChunk( int first_col, int last_col, QColor fore, QColor back );
81     void addChunk( const LineChunk& chunk, QColor fore, QColor back );
82 
83     // Draw the current line of text using the given painter,
84     // in the passed block (in pixels)
85     // The line must be cut to fit on the screen.
86     // leftExtraBackgroundPx is the an extra margin to start drawing
87     // the coloured // background, going all the way to the element
88     // left of the line looks better.
89     void draw( QPainter& painter, int xPos, int yPos,
90                int line_width, const QString& line,
91                int leftExtraBackgroundPx );
92 
93   private:
94     class Chunk {
95       public:
96         // Create a new chunk
97         Chunk( int start, int length, QColor fore, QColor back )
98             : start_( start ), length_( length ),
99             foreColor_ ( fore ), backColor_ ( back ) { };
100 
101         // Accessors
102         int start() const { return start_; }
103         int length() const { return length_; }
104         const QColor& foreColor() const { return foreColor_; }
105         const QColor& backColor() const { return backColor_; }
106 
107       private:
108         int start_;
109         int length_;
110         QColor foreColor_;
111         QColor backColor_;
112     };
113     QList<Chunk> list;
114     QColor backColor_;
115 };
116 
117 
118 // Utility class representing a buffer for number entered on the keyboard
119 // The buffer keep at most 7 digits, and reset itself after a timeout.
120 class DigitsBuffer : public QObject
121 {
122   Q_OBJECT
123 
124   public:
125     DigitsBuffer();
126 
127     // Reset the buffer.
128     void reset();
129     // Add a single digit to the buffer (discarded if it's not a digit),
130     // the timeout timer is reset.
131     void add( char character );
132     // Get the content of the buffer (0 if empty) and reset it.
133     int content();
134 
135   protected:
136     void timerEvent( QTimerEvent* event );
137 
138   private:
139     // Duration of the timeout in milliseconds.
140     static const int timeout_;
141 
142     QString digits_;
143 
144     QBasicTimer timer_;
145 };
146 
147 class Overview;
148 
149 // Base class representing the log view widget.
150 // It can be either the top (full) or bottom (filtered) view.
151 class AbstractLogView :
152     public QAbstractScrollArea, public SearchableWidgetInterface
153 {
154   Q_OBJECT
155 
156   public:
157     // Constructor of the widget, the data set is passed.
158     // The caller retains ownership of the data set.
159     // The pointer to the QFP is used for colouring and QuickFind searches
160     AbstractLogView( const AbstractLogData* newLogData,
161             const QuickFindPattern* const quickFind, QWidget* parent=0 );
162     ~AbstractLogView();
163 
164     // Refresh the widget when the data set has changed.
165     void updateData();
166     // Instructs the widget to update it's content geometry,
167     // used when the font is changed.
168     void updateDisplaySize();
169     // Return the line number of the top line of the view
170     int getTopLine() const;
171     // Return the text of the current selection.
172     QString getSelection() const;
173     // Instructs the widget to select the whole text.
174     void selectAll();
175 
176     bool isFollowEnabled() const { return followMode_; }
177 
178   protected:
179     virtual void mousePressEvent( QMouseEvent* mouseEvent );
180     virtual void mouseMoveEvent( QMouseEvent* mouseEvent );
181     virtual void mouseReleaseEvent( QMouseEvent* );
182     virtual void mouseDoubleClickEvent( QMouseEvent* mouseEvent );
183     virtual void timerEvent( QTimerEvent* timerEvent );
184     virtual void changeEvent( QEvent* changeEvent );
185     virtual void paintEvent( QPaintEvent* paintEvent );
186     virtual void resizeEvent( QResizeEvent* resizeEvent );
187     virtual void scrollContentsBy( int dx, int dy );
188     virtual void keyPressEvent( QKeyEvent* keyEvent );
189     virtual void wheelEvent( QWheelEvent* wheelEvent );
190     virtual bool event( QEvent * e );
191 
192     // Must be implemented to return wether the line number is
193     // a match, a mark or just a normal line (used for coloured bullets)
194     enum LineType { Normal, Marked, Match };
195     virtual LineType lineType( int lineNumber ) const = 0;
196 
197     // Line number to display for line at the given index
198     virtual qint64 displayLineNumber( int lineNumber ) const;
199     virtual qint64 maxDisplayLineNumber() const;
200 
201     // Get the overview associated with this view, or NULL if there is none
202     Overview* getOverview() const { return overview_; }
203     // Set the Overview and OverviewWidget
204     void setOverview( Overview* overview, OverviewWidget* overview_widget );
205 
206     // Returns the current "position" of the view as a line number,
207     // it is either the selected line or the middle of the view.
208     LineNumber getViewPosition() const;
209 
210   signals:
211     // Sent when a new line has been selected by the user.
212     void newSelection(int line);
213     // Sent up to the MainWindow to enable/disable the follow mode
214     void followModeChanged( bool enabled );
215     // Sent when the view wants the QuickFind widget pattern to change.
216     void changeQuickFind( const QString& newPattern,
217             QuickFindMux::QFDirection newDirection );
218     // Sent up when the current line number is updated
219     void updateLineNumber( int line );
220     // Sent up when quickFind wants to show a message to the user.
221     void notifyQuickFind( const QFNotification& message );
222     // Sent up when quickFind wants to clear the notification.
223     void clearQuickFindNotification();
224     // Sent when the view ask for a line to be marked
225     // (click in the left margin).
226     void markLine( qint64 line );
227     // Sent up when the user wants to add the selection to the search
228     void addToSearch( const QString& selection );
229     // Sent up when the mouse is hovered over a line's margin
230     void mouseHoveredOverLine( qint64 line );
231     // Sent up when the mouse leaves a line's margin
232     void mouseLeftHoveringZone();
233     // Sent up for view initiated quickfind searches
234     void searchNext();
235     void searchPrevious();
236     // Sent up when the user has moved within the view
237     void activity();
238     // Sent up when the user want to exit this view
239     // (switch to the next one)
240     void exitView();
241 
242   public slots:
243     // Makes the widget select and display the passed line.
244     // Scrolling as necessary
245     void selectAndDisplayLine( int line );
246 
247     // Use the current QFP to go and select the next match.
248     virtual void searchForward();
249     // Use the current QFP to go and select the previous match.
250     virtual void searchBackward();
251 
252     // Use the current QFP to go and select the next match (incremental)
253     virtual void incrementallySearchForward();
254     // Use the current QFP to go and select the previous match (incremental)
255     virtual void incrementallySearchBackward();
256     // Stop the current incremental search (typically when user press return)
257     virtual void incrementalSearchStop();
258     // Abort the current incremental search (typically when user press esc)
259     virtual void incrementalSearchAbort();
260 
261     // Signals the follow mode has been enabled.
262     void followSet( bool checked );
263 
264     // Signal the on/off status of the overview has been changed.
265     void refreshOverview();
266 
267     // Make the view jump to the specified line, regardless of weither it
268     // is on the screen or not.
269     // (does NOT emit followDisabled() )
270     void jumpToLine( int line );
271 
272     // Configure the setting of whether to show line number margin
273     void setLineNumbersVisible( bool lineNumbersVisible );
274 
275     // Force the next refresh to fully redraw the view by invalidating the cache.
276     // To be used if the data might have changed.
277     void forceRefresh();
278 
279   private slots:
280     void handlePatternUpdated();
281     void addToSearch();
282     void findNextSelected();
283     void findPreviousSelected();
284     void copy();
285 
286   private:
287     // Graphic parameters
288     static constexpr int OVERVIEW_WIDTH = 27;
289     static constexpr int HOOK_THRESHOLD = 600;
290     static constexpr int PULL_TO_FOLLOW_HOOKED_HEIGHT = 10;
291 
292     // Width of the bullet zone, including decoration
293     int bulletZoneWidthPx_;
294 
295     // Total size of all margins and decorations in pixels
296     int leftMarginPx_;
297 
298     // Digits buffer (for numeric keyboard entry)
299     DigitsBuffer digitsBuffer_;
300 
301     // Follow mode
302     bool followMode_;
303 
304     // ElasticHook for follow mode
305     ElasticHook followElasticHook_;
306 
307     // Whether to show line numbers or not
308     bool lineNumbersVisible_;
309 
310     // Pointer to the CrawlerWidget's data set
311     const AbstractLogData* logData;
312 
313     // Pointer to the Overview object
314     Overview* overview_;
315 
316     // Pointer to the OverviewWidget, this class doesn't own it,
317     // but is responsible for displaying it (for purely aesthetic
318     // reasons).
319     OverviewWidget* overviewWidget_;
320 
321     bool selectionStarted_;
322     // Start of the selection (characters)
323     QPoint selectionStartPos_;
324     // Current end of the selection (characters)
325     QPoint selectionCurrentEndPos_;
326     QBasicTimer autoScrollTimer_;
327 
328     // Hovering state
329     // Last line that has been hoovered on, -1 if none
330     qint64 lastHoveredLine_;
331 
332     // Marks (left margin click)
333     bool markingClickInitiated_;
334     qint64 markingClickLine_;
335 
336     Selection selection_;
337 
338     // Position of the view, those are crucial to control drawing
339     // firstLine gives the position of the view,
340     // lastLineAligned == true make the bottom of the last line aligned
341     // rather than the top of the top one.
342     LineNumber firstLine;
343     bool lastLineAligned;
344     int firstCol;
345 
346     // Text handling
347     int charWidth_;
348     int charHeight_;
349 
350     // Popup menu
351     QMenu* popupMenu_;
352     QAction* copyAction_;
353     QAction* findNextAction_;
354     QAction* findPreviousAction_;
355     QAction* addToSearchAction_;
356 
357     // Pointer to the CrawlerWidget's QFP object
358     const QuickFindPattern* const quickFindPattern_;
359     // Our own QuickFind object
360     QuickFind quickFind_;
361 
362 #ifdef GLOGG_PERF_MEASURE_FPS
363     // Performance measurement
364     PerfCounter perfCounter_;
365 #endif
366 
367     // Vertical offset (in pixels) at which the first line of text is written
368     int drawingTopOffset_ = 0;
369 
370     // Cache pixmap and associated info
371     struct TextAreaCache {
372         QPixmap pixmap_;
373         bool invalid_;
374         int first_line_;
375         int last_line_;
376         int first_column_;
377     };
378     struct PullToFollowCache {
379         QPixmap pixmap_;
380         int nb_columns_;
381     };
382     TextAreaCache textAreaCache_ = { {}, true, 0, 0, 0 };
383     PullToFollowCache pullToFollowCache_ = { {}, 0 };
384 
385     LineNumber getNbVisibleLines() const;
386     int getNbVisibleCols() const;
387     QPoint convertCoordToFilePos( const QPoint& pos ) const;
388     int convertCoordToLine( int yPos ) const;
389     int convertCoordToColumn( int xPos ) const;
390     void displayLine( LineNumber line );
391     void moveSelection( int y );
392     void jumpToStartOfLine();
393     void jumpToEndOfLine();
394     void jumpToRightOfScreen();
395     void jumpToTop();
396     void jumpToBottom();
397     void selectWordAtPosition( const QPoint& pos );
398 
399     void createMenu();
400 
401     void considerMouseHovering( int x_pos, int y_pos );
402 
403     // Search functions (for n/N)
404     void searchUsingFunction( qint64 (QuickFind::*search_function)() );
405 
406     void updateScrollBars();
407 
408     void drawTextArea( QPaintDevice* paint_device, int32_t delta_y );
409     QPixmap drawPullToFollowBar( int width, float pixel_ratio );
410 
411     void disableFollow();
412 
413     // Utils functions
414     bool isCharWord( char c );
415     void updateGlobalSelection();
416 };
417 
418 #endif
419