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"