File indexing completed on 2025-01-05 05:23:37

0001 /*
0002     This file is part of the Okteta Kasten module, made within the KDE community.
0003 
0004     SPDX-FileCopyrightText: 2009, 2023 Friedrich W. H. Kossebau <kossebau@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007 */
0008 
0009 #include "searchtool.hpp"
0010 
0011 // libconfigentries
0012 #include <casesensitivityconfigentry.hpp>
0013 // libbytearraysearch
0014 #include <bytearraysearchjob.hpp>
0015 #include <bytearraysearchutils.hpp>
0016 // controller
0017 #include "searchuserqueryable.hpp"
0018 // Okteta Kasten gui
0019 #include <Kasten/Okteta/ByteArrayView>
0020 // Okteta Kasten core
0021 #include <Kasten/Okteta/ByteArrayDocument>
0022 // Okteta core
0023 #include <Okteta/CharCodec>
0024 #include <Okteta/AbstractByteArrayModel>
0025 // KF
0026 #include <KConfigGroup>
0027 #include <KSharedConfig>
0028 #include <KLocalizedString>
0029 // Qt
0030 #include <QApplication>
0031 
0032 
0033 namespace Kasten {
0034 
0035 SearchTool::SearchTool()
0036 {
0037     setObjectName(QStringLiteral("Search"));
0038 
0039     const KConfigGroup configGroup(KSharedConfig::openConfig(), ConfigGroupId);
0040     mCaseSensitivity = configGroup.readEntry(CaseSensitivityConfigKey, DefaultCaseSensitivity);
0041 }
0042 
0043 SearchTool::~SearchTool() = default;
0044 
0045 bool SearchTool::isApplyable() const
0046 {
0047     return (mByteArrayView && mByteArrayModel);
0048 //     const int newPosition = finalTargetOffset();
0049 
0050 //     return ( mByteArrayView && mByteArrayModel
0051 //              && (0 <= newPosition) && (newPosition <= mByteArrayModel->size()) );
0052 }
0053 
0054 QString SearchTool::title() const { return i18nc("@title", "Search"); }
0055 
0056 bool SearchTool::hasSelectedData()   const { return mByteArrayView->hasSelectedData(); }
0057 QString SearchTool::charCodingName() const { return mByteArrayView->charCodingName(); }
0058 
0059 void SearchTool::setTargetModel(AbstractModel* model)
0060 {
0061     const bool oldIsApplyable = isApplyable();
0062 
0063     if (mByteArrayView) {
0064         mByteArrayView->disconnect(this);
0065     }
0066     if (mByteArrayModel) {
0067         mByteArrayModel->disconnect(this);
0068     }
0069 
0070     mByteArrayView = model ? model->findBaseModel<ByteArrayView*>() : nullptr;
0071 
0072     ByteArrayDocument* document =
0073         mByteArrayView ? qobject_cast<ByteArrayDocument*>(mByteArrayView->baseModel()) : nullptr;
0074     mByteArrayModel = document ? document->content() : nullptr;
0075 
0076     if (mByteArrayView && mByteArrayModel) {
0077         connect(mByteArrayView,  &ByteArrayView::charCodecChanged,
0078                 this, &SearchTool::charCodecChanged);
0079         // TODO: update isApplyable on cursor movement and size changes
0080     }
0081 
0082     const bool newIsApplyable = isApplyable();
0083     if (oldIsApplyable != newIsApplyable) {
0084         Q_EMIT isApplyableChanged(newIsApplyable);
0085     }
0086 }
0087 
0088 void SearchTool::setUserQueryAgent(If::SearchUserQueryable* userQueryAgent)
0089 {
0090     mUserQueryAgent = userQueryAgent;
0091 }
0092 
0093 void SearchTool::setSearchData(const QByteArray& searchData)
0094 {
0095 //     const bool oldIsApplyable = isApplyable();
0096 
0097     mSearchData = searchData;
0098 
0099 //     const bool newIsApplyable = isApplyable();
0100 //     if( oldIsApplyable != newIsApplyable )
0101 //         Q_EMIT isApplyableChanged( newIsApplyable );
0102 }
0103 
0104 void SearchTool::setCaseSensitivity(Qt::CaseSensitivity caseSensitivity)
0105 {
0106     if (mCaseSensitivity == caseSensitivity) {
0107         return;
0108     }
0109 
0110 //     const bool oldIsApplyable = isApplyable();
0111 
0112     mCaseSensitivity = caseSensitivity;
0113 
0114     KConfigGroup configGroup(KSharedConfig::openConfig(), ConfigGroupId);
0115     configGroup.writeEntry(CaseSensitivityConfigKey, mCaseSensitivity);
0116 
0117 //     const bool newIsApplyable = isApplyable();
0118 //     if( oldIsApplyable != newIsApplyable )
0119 //         Q_EMIT isApplyableChanged( newIsApplyable );
0120 }
0121 
0122 void SearchTool::search(FindDirection direction, bool fromCursor, bool inSelection)
0123 {
0124     mPreviousFound = false;
0125 
0126     if (!ByteArraySearchUtils::getSearchIndexes(&mSearchFirstIndex, &mSearchLastIndex,
0127                                                 mByteArrayModel,
0128                                                 mByteArrayView->selection(),
0129                                                 mByteArrayView->cursorPosition(),
0130                                                 mSearchData,
0131                                                 direction,
0132                                                 fromCursor, inSelection)) {
0133         // no search doable, so skip any search and finish now
0134         Q_EMIT dataNotFound();
0135         return;
0136     }
0137 
0138     doSearch(direction);
0139 }
0140 
0141 void SearchTool::doSearch(FindDirection direction)
0142 {
0143     // TODO: should start at last
0144     Okteta::Address startIndex = (direction == FindForward) ? mSearchFirstIndex : mSearchLastIndex /*-mSearchData.size()*/;
0145     bool wrapEnabled = (direction == FindForward) ? (mSearchLastIndex < startIndex) : (startIndex < mSearchFirstIndex);
0146 
0147     while (true) {
0148         const bool isWithin = !wrapEnabled || (direction == FindForward) ? (startIndex <= mByteArrayModel->size() - 1) : (startIndex >= 0);
0149 
0150         if (isWithin) {
0151             QApplication::setOverrideCursor(Qt::WaitCursor);
0152 
0153             Okteta::Address endIndex = wrapEnabled ?
0154                                     ((direction == FindForward) ? mByteArrayModel->size() - 1 : 0) :
0155                                     ((direction == FindForward) ? mSearchLastIndex : mSearchFirstIndex);
0156 
0157             auto* searchJob =
0158                 new ByteArraySearchJob(mByteArrayModel, mSearchData, startIndex, endIndex, mCaseSensitivity, mByteArrayView->charCodingName());
0159             const Okteta::AddressRange matchRange = searchJob->exec();
0160 
0161             QApplication::restoreOverrideCursor();
0162 
0163             if (matchRange.isValid()) {
0164                 mPreviousFound = true;
0165                 mByteArrayView->setSelection(matchRange.start(), matchRange.end());
0166                 break;
0167             }
0168         }
0169 
0170         if (wrapEnabled) {
0171             const bool wrapping = mUserQueryAgent ? mUserQueryAgent->queryContinue(direction) : true;
0172 
0173             if (!wrapping) {
0174                 break;
0175             }
0176             startIndex = (direction == FindForward) ? 0 : mByteArrayModel->size() - 1;
0177             wrapEnabled = false;
0178         } else {
0179             if (!mPreviousFound) {
0180                 Q_EMIT dataNotFound();
0181             }
0182             break;
0183         }
0184     }
0185     mByteArrayView->setFocus();
0186 }
0187 
0188 }
0189 
0190 #include "moc_searchtool.cpp"