File indexing completed on 2024-05-19 05:19:20
0001 /* 0002 This file is part of KJots. 0003 0004 SPDX-FileCopyrightText: 1997 Christoph Neerfeld <Christoph.Neerfeld@home.ivm.de> 0005 2002, 2003 Aaron J. Seigo <aseigo@kde.org> 0006 2003 Stanislav Kljuhhin <crz@hot.ee> 0007 2005-2006 Jaison Lee <lee.jaison@gmail.com> 0008 2020 Igor Poboiko <igor.poboiko@gmail.com> 0009 0010 SPDX-License-Identifier: GPL-2.0-or-later 0011 */ 0012 0013 //Own Header 0014 #include "kjotsbrowser.h" 0015 #include "kjotsmodel.h" 0016 0017 #ifdef HAVE_TEXT_TO_SPEECH_SUPPORT 0018 #include <TextEditTextToSpeech/TextToSpeechWidget> 0019 #endif 0020 0021 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0022 #include <KPIMTextEdit/RichTextEditFindBar> 0023 #include <KPIMTextEdit/SlideContainer> 0024 #else 0025 #include <TextCustomEditor/RichTextEditFindBar> 0026 #include <TextAddonsWidgets/SlideContainer> 0027 #endif 0028 0029 #include <QHelpEvent> 0030 #include <QToolTip> 0031 #include <QVBoxLayout> 0032 #include <QHBoxLayout> 0033 #include <QMenu> 0034 0035 #include <KActionCollection> 0036 #include <KLocalizedString> 0037 #include <KStandardAction> 0038 0039 using namespace Akonadi; 0040 0041 class Q_DECL_HIDDEN KJotsBrowserWidgetPrivate 0042 { 0043 public: 0044 explicit KJotsBrowserWidgetPrivate(std::unique_ptr<KJotsBrowser> browser, QWidget *widget) 0045 : mBrowser(std::move(browser)) 0046 , mSliderContainer(widget) 0047 , mFindBar(mBrowser.get(), &mSliderContainer) 0048 #ifdef HAVE_TEXT_TO_SPEECH_SUPPORT 0049 , mTextToSpeechWidget(widget) 0050 #endif 0051 { 0052 } 0053 0054 std::unique_ptr<KJotsBrowser> mBrowser; 0055 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0056 KPIMTextEdit::SlideContainer mSliderContainer; 0057 KPIMTextEdit::RichTextEditFindBar mFindBar; 0058 #else 0059 TextAddonsWidgets::SlideContainer mSliderContainer; 0060 TextCustomEditor::RichTextEditFindBar mFindBar; 0061 #endif 0062 #ifdef HAVE_TEXT_TO_SPEECH_SUPPORT 0063 TextEditTextToSpeech::TextToSpeechWidget mTextToSpeechWidget; 0064 #endif 0065 }; 0066 0067 KJotsBrowserWidget::KJotsBrowserWidget(std::unique_ptr<KJotsBrowser> browser, QWidget *parent) 0068 : QWidget(parent) 0069 , d(new KJotsBrowserWidgetPrivate(std::move(browser), this)) 0070 { 0071 d->mBrowser->setParent(this); 0072 d->mSliderContainer.setContent(&d->mFindBar); 0073 d->mFindBar.setHideWhenClose(false); 0074 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0075 connect(&d->mFindBar, &KPIMTextEdit::RichTextEditFindBar::hideFindBar, this, &KJotsBrowserWidget::slotHideFindBar); 0076 #else 0077 connect(&d->mFindBar, &TextCustomEditor::RichTextEditFindBar::hideFindBar, this, &KJotsBrowserWidget::slotHideFindBar); 0078 #endif 0079 #ifdef HAVE_TEXT_TO_SPEECH_SUPPORT 0080 connect(d->mBrowser.get(), &KJotsBrowser::say, &d->mTextToSpeechWidget, &TextEditTextToSpeech::TextToSpeechWidget::say); 0081 #endif 0082 0083 QVBoxLayout *lay = new QVBoxLayout(this); 0084 lay->setContentsMargins(0, 0, 0, 0); 0085 #ifdef HAVE_TEXT_TO_SPEECH_SUPPORT 0086 lay->addWidget(&d->mTextToSpeechWidget); 0087 #endif 0088 lay->addWidget(d->mBrowser.get()); 0089 lay->addWidget(&d->mSliderContainer); 0090 } 0091 0092 KJotsBrowserWidget::~KJotsBrowserWidget() = default; 0093 0094 KJotsBrowser* KJotsBrowserWidget::browser() 0095 { 0096 return d->mBrowser.get(); 0097 } 0098 0099 void KJotsBrowserWidget::slotFind() 0100 { 0101 if (d->mBrowser->textCursor().hasSelection()) { 0102 d->mFindBar.setText(d->mBrowser->textCursor().selectedText()); 0103 } 0104 d->mBrowser->moveCursor(QTextCursor::Start); 0105 0106 d->mFindBar.showFind(); 0107 d->mSliderContainer.slideIn(); 0108 d->mFindBar.focusAndSetCursor(); 0109 } 0110 0111 void KJotsBrowserWidget::slotFindNext() 0112 { 0113 if (d->mFindBar.isVisible()) { 0114 d->mFindBar.findNext(); 0115 } else { 0116 slotFind(); 0117 } 0118 } 0119 0120 void KJotsBrowserWidget::slotHideFindBar() 0121 { 0122 d->mSliderContainer.slideOut(); 0123 d->mBrowser->setFocus(); 0124 } 0125 0126 KJotsBrowser::KJotsBrowser(KActionCollection *actionCollection, QWidget *parent) 0127 : QTextBrowser(parent) 0128 , m_actionCollection(actionCollection) 0129 { 0130 setWordWrapMode(QTextOption::WordWrap); 0131 0132 connect(this, &KJotsBrowser::anchorClicked, this, [this](const QUrl &url){ 0133 if (!url.toString().startsWith(QLatin1Char('#'))) { 0134 // QTextBrowser tries to automatically handle the url. We only want it for anchor navigation 0135 // (i.e. "#page12" links). This can be overridden by setting the source to an invalid QUrl 0136 setSource(QUrl()); 0137 Q_EMIT linkClicked(url); 0138 } 0139 }); 0140 } 0141 0142 void KJotsBrowser::setModel(QAbstractItemModel *model) 0143 { 0144 m_model = model; 0145 } 0146 0147 void KJotsBrowser::contextMenuEvent(QContextMenuEvent *event) 0148 { 0149 QMenu *popup = createStandardContextMenu(event->pos()); 0150 if (!popup) { 0151 return; 0152 } 0153 popup->addSeparator(); 0154 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0155 popup->addAction(m_actionCollection->action(QString::fromLatin1(KStandardAction::name(KStandardAction::Find)))); 0156 #else 0157 popup->addAction(m_actionCollection->action(KStandardAction::name(KStandardAction::Find))); 0158 #endif 0159 #ifdef HAVE_TEXT_TO_SPEECH_SUPPORT 0160 popup->addSeparator(); 0161 if (!document()->isEmpty() && TextEditTextToSpeech::TextToSpeech::self()->isReady()) { 0162 QAction *speakAction = popup->addAction(i18nc("@info:action", "Speak Text")); 0163 speakAction->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-text-to-speech"))); 0164 connect(speakAction, &QAction::triggered, this, [this](){ 0165 const QString text = textCursor().hasSelection() ? textCursor().selectedText() : document()->toPlainText(); 0166 Q_EMIT say(text); 0167 }); 0168 } 0169 #endif 0170 popup->exec(event->globalPos()); 0171 delete popup; 0172 } 0173 0174 bool KJotsBrowser::event(QEvent *event) 0175 { 0176 if (event->type() == QEvent::ToolTip) { 0177 tooltipEvent(static_cast<QHelpEvent*>(event)); 0178 } 0179 return QTextBrowser::event(event); 0180 } 0181 0182 void KJotsBrowser::tooltipEvent(QHelpEvent *event) 0183 { 0184 if (!m_model) { 0185 return; 0186 } 0187 // This code is somewhat shared with KJotsEdit 0188 QUrl url(anchorAt(event->pos())); 0189 QString message; 0190 0191 if (url.isValid()) { 0192 QModelIndex idx; 0193 if (url.scheme() == QStringLiteral("akonadi")) { 0194 idx = KJotsModel::modelIndexForUrl(m_model, url); 0195 } else 0196 // This is #page_XXX internal links 0197 if (url.scheme().isEmpty() && url.host().isEmpty() && url.path().isEmpty() && url.query().isEmpty() 0198 && url.fragment().startsWith(QLatin1String("page_"))) 0199 { 0200 bool ok; 0201 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0202 Item::Id id = url.fragment().midRef(5).toInt(&ok); 0203 #else 0204 Item::Id id = QStringView(url.fragment()).mid(5).toInt(&ok); 0205 #endif 0206 const QModelIndexList idxs = EntityTreeModel::modelIndexesForItem(m_model, Item(id)); 0207 if (ok && !idxs.isEmpty()) { 0208 idx = idxs.first(); 0209 } 0210 } else { 0211 message = i18nc("@info:tooltip %1 is hyperlink address", "Click to follow the hyperlink: %1", url.toString(QUrl::RemovePassword)); 0212 } 0213 if (idx.isValid()) { 0214 if (idx.data(EntityTreeModel::ItemRole).value<Item>().isValid()) { 0215 message = i18nc("@info:tooltip %1 is a full path to note (i.e. Notes / Notebook / Note)", "Click to open note: %1", KJotsModel::itemPath(idx)); 0216 } else if (idx.data(EntityTreeModel::CollectionRole).value<Collection>().isValid()) { 0217 message = i18nc("@info:tooltip %1 is a full path to book (i.e. Notes / Notebook)", "Click to open book: %1", KJotsModel::itemPath(idx)); 0218 } 0219 } 0220 } 0221 0222 if (!message.isEmpty()) { 0223 QToolTip::showText(event->globalPos(), message); 0224 } else { 0225 QToolTip::hideText(); 0226 } 0227 } 0228 0229 /* ex: set tabstop=4 softtabstop=4 shiftwidth=4 expandtab: */ 0230 0231 #include "moc_kjotsbrowser.cpp"