VirtualBox

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

Last change on this file was 104358, checked in by vboxsync, 5 weeks ago

FE/Qt. bugref:10622. More refactoring around the retranslation functionality.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use