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