Changeset 55476 in vbox for trunk/src/VBox/Frontends/VirtualBox
- Timestamp:
- Apr 28, 2015 10:04:46 AM (10 years ago)
- 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 21 21 22 22 /* Qt includes: */ 23 # include <QStack> 24 # include <QPainter> 23 25 # include <QApplication> 24 # include <QPainter> 26 # include <QFontMetrics> 27 # include <QGraphicsSceneHoverEvent> 25 28 26 29 /* GUI includes: */ 27 30 # include "UIGraphicsTextPane.h" 28 31 32 /* Other VBox includes: */ 33 # include "iprt/assert.h" 34 29 35 #endif /* !VBOX_WITH_PRECOMPILED_HEADERS */ 30 31 #include <QFontMetrics>32 #include <QGraphicsSceneHoverEvent>33 #include <QTextLayout>34 35 36 36 37 UIGraphicsTextPane::UIGraphicsTextPane(QIGraphicsWidget *pParent, QPaintDevice *pPaintDevice) … … 362 363 QFontMetrics fm(font, pPaintDevice); 363 364 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); 403 370 404 371 /* 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()); 407 374 408 375 /* Configure layout: */ … … 475 442 } 476 443 444 445 const QString UIRichTextString::m_sstrAny = QString("[\\s\\S]*"); 446 const QMap<UIRichTextString::Type, QString> UIRichTextString::m_sPatterns = populatePatterns(); 447 const QMap<UIRichTextString::Type, bool> UIRichTextString::m_sPatternHasMeta = populatePatternHasMeta(); 448 449 UIRichTextString::UIRichTextString(Type type /* = Type_None */) 450 : m_type(type) 451 , m_strString(QString()) 452 , m_strStringMeta(QString()) 453 { 454 } 455 456 UIRichTextString::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 466 UIRichTextString::~UIRichTextString() 467 { 468 /* Erase the map: */ 469 qDeleteAll(m_strings.begin(), m_strings.end()); 470 m_strings.clear(); 471 } 472 473 QString 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 484 QList<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 509 void 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 518 void 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 */ 567 QMap<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 */ 577 QMap<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 */ 587 int 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 */ 600 QString 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 */ 611 QTextCharFormat 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 19 19 #define ___UIGraphicsTextPane_h___ 20 20 21 /* Qt includes: */ 22 #include <QTextLayout> 23 21 24 /* GUI includes: */ 22 25 #include "QIGraphicsWidget.h" 23 24 /* Forward declarations: */25 class QTextLayout;26 26 27 27 /* Typedefs: */ … … 130 130 }; 131 131 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. */ 135 class UIRichTextString 136 { 137 public: 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 170 private: 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 132 222 #endif /* !___UIGraphicsTextPane_h___ */ 223
Note:
See TracChangeset
for help on using the changeset viewer.