VirtualBox

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

Last change on this file since 102493 was 101561, checked in by vboxsync, 11 months ago

FE/Qt: bugref:10450: Get rid of Qt5 stuff; This one is about position/globalPosition stuff.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette