/* Inspired by konq_filetip.cc SPDX-FileCopyrightText: 2003-2007 Craig Drummond SPDX-FileCopyrightText: 1998, 1999 Torben Weis SPDX-FileCopyrightText: 2000, 2001, 2002 David Faure SPDX-FileCopyrightText: 2004 Martin Koller SPDX-License-Identifier: GPL-2.0-or-later */ #include "CharTip.h" #include "FontPreview.h" #include "UnicodeCategories.h" #include #include #include #include #include #include #include #include #include using namespace Qt::StringLiterals; namespace KFI { EUnicodeCategory getCategory(quint32 ucs2) { for (int i = 0; UNICODE_INVALID != constUnicodeCategoryList[i].category; ++i) { if (constUnicodeCategoryList[i].start <= ucs2 && constUnicodeCategoryList[i].end >= ucs2) { return constUnicodeCategoryList[i].category; } } return UNICODE_UNASSIGNED; } static QString toStr(EUnicodeCategory cat) { switch (cat) { case UNICODE_CONTROL: return i18n("Other, Control"); case UNICODE_FORMAT: return i18n("Other, Format"); case UNICODE_UNASSIGNED: return i18n("Other, Not Assigned"); case UNICODE_PRIVATE_USE: return i18n("Other, Private Use"); case UNICODE_SURROGATE: return i18n("Other, Surrogate"); case UNICODE_LOWERCASE_LETTER: return i18n("Letter, Lowercase"); case UNICODE_MODIFIER_LETTER: return i18n("Letter, Modifier"); case UNICODE_OTHER_LETTER: return i18n("Letter, Other"); case UNICODE_TITLECASE_LETTER: return i18n("Letter, Titlecase"); case UNICODE_UPPERCASE_LETTER: return i18n("Letter, Uppercase"); case UNICODE_COMBINING_MARK: return i18n("Mark, Spacing Combining"); case UNICODE_ENCLOSING_MARK: return i18n("Mark, Enclosing"); case UNICODE_NON_SPACING_MARK: return i18n("Mark, Non-Spacing"); case UNICODE_DECIMAL_NUMBER: return i18n("Number, Decimal Digit"); case UNICODE_LETTER_NUMBER: return i18n("Number, Letter"); case UNICODE_OTHER_NUMBER: return i18n("Number, Other"); case UNICODE_CONNECT_PUNCTUATION: return i18n("Punctuation, Connector"); case UNICODE_DASH_PUNCTUATION: return i18n("Punctuation, Dash"); case UNICODE_CLOSE_PUNCTUATION: return i18n("Punctuation, Close"); case UNICODE_FINAL_PUNCTUATION: return i18n("Punctuation, Final Quote"); case UNICODE_INITIAL_PUNCTUATION: return i18n("Punctuation, Initial Quote"); case UNICODE_OTHER_PUNCTUATION: return i18n("Punctuation, Other"); case UNICODE_OPEN_PUNCTUATION: return i18n("Punctuation, Open"); case UNICODE_CURRENCY_SYMBOL: return i18n("Symbol, Currency"); case UNICODE_MODIFIER_SYMBOL: return i18n("Symbol, Modifier"); case UNICODE_MATH_SYMBOL: return i18n("Symbol, Math"); case UNICODE_OTHER_SYMBOL: return i18n("Symbol, Other"); case UNICODE_LINE_SEPARATOR: return i18n("Separator, Line"); case UNICODE_PARAGRAPH_SEPARATOR: return i18n("Separator, Paragraph"); case UNICODE_SPACE_SEPARATOR: return i18n("Separator, Space"); default: return QString(); } } CCharTip::CCharTip(CFontPreview *parent) : QFrame(nullptr, Qt::ToolTip | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint) , m_parent(parent) { m_pixmapLabel = new QLabel(this); m_label = new QLabel(this); m_timer = new QTimer(this); QBoxLayout *layout = new QBoxLayout(QBoxLayout::LeftToRight, this); layout->setContentsMargins(8, 8, 8, 8); layout->setSpacing(0); layout->addWidget(m_pixmapLabel); layout->addWidget(m_label); setPalette(QToolTip::palette()); setFrameShape(QFrame::Box); setFrameShadow(QFrame::Plain); hide(); } CCharTip::~CCharTip() { } void CCharTip::setItem(const CFcEngine::TChar &ch) { hideTip(); m_item = ch; m_timer->disconnect(this); connect(m_timer, &QTimer::timeout, this, &CCharTip::showTip); m_timer->setSingleShot(true); m_timer->start(300); } void CCharTip::showTip() { if (!m_parent->underMouse()) { return; } static constexpr int constPixSize = 96; EUnicodeCategory cat(getCategory(m_item.ucs4)); QString details(u""_s); details += u""; details += u""; QString str(QString::fromUcs4(&(m_item.ucs4), 1)); details += u""; details += u""; // Note: the " below is just to stop Qt converting the xml entry into // a character! if ((0x0001 <= m_item.ucs4 && m_item.ucs4 <= 0xD7FF) || (0xE000 <= m_item.ucs4 && m_item.ucs4 <= 0xFFFD) || (0x10000 <= m_item.ucs4 && m_item.ucs4 <= 0x10FFFF)) { details += u""; } details += u"
" + i18n("Category") + u" " + toStr(cat) + u"
" + i18n("UCS-4") + u" " + u"U+" + QStringLiteral("%1").arg(m_item.ucs4, 4, 16) + u" 
" + i18n("UTF-16") + u" "; const ushort *utf16(str.utf16()); for (int i = 0; utf16[i]; ++i) { if (i) { details += u' '; } details += QStringLiteral("0x%1").arg(utf16[i], 4, 16); } details += u"
" + i18n("UTF-8") + u" "; QByteArray utf8(str.toUtf8()); for (int i = 0; i < utf8.size(); ++i) { if (i) { details += u' '; } details += QStringLiteral("0x%1").arg((unsigned char)(utf8.constData()[i]), 2, 16); } details += u"
" + i18n("XML Decimal Entity") + u" " + u"&#" + QString::number(m_item.ucs4) + u";
"; m_label->setText(details); QList range; range.append(CFcEngine::TRange(m_item.ucs4, 0)); QColor bgnd(Qt::white); bgnd.setAlpha(0); QImage img = m_parent->engine()->draw(m_parent->m_fontName, m_parent->m_styleInfo, m_parent->m_currentFace - 1, palette().text().color(), bgnd, constPixSize, constPixSize, false, range, nullptr); if (!img.isNull()) { m_pixmapLabel->setPixmap(QPixmap::fromImage(std::move(img))); } else { m_pixmapLabel->setPixmap(QPixmap()); } m_timer->disconnect(this); connect(m_timer, &QTimer::timeout, this, &CCharTip::hideTip); m_timer->setSingleShot(true); m_timer->start(15000); qApp->installEventFilter(this); reposition(); show(); } void CCharTip::hideTip() { m_timer->stop(); qApp->removeEventFilter(this); hide(); } void CCharTip::reposition() { QRect rect(m_item); rect.moveTopRight(m_parent->mapToGlobal(rect.topRight())); QPoint pos(rect.center()); QRect desk(QApplication::screenAt(rect.center())->geometry()); if ((rect.center().x() + width()) > desk.right()) { if (pos.x() - width() < 0) { pos.setX(0); } else { pos.setX(pos.x() - width()); } } // should the tooltip be shown above or below the ivi ? if (rect.bottom() + height() > desk.bottom()) { pos.setY(rect.top() - height()); } else { pos.setY(rect.bottom() + 1); } move(pos); update(); } void CCharTip::resizeEvent(QResizeEvent *event) { QFrame::resizeEvent(event); reposition(); } bool CCharTip::eventFilter(QObject *, QEvent *e) { switch (e->type()) { case QEvent::Leave: case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::KeyPress: case QEvent::KeyRelease: case QEvent::FocusIn: case QEvent::FocusOut: case QEvent::Wheel: hideTip(); default: break; } return false; } }