VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/extensions/QILabel.cpp

Last change on this file was 103362, checked in by vboxsync, 3 months ago

FE/Qt: bugref:10450: Get rid of QRegExp which is a part of Qt5 legacy API; This is necessary to get rid of Qt5 support lib.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.2 KB
Line 
1/* $Id: QILabel.cpp 103362 2024-02-14 16:50:56Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - Qt extensions: QILabel class implementation.
4 */
5
6/*
7 * Copyright (C) 2006-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/*
29 * This class is based on the original QLabel implementation.
30 */
31
32/* Qt includes: */
33#include <QApplication>
34#include <QClipboard>
35#include <QContextMenuEvent>
36#include <QDrag>
37#include <QFocusEvent>
38#include <QMenu>
39#include <QMimeData>
40#include <QMouseEvent>
41#include <QPainter>
42#include <QStyleOptionFocusRect>
43
44/* GUI includes: */
45#include "QILabel.h"
46
47/* Type definitions: */
48#define HOR_PADDING 1
49
50
51/* static */
52const QRegularExpression QILabel::s_regExpCopy = QRegularExpression("<[^>]*>");
53const QRegularExpression QILabel::s_regExpElide = QRegularExpression("(<compact\\s+elipsis=\"(start|middle|end)\"?>([^<]*)</compact>)");
54
55QILabel::QILabel(QWidget *pParent /* = 0 */, Qt::WindowFlags enmFlags /* = Qt::WindowFlags() */)
56 : QLabel(pParent, enmFlags)
57{
58 init();
59}
60
61QILabel::QILabel(const QString &strText, QWidget *pParent /* = 0 */, Qt::WindowFlags enmFlags /* = Qt::WindowFlags() */)
62 : QLabel(pParent, enmFlags)
63{
64 init();
65 setFullText(strText);
66}
67
68void QILabel::setFullSizeSelection(bool fEnabled)
69{
70 /* Remember new value: */
71 m_fFullSizeSelection = fEnabled;
72 if (m_fFullSizeSelection)
73 {
74 /* Enable mouse interaction only */
75 setTextInteractionFlags(Qt::LinksAccessibleByMouse);
76 /* The label should be able to get the focus */
77 setFocusPolicy(Qt::StrongFocus);
78 /* Change the appearance in the focus state a little bit.
79 * Note: Unfortunately QLabel, precisely the text of a QLabel isn't
80 * styleable. The trolls have forgotten the simplest case ... So this
81 * is done by changing the currently used palette in the In/Out-focus
82 * events below. Next broken feature is drawing a simple dotted line
83 * around the label. So this is done manually in the paintEvent. Not
84 * sure if the stylesheet stuff is ready for production environments. */
85 setStyleSheet(QString("QLabel::focus {\
86 background-color: palette(highlight);\
87 }\
88 QLabel {\
89 padding: 0px %1px 0px %1px;\
90 }").arg(HOR_PADDING));
91 }
92 else
93 {
94 /* Text should be selectable/copyable */
95 setTextInteractionFlags(Qt::TextBrowserInteraction);
96 /* No Focus an the label */
97 setFocusPolicy(Qt::NoFocus);
98 /* No focus style change */
99 setStyleSheet("");
100 }
101}
102
103void QILabel::useSizeHintForWidth(int iWidthHint) const
104{
105 /* Remember new value: */
106 m_iWidthHint = iWidthHint;
107 updateSizeHint();
108}
109
110QSize QILabel::sizeHint() const
111{
112 /* Update size-hint if it's invalid: */
113 if (!m_fHintValid)
114 updateSizeHint();
115
116 /* If there is an updated sizeHint() present - using it: */
117 return m_ownSizeHint.isValid() ? m_ownSizeHint : QLabel::sizeHint();
118}
119
120QSize QILabel::minimumSizeHint() const
121{
122 /* Update size-hint if it's invalid: */
123 if (!m_fHintValid)
124 updateSizeHint();
125
126 /* If there is an updated minimumSizeHint() present - using it. */
127 return m_ownSizeHint.isValid() ? m_ownSizeHint : QLabel::minimumSizeHint();
128}
129
130void QILabel::clear()
131{
132 QLabel::clear();
133 setFullText("");
134}
135
136void QILabel::setText(const QString &strText)
137{
138 /* Call to wrapper below: */
139 setFullText(strText);
140
141 /* If QILabel forced to be fixed vertically */
142 if (minimumHeight() == maximumHeight())
143 {
144 /* Check if new text requires label growing */
145 QSize sh(width(), heightForWidth(width()));
146 if (sh.height() > minimumHeight())
147 setFixedHeight(sh.height());
148 }
149}
150
151void QILabel::copy()
152{
153 /* Strip the text of all HTML subsets: */
154 QString strText = removeHtmlTags(m_strText);
155 /* Copy the current text to the global and selection clipboard. */
156 QApplication::clipboard()->setText(strText, QClipboard::Clipboard);
157 QApplication::clipboard()->setText(strText, QClipboard::Selection);
158}
159
160void QILabel::resizeEvent(QResizeEvent *pEvent)
161{
162 /* Call to base-class: */
163 QLabel::resizeEvent(pEvent);
164 /* Recalculate the elipsis of the text after every resize. */
165 updateText();
166}
167
168void QILabel::mousePressEvent(QMouseEvent *pEvent)
169{
170 /* Start dragging: */
171 if (pEvent->button() == Qt::LeftButton && geometry().contains(pEvent->position().toPoint()) && m_fFullSizeSelection)
172 m_fStartDragging = true;
173 /* Call to base-class: */
174 else
175 QLabel::mousePressEvent(pEvent);
176}
177
178void QILabel::mouseReleaseEvent(QMouseEvent *pEvent)
179{
180 /* Reset dragging: */
181 m_fStartDragging = false;
182 /* Call to base-class: */
183 QLabel::mouseReleaseEvent(pEvent);
184}
185
186void QILabel::mouseMoveEvent(QMouseEvent *pEvent)
187{
188 /* If we have an order to start dragging: */
189 if (m_fStartDragging)
190 {
191 /* Reset dragging: */
192 m_fStartDragging = false;
193 /* Create a drag object out of the given data: */
194 QDrag *pDrag = new QDrag(this);
195 QMimeData *pMimeData = new QMimeData;
196 pMimeData->setText(removeHtmlTags(m_strText));
197 pDrag->setMimeData(pMimeData);
198 /* Start the dragging finally: */
199 pDrag->exec();
200 }
201 /* Call to base-class: */
202 else
203 QLabel::mouseMoveEvent(pEvent);
204}
205
206void QILabel::contextMenuEvent(QContextMenuEvent *pEvent)
207{
208 /* If we have an order for full-size selection: */
209 if (m_fFullSizeSelection)
210 {
211 /* Create a context menu for the copy to clipboard action: */
212 QMenu menu;
213 m_pCopyAction->setText(tr("&Copy"));
214 menu.addAction(m_pCopyAction);
215 menu.exec(pEvent->globalPos());
216 }
217 /* Call to base-class: */
218 else
219 QLabel::contextMenuEvent(pEvent);
220}
221
222void QILabel::focusInEvent(QFocusEvent *)
223{
224 /* If we have an order for full-size selection: */
225 if (m_fFullSizeSelection)
226 {
227 /* Set the text color to the current used highlight text color: */
228 QPalette pal = qApp->palette();
229 pal.setBrush(QPalette::WindowText, pal.brush(QPalette::HighlightedText));
230 setPalette(pal);
231 }
232}
233
234void QILabel::focusOutEvent(QFocusEvent *pEvent)
235{
236 /* Reset to the default palette: */
237 if (m_fFullSizeSelection && pEvent->reason() != Qt::PopupFocusReason)
238 setPalette(qApp->palette());
239}
240
241void QILabel::paintEvent(QPaintEvent *pEvent)
242{
243 /* Call to base-class: */
244 QLabel::paintEvent(pEvent);
245
246 /* If we have an order for full-size selection and have focus: */
247 if (m_fFullSizeSelection && hasFocus())
248 {
249 /* Paint a focus rect based on the current style: */
250 QPainter painter(this);
251 QStyleOptionFocusRect option;
252 option.initFrom(this);
253 style()->drawPrimitive(QStyle::PE_FrameFocusRect, &option, &painter, this);
254 }
255}
256
257void QILabel::init()
258{
259 /* Initial setup: */
260 m_fHintValid = false;
261 m_iWidthHint = -1;
262 m_fStartDragging = false;
263 setFullSizeSelection(false);
264 setOpenExternalLinks(true);
265
266 /* Create invisible copy action: */
267 m_pCopyAction = new QAction(this);
268 if (m_pCopyAction)
269 {
270 /* Configure action: */
271 m_pCopyAction->setShortcut(QKeySequence(QKeySequence::Copy));
272 m_pCopyAction->setShortcutContext(Qt::WidgetShortcut);
273 connect(m_pCopyAction, &QAction::triggered, this, &QILabel::copy);
274 /* Add action to label: */
275 addAction(m_pCopyAction);
276 }
277}
278
279void QILabel::updateSizeHint() const
280{
281 /* Recalculate size-hint if necessary: */
282 m_ownSizeHint = m_iWidthHint == -1 ? QSize() : QSize(m_iWidthHint, heightForWidth(m_iWidthHint));
283 m_fHintValid = true;
284}
285
286void QILabel::setFullText(const QString &strText)
287{
288 /* Reapply size-policy: */
289 QSizePolicy sp = sizePolicy();
290 sp.setHeightForWidth(wordWrap());
291 setSizePolicy(sp);
292
293 /* Reset size-hint validity: */
294 m_fHintValid = false;
295
296 /* Remember new value: */
297 m_strText = strText;
298 updateText();
299}
300
301void QILabel::updateText()
302{
303 /* Compress text: */
304 const QString strCompText = compressText(m_strText);
305
306 /* Assign it: */
307 QLabel::setText(strCompText);
308
309 /* Only set the tool-tip if the text is shortened in any way: */
310 if (removeHtmlTags(strCompText) != removeHtmlTags(m_strText))
311 setToolTip(removeHtmlTags(m_strText));
312 else
313 setToolTip("");
314}
315
316QString QILabel::compressText(const QString &strText) const
317{
318 /* Prepare result: */
319 QStringList result;
320 QFontMetrics fm = fontMetrics();
321 /* Split up any multi-line text: */
322 foreach (QString strLine, strText.split(QRegularExpression("<br */?>")))
323 {
324 /* Search for the compact tag: */
325 const QRegularExpressionMatch mt = s_regExpElide.match(strLine);
326 if (mt.hasMatch())
327 {
328 /* USe the untouchable text to work on: */
329 const QString strWork = strLine;
330 /* Grep out the necessary info of the regexp: */
331 const QString strCompact = mt.captured(1);
332 const QString strElideMode = mt.captured(2);
333 const QString strElide = mt.captured(3);
334 /* Remove the whole compact tag (also the text): */
335 const QString strFlat = removeHtmlTags(QString(strWork).remove(strCompact));
336 /* What size will the text have without the compact text: */
337#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
338 const int iFlatWidth = fm.horizontalAdvance(strFlat);
339#else
340 const int iFlatWidth = fm.width(strFlat);
341#endif
342 /* Create the shortened text: */
343 const QString strNew = fm.elidedText(strElide, toTextElideMode(strElideMode), width() - (2 * HOR_PADDING) - iFlatWidth);
344 /* Replace the compact part with the shortened text in the initial string: */
345 strLine = QString(strWork).replace(strCompact, strNew);
346 }
347 /* Append the line: */
348 result << strLine;
349 }
350 /* Return result: */
351 return result.join("<br />");
352}
353
354/* static */
355QString QILabel::removeHtmlTags(const QString &strText)
356{
357 /* Remove all HTML tags from the text and return it: */
358 return QString(strText).remove(s_regExpCopy);
359}
360
361/* static */
362Qt::TextElideMode QILabel::toTextElideMode(const QString &strType)
363{
364 /* Converts a string-represented type to a Qt elide mode: */
365 Qt::TextElideMode enmMode = Qt::ElideNone;
366 if (strType == "start")
367 enmMode = Qt::ElideLeft;
368 else if (strType == "middle")
369 enmMode = Qt::ElideMiddle;
370 else if (strType == "end")
371 enmMode = Qt::ElideRight;
372 return enmMode;
373}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use