VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/extensions/QIRichTextLabel.cpp@ 104158

Last change on this file since 104158 was 98312, checked in by vboxsync, 2 years ago

FE/Qt: Implement Copy action for our QIRichTextLabel widget; Default one QTextEdit action was not triggerable by shortcut and was not respecting selection context.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.4 KB
Line 
1/* $Id: QIRichTextLabel.cpp 98312 2023-01-26 12:52:40Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - Qt extensions: QIRichTextLabel class implementation.
4 */
5
6/*
7 * Copyright (C) 2012-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#include <QAccessibleWidget>
30#include <QAction>
31#include <QApplication>
32#include <QClipboard>
33#include <QtMath>
34#include <QUrl>
35#include <QVBoxLayout>
36
37/* GUI includes: */
38#include "QIRichTextLabel.h"
39
40/* Other VBox includes: */
41#include "iprt/assert.h"
42
43/* Forward declarations: */
44class QIRichTextLabel;
45
46
47/** QAccessibleObject extension used as an accessibility interface for QIRichTextLabel. */
48class UIAccessibilityInterfaceForQIRichTextLabel : public QAccessibleWidget
49{
50public:
51
52 /** Returns an accessibility interface for passed @a strClassname and @a pObject. */
53 static QAccessibleInterface *pFactory(const QString &strClassname, QObject *pObject)
54 {
55 /* Creating QIRichTextLabel accessibility interface: */
56 if (pObject && strClassname == QLatin1String("QIRichTextLabel"))
57 return new UIAccessibilityInterfaceForQIRichTextLabel(qobject_cast<QWidget*>(pObject));
58
59 /* Null by default: */
60 return 0;
61 }
62
63 /** Constructs an accessibility interface passing @a pWidget to the base-class. */
64 UIAccessibilityInterfaceForQIRichTextLabel(QWidget *pWidget)
65 : QAccessibleWidget(pWidget, QAccessible::StaticText)
66 {}
67
68 /** Returns a text for the passed @a enmTextRole. */
69 virtual QString text(QAccessible::Text enmTextRole) const RT_OVERRIDE;
70
71private:
72
73 /** Returns corresponding QIRichTextLabel. */
74 QIRichTextLabel *label() const;
75};
76
77
78/*********************************************************************************************************************************
79* Class UIAccessibilityInterfaceForQIRichTextLabel implementation. *
80*********************************************************************************************************************************/
81
82QString UIAccessibilityInterfaceForQIRichTextLabel::text(QAccessible::Text enmTextRole) const
83{
84 /* Make sure label still alive: */
85 AssertPtrReturn(label(), QString());
86
87 /* Return the description: */
88 if (enmTextRole == QAccessible::Description)
89 return label()->plainText();
90
91 /* Null-string by default: */
92 return QString();
93}
94
95QIRichTextLabel *UIAccessibilityInterfaceForQIRichTextLabel::label() const
96{
97 return qobject_cast<QIRichTextLabel*>(widget());
98}
99
100
101/*********************************************************************************************************************************
102* Class QIRichTextLabel implementation. *
103*********************************************************************************************************************************/
104
105QIRichTextLabel::QIRichTextLabel(QWidget *pParent)
106 : QIWithRetranslateUI<QWidget>(pParent)
107 , m_pTextBrowser()
108 , m_pActionCopy(0)
109 , m_fCopyAvailable(false)
110 , m_iMinimumTextWidth(0)
111{
112 /* Install QIRichTextLabel accessibility interface factory: */
113 QAccessible::installFactory(UIAccessibilityInterfaceForQIRichTextLabel::pFactory);
114
115 /* Configure self: */
116 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
117
118 /* Create main layout: */
119 QVBoxLayout *pMainLayout = new QVBoxLayout(this);
120 if (pMainLayout)
121 {
122 /* Configure layout: */
123 pMainLayout->setContentsMargins(0, 0, 0, 0);
124
125 /* Create text-browser: */
126 m_pTextBrowser = new QTextBrowser;
127 if (m_pTextBrowser)
128 {
129 /* Configure text-browser: */
130 m_pTextBrowser->setReadOnly(true);
131 m_pTextBrowser->setFocusPolicy(Qt::ClickFocus);
132 m_pTextBrowser->setFrameShape(QFrame::NoFrame);
133 m_pTextBrowser->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
134 m_pTextBrowser->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
135 m_pTextBrowser->setContextMenuPolicy(Qt::ActionsContextMenu);
136 m_pTextBrowser->setOpenExternalLinks(true);
137
138 /* Tune text-browser viewport palette: */
139 m_pTextBrowser->viewport()->setAutoFillBackground(false);
140 QPalette pal = m_pTextBrowser->viewport()->palette();
141 pal.setColor(QPalette::Active, QPalette::Text, pal.color(QPalette::Active, QPalette::WindowText));
142 pal.setColor(QPalette::Inactive, QPalette::Text, pal.color(QPalette::Inactive, QPalette::WindowText));
143 pal.setColor(QPalette::Disabled, QPalette::Text, pal.color(QPalette::Disabled, QPalette::WindowText));
144 m_pTextBrowser->viewport()->setPalette(pal);
145
146 /* Setup connections finally: */
147 connect(m_pTextBrowser, &QTextBrowser::anchorClicked, this, &QIRichTextLabel::sigLinkClicked);
148 connect(m_pTextBrowser, &QTextBrowser::copyAvailable, this, &QIRichTextLabel::sltHandleCopyAvailable);
149
150 /* Create context-menu copy action for text-browser: */
151 m_pActionCopy = new QAction(m_pTextBrowser);
152 if (m_pActionCopy)
153 {
154 m_pActionCopy->setShortcut(QKeySequence(QKeySequence::Copy));
155 m_pActionCopy->setShortcutContext(Qt::WidgetShortcut);
156 connect(m_pActionCopy, &QAction::triggered, this, &QIRichTextLabel::copy);
157 m_pTextBrowser->addAction(m_pActionCopy);
158 }
159 }
160
161 /* Add into layout: */
162 pMainLayout->addWidget(m_pTextBrowser);
163 }
164
165 /* Apply language settings: */
166 retranslateUi();
167}
168
169QString QIRichTextLabel::text() const
170{
171 return m_pTextBrowser->toHtml();
172}
173
174QString QIRichTextLabel::plainText() const
175{
176 return m_pTextBrowser->toPlainText();
177}
178
179void QIRichTextLabel::registerImage(const QImage &image, const QString &strName)
180{
181 m_pTextBrowser->document()->addResource(QTextDocument::ImageResource, QUrl(strName), QVariant(image));
182}
183
184void QIRichTextLabel::registerPixmap(const QPixmap &pixmap, const QString &strName)
185{
186 m_pTextBrowser->document()->addResource(QTextDocument::ImageResource, QUrl(strName), QVariant(pixmap));
187}
188
189QTextOption::WrapMode QIRichTextLabel::wordWrapMode() const
190{
191 return m_pTextBrowser->wordWrapMode();
192}
193
194void QIRichTextLabel::setWordWrapMode(QTextOption::WrapMode policy)
195{
196 m_pTextBrowser->setWordWrapMode(policy);
197}
198
199void QIRichTextLabel::installEventFilter(QObject *pFilterObj)
200{
201 QWidget::installEventFilter(pFilterObj);
202 m_pTextBrowser->installEventFilter(pFilterObj);
203}
204
205QFont QIRichTextLabel::browserFont() const
206{
207 return m_pTextBrowser->font();
208}
209
210void QIRichTextLabel::setBrowserFont(const QFont &newFont)
211{
212 m_pTextBrowser->setFont(newFont);
213}
214
215int QIRichTextLabel::minimumTextWidth() const
216{
217 return m_iMinimumTextWidth;
218}
219
220void QIRichTextLabel::setMinimumTextWidth(int iMinimumTextWidth)
221{
222 /* Remember minimum text width: */
223 m_iMinimumTextWidth = iMinimumTextWidth;
224
225 /* Get corresponding QTextDocument: */
226 QTextDocument *pTextDocument = m_pTextBrowser->document();
227 /* Bug in QTextDocument (?) : setTextWidth doesn't work from the first time. */
228 for (int iTry = 0; pTextDocument->textWidth() != m_iMinimumTextWidth && iTry < 3; ++iTry)
229 pTextDocument->setTextWidth(m_iMinimumTextWidth);
230 /* Get corresponding QTextDocument size: */
231 QSize size = pTextDocument->size().toSize();
232
233 /* Resize to content size: */
234 m_pTextBrowser->setMinimumSize(size);
235 layout()->activate();
236}
237
238void QIRichTextLabel::setText(const QString &strText)
239{
240 /* Set text: */
241 m_pTextBrowser->setHtml(strText);
242
243 /* Get corresponding QTextDocument: */
244 QTextDocument *pTextDocument = m_pTextBrowser->document();
245
246 // WORKAROUND:
247 // Ok, here is the trick. In Qt 5.6.x initial QTextDocument size is always 0x0
248 // even if contents present. To make QTextDocument calculate initial size we
249 // need to pass it some initial text-width, that way size should be calualated
250 // on the basis of passed width. No idea why but in Qt 5.6.x first calculated
251 // size doesn't actually linked to initially passed text-width, somehow it
252 // always have 640px width and various height which depends on currently set
253 // contents. So, we just using 640px as initial text-width.
254 pTextDocument->setTextWidth(640);
255
256 /* Now get that initial size which is 640xY, and propose new text-width as 4/3
257 * of hypothetical width current content would have laid out as square: */
258 const QSize oldSize = pTextDocument->size().toSize();
259 const int iProposedWidth = qSqrt(oldSize.width() * oldSize.height()) * 4 / 3;
260 pTextDocument->setTextWidth(iProposedWidth);
261
262 /* Get effective QTextDocument size: */
263 const QSize newSize = pTextDocument->size().toSize();
264
265 /* Set minimum text width to corresponding value: */
266 setMinimumTextWidth(m_iMinimumTextWidth == 0 ? newSize.width() : m_iMinimumTextWidth);
267}
268
269void QIRichTextLabel::copy()
270{
271 // WORKAROUND:
272 // We should distinguish whether copy() is available or not.
273 // If it is, we can use QTextBrowser::copy() directly to
274 // copy selected part of text. Otherwise we have to use
275 // QTextBrowser::toPlainText() to get the whole desirable
276 // text and put it to QClipboard ourselves.
277 if (m_fCopyAvailable)
278 m_pTextBrowser->copy();
279 else
280 {
281 /* Copy the current text to the global and selection clipboards: */
282 const QString strText = m_pTextBrowser->toPlainText();
283 QApplication::clipboard()->setText(strText, QClipboard::Clipboard);
284 QApplication::clipboard()->setText(strText, QClipboard::Selection);
285 }
286}
287
288void QIRichTextLabel::retranslateUi()
289{
290 if (m_pActionCopy)
291 m_pActionCopy->setText(tr("&Copy"));
292}
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