VirtualBox

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

Last change on this file since 100347 was 100344, checked in by vboxsync, 19 months ago

FE/Qt: bugref:10450: Qt6 compatibility bits for QMouseEvent; Replacing QMouseEvent::pos with QSinglePointEvent::position; S.a. r157776.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.3 KB
Line 
1/* $Id: QILabel.cpp 100344 2023-07-03 10:09:28Z 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("<[^>]*>");
53QRegExp QILabel::s_regExpElide = QRegExp("(<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#ifndef VBOX_IS_QT6_OR_LATER /* QMouseEvent::pos was replaced with QSinglePointEvent::position in Qt6 */
172 if (pEvent->button() == Qt::LeftButton && geometry().contains(pEvent->pos()) && m_fFullSizeSelection)
173#else
174 if (pEvent->button() == Qt::LeftButton && geometry().contains(pEvent->position().toPoint()) && m_fFullSizeSelection)
175#endif
176 m_fStartDragging = true;
177 /* Call to base-class: */
178 else
179 QLabel::mousePressEvent(pEvent);
180}
181
182void QILabel::mouseReleaseEvent(QMouseEvent *pEvent)
183{
184 /* Reset dragging: */
185 m_fStartDragging = false;
186 /* Call to base-class: */
187 QLabel::mouseReleaseEvent(pEvent);
188}
189
190void QILabel::mouseMoveEvent(QMouseEvent *pEvent)
191{
192 /* If we have an order to start dragging: */
193 if (m_fStartDragging)
194 {
195 /* Reset dragging: */
196 m_fStartDragging = false;
197 /* Create a drag object out of the given data: */
198 QDrag *pDrag = new QDrag(this);
199 QMimeData *pMimeData = new QMimeData;
200 pMimeData->setText(removeHtmlTags(m_strText));
201 pDrag->setMimeData(pMimeData);
202 /* Start the dragging finally: */
203 pDrag->exec();
204 }
205 /* Call to base-class: */
206 else
207 QLabel::mouseMoveEvent(pEvent);
208}
209
210void QILabel::contextMenuEvent(QContextMenuEvent *pEvent)
211{
212 /* If we have an order for full-size selection: */
213 if (m_fFullSizeSelection)
214 {
215 /* Create a context menu for the copy to clipboard action: */
216 QMenu menu;
217 m_pCopyAction->setText(tr("&Copy"));
218 menu.addAction(m_pCopyAction);
219 menu.exec(pEvent->globalPos());
220 }
221 /* Call to base-class: */
222 else
223 QLabel::contextMenuEvent(pEvent);
224}
225
226void QILabel::focusInEvent(QFocusEvent *)
227{
228 /* If we have an order for full-size selection: */
229 if (m_fFullSizeSelection)
230 {
231 /* Set the text color to the current used highlight text color: */
232 QPalette pal = qApp->palette();
233 pal.setBrush(QPalette::WindowText, pal.brush(QPalette::HighlightedText));
234 setPalette(pal);
235 }
236}
237
238void QILabel::focusOutEvent(QFocusEvent *pEvent)
239{
240 /* Reset to the default palette: */
241 if (m_fFullSizeSelection && pEvent->reason() != Qt::PopupFocusReason)
242 setPalette(qApp->palette());
243}
244
245void QILabel::paintEvent(QPaintEvent *pEvent)
246{
247 /* Call to base-class: */
248 QLabel::paintEvent(pEvent);
249
250 /* If we have an order for full-size selection and have focus: */
251 if (m_fFullSizeSelection && hasFocus())
252 {
253 /* Paint a focus rect based on the current style: */
254 QPainter painter(this);
255 QStyleOptionFocusRect option;
256 option.initFrom(this);
257 style()->drawPrimitive(QStyle::PE_FrameFocusRect, &option, &painter, this);
258 }
259}
260
261void QILabel::init()
262{
263 /* Initial setup: */
264 m_fHintValid = false;
265 m_iWidthHint = -1;
266 m_fStartDragging = false;
267 setFullSizeSelection(false);
268 setOpenExternalLinks(true);
269
270 /* Create invisible copy action: */
271 m_pCopyAction = new QAction(this);
272 if (m_pCopyAction)
273 {
274 /* Configure action: */
275 m_pCopyAction->setShortcut(QKeySequence(QKeySequence::Copy));
276 m_pCopyAction->setShortcutContext(Qt::WidgetShortcut);
277 connect(m_pCopyAction, &QAction::triggered, this, &QILabel::copy);
278 /* Add action to label: */
279 addAction(m_pCopyAction);
280 }
281}
282
283void QILabel::updateSizeHint() const
284{
285 /* Recalculate size-hint if necessary: */
286 m_ownSizeHint = m_iWidthHint == -1 ? QSize() : QSize(m_iWidthHint, heightForWidth(m_iWidthHint));
287 m_fHintValid = true;
288}
289
290void QILabel::setFullText(const QString &strText)
291{
292 /* Reapply size-policy: */
293 QSizePolicy sp = sizePolicy();
294 sp.setHeightForWidth(wordWrap());
295 setSizePolicy(sp);
296
297 /* Reset size-hint validity: */
298 m_fHintValid = false;
299
300 /* Remember new value: */
301 m_strText = strText;
302 updateText();
303}
304
305void QILabel::updateText()
306{
307 /* Compress text: */
308 const QString strCompText = compressText(m_strText);
309
310 /* Assign it: */
311 QLabel::setText(strCompText);
312
313 /* Only set the tool-tip if the text is shortened in any way: */
314 if (removeHtmlTags(strCompText) != removeHtmlTags(m_strText))
315 setToolTip(removeHtmlTags(m_strText));
316 else
317 setToolTip("");
318}
319
320QString QILabel::compressText(const QString &strText) const
321{
322 /* Prepare result: */
323 QStringList result;
324 QFontMetrics fm = fontMetrics();
325 /* Split up any multi-line text: */
326 foreach (QString strLine, strText.split(QRegularExpression("<br */?>")))
327 {
328 /* Search for the compact tag: */
329 if (s_regExpElide.indexIn(strLine) > -1)
330 {
331 /* USe the untouchable text to work on: */
332 const QString strWork = strLine;
333 /* Grep out the necessary info of the regexp: */
334 const QString strCompact = s_regExpElide.cap(1);
335 const QString strElideMode = s_regExpElide.cap(2);
336 const QString strElide = s_regExpElide.cap(3);
337 /* Remove the whole compact tag (also the text): */
338 const QString strFlat = removeHtmlTags(QString(strWork).remove(strCompact));
339 /* What size will the text have without the compact text: */
340#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
341 const int iFlatWidth = fm.horizontalAdvance(strFlat);
342#else
343 const int iFlatWidth = fm.width(strFlat);
344#endif
345 /* Create the shortened text: */
346 const QString strNew = fm.elidedText(strElide, toTextElideMode(strElideMode), width() - (2 * HOR_PADDING) - iFlatWidth);
347 /* Replace the compact part with the shortened text in the initial string: */
348 strLine = QString(strWork).replace(strCompact, strNew);
349 }
350 /* Append the line: */
351 result << strLine;
352 }
353 /* Return result: */
354 return result.join("<br />");
355}
356
357/* static */
358QString QILabel::removeHtmlTags(const QString &strText)
359{
360 /* Remove all HTML tags from the text and return it: */
361 return QString(strText).remove(s_regExpCopy);
362}
363
364/* static */
365Qt::TextElideMode QILabel::toTextElideMode(const QString &strType)
366{
367 /* Converts a string-represented type to a Qt elide mode: */
368 Qt::TextElideMode enmMode = Qt::ElideNone;
369 if (strType == "start")
370 enmMode = Qt::ElideLeft;
371 else if (strType == "middle")
372 enmMode = Qt::ElideMiddle;
373 else if (strType == "end")
374 enmMode = Qt::ElideRight;
375 return enmMode;
376}
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