File indexing completed on 2025-01-05 05:23:27
0001 /* 0002 This file is part of the Okteta Kasten module, made within the KDE community. 0003 0004 SPDX-FileCopyrightText: 2007-2009, 2012 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 "bookmarkscontroller.hpp" 0010 0011 // controller 0012 #include "bookmarkeditpopup.hpp" 0013 // Okteta Kasten gui 0014 #include <Kasten/Okteta/ByteArrayView> 0015 // Okteta Kasten core 0016 #include <Kasten/Okteta/ByteArrayDocument> 0017 // Kasten core 0018 #include <Kasten/AbstractModel> 0019 // Okteta gui 0020 #include <Okteta/OffsetFormat> 0021 // Okteta core 0022 #include <Okteta/TextByteArrayAnalyzer> 0023 #include <Okteta/CharCodec> 0024 #include <Okteta/Bookmarkable> 0025 #include <Okteta/BookmarksConstIterator> 0026 #include <Okteta/Bookmark> 0027 #include <Okteta/AbstractByteArrayModel> 0028 // KF 0029 #include <KXMLGUIClient> 0030 #include <KLocalizedString> 0031 #include <KActionCollection> 0032 #include <KStandardAction> 0033 // Qt 0034 #include <QActionGroup> 0035 #include <QAction> 0036 0037 namespace Kasten { 0038 0039 static constexpr char BookmarkListActionListId[] = "bookmark_list"; 0040 0041 // TODO: Sortieren nach Offset oder Zeit 0042 0043 BookmarksController::BookmarksController(KXMLGUIClient* guiClient) 0044 : mGuiClient(guiClient) 0045 { 0046 KActionCollection* actionCollection = mGuiClient->actionCollection(); 0047 0048 mCreateAction = KStandardAction::addBookmark(this, &BookmarksController::createBookmark, this); 0049 0050 mDeleteAction = new QAction(QIcon::fromTheme(QStringLiteral("bookmark-remove")), 0051 i18nc("@action:inmenu", "Remove Bookmark"), this); 0052 mDeleteAction->setObjectName(QStringLiteral("bookmark_remove")); 0053 connect(mDeleteAction, &QAction::triggered, this, &BookmarksController::deleteBookmark); 0054 actionCollection->setDefaultShortcut(mDeleteAction, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_B)); 0055 0056 mDeleteAllAction = new QAction(QIcon::fromTheme(QStringLiteral("bookmark-remove")), 0057 i18nc("@action:inmenu", "Remove All Bookmarks"), this); 0058 mDeleteAllAction->setObjectName(QStringLiteral("bookmark_remove_all")); 0059 connect(mDeleteAllAction, &QAction::triggered, this, &BookmarksController::deleteAllBookmarks); 0060 // actionCollection->setDefaultShortcut(mDeleteAllAction, QKeySequence(Qt::CTRL | Qt::Key_G)); 0061 0062 mGotoNextBookmarkAction = new QAction(QIcon::fromTheme(QStringLiteral("go-next")), 0063 i18nc("@action:inmenu", "Go to Next Bookmark"), this); 0064 mGotoNextBookmarkAction->setObjectName(QStringLiteral("bookmark_next")); 0065 connect(mGotoNextBookmarkAction, &QAction::triggered, this, &BookmarksController::gotoNextBookmark); 0066 actionCollection->setDefaultShortcut(mGotoNextBookmarkAction, QKeySequence(Qt::ALT | Qt::Key_Down)); 0067 0068 mGotoPreviousBookmarkAction = new QAction(QIcon::fromTheme(QStringLiteral("go-previous")), 0069 i18nc("@action:inmenu", "Go to Previous Bookmark"), this); 0070 mGotoPreviousBookmarkAction->setObjectName(QStringLiteral("bookmark_previous")); 0071 connect(mGotoPreviousBookmarkAction, &QAction::triggered, this, &BookmarksController::gotoPreviousBookmark); 0072 actionCollection->setDefaultShortcut(mGotoPreviousBookmarkAction, QKeySequence(Qt::ALT | Qt::Key_Up)); 0073 0074 mBookmarksActionGroup = new QActionGroup(this); // TODO: do we use this only for the signal mapping? 0075 // mBookmarksActionGroup->setExclusive( true ); 0076 connect(mBookmarksActionGroup, &QActionGroup::triggered, 0077 this, &BookmarksController::onBookmarkTriggered); 0078 0079 actionCollection->addActions({ 0080 mCreateAction, 0081 mDeleteAction, 0082 mDeleteAllAction, 0083 mGotoNextBookmarkAction, 0084 mGotoPreviousBookmarkAction 0085 }); 0086 0087 setTargetModel(nullptr); 0088 } 0089 0090 BookmarksController::~BookmarksController() = default; 0091 0092 void BookmarksController::setTargetModel(AbstractModel* model) 0093 { 0094 if (mByteArrayView) { 0095 mByteArrayView->disconnect(this); 0096 } 0097 if (mByteArray) { 0098 mByteArray->disconnect(this); 0099 } 0100 0101 mByteArrayView = model ? model->findBaseModel<ByteArrayView*>() : nullptr; 0102 0103 ByteArrayDocument* document = 0104 mByteArrayView ? qobject_cast<ByteArrayDocument*>(mByteArrayView->baseModel()) : nullptr; 0105 mByteArray = document ? document->content() : nullptr; 0106 mBookmarks = (mByteArray && mByteArrayView) ? qobject_cast<Okteta::Bookmarkable*>(mByteArray) : nullptr; 0107 0108 const bool hasViewWithBookmarks = (mBookmarks != nullptr); 0109 int bookmarksCount = 0; 0110 if (hasViewWithBookmarks) { 0111 bookmarksCount = mBookmarks->bookmarksCount(); 0112 connect(mByteArray, SIGNAL(bookmarksAdded(QVector<Okteta::Bookmark>)), 0113 SLOT(onBookmarksAdded(QVector<Okteta::Bookmark>))); 0114 connect(mByteArray, SIGNAL(bookmarksRemoved(QVector<Okteta::Bookmark>)), 0115 SLOT(onBookmarksRemoved(QVector<Okteta::Bookmark>))); 0116 connect(mByteArray, SIGNAL(bookmarksModified(QVector<int>)), 0117 SLOT(updateBookmarks())); 0118 connect(mByteArrayView, &ByteArrayView::cursorPositionChanged, 0119 this, &BookmarksController::onCursorPositionChanged); 0120 connect(mByteArrayView, &ByteArrayView::offsetCodingChanged, 0121 this, &BookmarksController::updateBookmarks); 0122 } 0123 0124 updateBookmarks(); 0125 0126 const bool hasBookmarks = hasViewWithBookmarks && (bookmarksCount != 0); 0127 if (hasViewWithBookmarks) { 0128 onCursorPositionChanged(mByteArrayView->cursorPosition()); 0129 } else { 0130 mCreateAction->setEnabled(false); 0131 mDeleteAction->setEnabled(false); 0132 mGotoNextBookmarkAction->setEnabled(false); 0133 mGotoPreviousBookmarkAction->setEnabled(false); 0134 } 0135 mDeleteAllAction->setEnabled(hasBookmarks); 0136 } 0137 0138 void BookmarksController::updateBookmarks() 0139 { 0140 mGuiClient->unplugActionList(QLatin1String(BookmarkListActionListId)); 0141 0142 qDeleteAll(mBookmarksActionGroup->actions()); 0143 0144 if (!mBookmarks) { 0145 return; 0146 } 0147 0148 const int startOffset = mByteArrayView->startOffset(); 0149 Okteta::OffsetFormat::print printFunction = 0150 Okteta::OffsetFormat::printFunction((Okteta::OffsetFormat::Format)mByteArrayView->offsetCoding()); 0151 0152 char codedOffset[Okteta::OffsetFormat::MaxFormatWidth + 1]; 0153 0154 constexpr int firstWithNumericShortCut = 1; 0155 constexpr int lastWithNumericShortCut = 9; 0156 int b = firstWithNumericShortCut; 0157 0158 Okteta::BookmarksConstIterator bit = mBookmarks->createBookmarksConstIterator(); 0159 while (bit.hasNext()) { 0160 const Okteta::Bookmark& bookmark = bit.next(); 0161 printFunction(codedOffset, startOffset + bookmark.offset()); 0162 QString title = i18nc("@item description of bookmark", "%1: %2", QString::fromUtf8(codedOffset), bookmark.name()); 0163 if (b <= lastWithNumericShortCut) { 0164 title = QStringLiteral("&%1 %2").arg(b).arg(title); 0165 // = KStringHandler::rsqueeze( view->title(), MaxEntryLength ); 0166 ++b; 0167 } 0168 auto* action = new QAction(title, mBookmarksActionGroup); 0169 0170 action->setData(bookmark.offset()); 0171 mBookmarksActionGroup->addAction(action); 0172 } 0173 mGuiClient->plugActionList(QString::fromUtf8(BookmarkListActionListId), 0174 mBookmarksActionGroup->actions()); 0175 } 0176 0177 void BookmarksController::onBookmarksAdded(const QVector<Okteta::Bookmark>& bookmarks) 0178 { 0179 Q_UNUSED(bookmarks) 0180 const int currentPosition = mByteArrayView->cursorPosition(); 0181 onCursorPositionChanged(currentPosition); 0182 0183 const int bookmarksCount = mBookmarks->bookmarksCount(); 0184 const bool hasBookmarks = (bookmarksCount != 0); 0185 0186 mDeleteAllAction->setEnabled(hasBookmarks); 0187 0188 updateBookmarks(); 0189 } 0190 0191 void BookmarksController::onBookmarksRemoved(const QVector<Okteta::Bookmark>& bookmarks) 0192 { 0193 Q_UNUSED(bookmarks) 0194 const int currentPosition = mByteArrayView->cursorPosition(); 0195 onCursorPositionChanged(currentPosition); 0196 0197 const int bookmarksCount = mBookmarks->bookmarksCount(); 0198 const bool hasBookmarks = (bookmarksCount != 0); 0199 0200 mDeleteAllAction->setEnabled(hasBookmarks); 0201 0202 updateBookmarks(); 0203 } 0204 0205 void BookmarksController::onCursorPositionChanged(Okteta::Address newPosition) 0206 { 0207 const int bookmarksCount = mBookmarks->bookmarksCount(); 0208 const bool hasBookmarks = (bookmarksCount != 0); 0209 const bool isInsideByteArray = (newPosition < mByteArray->size()); 0210 bool isAtBookmark; 0211 bool hasPrevious; 0212 bool hasNext; 0213 if (hasBookmarks) { 0214 isAtBookmark = mBookmarks->containsBookmarkFor(newPosition); 0215 Okteta::BookmarksConstIterator bookmarksIterator = mBookmarks->createBookmarksConstIterator(); 0216 hasPrevious = bookmarksIterator.findPreviousFrom(newPosition - 1); 0217 hasNext = bookmarksIterator.findNextFrom(newPosition + 1); 0218 } else { 0219 isAtBookmark = false; 0220 hasPrevious = false; 0221 hasNext = false; 0222 } 0223 0224 mCreateAction->setEnabled(!isAtBookmark && isInsideByteArray); 0225 mDeleteAction->setEnabled(isAtBookmark); 0226 mGotoNextBookmarkAction->setEnabled(hasNext); 0227 mGotoPreviousBookmarkAction->setEnabled(hasPrevious); 0228 } 0229 0230 void BookmarksController::createBookmark() 0231 { 0232 const int cursorPosition = mByteArrayView->cursorPosition(); 0233 0234 // search for text at cursor 0235 const Okteta::CharCodec* charCodec = Okteta::CharCodec::createCodec(mByteArrayView->charCodingName()); 0236 const Okteta::TextByteArrayAnalyzer textAnalyzer(mByteArray, charCodec); 0237 QString bookmarkName = textAnalyzer.text(cursorPosition, cursorPosition + MaxBookmarkNameSize - 1); 0238 delete charCodec; 0239 0240 if (bookmarkName.isEmpty()) { 0241 bookmarkName = i18nc("default name of a bookmark", "Bookmark"); // %1").arg( 0 ) ); // TODO: use counter like with new file, globally 0242 0243 } 0244 auto* bookmarkEditPopup = new BookmarkEditPopup(mByteArrayView->widget()); 0245 QPoint popupPoint = mByteArrayView->cursorRect().topLeft(); 0246 // popupPoint.ry() += mSlider->height() / 2; 0247 popupPoint = mByteArrayView->widget()->mapToGlobal(popupPoint); 0248 0249 bookmarkEditPopup->setPosition(popupPoint); 0250 bookmarkEditPopup->setName(bookmarkName); 0251 bookmarkEditPopup->setCursorPosition(cursorPosition); 0252 connect(bookmarkEditPopup, &BookmarkEditPopup::bookmarkAccepted, 0253 this, &BookmarksController::addBookmark); 0254 bookmarkEditPopup->open(); 0255 } 0256 0257 void BookmarksController::addBookmark(int cursorPosition, const QString& name) 0258 { 0259 Okteta::Bookmark bookmark(cursorPosition); 0260 bookmark.setName(name); 0261 0262 const QVector<Okteta::Bookmark> bookmarks { bookmark }; 0263 mBookmarks->addBookmarks(bookmarks); 0264 } 0265 0266 void BookmarksController::deleteBookmark() 0267 { 0268 const int cursorPosition = mByteArrayView->cursorPosition(); 0269 const QVector<Okteta::Bookmark> bookmarks { cursorPosition }; 0270 mBookmarks->removeBookmarks(bookmarks); 0271 } 0272 0273 void BookmarksController::deleteAllBookmarks() 0274 { 0275 mBookmarks->removeAllBookmarks(); 0276 } 0277 0278 void BookmarksController::gotoNextBookmark() 0279 { 0280 const int positionAfter = mByteArrayView->cursorPosition() + 1; 0281 0282 Okteta::BookmarksConstIterator bookmarksIterator = mBookmarks->createBookmarksConstIterator(); 0283 const bool hasNext = bookmarksIterator.findNextFrom(positionAfter); 0284 if (hasNext) { 0285 const int newPosition = bookmarksIterator.next().offset(); 0286 mByteArrayView->setCursorPosition(newPosition); 0287 } 0288 } 0289 0290 void BookmarksController::gotoPreviousBookmark() 0291 { 0292 const int positionBefore = mByteArrayView->cursorPosition() - 1; 0293 0294 Okteta::BookmarksConstIterator bookmarksIterator = mBookmarks->createBookmarksConstIterator(); 0295 const bool hasPrevious = bookmarksIterator.findPreviousFrom(positionBefore); 0296 if (hasPrevious) { 0297 const int newPosition = bookmarksIterator.previous().offset(); 0298 mByteArrayView->setCursorPosition(newPosition); 0299 } 0300 } 0301 0302 void BookmarksController::onBookmarkTriggered(QAction* action) 0303 { 0304 const int newPosition = action->data().toInt(); 0305 mByteArrayView->setCursorPosition(newPosition); 0306 } 0307 0308 } 0309 0310 #include "moc_bookmarkscontroller.cpp"