1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtWidgets module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qpainter.h"
41#include "qevent.h"
42#include "qdrawutil.h"
43#include "qapplication.h"
44#if QT_CONFIG(abstractbutton)
45#include "qabstractbutton.h"
46#endif
47#include "qstyle.h"
48#include "qstyleoption.h"
49#include <limits.h>
50#include "qaction.h"
51#include "qclipboard.h"
52#include <qdebug.h>
53#include <qurl.h>
54#include "qlabel_p.h"
55#include "private/qstylesheetstyle_p.h"
56#include <qmath.h>
57
58#ifndef QT_NO_ACCESSIBILITY
59#include <qaccessible.h>
60#endif
61
62QT_BEGIN_NAMESPACE
63
64QLabelPrivate::QLabelPrivate()
65 : QFramePrivate(),
66 sh(),
67 msh(),
68 text(),
69 pixmap(nullptr),
70 scaledpixmap(nullptr),
71 cachedimage(nullptr),
72#ifndef QT_NO_PICTURE
73 picture(nullptr),
74#endif
75#if QT_CONFIG(movie)
76 movie(),
77#endif
78 control(nullptr),
79 shortcutCursor(),
80#ifndef QT_NO_CURSOR
81 cursor(),
82#endif
83#ifndef QT_NO_SHORTCUT
84 buddy(),
85 shortcutId(0),
86#endif
87 textformat(Qt::AutoText),
88 textInteractionFlags(Qt::LinksAccessibleByMouse),
89 sizePolicy(),
90 margin(0),
91 align(Qt::AlignLeft | Qt::AlignVCenter | Qt::TextExpandTabs),
92 indent(-1),
93 valid_hints(false),
94 scaledcontents(false),
95 textLayoutDirty(false),
96 textDirty(false),
97 isRichText(false),
98 isTextLabel(false),
99 hasShortcut(/*???*/),
100#ifndef QT_NO_CURSOR
101 validCursor(false),
102 onAnchor(false),
103#endif
104 openExternalLinks(false)
105{
106}
107
108QLabelPrivate::~QLabelPrivate()
109{
110}
111
112/*!
113 \class QLabel
114 \brief The QLabel widget provides a text or image display.
115
116 \ingroup basicwidgets
117 \inmodule QtWidgets
118
119 \image windows-label.png
120
121 QLabel is used for displaying text or an image. No user
122 interaction functionality is provided. The visual appearance of
123 the label can be configured in various ways, and it can be used
124 for specifying a focus mnemonic key for another widget.
125
126 A QLabel can contain any of the following content types:
127
128 \table
129 \header \li Content \li Setting
130 \row \li Plain text
131 \li Pass a QString to setText().
132 \row \li Rich text
133 \li Pass a QString that contains rich text to setText().
134 \row \li A pixmap
135 \li Pass a QPixmap to setPixmap().
136 \row \li A movie
137 \li Pass a QMovie to setMovie().
138 \row \li A number
139 \li Pass an \e int or a \e double to setNum(), which converts
140 the number to plain text.
141 \row \li Nothing
142 \li The same as an empty plain text. This is the default. Set
143 by clear().
144 \endtable
145
146 \warning When passing a QString to the constructor or calling setText(),
147 make sure to sanitize your input, as QLabel tries to guess whether it
148 displays the text as plain text or as rich text, a subset of HTML 4
149 markup. You may want to call
150 setTextFormat() explicitly, e.g. in case you expect the text to be in
151 plain format but cannot control the text source (for instance when
152 displaying data loaded from the Web).
153
154 When the content is changed using any of these functions, any
155 previous content is cleared.
156
157 By default, labels display \l{alignment}{left-aligned, vertically-centered}
158 text and images, where any tabs in the text to be displayed are
159 \l{Qt::TextExpandTabs}{automatically expanded}. However, the look
160 of a QLabel can be adjusted and fine-tuned in several ways.
161
162 The positioning of the content within the QLabel widget area can
163 be tuned with setAlignment() and setIndent(). Text content can
164 also wrap lines along word boundaries with setWordWrap(). For
165 example, this code sets up a sunken panel with a two-line text in
166 the bottom right corner (both lines being flush with the right
167 side of the label):
168
169 \snippet code/src_gui_widgets_qlabel.cpp 0
170
171 The properties and functions QLabel inherits from QFrame can also
172 be used to specify the widget frame to be used for any given label.
173
174 A QLabel is often used as a label for an interactive widget. For
175 this use QLabel provides a useful mechanism for adding an
176 mnemonic (see QKeySequence) that will set the keyboard focus to
177 the other widget (called the QLabel's "buddy"). For example:
178
179 \snippet code/src_gui_widgets_qlabel.cpp 1
180
181 In this example, keyboard focus is transferred to the label's
182 buddy (the QLineEdit) when the user presses Alt+P. If the buddy
183 was a button (inheriting from QAbstractButton), triggering the
184 mnemonic would emulate a button click.
185
186 \sa QLineEdit, QTextEdit, QPixmap, QMovie,
187 {fowler}{GUI Design Handbook: Label}
188*/
189
190#ifndef QT_NO_PICTURE
191/*!
192 Returns the label's picture or nullptr if the label doesn't have a
193 picture.
194*/
195
196const QPicture *QLabel::picture() const
197{
198 Q_D(const QLabel);
199 return d->picture;
200}
201#endif
202
203
204/*!
205 Constructs an empty label.
206
207 The \a parent and widget flag \a f, arguments are passed
208 to the QFrame constructor.
209
210 \sa setAlignment(), setFrameStyle(), setIndent()
211*/
212QLabel::QLabel(QWidget *parent, Qt::WindowFlags f)
213 : QFrame(*new QLabelPrivate(), parent, f)
214{
215 Q_D(QLabel);
216 d->init();
217}
218
219/*!
220 Constructs a label that displays the text, \a text.
221
222 The \a parent and widget flag \a f, arguments are passed
223 to the QFrame constructor.
224
225 \sa setText(), setAlignment(), setFrameStyle(), setIndent()
226*/
227QLabel::QLabel(const QString &text, QWidget *parent, Qt::WindowFlags f)
228 : QLabel(parent, f)
229{
230 setText(text);
231}
232
233
234
235/*!
236 Destroys the label.
237*/
238
239QLabel::~QLabel()
240{
241 Q_D(QLabel);
242 d->clearContents();
243}
244
245void QLabelPrivate::init()
246{
247 Q_Q(QLabel);
248
249 q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred,
250 QSizePolicy::Label));
251 setLayoutItemMargins(QStyle::SE_LabelLayoutItem);
252}
253
254
255/*!
256 \property QLabel::text
257 \brief the label's text
258
259 If no text has been set this will return an empty string. Setting
260 the text clears any previous content.
261
262 The text will be interpreted either as plain text or as rich
263 text, depending on the text format setting; see setTextFormat().
264 The default setting is Qt::AutoText; i.e. QLabel will try to
265 auto-detect the format of the text set.
266 See \l {Supported HTML Subset} for the definition of rich text.
267
268 If a buddy has been set, the buddy mnemonic key is updated
269 from the new text.
270
271 Note that QLabel is well-suited to display small rich text
272 documents, such as small documents that get their document
273 specific settings (font, text color, link color) from the label's
274 palette and font properties. For large documents, use QTextEdit
275 in read-only mode instead. QTextEdit can also provide a scroll bar
276 when necessary.
277
278 \note This function enables mouse tracking if \a text contains rich
279 text.
280
281 \sa setTextFormat(), setBuddy(), alignment
282*/
283
284void QLabel::setText(const QString &text)
285{
286 Q_D(QLabel);
287 if (d->text == text)
288 return;
289
290 QWidgetTextControl *oldControl = d->control;
291 d->control = nullptr;
292
293 d->clearContents();
294 d->text = text;
295 d->isTextLabel = true;
296 d->textDirty = true;
297 d->isRichText = d->textformat == Qt::RichText
298 || (d->textformat == Qt::AutoText && Qt::mightBeRichText(d->text));
299
300 d->control = oldControl;
301
302 if (d->needTextControl()) {
303 d->ensureTextControl();
304 } else {
305 delete d->control;
306 d->control = nullptr;
307 }
308
309 if (d->isRichText) {
310 setMouseTracking(true);
311 } else {
312 // Note: mouse tracking not disabled intentionally
313 }
314
315#ifndef QT_NO_SHORTCUT
316 if (d->buddy)
317 d->updateShortcut();
318#endif
319
320 d->updateLabel();
321
322#ifndef QT_NO_ACCESSIBILITY
323 if (accessibleName().isEmpty()) {
324 QAccessibleEvent event(this, QAccessible::NameChanged);
325 QAccessible::updateAccessibility(&event);
326 }
327#endif
328}
329
330QString QLabel::text() const
331{
332 Q_D(const QLabel);
333 return d->text;
334}
335
336/*!
337 Clears any label contents.
338*/
339
340void QLabel::clear()
341{
342 Q_D(QLabel);
343 d->clearContents();
344 d->updateLabel();
345}
346
347/*!
348 \property QLabel::pixmap
349 \brief the label's pixmap
350
351 If no pixmap has been set this will return nullptr.
352
353 Setting the pixmap clears any previous content. The buddy
354 shortcut, if any, is disabled.
355*/
356void QLabel::setPixmap(const QPixmap &pixmap)
357{
358 Q_D(QLabel);
359 if (!d->pixmap || d->pixmap->cacheKey() != pixmap.cacheKey()) {
360 d->clearContents();
361 d->pixmap = new QPixmap(pixmap);
362 }
363
364 if (d->pixmap->depth() == 1 && !d->pixmap->mask())
365 d->pixmap->setMask(*((QBitmap *)d->pixmap));
366
367 d->updateLabel();
368}
369
370const QPixmap *QLabel::pixmap() const
371{
372 Q_D(const QLabel);
373 return d->pixmap;
374}
375
376#ifndef QT_NO_PICTURE
377/*!
378 Sets the label contents to \a picture. Any previous content is
379 cleared.
380
381 The buddy shortcut, if any, is disabled.
382
383 \sa picture(), setBuddy()
384*/
385
386void QLabel::setPicture(const QPicture &picture)
387{
388 Q_D(QLabel);
389 d->clearContents();
390 d->picture = new QPicture(picture);
391
392 d->updateLabel();
393}
394#endif // QT_NO_PICTURE
395
396/*!
397 Sets the label contents to plain text containing the textual
398 representation of integer \a num. Any previous content is cleared.
399 Does nothing if the integer's string representation is the same as
400 the current contents of the label.
401
402 The buddy shortcut, if any, is disabled.
403
404 \sa setText(), QString::setNum(), setBuddy()
405*/
406
407void QLabel::setNum(int num)
408{
409 QString str;
410 str.setNum(num);
411 setText(str);
412}
413
414/*!
415 \overload
416
417 Sets the label contents to plain text containing the textual
418 representation of double \a num. Any previous content is cleared.
419 Does nothing if the double's string representation is the same as
420 the current contents of the label.
421
422 The buddy shortcut, if any, is disabled.
423
424 \sa setText(), QString::setNum(), setBuddy()
425*/
426
427void QLabel::setNum(double num)
428{
429 QString str;
430 str.setNum(num);
431 setText(str);
432}
433
434/*!
435 \property QLabel::alignment
436 \brief the alignment of the label's contents
437
438 By default, the contents of the label are left-aligned and vertically-centered.
439
440 \sa text
441*/
442
443void QLabel::setAlignment(Qt::Alignment alignment)
444{
445 Q_D(QLabel);
446 if (alignment == (d->align & (Qt::AlignVertical_Mask|Qt::AlignHorizontal_Mask)))
447 return;
448 d->align = (d->align & ~(Qt::AlignVertical_Mask|Qt::AlignHorizontal_Mask))
449 | (alignment & (Qt::AlignVertical_Mask|Qt::AlignHorizontal_Mask));
450
451 d->updateLabel();
452}
453
454
455Qt::Alignment QLabel::alignment() const
456{
457 Q_D(const QLabel);
458 return QFlag(d->align & (Qt::AlignVertical_Mask|Qt::AlignHorizontal_Mask));
459}
460
461
462/*!
463 \property QLabel::wordWrap
464 \brief the label's word-wrapping policy
465
466 If this property is \c true then label text is wrapped where
467 necessary at word-breaks; otherwise it is not wrapped at all.
468
469 By default, word wrap is disabled.
470
471 \sa text
472*/
473void QLabel::setWordWrap(bool on)
474{
475 Q_D(QLabel);
476 if (on)
477 d->align |= Qt::TextWordWrap;
478 else
479 d->align &= ~Qt::TextWordWrap;
480
481 d->updateLabel();
482}
483
484bool QLabel::wordWrap() const
485{
486 Q_D(const QLabel);
487 return d->align & Qt::TextWordWrap;
488}
489
490/*!
491 \property QLabel::indent
492 \brief the label's text indent in pixels
493
494 If a label displays text, the indent applies to the left edge if
495 alignment() is Qt::AlignLeft, to the right edge if alignment() is
496 Qt::AlignRight, to the top edge if alignment() is Qt::AlignTop, and
497 to the bottom edge if alignment() is Qt::AlignBottom.
498
499 If indent is negative, or if no indent has been set, the label
500 computes the effective indent as follows: If frameWidth() is 0,
501 the effective indent becomes 0. If frameWidth() is greater than 0,
502 the effective indent becomes half the width of the "x" character
503 of the widget's current font().
504
505 By default, the indent is -1, meaning that an effective indent is
506 calculating in the manner described above.
507
508 \sa alignment, margin, frameWidth(), font()
509*/
510
511void QLabel::setIndent(int indent)
512{
513 Q_D(QLabel);
514 d->indent = indent;
515 d->updateLabel();
516}
517
518int QLabel::indent() const
519{
520 Q_D(const QLabel);
521 return d->indent;
522}
523
524
525/*!
526 \property QLabel::margin
527 \brief the width of the margin
528
529 The margin is the distance between the innermost pixel of the
530 frame and the outermost pixel of contents.
531
532 The default margin is 0.
533
534 \sa indent
535*/
536int QLabel::margin() const
537{
538 Q_D(const QLabel);
539 return d->margin;
540}
541
542void QLabel::setMargin(int margin)
543{
544 Q_D(QLabel);
545 if (d->margin == margin)
546 return;
547 d->margin = margin;
548 d->updateLabel();
549}
550
551/*!
552 Returns the size that will be used if the width of the label is \a
553 w. If \a w is -1, the sizeHint() is returned. If \a w is 0 minimumSizeHint() is returned
554*/
555QSize QLabelPrivate::sizeForWidth(int w) const
556{
557 Q_Q(const QLabel);
558 if(q->minimumWidth() > 0)
559 w = qMax(w, q->minimumWidth());
560 QSize contentsMargin(leftmargin + rightmargin, topmargin + bottommargin);
561
562 QRect br;
563
564 int hextra = 2 * margin;
565 int vextra = hextra;
566 QFontMetrics fm = q->fontMetrics();
567
568 if (pixmap && !pixmap->isNull()) {
569 br = pixmap->rect();
570 br.setSize(br.size() / pixmap->devicePixelRatio());
571#ifndef QT_NO_PICTURE
572 } else if (picture && !picture->isNull()) {
573 br = picture->boundingRect();
574#endif
575#if QT_CONFIG(movie)
576 } else if (movie && !movie->currentPixmap().isNull()) {
577 br = movie->currentPixmap().rect();
578 br.setSize(br.size() / movie->currentPixmap().devicePixelRatio());
579#endif
580 } else if (isTextLabel) {
581 int align = QStyle::visualAlignment(textDirection(), QFlag(this->align));
582 // Add indentation
583 int m = indent;
584
585 if (m < 0 && q->frameWidth()) // no indent, but we do have a frame
586 m = fm.horizontalAdvance(QLatin1Char('x')) - margin*2;
587 if (m > 0) {
588 if ((align & Qt::AlignLeft) || (align & Qt::AlignRight))
589 hextra += m;
590 if ((align & Qt::AlignTop) || (align & Qt::AlignBottom))
591 vextra += m;
592 }
593
594 if (control) {
595 ensureTextLayouted();
596 const qreal oldTextWidth = control->textWidth();
597 // Calculate the length of document if w is the width
598 if (align & Qt::TextWordWrap) {
599 if (w >= 0) {
600 w = qMax(w-hextra-contentsMargin.width(), 0); // strip margin and indent
601 control->setTextWidth(w);
602 } else {
603 control->adjustSize();
604 }
605 } else {
606 control->setTextWidth(-1);
607 }
608
609 QSizeF controlSize = control->size();
610 br = QRect(QPoint(0, 0), QSize(qCeil(controlSize.width()), qCeil(controlSize.height())));
611
612 // restore state
613 control->setTextWidth(oldTextWidth);
614 } else {
615 // Turn off center alignment in order to avoid rounding errors for centering,
616 // since centering involves a division by 2. At the end, all we want is the size.
617 int flags = align & ~(Qt::AlignVCenter | Qt::AlignHCenter);
618 if (hasShortcut) {
619 flags |= Qt::TextShowMnemonic;
620 QStyleOption opt;
621 opt.initFrom(q);
622 if (!q->style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, q))
623 flags |= Qt::TextHideMnemonic;
624 }
625
626 bool tryWidth = (w < 0) && (align & Qt::TextWordWrap);
627 if (tryWidth)
628 w = qMin(fm.averageCharWidth() * 80, q->maximumSize().width());
629 else if (w < 0)
630 w = 2000;
631 w -= (hextra + contentsMargin.width());
632 br = fm.boundingRect(0, 0, w ,2000, flags, text);
633 if (tryWidth && br.height() < 4*fm.lineSpacing() && br.width() > w/2)
634 br = fm.boundingRect(0, 0, w/2, 2000, flags, text);
635 if (tryWidth && br.height() < 2*fm.lineSpacing() && br.width() > w/4)
636 br = fm.boundingRect(0, 0, w/4, 2000, flags, text);
637 }
638 } else {
639 br = QRect(QPoint(0, 0), QSize(fm.averageCharWidth(), fm.lineSpacing()));
640 }
641
642 const QSize contentsSize(br.width() + hextra, br.height() + vextra);
643 return (contentsSize + contentsMargin).expandedTo(q->minimumSize());
644}
645
646
647/*!
648 \reimp
649*/
650
651int QLabel::heightForWidth(int w) const
652{
653 Q_D(const QLabel);
654 if (d->isTextLabel)
655 return d->sizeForWidth(w).height();
656 return QWidget::heightForWidth(w);
657}
658
659/*!
660 \property QLabel::openExternalLinks
661 \since 4.2
662
663 Specifies whether QLabel should automatically open links using
664 QDesktopServices::openUrl() instead of emitting the
665 linkActivated() signal.
666
667 \b{Note:} The textInteractionFlags set on the label need to include
668 either LinksAccessibleByMouse or LinksAccessibleByKeyboard.
669
670 The default value is false.
671
672 \sa textInteractionFlags()
673*/
674bool QLabel::openExternalLinks() const
675{
676 Q_D(const QLabel);
677 return d->openExternalLinks;
678}
679
680void QLabel::setOpenExternalLinks(bool open)
681{
682 Q_D(QLabel);
683 d->openExternalLinks = open;
684 if (d->control)
685 d->control->setOpenExternalLinks(open);
686}
687
688/*!
689 \property QLabel::textInteractionFlags
690 \since 4.2
691
692 Specifies how the label should interact with user input if it displays text.
693
694 If the flags contain Qt::LinksAccessibleByKeyboard the focus policy is also
695 automatically set to Qt::StrongFocus. If Qt::TextSelectableByKeyboard is set
696 then the focus policy is set to Qt::ClickFocus.
697
698 The default value is Qt::LinksAccessibleByMouse.
699*/
700void QLabel::setTextInteractionFlags(Qt::TextInteractionFlags flags)
701{
702 Q_D(QLabel);
703 if (d->textInteractionFlags == flags)
704 return;
705 d->textInteractionFlags = flags;
706 if (flags & Qt::LinksAccessibleByKeyboard)
707 setFocusPolicy(Qt::StrongFocus);
708 else if (flags & (Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse))
709 setFocusPolicy(Qt::ClickFocus);
710 else
711 setFocusPolicy(Qt::NoFocus);
712
713 if (d->needTextControl()) {
714 d->ensureTextControl();
715 } else {
716 delete d->control;
717 d->control = nullptr;
718 }
719
720 if (d->control)
721 d->control->setTextInteractionFlags(d->textInteractionFlags);
722}
723
724Qt::TextInteractionFlags QLabel::textInteractionFlags() const
725{
726 Q_D(const QLabel);
727 return d->textInteractionFlags;
728}
729
730/*!
731 Selects text from position \a start and for \a length characters.
732
733 \sa selectedText()
734
735 \b{Note:} The textInteractionFlags set on the label need to include
736 either TextSelectableByMouse or TextSelectableByKeyboard.
737
738 \since 4.7
739*/
740void QLabel::setSelection(int start, int length)
741{
742 Q_D(QLabel);
743 if (d->control) {
744 d->ensureTextPopulated();
745 QTextCursor cursor = d->control->textCursor();
746 cursor.setPosition(start);
747 cursor.setPosition(start + length, QTextCursor::KeepAnchor);
748 d->control->setTextCursor(cursor);
749 }
750}
751
752/*!
753 \property QLabel::hasSelectedText
754 \brief whether there is any text selected
755
756 hasSelectedText() returns \c true if some or all of the text has been
757 selected by the user; otherwise returns \c false.
758
759 By default, this property is \c false.
760
761 \sa selectedText()
762
763 \b{Note:} The textInteractionFlags set on the label need to include
764 either TextSelectableByMouse or TextSelectableByKeyboard.
765
766 \since 4.7
767*/
768bool QLabel::hasSelectedText() const
769{
770 Q_D(const QLabel);
771 if (d->control)
772 return d->control->textCursor().hasSelection();
773 return false;
774}
775
776/*!
777 \property QLabel::selectedText
778 \brief the selected text
779
780 If there is no selected text this property's value is
781 an empty string.
782
783 By default, this property contains an empty string.
784
785 \sa hasSelectedText()
786
787 \b{Note:} The textInteractionFlags set on the label need to include
788 either TextSelectableByMouse or TextSelectableByKeyboard.
789
790 \since 4.7
791*/
792QString QLabel::selectedText() const
793{
794 Q_D(const QLabel);
795 if (d->control)
796 return d->control->textCursor().selectedText();
797 return QString();
798}
799
800/*!
801 selectionStart() returns the index of the first selected character in the
802 label or -1 if no text is selected.
803
804 \sa selectedText()
805
806 \b{Note:} The textInteractionFlags set on the label need to include
807 either TextSelectableByMouse or TextSelectableByKeyboard.
808
809 \since 4.7
810*/
811int QLabel::selectionStart() const
812{
813 Q_D(const QLabel);
814 if (d->control && d->control->textCursor().hasSelection())
815 return d->control->textCursor().selectionStart();
816 return -1;
817}
818
819/*!\reimp
820*/
821QSize QLabel::sizeHint() const
822{
823 Q_D(const QLabel);
824 if (!d->valid_hints)
825 (void) QLabel::minimumSizeHint();
826 return d->sh;
827}
828
829/*!
830 \reimp
831*/
832QSize QLabel::minimumSizeHint() const
833{
834 Q_D(const QLabel);
835 if (d->valid_hints) {
836 if (d->sizePolicy == sizePolicy())
837 return d->msh;
838 }
839
840 ensurePolished();
841 d->valid_hints = true;
842 d->sh = d->sizeForWidth(-1); // wrap ? golden ratio : min doc size
843 QSize msh(-1, -1);
844
845 if (!d->isTextLabel) {
846 msh = d->sh;
847 } else {
848 msh.rheight() = d->sizeForWidth(QWIDGETSIZE_MAX).height(); // height for one line
849 msh.rwidth() = d->sizeForWidth(0).width(); // wrap ? size of biggest word : min doc size
850 if (d->sh.height() < msh.height())
851 msh.rheight() = d->sh.height();
852 }
853 d->msh = msh;
854 d->sizePolicy = sizePolicy();
855 return msh;
856}
857
858/*!\reimp
859*/
860void QLabel::mousePressEvent(QMouseEvent *ev)
861{
862 Q_D(QLabel);
863 d->sendControlEvent(ev);
864}
865
866/*!\reimp
867*/
868void QLabel::mouseMoveEvent(QMouseEvent *ev)
869{
870 Q_D(QLabel);
871 d->sendControlEvent(ev);
872}
873
874/*!\reimp
875*/
876void QLabel::mouseReleaseEvent(QMouseEvent *ev)
877{
878 Q_D(QLabel);
879 d->sendControlEvent(ev);
880}
881
882#ifndef QT_NO_CONTEXTMENU
883/*!\reimp
884*/
885void QLabel::contextMenuEvent(QContextMenuEvent *ev)
886{
887 Q_D(QLabel);
888 if (!d->isTextLabel) {
889 ev->ignore();
890 return;
891 }
892 QMenu *menu = d->createStandardContextMenu(ev->pos());
893 if (!menu) {
894 ev->ignore();
895 return;
896 }
897 ev->accept();
898 menu->setAttribute(Qt::WA_DeleteOnClose);
899 menu->popup(ev->globalPos());
900}
901#endif // QT_NO_CONTEXTMENU
902
903/*!
904 \reimp
905*/
906void QLabel::focusInEvent(QFocusEvent *ev)
907{
908 Q_D(QLabel);
909 if (d->isTextLabel) {
910 d->ensureTextControl();
911 d->sendControlEvent(ev);
912 }
913 QFrame::focusInEvent(ev);
914}
915
916/*!
917 \reimp
918*/
919void QLabel::focusOutEvent(QFocusEvent *ev)
920{
921 Q_D(QLabel);
922 if (d->control) {
923 d->sendControlEvent(ev);
924 QTextCursor cursor = d->control->textCursor();
925 Qt::FocusReason reason = ev->reason();
926 if (reason != Qt::ActiveWindowFocusReason
927 && reason != Qt::PopupFocusReason
928 && cursor.hasSelection()) {
929 cursor.clearSelection();
930 d->control->setTextCursor(cursor);
931 }
932 }
933
934 QFrame::focusOutEvent(ev);
935}
936
937/*!\reimp
938*/
939bool QLabel::focusNextPrevChild(bool next)
940{
941 Q_D(QLabel);
942 if (d->control && d->control->setFocusToNextOrPreviousAnchor(next))
943 return true;
944 return QFrame::focusNextPrevChild(next);
945}
946
947/*!\reimp
948*/
949void QLabel::keyPressEvent(QKeyEvent *ev)
950{
951 Q_D(QLabel);
952 d->sendControlEvent(ev);
953}
954
955/*!\reimp
956*/
957bool QLabel::event(QEvent *e)
958{
959 Q_D(QLabel);
960 QEvent::Type type = e->type();
961
962#ifndef QT_NO_SHORTCUT
963 if (type == QEvent::Shortcut) {
964 QShortcutEvent *se = static_cast<QShortcutEvent *>(e);
965 if (se->shortcutId() == d->shortcutId) {
966 QWidget *w = d->buddy;
967 if (!w)
968 return QFrame::event(e);
969 if (w->focusPolicy() != Qt::NoFocus)
970 w->setFocus(Qt::ShortcutFocusReason);
971#if QT_CONFIG(abstractbutton)
972 QAbstractButton *button = qobject_cast<QAbstractButton *>(w);
973 if (button && !se->isAmbiguous())
974 button->animateClick();
975 else
976#endif
977 window()->setAttribute(Qt::WA_KeyboardFocusChange);
978 return true;
979 }
980 } else
981#endif
982 if (type == QEvent::Resize) {
983 if (d->control)
984 d->textLayoutDirty = true;
985 } else if (e->type() == QEvent::StyleChange
986#ifdef Q_OS_MAC
987 || e->type() == QEvent::MacSizeChange
988#endif
989 ) {
990 d->setLayoutItemMargins(QStyle::SE_LabelLayoutItem);
991 d->updateLabel();
992 }
993
994 return QFrame::event(e);
995}
996
997/*!\reimp
998*/
999void QLabel::paintEvent(QPaintEvent *)
1000{
1001 Q_D(QLabel);
1002 QStyle *style = QWidget::style();
1003 QPainter painter(this);
1004 drawFrame(&painter);
1005 QRect cr = contentsRect();
1006 cr.adjust(d->margin, d->margin, -d->margin, -d->margin);
1007 int align = QStyle::visualAlignment(d->isTextLabel ? d->textDirection()
1008 : layoutDirection(), QFlag(d->align));
1009
1010#if QT_CONFIG(movie)
1011 if (d->movie && !d->movie->currentPixmap().isNull()) {
1012 if (d->scaledcontents)
1013 style->drawItemPixmap(&painter, cr, align, d->movie->currentPixmap().scaled(cr.size()));
1014 else
1015 style->drawItemPixmap(&painter, cr, align, d->movie->currentPixmap());
1016 }
1017 else
1018#endif
1019 if (d->isTextLabel) {
1020 QRectF lr = d->layoutRect().toAlignedRect();
1021 QStyleOption opt;
1022 opt.initFrom(this);
1023#ifndef QT_NO_STYLE_STYLESHEET
1024 if (QStyleSheetStyle* cssStyle = qt_styleSheet(style))
1025 cssStyle->styleSheetPalette(this, &opt, &opt.palette);
1026#endif
1027 if (d->control) {
1028#ifndef QT_NO_SHORTCUT
1029 const bool underline = static_cast<bool>(style->styleHint(QStyle::SH_UnderlineShortcut,
1030 nullptr, this, nullptr));
1031 if (d->shortcutId != 0
1032 && underline != d->shortcutCursor.charFormat().fontUnderline()) {
1033 QTextCharFormat fmt;
1034 fmt.setFontUnderline(underline);
1035 d->shortcutCursor.mergeCharFormat(fmt);
1036 }
1037#endif
1038 d->ensureTextLayouted();
1039
1040 QAbstractTextDocumentLayout::PaintContext context;
1041 // Adjust the palette
1042 context.palette = opt.palette;
1043
1044 if (foregroundRole() != QPalette::Text && isEnabled())
1045 context.palette.setColor(QPalette::Text, context.palette.color(foregroundRole()));
1046
1047 painter.save();
1048 painter.translate(lr.topLeft());
1049 painter.setClipRect(lr.translated(-lr.x(), -lr.y()));
1050 d->control->setPalette(context.palette);
1051 d->control->drawContents(&painter, QRectF(), this);
1052 painter.restore();
1053 } else {
1054 int flags = align | (d->textDirection() == Qt::LeftToRight ? Qt::TextForceLeftToRight
1055 : Qt::TextForceRightToLeft);
1056 if (d->hasShortcut) {
1057 flags |= Qt::TextShowMnemonic;
1058 if (!style->styleHint(QStyle::SH_UnderlineShortcut, &opt, this))
1059 flags |= Qt::TextHideMnemonic;
1060 }
1061 style->drawItemText(&painter, lr.toRect(), flags, opt.palette, isEnabled(), d->text, foregroundRole());
1062 }
1063 } else
1064#ifndef QT_NO_PICTURE
1065 if (d->picture) {
1066 QRect br = d->picture->boundingRect();
1067 int rw = br.width();
1068 int rh = br.height();
1069 if (d->scaledcontents) {
1070 painter.save();
1071 painter.translate(cr.x(), cr.y());
1072 painter.scale((double)cr.width()/rw, (double)cr.height()/rh);
1073 painter.drawPicture(-br.x(), -br.y(), *d->picture);
1074 painter.restore();
1075 } else {
1076 int xo = 0;
1077 int yo = 0;
1078 if (align & Qt::AlignVCenter)
1079 yo = (cr.height()-rh)/2;
1080 else if (align & Qt::AlignBottom)
1081 yo = cr.height()-rh;
1082 if (align & Qt::AlignRight)
1083 xo = cr.width()-rw;
1084 else if (align & Qt::AlignHCenter)
1085 xo = (cr.width()-rw)/2;
1086 painter.drawPicture(cr.x()+xo-br.x(), cr.y()+yo-br.y(), *d->picture);
1087 }
1088 } else
1089#endif
1090 if (d->pixmap && !d->pixmap->isNull()) {
1091 QPixmap pix;
1092 if (d->scaledcontents) {
1093 QSize scaledSize = cr.size() * devicePixelRatioF();
1094 if (!d->scaledpixmap || d->scaledpixmap->size() != scaledSize) {
1095 if (!d->cachedimage)
1096 d->cachedimage = new QImage(d->pixmap->toImage());
1097 delete d->scaledpixmap;
1098 QImage scaledImage =
1099 d->cachedimage->scaled(scaledSize,
1100 Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
1101 d->scaledpixmap = new QPixmap(QPixmap::fromImage(std::move(scaledImage)));
1102 d->scaledpixmap->setDevicePixelRatio(devicePixelRatioF());
1103 }
1104 pix = *d->scaledpixmap;
1105 } else
1106 pix = *d->pixmap;
1107 QStyleOption opt;
1108 opt.initFrom(this);
1109 if (!isEnabled())
1110 pix = style->generatedIconPixmap(QIcon::Disabled, pix, &opt);
1111 style->drawItemPixmap(&painter, cr, align, pix);
1112 }
1113}
1114
1115
1116/*!
1117 Updates the label, but not the frame.
1118*/
1119
1120void QLabelPrivate::updateLabel()
1121{
1122 Q_Q(QLabel);
1123 valid_hints = false;
1124
1125 if (isTextLabel) {
1126 QSizePolicy policy = q->sizePolicy();
1127 const bool wrap = align & Qt::TextWordWrap;
1128 policy.setHeightForWidth(wrap);
1129 if (policy != q->sizePolicy()) // ### should be replaced by WA_WState_OwnSizePolicy idiom
1130 q->setSizePolicy(policy);
1131 textLayoutDirty = true;
1132 }
1133 q->updateGeometry();
1134 q->update(q->contentsRect());
1135}
1136
1137#ifndef QT_NO_SHORTCUT
1138/*!
1139 Sets this label's buddy to \a buddy.
1140
1141 When the user presses the shortcut key indicated by this label,
1142 the keyboard focus is transferred to the label's buddy widget.
1143
1144 The buddy mechanism is only available for QLabels that contain
1145 text in which one character is prefixed with an ampersand, '&'.
1146 This character is set as the shortcut key. See the \l
1147 QKeySequence::mnemonic() documentation for details (to display an
1148 actual ampersand, use '&&').
1149
1150 In a dialog, you might create two data entry widgets and a label
1151 for each, and set up the geometry layout so each label is just to
1152 the left of its data entry widget (its "buddy"), for example:
1153 \snippet code/src_gui_widgets_qlabel.cpp 2
1154
1155 With the code above, the focus jumps to the Name field when the
1156 user presses Alt+N, and to the Phone field when the user presses
1157 Alt+P.
1158
1159 To unset a previously set buddy, call this function with \a buddy
1160 set to nullptr.
1161
1162 \sa buddy(), setText(), QShortcut, setAlignment()
1163*/
1164
1165void QLabel::setBuddy(QWidget *buddy)
1166{
1167 Q_D(QLabel);
1168
1169 if (d->buddy)
1170 disconnect(d->buddy, SIGNAL(destroyed()), this, SLOT(_q_buddyDeleted()));
1171
1172 d->buddy = buddy;
1173
1174 if (buddy)
1175 connect(buddy, SIGNAL(destroyed()), this, SLOT(_q_buddyDeleted()));
1176
1177 if (d->isTextLabel) {
1178 if (d->shortcutId)
1179 releaseShortcut(d->shortcutId);
1180 d->shortcutId = 0;
1181 d->textDirty = true;
1182 if (buddy)
1183 d->updateShortcut(); // grab new shortcut
1184 d->updateLabel();
1185 }
1186}
1187
1188
1189/*!
1190 Returns this label's buddy, or nullptr if no buddy is currently set.
1191
1192 \sa setBuddy()
1193*/
1194
1195QWidget * QLabel::buddy() const
1196{
1197 Q_D(const QLabel);
1198 return d->buddy;
1199}
1200
1201void QLabelPrivate::updateShortcut()
1202{
1203 Q_Q(QLabel);
1204 Q_ASSERT(shortcutId == 0);
1205 // Introduce an extra boolean to indicate the presence of a shortcut in the
1206 // text. We cannot use the shortcutId itself because on the mac mnemonics are
1207 // off by default, so QKeySequence::mnemonic always returns an empty sequence.
1208 // But then we do want to hide the ampersands, so we can't use shortcutId.
1209 hasShortcut = false;
1210
1211 if (!text.contains(QLatin1Char('&')))
1212 return;
1213 hasShortcut = true;
1214 shortcutId = q->grabShortcut(QKeySequence::mnemonic(text));
1215}
1216
1217
1218void QLabelPrivate::_q_buddyDeleted()
1219{
1220 Q_Q(QLabel);
1221 q->setBuddy(nullptr);
1222}
1223
1224#endif // QT_NO_SHORTCUT
1225
1226#if QT_CONFIG(movie)
1227void QLabelPrivate::_q_movieUpdated(const QRect& rect)
1228{
1229 Q_Q(QLabel);
1230 if (movie && movie->isValid()) {
1231 QRect r;
1232 if (scaledcontents) {
1233 QRect cr = q->contentsRect();
1234 QRect pixmapRect(cr.topLeft(), movie->currentPixmap().size());
1235 if (pixmapRect.isEmpty())
1236 return;
1237 r.setRect(cr.left(), cr.top(),
1238 (rect.width() * cr.width()) / pixmapRect.width(),
1239 (rect.height() * cr.height()) / pixmapRect.height());
1240 } else {
1241 r = q->style()->itemPixmapRect(q->contentsRect(), align, movie->currentPixmap());
1242 r.translate(rect.x(), rect.y());
1243 r.setWidth(qMin(r.width(), rect.width()));
1244 r.setHeight(qMin(r.height(), rect.height()));
1245 }
1246 q->update(r);
1247 }
1248}
1249
1250void QLabelPrivate::_q_movieResized(const QSize& size)
1251{
1252 Q_Q(QLabel);
1253 q->update(); //we need to refresh the whole background in case the new size is smaler
1254 valid_hints = false;
1255 _q_movieUpdated(QRect(QPoint(0,0), size));
1256 q->updateGeometry();
1257}
1258
1259/*!
1260 Sets the label contents to \a movie. Any previous content is
1261 cleared. The label does NOT take ownership of the movie.
1262
1263 The buddy shortcut, if any, is disabled.
1264
1265 \sa movie(), setBuddy()
1266*/
1267
1268void QLabel::setMovie(QMovie *movie)
1269{
1270 Q_D(QLabel);
1271 d->clearContents();
1272
1273 if (!movie)
1274 return;
1275
1276 d->movie = movie;
1277 connect(movie, SIGNAL(resized(QSize)), this, SLOT(_q_movieResized(QSize)));
1278 connect(movie, SIGNAL(updated(QRect)), this, SLOT(_q_movieUpdated(QRect)));
1279
1280 // Assume that if the movie is running,
1281 // resize/update signals will come soon enough
1282 if (movie->state() != QMovie::Running)
1283 d->updateLabel();
1284}
1285
1286#endif // QT_CONFIG(movie)
1287
1288/*!
1289 \internal
1290
1291 Clears any contents, without updating/repainting the label.
1292*/
1293
1294void QLabelPrivate::clearContents()
1295{
1296 delete control;
1297 control = nullptr;
1298 isTextLabel = false;
1299 hasShortcut = false;
1300
1301#ifndef QT_NO_PICTURE
1302 delete picture;
1303 picture = nullptr;
1304#endif
1305 delete scaledpixmap;
1306 scaledpixmap = nullptr;
1307 delete cachedimage;
1308 cachedimage = nullptr;
1309 delete pixmap;
1310 pixmap = nullptr;
1311
1312 text.clear();
1313 Q_Q(QLabel);
1314#ifndef QT_NO_SHORTCUT
1315 if (shortcutId)
1316 q->releaseShortcut(shortcutId);
1317 shortcutId = 0;
1318#endif
1319#if QT_CONFIG(movie)
1320 if (movie) {
1321 QObject::disconnect(movie, SIGNAL(resized(QSize)), q, SLOT(_q_movieResized(QSize)));
1322 QObject::disconnect(movie, SIGNAL(updated(QRect)), q, SLOT(_q_movieUpdated(QRect)));
1323 }
1324 movie = nullptr;
1325#endif
1326#ifndef QT_NO_CURSOR
1327 if (onAnchor) {
1328 if (validCursor)
1329 q->setCursor(cursor);
1330 else
1331 q->unsetCursor();
1332 }
1333 validCursor = false;
1334 onAnchor = false;
1335#endif
1336}
1337
1338
1339#if QT_CONFIG(movie)
1340
1341/*!
1342 Returns a pointer to the label's movie, or nullptr if no movie has been
1343 set.
1344
1345 \sa setMovie()
1346*/
1347
1348QMovie *QLabel::movie() const
1349{
1350 Q_D(const QLabel);
1351 return d->movie;
1352}
1353
1354#endif // QT_CONFIG(movie)
1355
1356/*!
1357 \property QLabel::textFormat
1358 \brief the label's text format
1359
1360 See the Qt::TextFormat enum for an explanation of the possible
1361 options.
1362
1363 The default format is Qt::AutoText.
1364
1365 \sa text()
1366*/
1367
1368Qt::TextFormat QLabel::textFormat() const
1369{
1370 Q_D(const QLabel);
1371 return d->textformat;
1372}
1373
1374void QLabel::setTextFormat(Qt::TextFormat format)
1375{
1376 Q_D(QLabel);
1377 if (format != d->textformat) {
1378 d->textformat = format;
1379 QString t = d->text;
1380 if (!t.isNull()) {
1381 d->text.clear();
1382 setText(t);
1383 }
1384 }
1385}
1386
1387/*!
1388 \reimp
1389*/
1390void QLabel::changeEvent(QEvent *ev)
1391{
1392 Q_D(QLabel);
1393 if(ev->type() == QEvent::FontChange || ev->type() == QEvent::ApplicationFontChange) {
1394 if (d->isTextLabel) {
1395 if (d->control)
1396 d->control->document()->setDefaultFont(font());
1397 d->updateLabel();
1398 }
1399 } else if (ev->type() == QEvent::PaletteChange && d->control) {
1400 d->control->setPalette(palette());
1401 } else if (ev->type() == QEvent::ContentsRectChange) {
1402 d->updateLabel();
1403 }
1404 QFrame::changeEvent(ev);
1405}
1406
1407/*!
1408 \property QLabel::scaledContents
1409 \brief whether the label will scale its contents to fill all
1410 available space.
1411
1412 When enabled and the label shows a pixmap, it will scale the
1413 pixmap to fill the available space.
1414
1415 This property's default is false.
1416*/
1417bool QLabel::hasScaledContents() const
1418{
1419 Q_D(const QLabel);
1420 return d->scaledcontents;
1421}
1422
1423void QLabel::setScaledContents(bool enable)
1424{
1425 Q_D(QLabel);
1426 if ((bool)d->scaledcontents == enable)
1427 return;
1428 d->scaledcontents = enable;
1429 if (!enable) {
1430 delete d->scaledpixmap;
1431 d->scaledpixmap = nullptr;
1432 delete d->cachedimage;
1433 d->cachedimage = nullptr;
1434 }
1435 update(contentsRect());
1436}
1437
1438Qt::LayoutDirection QLabelPrivate::textDirection() const
1439{
1440 if (control) {
1441 QTextOption opt = control->document()->defaultTextOption();
1442 return opt.textDirection();
1443 }
1444
1445 return text.isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight;
1446}
1447
1448
1449// Returns the rect that is available for us to draw the document
1450QRect QLabelPrivate::documentRect() const
1451{
1452 Q_Q(const QLabel);
1453 Q_ASSERT_X(isTextLabel, "documentRect", "document rect called for label that is not a text label!");
1454 QRect cr = q->contentsRect();
1455 cr.adjust(margin, margin, -margin, -margin);
1456 const int align = QStyle::visualAlignment(isTextLabel ? textDirection()
1457 : q->layoutDirection(), QFlag(this->align));
1458 int m = indent;
1459 if (m < 0 && q->frameWidth()) // no indent, but we do have a frame
1460 m = q->fontMetrics().horizontalAdvance(QLatin1Char('x')) / 2 - margin;
1461 if (m > 0) {
1462 if (align & Qt::AlignLeft)
1463 cr.setLeft(cr.left() + m);
1464 if (align & Qt::AlignRight)
1465 cr.setRight(cr.right() - m);
1466 if (align & Qt::AlignTop)
1467 cr.setTop(cr.top() + m);
1468 if (align & Qt::AlignBottom)
1469 cr.setBottom(cr.bottom() - m);
1470 }
1471 return cr;
1472}
1473
1474void QLabelPrivate::ensureTextPopulated() const
1475{
1476 if (!textDirty)
1477 return;
1478 if (control) {
1479 QTextDocument *doc = control->document();
1480 if (textDirty) {
1481#ifndef QT_NO_TEXTHTMLPARSER
1482 if (isRichText)
1483 doc->setHtml(text);
1484 else
1485 doc->setPlainText(text);
1486#else
1487 doc->setPlainText(text);
1488#endif
1489 doc->setUndoRedoEnabled(false);
1490
1491#ifndef QT_NO_SHORTCUT
1492 if (hasShortcut) {
1493 // Underline the first character that follows an ampersand (and remove the others ampersands)
1494 int from = 0;
1495 bool found = false;
1496 QTextCursor cursor;
1497 while (!(cursor = control->document()->find((QLatin1String("&")), from)).isNull()) {
1498 cursor.deleteChar(); // remove the ampersand
1499 cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
1500 from = cursor.position();
1501 if (!found && cursor.selectedText() != QLatin1String("&")) { //not a second &
1502 found = true;
1503 shortcutCursor = cursor;
1504 }
1505 }
1506 }
1507#endif
1508 }
1509 }
1510 textDirty = false;
1511}
1512
1513void QLabelPrivate::ensureTextLayouted() const
1514{
1515 if (!textLayoutDirty)
1516 return;
1517 ensureTextPopulated();
1518 if (control) {
1519 QTextDocument *doc = control->document();
1520 QTextOption opt = doc->defaultTextOption();
1521
1522 opt.setAlignment(QFlag(this->align));
1523
1524 if (this->align & Qt::TextWordWrap)
1525 opt.setWrapMode(QTextOption::WordWrap);
1526 else
1527 opt.setWrapMode(QTextOption::ManualWrap);
1528
1529 doc->setDefaultTextOption(opt);
1530
1531 QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
1532 fmt.setMargin(0);
1533 doc->rootFrame()->setFrameFormat(fmt);
1534 doc->setTextWidth(documentRect().width());
1535 }
1536 textLayoutDirty = false;
1537}
1538
1539void QLabelPrivate::ensureTextControl() const
1540{
1541 Q_Q(const QLabel);
1542 if (!isTextLabel)
1543 return;
1544 if (!control) {
1545 control = new QWidgetTextControl(const_cast<QLabel *>(q));
1546 control->document()->setUndoRedoEnabled(false);
1547 control->document()->setDefaultFont(q->font());
1548 control->setTextInteractionFlags(textInteractionFlags);
1549 control->setOpenExternalLinks(openExternalLinks);
1550 control->setPalette(q->palette());
1551 control->setFocus(q->hasFocus());
1552 QObject::connect(control, SIGNAL(updateRequest(QRectF)),
1553 q, SLOT(update()));
1554 QObject::connect(control, SIGNAL(linkHovered(QString)),
1555 q, SLOT(_q_linkHovered(QString)));
1556 QObject::connect(control, SIGNAL(linkActivated(QString)),
1557 q, SIGNAL(linkActivated(QString)));
1558 textLayoutDirty = true;
1559 textDirty = true;
1560 }
1561}
1562
1563void QLabelPrivate::sendControlEvent(QEvent *e)
1564{
1565 Q_Q(QLabel);
1566 if (!isTextLabel || !control || textInteractionFlags == Qt::NoTextInteraction) {
1567 e->ignore();
1568 return;
1569 }
1570 control->processEvent(e, -layoutRect().topLeft(), q);
1571}
1572
1573void QLabelPrivate::_q_linkHovered(const QString &anchor)
1574{
1575 Q_Q(QLabel);
1576#ifndef QT_NO_CURSOR
1577 if (anchor.isEmpty()) { // restore cursor
1578 if (validCursor)
1579 q->setCursor(cursor);
1580 else
1581 q->unsetCursor();
1582 onAnchor = false;
1583 } else if (!onAnchor) {
1584 validCursor = q->testAttribute(Qt::WA_SetCursor);
1585 if (validCursor) {
1586 cursor = q->cursor();
1587 }
1588 q->setCursor(Qt::PointingHandCursor);
1589 onAnchor = true;
1590 }
1591#endif
1592 emit q->linkHovered(anchor);
1593}
1594
1595// Return the layout rect - this is the rect that is given to the layout painting code
1596// This may be different from the document rect since vertical alignment is not
1597// done by the text layout code
1598QRectF QLabelPrivate::layoutRect() const
1599{
1600 QRectF cr = documentRect();
1601 if (!control)
1602 return cr;
1603 ensureTextLayouted();
1604 // Caculate y position manually
1605 qreal rh = control->document()->documentLayout()->documentSize().height();
1606 qreal yo = 0;
1607 if (align & Qt::AlignVCenter)
1608 yo = qMax((cr.height()-rh)/2, qreal(0));
1609 else if (align & Qt::AlignBottom)
1610 yo = qMax(cr.height()-rh, qreal(0));
1611 return QRectF(cr.x(), yo + cr.y(), cr.width(), cr.height());
1612}
1613
1614// Returns the point in the document rect adjusted with p
1615QPoint QLabelPrivate::layoutPoint(const QPoint& p) const
1616{
1617 QRect lr = layoutRect().toRect();
1618 return p - lr.topLeft();
1619}
1620
1621#ifndef QT_NO_CONTEXTMENU
1622QMenu *QLabelPrivate::createStandardContextMenu(const QPoint &pos)
1623{
1624 QString linkToCopy;
1625 QPoint p;
1626 if (control && isRichText) {
1627 p = layoutPoint(pos);
1628 linkToCopy = control->document()->documentLayout()->anchorAt(p);
1629 }
1630
1631 if (linkToCopy.isEmpty() && !control)
1632 return nullptr;
1633
1634 return control->createStandardContextMenu(p, q_func());
1635}
1636#endif
1637
1638/*!
1639 \fn void QLabel::linkHovered(const QString &link)
1640 \since 4.2
1641
1642 This signal is emitted when the user hovers over a link. The URL
1643 referred to by the anchor is passed in \a link.
1644
1645 \sa linkActivated()
1646*/
1647
1648
1649/*!
1650 \fn void QLabel::linkActivated(const QString &link)
1651 \since 4.2
1652
1653 This signal is emitted when the user clicks a link. The URL
1654 referred to by the anchor is passed in \a link.
1655
1656 \sa linkHovered()
1657*/
1658
1659QT_END_NAMESPACE
1660
1661#include "moc_qlabel.cpp"
1662