VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/logviewer/UIVMLogViewerTextEdit.cpp@ 82781

Last change on this file since 82781 was 80339, checked in by vboxsync, 5 years ago

FE/Qt: bugref:9072: Replacing repaint() calls with update()

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.1 KB
Line 
1/* $Id: UIVMLogViewerTextEdit.cpp 80339 2019-08-18 10:38:58Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIVMLogViewer class implementation.
4 */
5
6/*
7 * Copyright (C) 2010-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Qt includes: */
19#if defined(RT_OS_SOLARIS)
20# include <QFontDatabase>
21#endif
22#include <QMenu>
23#include <QPainter>
24#include <QPlainTextEdit>
25#include <QScrollBar>
26#include <QStyle>
27#include <QTextBlock>
28
29/* GUI includes: */
30#include "UIIconPool.h"
31#include "UIVMLogViewerTextEdit.h"
32#include "UIVMLogViewerWidget.h"
33
34/** We use a modified scrollbar style for our QPlainTextEdits to get the
35 markings on the scrollbars correctly. The default scrollbarstyle does not
36 reveal the height of the pushbuttons on the scrollbar (on either side of it, with arrow on them)
37 to compute the marking locations correctly. Thus we turn these push buttons off: */
38const QString verticalScrollBarStyle("QScrollBar:vertical {"
39 "border: 1px ridge grey; "
40 "margin: 0px 0px 0 0px;}"
41 "QScrollBar::handle:vertical {"
42 "min-height: 10px;"
43 "background: grey;}"
44 "QScrollBar::add-line:vertical {"
45 "width: 0px;}"
46 "QScrollBar::sub-line:vertical {"
47 "width: 0px;}");
48
49const QString horizontalScrollBarStyle("QScrollBar:horizontal {"
50 "border: 1px ridge grey; "
51 "margin: 0px 0px 0 0px;}"
52 "QScrollBar::handle:horizontal {"
53 "min-height: 10px;"
54 "background: grey;}"
55 "QScrollBar::add-line:horizontal {"
56 "height: 0px;}"
57 "QScrollBar::sub-line:horizontal {"
58 "height: 0px;}");
59
60
61/*********************************************************************************************************************************
62* UIIndicatorScrollBar definition. *
63*********************************************************************************************************************************/
64
65class UIIndicatorScrollBar : public QScrollBar
66{
67 Q_OBJECT;
68
69public:
70
71 UIIndicatorScrollBar(QWidget *parent = 0);
72 void setMarkingsVector(const QVector<float> &vector);
73 void clearMarkingsVector();
74
75protected:
76
77 virtual void paintEvent(QPaintEvent *pEvent) /* override */;
78
79private:
80
81 /* Stores the relative (to scrollbar's height) positions of markings,
82 where we draw a horizontal line. Values are in [0.0, 1.0]*/
83 QVector<float> m_markingsVector;
84};
85
86
87/*********************************************************************************************************************************
88* UIIndicatorScrollBar implemetation. *
89*********************************************************************************************************************************/
90
91UIIndicatorScrollBar::UIIndicatorScrollBar(QWidget *parent /*= 0 */)
92 :QScrollBar(parent)
93{
94 setStyleSheet(verticalScrollBarStyle);
95}
96
97void UIIndicatorScrollBar::setMarkingsVector(const QVector<float> &vector)
98{
99 m_markingsVector = vector;
100}
101
102void UIIndicatorScrollBar::clearMarkingsVector()
103{
104 m_markingsVector.clear();
105}
106
107void UIIndicatorScrollBar::paintEvent(QPaintEvent *pEvent) /* override */
108{
109 QScrollBar::paintEvent(pEvent);
110 /* Put a red line to mark the bookmark positions: */
111 for (int i = 0; i < m_markingsVector.size(); ++i)
112 {
113 QPointF p1 = QPointF(0, m_markingsVector[i] * height());
114 QPointF p2 = QPointF(width(), m_markingsVector[i] * height());
115
116 QPainter painter(this);
117 painter.setRenderHint(QPainter::Antialiasing, true);
118 painter.setPen(QPen(QColor(255, 0, 0, 75), 1.1f));
119 painter.drawLine(p1, p2);
120 }
121}
122
123
124/*********************************************************************************************************************************
125* UILineNumberArea definition. *
126*********************************************************************************************************************************/
127
128class UILineNumberArea : public QWidget
129{
130public:
131 UILineNumberArea(UIVMLogViewerTextEdit *textEdit);
132 QSize sizeHint() const;
133
134protected:
135
136 void paintEvent(QPaintEvent *event);
137 void mouseMoveEvent(QMouseEvent *pEvent);
138 void mousePressEvent(QMouseEvent *pEvent);
139
140private:
141 UIVMLogViewerTextEdit *m_pTextEdit;
142};
143
144
145/*********************************************************************************************************************************
146* UILineNumberArea implemetation. *
147*********************************************************************************************************************************/
148
149UILineNumberArea::UILineNumberArea(UIVMLogViewerTextEdit *textEdit)
150 :QWidget(textEdit)
151 , m_pTextEdit(textEdit)
152{
153 setMouseTracking(true);
154}
155
156QSize UILineNumberArea::sizeHint() const
157{
158 if (!m_pTextEdit)
159 return QSize();
160 return QSize(m_pTextEdit->lineNumberAreaWidth(), 0);
161}
162
163void UILineNumberArea::paintEvent(QPaintEvent *event)
164{
165 if (m_pTextEdit)
166 m_pTextEdit->lineNumberAreaPaintEvent(event);
167}
168
169void UILineNumberArea::mouseMoveEvent(QMouseEvent *pEvent)
170{
171 if (m_pTextEdit)
172 m_pTextEdit->setMouseCursorLine(m_pTextEdit->lineNumberForPos(pEvent->pos()));
173 update();
174}
175
176void UILineNumberArea::mousePressEvent(QMouseEvent *pEvent)
177{
178 if (m_pTextEdit)
179 m_pTextEdit->toggleBookmark(m_pTextEdit->bookmarkForPos(pEvent->pos()));
180}
181
182
183/*********************************************************************************************************************************
184* UIVMLogViewerTextEdit implemetation. *
185*********************************************************************************************************************************/
186
187UIVMLogViewerTextEdit::UIVMLogViewerTextEdit(QWidget* parent /* = 0 */)
188 : QIWithRetranslateUI<QPlainTextEdit>(parent)
189 , m_pLineNumberArea(0)
190 , m_mouseCursorLine(-1)
191 , m_bShownTextIsFiltered(false)
192 , m_bShowLineNumbers(true)
193 , m_bWrapLines(true)
194 , m_bHasContextMenu(false)
195{
196 configure();
197 prepare();
198}
199
200void UIVMLogViewerTextEdit::configure()
201{
202 setMouseTracking(true);
203
204 /* Prepare modified standard palette: */
205 QPalette pal = style() ? style()->standardPalette() : palette(); // fallback if no style exist.
206 pal.setColor(QPalette::Inactive, QPalette::Highlight, pal.color(QPalette::Active, QPalette::Highlight));
207 pal.setColor(QPalette::Inactive, QPalette::HighlightedText, pal.color(QPalette::Active, QPalette::HighlightedText));
208 setPalette(pal);
209
210 /* Configure this' wrap mode: */
211 setWrapLines(false);
212 setReadOnly(true);
213}
214
215void UIVMLogViewerTextEdit::prepare()
216{
217 prepareWidgets();
218 retranslateUi();
219}
220
221void UIVMLogViewerTextEdit::prepareWidgets()
222{
223 m_pLineNumberArea = new UILineNumberArea(this);
224
225 connect(this, &UIVMLogViewerTextEdit::blockCountChanged, this, &UIVMLogViewerTextEdit::sltUpdateLineNumberAreaWidth);
226 connect(this, &UIVMLogViewerTextEdit::updateRequest, this, &UIVMLogViewerTextEdit::sltHandleUpdateRequest);
227 sltUpdateLineNumberAreaWidth(0);
228
229 setVerticalScrollBar(new UIIndicatorScrollBar());
230 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
231 QScrollBar *pHorizontalScrollBar = horizontalScrollBar();
232 if (pHorizontalScrollBar)
233 pHorizontalScrollBar->setStyleSheet(horizontalScrollBarStyle);
234}
235
236void UIVMLogViewerTextEdit::setCurrentFont(QFont font)
237{
238 setFont(font);
239 if (m_pLineNumberArea)
240 m_pLineNumberArea->setFont(font);
241}
242
243int UIVMLogViewerTextEdit::lineNumberAreaWidth()
244{
245 if (!m_bShowLineNumbers)
246 return 0;
247
248 int digits = 1;
249 int max = qMax(1, blockCount());
250 while (max >= 10) {
251 max /= 10;
252 ++digits;
253 }
254
255 int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits;
256
257 return space;
258}
259
260void UIVMLogViewerTextEdit::lineNumberAreaPaintEvent(QPaintEvent *event)
261{
262 if (!m_bShowLineNumbers)
263 return;
264 QPainter painter(m_pLineNumberArea);
265 painter.fillRect(event->rect(), Qt::lightGray);
266 QTextBlock block = firstVisibleBlock();
267 int blockNumber = block.blockNumber();
268 int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
269 int bottom = top + (int) blockBoundingRect(block).height();
270 while (block.isValid() && top <= event->rect().bottom()) {
271 if (block.isVisible() && bottom >= event->rect().top()) {
272 QString number = QString::number(blockNumber + 1);
273 /* Mark this line if it is bookmarked, but only if the text is not filtered. */
274 if (m_bookmarkLineSet.contains(blockNumber + 1) && !m_bShownTextIsFiltered)
275 {
276 QPainterPath path;
277 path.addRect(0, top, m_pLineNumberArea->width(), m_pLineNumberArea->fontMetrics().lineSpacing());
278 painter.fillPath(path, QColor(204, 255, 51, 125));
279 painter.drawPath(path);
280 }
281 /* Draw a unfilled red rectangled around the line number to indicate line the mouse cursor is currently
282 hovering on. Do this only if mouse is over the ext edit or the context menu is around: */
283 if ((blockNumber + 1) == m_mouseCursorLine && (underMouse() || m_bHasContextMenu))
284 {
285 painter.setPen(Qt::red);
286 painter.drawRect(0, top, m_pLineNumberArea->width(), m_pLineNumberArea->fontMetrics().lineSpacing());
287 }
288
289 painter.setPen(Qt::black);
290 painter.drawText(0, top, m_pLineNumberArea->width(), m_pLineNumberArea->fontMetrics().lineSpacing(),
291 Qt::AlignRight, number);
292 }
293 block = block.next();
294 top = bottom;
295 bottom = top + (int) blockBoundingRect(block).height();
296 ++blockNumber;
297 }
298}
299
300void UIVMLogViewerTextEdit::retranslateUi()
301{
302 m_strBackgroungText = QString(UIVMLogViewerWidget::tr("Filtered"));
303}
304
305void UIVMLogViewerTextEdit::contextMenuEvent(QContextMenuEvent *pEvent)
306{
307 /* If shown text is filtered, do not create Bookmark action since
308 we disable all bookmarking related functionalities in this case. */
309 if (m_bShownTextIsFiltered)
310 {
311 QPlainTextEdit::contextMenuEvent(pEvent);
312 return;
313 }
314 m_bHasContextMenu = true;
315 QMenu *menu = createStandardContextMenu();
316
317
318 QAction *pAction = menu->addAction(UIVMLogViewerWidget::tr("Bookmark"));
319 if (pAction)
320 {
321 pAction->setCheckable(true);
322 QPair<int, QString> menuBookmark = bookmarkForPos(pEvent->pos());
323 pAction->setChecked(m_bookmarkLineSet.contains(menuBookmark.first));
324 if (pAction->isChecked())
325 pAction->setIcon(UIIconPool::iconSet(":/log_viewer_bookmark_on_16px.png"));
326 else
327 pAction->setIcon(UIIconPool::iconSet(":/log_viewer_bookmark_off_16px.png"));
328
329 m_iContextMenuBookmark = menuBookmark;
330 connect(pAction, &QAction::triggered, this, &UIVMLogViewerTextEdit::sltBookmark);
331
332 }
333 menu->exec(pEvent->globalPos());
334
335 if (pAction)
336 disconnect(pAction, &QAction::triggered, this, &UIVMLogViewerTextEdit::sltBookmark);
337
338 delete menu;
339 m_bHasContextMenu = false;
340}
341
342void UIVMLogViewerTextEdit::resizeEvent(QResizeEvent *pEvent)
343{
344 QPlainTextEdit::resizeEvent(pEvent);
345 if (m_pLineNumberArea)
346 {
347 QRect cr = contentsRect();
348 m_pLineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
349 }
350}
351
352void UIVMLogViewerTextEdit::mouseMoveEvent(QMouseEvent *pEvent)
353{
354 setMouseCursorLine(lineNumberForPos(pEvent->pos()));
355 if (m_pLineNumberArea)
356 m_pLineNumberArea->update();
357 QPlainTextEdit::mouseMoveEvent(pEvent);
358}
359
360void UIVMLogViewerTextEdit::leaveEvent(QEvent * pEvent)
361{
362 QPlainTextEdit::leaveEvent(pEvent);
363 /* Force a redraw as mouse leaves this to remove the mouse
364 cursor track rectangle (the red rectangle we draw on the line number area). */
365 update();
366}
367
368void UIVMLogViewerTextEdit::sltUpdateLineNumberAreaWidth(int /* newBlockCount */)
369{
370 setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
371}
372
373void UIVMLogViewerTextEdit::sltHandleUpdateRequest(const QRect &rect, int dy)
374{
375 if (dy)
376 m_pLineNumberArea->scroll(0, dy);
377 else
378 m_pLineNumberArea->update(0, rect.y(), m_pLineNumberArea->width(), rect.height());
379
380 if (rect.contains(viewport()->rect()))
381 sltUpdateLineNumberAreaWidth(0);
382
383 if (viewport())
384 viewport()->update();
385}
386
387void UIVMLogViewerTextEdit::sltBookmark()
388{
389 toggleBookmark(m_iContextMenuBookmark);
390}
391
392void UIVMLogViewerTextEdit::setScrollBarMarkingsVector(const QVector<float> &vector)
393{
394 UIIndicatorScrollBar* vScrollBar = qobject_cast<UIIndicatorScrollBar*>(verticalScrollBar());
395 if (vScrollBar)
396 vScrollBar->setMarkingsVector(vector);
397}
398
399void UIVMLogViewerTextEdit::clearScrollBarMarkingsVector()
400{
401 UIIndicatorScrollBar* vScrollBar = qobject_cast<UIIndicatorScrollBar*>(verticalScrollBar());
402 if (vScrollBar)
403 vScrollBar->clearMarkingsVector();
404}
405
406void UIVMLogViewerTextEdit::scrollToLine(int lineNumber)
407{
408 QTextDocument* pDocument = document();
409 if (!pDocument)
410 return;
411
412 moveCursor(QTextCursor::End);
413 int halfPageLineCount = 0.5 * visibleLineCount() ;
414 QTextCursor cursor(pDocument->findBlockByLineNumber(qMax(lineNumber - halfPageLineCount, 0)));
415 setTextCursor(cursor);
416}
417
418int UIVMLogViewerTextEdit::visibleLineCount()
419{
420 int height = 0;
421 if (viewport())
422 height = viewport()->height();
423 if (verticalScrollBar() && verticalScrollBar()->isVisible())
424 height -= horizontalScrollBar()->height();
425 int singleLineHeight = fontMetrics().lineSpacing();
426 if (singleLineHeight == 0)
427 return 0;
428 return height / singleLineHeight;
429}
430
431void UIVMLogViewerTextEdit::setBookmarkLineSet(const QSet<int>& lineSet)
432{
433 m_bookmarkLineSet = lineSet;
434 update();
435}
436
437int UIVMLogViewerTextEdit::lineNumberForPos(const QPoint &position)
438{
439 QTextCursor cursor = cursorForPosition(position);
440 QTextBlock block = cursor.block();
441 return block.blockNumber() + 1;
442}
443
444
445QPair<int, QString> UIVMLogViewerTextEdit::bookmarkForPos(const QPoint &position)
446{
447 QTextBlock block = cursorForPosition(position).block();
448 return QPair<int, QString>(lineNumberForPos(position), block.text());
449}
450
451void UIVMLogViewerTextEdit::setMouseCursorLine(int lineNumber)
452{
453 m_mouseCursorLine = lineNumber;
454}
455
456void UIVMLogViewerTextEdit::toggleBookmark(const QPair<int, QString>& bookmark)
457{
458 if (m_bShownTextIsFiltered)
459 return;
460
461 int lineNumber = bookmark.first;
462
463 if (m_bookmarkLineSet.contains(lineNumber))
464 emit sigDeleteBookmark(bookmark);
465 else
466 emit sigAddBookmark(bookmark);
467}
468
469void UIVMLogViewerTextEdit::setShownTextIsFiltered(bool warning)
470{
471 if (m_bShownTextIsFiltered == warning)
472 return;
473 m_bShownTextIsFiltered = warning;
474 if (viewport())
475 viewport()->update();
476}
477
478void UIVMLogViewerTextEdit::setShowLineNumbers(bool bShowLineNumbers)
479{
480 if (m_bShowLineNumbers == bShowLineNumbers)
481 return;
482 m_bShowLineNumbers = bShowLineNumbers;
483 emit updateRequest(viewport()->rect(), 0);
484}
485
486bool UIVMLogViewerTextEdit::showLineNumbers() const
487{
488 return m_bShowLineNumbers;
489}
490
491void UIVMLogViewerTextEdit::setWrapLines(bool bWrapLines)
492{
493 if (m_bWrapLines == bWrapLines)
494 return;
495 m_bWrapLines = bWrapLines;
496 if (m_bWrapLines)
497 {
498 setLineWrapMode(QPlainTextEdit::WidgetWidth);
499 setWordWrapMode(QTextOption::WordWrap);
500 }
501 else
502 {
503 setWordWrapMode(QTextOption::NoWrap);
504 setWordWrapMode(QTextOption::NoWrap);
505 }
506 update();
507}
508
509bool UIVMLogViewerTextEdit::wrapLines() const
510{
511 return m_bWrapLines;
512}
513
514int UIVMLogViewerTextEdit::currentVerticalScrollBarValue() const
515{
516 if (!verticalScrollBar())
517 return -1;
518 return verticalScrollBar()->value();
519}
520
521void UIVMLogViewerTextEdit::setCurrentVerticalScrollBarValue(int value)
522{
523 if (!verticalScrollBar())
524 return;
525
526 setCenterOnScroll(true);
527
528 verticalScrollBar()->setValue(value);
529 verticalScrollBar()->setSliderPosition(value);
530 viewport()->update();
531 update();
532}
533
534#include "UIVMLogViewerTextEdit.moc"
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use