VirtualBox

Ignore:
Timestamp:
Apr 28, 2015 10:04:46 AM (10 years ago)
Author:
vboxsync
Message:

FE/Qt: 7720: Initial implementation for rich-text support of the details-pane elements.

Location:
trunk/src/VBox/Frontends/VirtualBox/src/widgets/graphics
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsTextPane.cpp

    r53412 r55476  
    2121
    2222/* Qt includes: */
     23# include <QStack>
     24# include <QPainter>
    2325# include <QApplication>
    24 # include <QPainter>
     26# include <QFontMetrics>
     27# include <QGraphicsSceneHoverEvent>
    2528
    2629/* GUI includes: */
    2730# include "UIGraphicsTextPane.h"
    2831
     32/* Other VBox includes: */
     33# include "iprt/assert.h"
     34
    2935#endif /* !VBOX_WITH_PRECOMPILED_HEADERS */
    30 
    31 #include <QFontMetrics>
    32 #include <QGraphicsSceneHoverEvent>
    33 #include <QTextLayout>
    34 
    3536
    3637UIGraphicsTextPane::UIGraphicsTextPane(QIGraphicsWidget *pParent, QPaintDevice *pPaintDevice)
     
    362363    QFontMetrics fm(font, pPaintDevice);
    363364    int iLeading = fm.leading();
    364     QString strModifiedText(strText);
    365     QList<QTextLayout::FormatRange> formatRangeList;
    366 
    367     /* Handle bold sub-strings: */
    368     QRegExp boldRegExp("<b>([\\s\\S]+)</b>");
    369     boldRegExp.setMinimal(true);
    370     while (boldRegExp.indexIn(strModifiedText) != -1)
    371     {
    372         /* Prepare format: */
    373         QTextLayout::FormatRange formatRange;
    374         QFont font = formatRange.format.font();
    375         font.setBold(true);
    376         formatRange.format.setFont(font);
    377         formatRange.start = boldRegExp.pos(0);
    378         formatRange.length = boldRegExp.cap(1).size();
    379         /* Add format range to list: */
    380         formatRangeList << formatRange;
    381         /* Replace sub-string: */
    382         strModifiedText.replace(boldRegExp.cap(0), boldRegExp.cap(1));
    383     }
    384 
    385     /* Handle anchored sub-strings: */
    386     QRegExp anchoredRegExp("<a href=([^>]+)>([^<>]+)</a>");
    387     anchoredRegExp.setMinimal(true);
    388     while (anchoredRegExp.indexIn(strModifiedText) != -1)
    389     {
    390         /* Prepare format: */
    391         QTextLayout::FormatRange formatRange;
    392         formatRange.format.setAnchor(true);
    393         formatRange.format.setAnchorHref(anchoredRegExp.cap(1));
    394         if (formatRange.format.anchorHref() == strHoveredAnchor)
    395             formatRange.format.setForeground(qApp->palette().color(QPalette::Link));
    396         formatRange.start = anchoredRegExp.pos(0);
    397         formatRange.length = anchoredRegExp.cap(2).size();
    398         /* Add format range to list: */
    399         formatRangeList << formatRange;
    400         /* Replace sub-string: */
    401         strModifiedText.replace(anchoredRegExp.cap(0), anchoredRegExp.cap(2));
    402     }
     365
     366    /* Parse incoming string with UIRichTextString capabilities: */
     367    //printf("Text: {%s}\n", strText.toAscii().constData());
     368    UIRichTextString ms(strText);
     369    ms.setHoveredAnchor(strHoveredAnchor);
    403370
    404371    /* Create layout; */
    405     QTextLayout *pTextLayout = new QTextLayout(strModifiedText, font, pPaintDevice);
    406     pTextLayout->setAdditionalFormats(formatRangeList);
     372    QTextLayout *pTextLayout = new QTextLayout(ms.toString(), font, pPaintDevice);
     373    pTextLayout->setAdditionalFormats(ms.formatRanges());
    407374
    408375    /* Configure layout: */
     
    475442}
    476443
     444
     445const QString UIRichTextString::m_sstrAny = QString("[\\s\\S]*");
     446const QMap<UIRichTextString::Type, QString> UIRichTextString::m_sPatterns = populatePatterns();
     447const QMap<UIRichTextString::Type, bool> UIRichTextString::m_sPatternHasMeta = populatePatternHasMeta();
     448
     449UIRichTextString::UIRichTextString(Type type /* = Type_None */)
     450    : m_type(type)
     451    , m_strString(QString())
     452    , m_strStringMeta(QString())
     453{
     454}
     455
     456UIRichTextString::UIRichTextString(const QString &strString, Type type /* = Type_None */, const QString &strStringMeta /* = QString() */)
     457    : m_type(type)
     458    , m_strString(strString)
     459    , m_strStringMeta(strStringMeta)
     460{
     461    //printf("Creating new UIRichTextString with string=\"%s\" and string-meta=\"%s\"\n",
     462    //       m_strString.toAscii().constData(), m_strStringMeta.toAscii().constData());
     463    parse();
     464}
     465
     466UIRichTextString::~UIRichTextString()
     467{
     468    /* Erase the map: */
     469    qDeleteAll(m_strings.begin(), m_strings.end());
     470    m_strings.clear();
     471}
     472
     473QString UIRichTextString::toString() const
     474{
     475    /* Add own string first: */
     476    QString strString = m_strString;
     477    /* Add all the strings of children finally: */
     478    foreach (const int &iPosition, m_strings.keys())
     479        strString.insert(iPosition, m_strings.value(iPosition)->toString());
     480    /* Return result: */
     481    return strString;
     482}
     483
     484QList<QTextLayout::FormatRange> UIRichTextString::formatRanges(int iShift /* = 0 */) const
     485{
     486    /* Prepare format range list: */
     487    QList<QTextLayout::FormatRange> ranges;
     488    /* Add own format range first: */
     489    QTextLayout::FormatRange range;
     490    range.start = iShift;
     491    range.length = toString().size();
     492    range.format = textCharFormat(m_type);
     493    /* Enable anchor if present: */
     494    if (!m_strAnchor.isNull())
     495    {
     496        range.format.setAnchorHref(m_strAnchor);
     497        /* Highlight anchor if hovered: */
     498        if (range.format.anchorHref() == m_strHoveredAnchor)
     499            range.format.setForeground(qApp->palette().color(QPalette::Link));
     500    }
     501    ranges.append(range);
     502    /* Add all the format ranges of children finally: */
     503    foreach (const int &iPosition, m_strings.keys())
     504        ranges.append(m_strings.value(iPosition)->formatRanges(iShift + iPosition));
     505    /* Return result: */
     506    return ranges;
     507}
     508
     509void UIRichTextString::setHoveredAnchor(const QString &strHoveredAnchor)
     510{
     511    /* Define own hovered anchor first: */
     512    m_strHoveredAnchor = strHoveredAnchor;
     513    /* Propagate hovered anchor to children finally: */
     514    foreach (const int &iPosition, m_strings.keys())
     515        m_strings.value(iPosition)->setHoveredAnchor(m_strHoveredAnchor);
     516}
     517
     518void UIRichTextString::parse()
     519{
     520    /* Assign the meta to anchor directly for now,
     521     * will do a separate parsing when there will
     522     * be more than one type of meta: */
     523    if (!m_strStringMeta.isNull())
     524        m_strAnchor = m_strStringMeta;
     525
     526    /* Parse the passed QString with all the known patterns: */
     527    foreach (const Type &enmPattern, m_sPatterns.keys())
     528    {
     529        /* Get the current pattern: */
     530        const QString strPattern = m_sPatterns.value(enmPattern);
     531
     532        /* Recursively parse the string: */
     533        int iMaxLevel = 0;
     534        do
     535        {
     536            /* Search for the maximum level of the current pattern: */
     537            iMaxLevel = searchForMaxLevel(m_strString, strPattern, strPattern);
     538            //printf(" Maximum level for the pattern \"%s\" is %d.\n",
     539            //       strPattern.toAscii().constData(), iMaxLevel);
     540            /* If current pattern of at least level 1 is found: */
     541            if (iMaxLevel > 0)
     542            {
     543                /* Compose full pattern of the corresponding level: */
     544                const QString strFullPattern = composeFullPattern(strPattern, strPattern, iMaxLevel);
     545                //printf("  Full pattern: %s\n", strFullPattern.toAscii().constData());
     546                QRegExp regExp(strFullPattern);
     547                regExp.setMinimal(true);
     548                const int iPosition = regExp.indexIn(m_strString);
     549                AssertReturnVoid(iPosition != -1);
     550                if (iPosition != -1)
     551                {
     552                    /* Cut the found string: */
     553                    m_strString.remove(iPosition, regExp.cap(0).size());
     554                    /* And paste that string as our child: */
     555                    const bool fPatterHasMeta = m_sPatternHasMeta.value(enmPattern);
     556                    const QString strSubString = !fPatterHasMeta ? regExp.cap(1) : regExp.cap(2);
     557                    const QString strSubMeta   = !fPatterHasMeta ? QString()     : regExp.cap(1);
     558                    m_strings.insert(iPosition, new UIRichTextString(strSubString, enmPattern, strSubMeta));
     559                }
     560            }
     561        }
     562        while (iMaxLevel > 0);
     563    }
     564}
     565
     566/* static */
     567QMap<UIRichTextString::Type, QString> UIRichTextString::populatePatterns()
     568{
     569    QMap<Type, QString> patterns;
     570    patterns.insert(Type_Anchor, QString("<a href=([^>]+)>(%1)</a>"));
     571    patterns.insert(Type_Bold,   QString("<b>(%1)</b>"));
     572    patterns.insert(Type_Italic, QString("<i>(%1)</i>"));
     573    return patterns;
     574}
     575
     576/* static */
     577QMap<UIRichTextString::Type, bool> UIRichTextString::populatePatternHasMeta()
     578{
     579    QMap<Type, bool> patternHasMeta;
     580    patternHasMeta.insert(Type_Anchor, true);
     581    patternHasMeta.insert(Type_Bold,   false);
     582    patternHasMeta.insert(Type_Italic, false);
     583    return patternHasMeta;
     584}
     585
     586/* static */
     587int UIRichTextString::searchForMaxLevel(const QString &strString, const QString &strPattern,
     588                                        const QString &strCurrentPattern, int iCurrentLevel /* = 0 */)
     589{
     590    QRegExp regExp(strCurrentPattern.arg(m_sstrAny));
     591    regExp.setMinimal(true);
     592    if (regExp.indexIn(strString) != -1)
     593        return searchForMaxLevel(strString, strPattern,
     594                                 strCurrentPattern.arg(m_sstrAny + strPattern + m_sstrAny),
     595                                 iCurrentLevel + 1);
     596    return iCurrentLevel;
     597}
     598
     599/* static */
     600QString UIRichTextString::composeFullPattern(const QString &strPattern,
     601                                             const QString &strCurrentPattern, int iCurrentLevel)
     602{
     603    if (iCurrentLevel > 1)
     604        return composeFullPattern(strPattern,
     605                                  strCurrentPattern.arg(m_sstrAny + strPattern + m_sstrAny),
     606                                  iCurrentLevel - 1);
     607    return strCurrentPattern.arg(m_sstrAny);
     608}
     609
     610/* static */
     611QTextCharFormat UIRichTextString::textCharFormat(Type type)
     612{
     613    QTextCharFormat format;
     614    switch (type)
     615    {
     616        case Type_Anchor:
     617        {
     618            format.setAnchor(true);
     619            break;
     620        }
     621        case Type_Bold:
     622        {
     623            QFont font = format.font();
     624            font.setBold(true);
     625            format.setFont(font);
     626            break;
     627        }
     628        case Type_Italic:
     629        {
     630            QFont font = format.font();
     631            font.setItalic(true);
     632            format.setFont(font);
     633            break;
     634        }
     635    }
     636    return format;
     637}
     638
  • trunk/src/VBox/Frontends/VirtualBox/src/widgets/graphics/UIGraphicsTextPane.h

    r55401 r55476  
    1919#define ___UIGraphicsTextPane_h___
    2020
     21/* Qt includes: */
     22#include <QTextLayout>
     23
    2124/* GUI includes: */
    2225#include "QIGraphicsWidget.h"
    23 
    24 /* Forward declarations: */
    25 class QTextLayout;
    2626
    2727/* Typedefs: */
     
    130130};
    131131
     132/** Rich text string implementation which parses the passed QString
     133  * and holds it as the tree of the formatted rich text blocks.
     134  * @todo: To be moved into separate files. */
     135class UIRichTextString
     136{
     137public:
     138
     139    /** Rich text block types. */
     140    enum Type
     141    {
     142        Type_None,
     143        Type_Anchor,
     144        Type_Bold,
     145        Type_Italic,
     146    };
     147
     148    /** Default (empty) constructor. */
     149    UIRichTextString(Type type = Type_None);
     150
     151    /** Constructor taking passed QString.
     152      * @param strString     holds the QString being parsed and held as tree of rich text blocks,
     153      * @param type          holds the type of <i>this</i> rich text block,
     154      * @param strStringMeta holds the QString containing meta data describing <i>this</i> rich text block. */
     155    UIRichTextString(const QString &strString, Type type = Type_None, const QString &strStringMeta = QString());
     156
     157    /** Destructor. */
     158    ~UIRichTextString();
     159
     160    /** Returns the QString representation. */
     161    QString toString() const;
     162
     163    /** Returns the list of existing format ranges appropriate for QTextLayout.
     164      * @param iShift holds the shift of <i>this</i> rich text block accordig to it's parent. */
     165    QList<QTextLayout::FormatRange> formatRanges(int iShift = 0) const;
     166
     167    /** Defines the hovered anchor for <i>this</i> rich text block. */
     168    void setHoveredAnchor(const QString &strHoveredAnchor);
     169
     170private:
     171
     172    /** Parses the string. */
     173    void parse();
     174
     175    /** Used to populate const static map of known patterns.
     176      * @note Keep it sync with the method below - #populatePatternHasMeta(). */
     177    static QMap<Type, QString> populatePatterns();
     178    /** Used to populate const static map of meta flags for the known patterns.
     179      * @note Keep it sync with the method above - #populatePatterns(). */
     180    static QMap<Type, bool> populatePatternHasMeta();
     181
     182    /** Recursively searching for the maximum level of the passed pattern.
     183      * @param strString         holds the string to check for the current (recursively advanced) pattern in,
     184      * @param strPattern        holds the etalon pattern to recursively advance the current pattern with,
     185      * @param strCurrentPattern holds the current (recursively advanced) pattern to check for the presence of,
     186      * @param iCurrentLevel     holds the current level of the recursively advanced pattern. */
     187    static int searchForMaxLevel(const QString &strString, const QString &strPattern,
     188                                 const QString &strCurrentPattern, int iCurrentLevel = 0);
     189
     190    /** Recursively composing the pattern of the maximum level.
     191      * @param strPattern        holds the etalon pattern to recursively update the current pattern with,
     192      * @param strCurrentPattern holds the current (recursively advanced) pattern,
     193      * @param iCurrentLevel     holds the amount of the levels left to recursively advance current pattern. */
     194    static QString composeFullPattern(const QString &strPattern,
     195                                      const QString &strCurrentPattern, int iCurrentLevel);
     196
     197    /** Composes the QTextCharFormat correpoding to passed @a type. */
     198    static QTextCharFormat textCharFormat(Type type);
     199
     200    /** Holds the type of <i>this</i> rich text block. */
     201    Type m_type;
     202    /** Holds the string of <i>this</i> rich text block. */
     203    QString m_strString;
     204    /** Holds the string meta data of <i>this</i> rich text block. */
     205    QString m_strStringMeta;
     206    /** Holds the children of <i>this</i> rich text block. */
     207    QMap<int, UIRichTextString*> m_strings;
     208
     209    /** Holds the anchor of <i>this</i> rich text block. */
     210    QString m_strAnchor;
     211    /** Holds the anchor to highlight in <i>this</i> rich text block and in it's children. */
     212    QString m_strHoveredAnchor;
     213
     214    /** Holds the 'any' string pattern. */
     215    static const QString m_sstrAny;
     216    /** Holds the map of known patterns. */
     217    static const QMap<Type, QString> m_sPatterns;
     218    /** Holds the map of meta flags for the known patterns. */
     219    static const QMap<Type, bool> m_sPatternHasMeta;
     220};
     221
    132222#endif /* !___UIGraphicsTextPane_h___ */
     223
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette