diff --git a/src/mode/katemodemenulist.cpp b/src/mode/katemodemenulist.cpp index 53261599..877bff98 100644 --- a/src/mode/katemodemenulist.cpp +++ b/src/mode/katemodemenulist.cpp @@ -1,801 +1,853 @@ -/* This file is part of the KDE libraries and the Kate part. +/* This file is part of the KDE libraries and the KTextEditor project. * * Copyright (C) 2019 Nibaldo González S. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ //BEGIN Includes #include "katemodemenulist.h" #include "katedocument.h" #include "kateconfig.h" #include "kateview.h" #include "kateglobal.h" #include "katesyntaxmanager.h" #include "katepartdebug.h" #include #include #include #include +#include +#include //END Includes namespace { /** * Detect words delimiters: * ! " # $ % & ' ( ) * + , - . / : ; * < = > ? [ \ ] ^ ` { | } ~ « » */ static bool isDelimiter(const ushort c) { return (c <= 126 && c >= 33 && (c >= 123 || c <= 47 || (c <= 96 && c >= 58 && c != 95 && (c >= 91 || c <= 63)))) || c == 171 || c == 187; } } void KateModeMenuList::init(const SearchBarPosition searchBarPos) { + m_list = new KateModeMenuListData::ListView(this); + m_searchBar = new KateModeMenuListData::SearchLine(this); + /* * Load list widget, scroll bar and items. */ - m_list = new ModeListWidget(this); m_scroll = new QScrollBar(Qt::Vertical, this); m_list->setVerticalScrollBar(m_scroll); - // The vertical scroll bar will be added in another layout m_list->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_list->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_list->setIconSize(QSize(m_iconSize, m_iconSize)); m_list->setResizeMode(QListView::Adjust); // Initial size of the list widget, this can be modified later m_list->setSizeList(428); - loadHighlightingList(); + loadHighlightingModel(); // Data model (items) // Add scroll bar and set margin QHBoxLayout *layoutScrollBar = new QHBoxLayout(); layoutScrollBar->addWidget(m_scroll); layoutScrollBar->setMargin(2); /* * Search bar widget. */ - m_searchBar = new ModeLineEdit(this, m_list); - m_searchBar->setCaseSensitivity(Qt::CaseInsensitive); m_searchBar->setPlaceholderText(i18nc("Placeholder in search bar", "Search...")); + m_searchBar->setToolTip(i18nc("ToolTip of the search bar of modes of syntax highlighting", "Search for syntax highlighting modes by language name or file extension (for example, C++ or .cpp)")); + m_searchBar->setMaxLength(200); m_list->setFocusProxy(m_searchBar); /* * Set layouts and widgets. */ QWidget *container = new QWidget(); QVBoxLayout *layoutContainer = new QVBoxLayout(); m_layoutList = new QGridLayout(); QHBoxLayout *layoutSearchBar = new QHBoxLayout(); - // Ooverlap scroll bar above the list widget + // Overlap scroll bar above the list widget m_layoutList->addWidget(m_list, 0, 0, Qt::AlignLeft); m_layoutList->addLayout(layoutScrollBar, 0, 0, Qt::AlignRight); layoutSearchBar->addWidget(m_searchBar); if (searchBarPos == Top) { layoutContainer->addLayout(layoutSearchBar); } layoutContainer->addLayout(m_layoutList); if (searchBarPos == Bottom) { layoutContainer->addLayout(layoutSearchBar); } container->setLayout(layoutContainer); QWidgetAction *widAct = new QWidgetAction(this); widAct->setDefaultWidget(container); - this->addAction(widAct); + addAction(widAct); // Detect selected item with one click. // This also applies to double-clicks. - connect(m_list, &KateModeMenuList::ModeListWidget::itemClicked, + connect(m_list, &KateModeMenuListData::ListView::clicked, this, &KateModeMenuList::selectHighlighting); - - return; } -void KateModeMenuList::loadHighlightingList() +void KateModeMenuList::loadHighlightingModel() { + m_model = new QStandardItemModel(0, 0); + m_list->setModel(m_model); + QString *prevHlSection = nullptr; /* * The width of the text container in the item, in pixels. This is used to make * a custom word wrap and prevent the item's text from passing under the scroll bar. * NOTE: 12 = the edges */ const int maxWidthText = m_list->sizeHint().width() - m_scroll->sizeHint().width() - m_iconSize - 12; /* * Get list of modes from KateModeManager::list(). * We assume that the modes are arranged according to sections, alphabetically; * and the attribute "translatedSection" isn't empty if "section" has a value. */ for (auto *hl : KTextEditor::EditorPrivate::self()->modeManager()->list()) { /* * Detects a new section. */ if ( !hl->translatedSection.isEmpty() && (prevHlSection == nullptr || hl->translatedSection != *prevHlSection) ) { QPixmap transparent = QPixmap(m_iconSize / 2, m_iconSize / 2); transparent.fill(Qt::transparent); /* * Add a separator to the list. */ - ModeListWidgetItem *separator = new ModeListWidgetItem(); + KateModeMenuListData::ListItem *separator = new KateModeMenuListData::ListItem(); separator->setFlags(Qt::NoItemFlags); - separator->setSizeHint(QSize(separator->sizeHint().width() - 4, 4)); + separator->setSizeHint(QSize(separator->sizeHint().width() - 2, 4)); separator->setBackground(QBrush(transparent)); QFrame *line = new QFrame(); line->setFrameStyle(QFrame::HLine); - m_list->addItem(separator); - m_list->setItemWidget(separator, line); + m_model->appendRow(separator); + m_list->setIndexWidget( m_model->index(separator->row(), 0), line ); /* * Add the section name to the list. */ - ModeListWidgetItem *section = new ModeListWidgetItem(); + KateModeMenuListData::ListItem *section = new KateModeMenuListData::ListItem(); section->setFlags(Qt::NoItemFlags); QLabel *label = new QLabel(hl->sectionTranslated()); if (m_list->layoutDirection() == Qt::RightToLeft) { label->setAlignment(Qt::AlignRight); } label->setTextFormat(Qt::RichText); label->setIndent(6); // NOTE: Names of sections in bold. The font color // should change according to Kate's color theme. QFont font = label->font(); font.setWeight(QFont::Bold); label->setFont(font); section->setBackground(QBrush(transparent)); - m_list->addItem(section); - m_list->setItemWidget(section, label); - m_list->addItem(section); + m_model->appendRow(section); + m_list->setIndexWidget( m_model->index(section->row(), 0), label ); } prevHlSection = hl->translatedSection.isNull() ? nullptr : &hl->translatedSection; /* * Create item in the list with the language name. */ - ModeListWidgetItem *item = new ModeListWidgetItem(); + KateModeMenuListData::ListItem *item = new KateModeMenuListData::ListItem(); item->setText(setWordWrap( hl->nameTranslated(), maxWidthText, m_list->fontMetrics() )); item->setMode(hl); - //item->generateSearchName( item->getMode()->translatedName.isEmpty() ? &item->getMode()->name : &item->getMode()->translatedName ); + // NOTE: Search names generated in: KateModeMenuListData::SearchLine::updateSearch() + // item->generateSearchName( hl->translatedName.isEmpty() ? &hl->name : &hl->translatedName ); - m_list->addDefaultItem(item); + // Set empty icon + QPixmap emptyIcon(m_iconSize, m_iconSize); + emptyIcon.fill(Qt::transparent); + item->setIcon(QIcon(emptyIcon)); + item->setEditable(false); + // Add item + m_model->appendRow(item); } - return; } void KateModeMenuList::setButton(QPushButton* button, const bool bAutoUpdateTextButton, AlignmentButton position) { if (position == Inverse) { if (layoutDirection() == Qt::RightToLeft) { m_position = KateModeMenuList::Left; } else { m_position = KateModeMenuList::Right; } } else if (position == Left && layoutDirection() != Qt::RightToLeft) { m_position = KateModeMenuList::Default; } else { m_position = position; } m_pushButton = button; m_bAutoUpdateTextButton = bAutoUpdateTextButton; - return; +} + + +void KateModeMenuList::setSizeList(const int height, const int width) +{ + m_list->setSizeList(height, width); +} + + +void KateModeMenuList::autoScroll() +{ + if (m_autoScroll == ScrollToSelectedItem) { + m_list->setCurrentItem(m_selectedItem->row()); + m_list->scrollToItem(m_selectedItem->row(), QAbstractItemView::PositionAtCenter); + } else { + m_list->setCurrentItem(0); + m_list->scrollToTop(); + } } void KateModeMenuList::showEvent(QShowEvent* event) { Q_UNUSED(event); + // TODO: Put the menu in the center of the window if the status bar is hidden. // Set the menu position if (m_pushButton && m_pushButton->isVisible()) { if (m_position == Right) { // New menu position int newMenu_x = pos().x() - geometry().width() + m_pushButton->geometry().width(); // Get position of the right edge of the toggle button const int buttonPositionRight = m_pushButton->mapToGlobal(QPoint(0, 0)).x() + m_pushButton->geometry().width(); if (newMenu_x < 0) { newMenu_x = 0; } else if ( newMenu_x + geometry().width() < buttonPositionRight ) { newMenu_x = buttonPositionRight - geometry().width(); } move(newMenu_x, pos().y()); } else if (m_position == Left) { move(m_pushButton->mapToGlobal(QPoint(0, 0)).x(), pos().y()); } } - /* TODO: Add keyboard shortcut to show the menu. Put the menu - * in the center of the window if the status bar is hidden. - */ - // Select text from the search bar if (!m_searchBar->text().isEmpty()) { if (m_searchBar->text().simplified().isEmpty()) { m_searchBar->clear(); } else { m_searchBar->selectAll(); } } // Set focus on the list. The list widget uses focus proxy to the search bar. m_list->setFocus(Qt::ActiveWindowFocusReason); KTextEditor::DocumentPrivate *doc = m_doc; + if (!doc) { + return; + } - // First show - if (!m_selectedItem) { - /* - * Generate search names in items. - * This could be in "loadHighlightingList()". - */ - for (int i = 0; i < m_list->count(); ++i) { - ModeListWidgetItem *item = static_cast(m_list->item(i)); - if (item->hasMode() && !item->getSearchName()) { - item->generateSearchName( item->getMode()->translatedName.isEmpty() ? &item->getMode()->name : &item->getMode()->translatedName ); - } - } - if (doc) { - selectHighlightingFromExternal(doc->fileType()); - } - } else if ( doc && m_selectedItem->hasMode() && m_selectedItem->getMode()->name != doc->fileType() ) { + // First show or if an external changed the current syntax highlighting. + if (!m_selectedItem || ( m_selectedItem->hasMode() && m_selectedItem->getMode()->name != doc->fileType() )) { selectHighlightingFromExternal(doc->fileType()); } - return; } -void KateModeMenuList::updateSelectedItem(ModeListWidgetItem *item) +void KateModeMenuList::updateSelectedItem(KateModeMenuListData::ListItem *item) { // Change the previously selected item to empty icon if (m_selectedItem) { QPixmap emptyIcon(m_iconSize, m_iconSize); emptyIcon.fill(Qt::transparent); m_selectedItem->setIcon(QIcon(emptyIcon)); } // Update the selected item item->setIcon(m_checkIcon); m_selectedItem = item; - m_list->setCurrentItem(item, QItemSelectionModel::ClearAndSelect); + m_list->setCurrentItem(item->row()); // Change text of the trigger button if (m_bAutoUpdateTextButton && m_pushButton && item->hasMode()) { m_pushButton->setText(item->getMode()->nameTranslated()); } - return; } -void KateModeMenuList::selectHighlightingSetVisibility(QListWidgetItem *pItem, const bool bHideMenu) +void KateModeMenuList::selectHighlightingSetVisibility(QStandardItem *pItem, const bool bHideMenu) { - ModeListWidgetItem *item = static_cast(pItem); + KateModeMenuListData::ListItem *item = static_cast(pItem); updateSelectedItem(item); - // Hide menu if (bHideMenu) { - this->hide(); + hide(); } // Apply syntax highlighting KTextEditor::DocumentPrivate *doc = m_doc; if (doc && item->hasMode()) { doc->updateFileType(item->getMode()->name, true); } - return; } -void KateModeMenuList::selectHighlighting(QListWidgetItem *pItem) +void KateModeMenuList::selectHighlighting(const QModelIndex &index) { - selectHighlightingSetVisibility(pItem, true); - return; + selectHighlightingSetVisibility(m_model->item(index.row(), 0), true); } void KateModeMenuList::selectHighlightingFromExternal(const QString &nameMode) { - for (int i = 0; i < m_list->count(); ++i) { - ModeListWidgetItem *item = static_cast( m_list->item(i) ); + for (int i = 0; i < m_model->rowCount(); ++i) { + KateModeMenuListData::ListItem *item = static_cast( m_model->item(i, 0) ); - if (!item->hasMode() || m_list->item(i)->text().isEmpty()) { + if (!item->hasMode() || m_model->item(i, 0)->text().isEmpty()) { continue; } if (item->getMode()->name == nameMode || ( nameMode.isEmpty() && item->getMode()->name == QLatin1String("Normal") )) { updateSelectedItem(item); // Clear search if (!m_searchBar->text().isEmpty()) { // Prevent the empty list message from being seen over the items for a short time if (m_emptyListMsg) { m_emptyListMsg->hide(); } - // NOTE: This calls updateSearch(), it's scrolled to the selected item + // NOTE: This calls updateSearch(), it's scrolled to the selected item or the first item. m_searchBar->clear(); + } else if (m_autoScroll == ScrollToSelectedItem) { + m_list->scrollToItem(i); } else { - m_list->scrollToItem(item, QAbstractItemView::PositionAtCenter); + // autoScroll() + m_list->setCurrentItem(0); + m_list->scrollToTop(); } return; } } - return; } void KateModeMenuList::selectHighlightingFromExternal() { KTextEditor::DocumentPrivate *doc = m_doc; if (doc) { selectHighlightingFromExternal(doc->fileType()); } - return; } void KateModeMenuList::loadEmptyMsg() { m_emptyListMsg = new QLabel(i18nc("A search yielded no results", "No items matching your search")); m_emptyListMsg->setMargin(15); m_emptyListMsg->setWordWrap(true); QColor color = m_emptyListMsg->palette().color(QPalette::Text); m_emptyListMsg->setStyleSheet( QLatin1String("font-size: 14pt; color: rgba(") + QString::number(color.red()) + QLatin1Char(',') + QString::number(color.green()) + QLatin1Char(',') + QString::number(color.blue()) + QLatin1String(", 0.3);") ); m_emptyListMsg->setAlignment(Qt::AlignCenter); m_layoutList->addWidget(m_emptyListMsg, 0, 0, Qt::AlignCenter); - return; } QString KateModeMenuList::setWordWrap(const QString &text, const int maxWidth, const QFontMetrics &fontMetrics) const { // Get the length of the text, in pixels, and compare it with the container if (fontMetrics.boundingRect(text).width() <= maxWidth) { return text; } // Add line breaks in the text to fit in the container QStringList words = text.split(QLatin1Char(' ')); if (words.count() < 1) { return text; } QString newText = QString(); QString tmpLineText = QString(); for (int i = 0; i < words.count() - 1; ++i) { tmpLineText += words[i]; // This prevents the last line of text from having only one word with 1 or 2 chars if ( i == words.count() - 3 && words[i + 2].length() <= 2 && fontMetrics.boundingRect( tmpLineText + QLatin1Char(' ') + words[i + 1] + QLatin1Char(' ') + words[i + 2] ).width() > maxWidth ) { newText += tmpLineText + QLatin1Char('\n'); tmpLineText.clear(); } // Add line break if the maxWidth is exceeded with the next word else if ( fontMetrics.boundingRect( tmpLineText + QLatin1Char(' ') + words[i + 1] ).width() > maxWidth ) { newText += tmpLineText + QLatin1Char('\n'); tmpLineText.clear(); } else { tmpLineText.append(QLatin1Char(' ')); } } // Add line breaks in delimiters, if the last word is greater than the container if (fontMetrics.boundingRect( words[words.count() - 1] ).width() > maxWidth) { const int lastw = words.count() - 1; for (int c = words[lastw].length() - 1; c >= 0; --c) { if (isDelimiter(words[lastw][c].unicode()) && fontMetrics.boundingRect( words[lastw].mid(0, c + 1) ).width() <= maxWidth) { words[lastw].insert(c + 1, QLatin1Char('\n')); break; } } } if (!tmpLineText.isEmpty()) { newText += tmpLineText; } newText += words[words.count() - 1]; return newText; } -void KateModeMenuList::ModeListWidget::addDefaultItem(QListWidgetItem *item) +void KateModeMenuListData::ListView::setSizeList(const int height, const int width) { - // Set empty icon - QPixmap emptyIcon(m_parentMenu->m_iconSize, m_parentMenu->m_iconSize); - emptyIcon.fill(Qt::transparent); - item->setIcon(QIcon(emptyIcon)); - // Add item - this->addItem(item); - return; + setMinimumWidth(width); + setMaximumWidth(width); + setMinimumHeight(height); + setMaximumHeight(height); } -void KateModeMenuList::ModeListWidget::setSizeList(const int height, const int width) +bool KateModeMenuListData::ListItem::generateSearchName(const QString *itemName) { - this->setMinimumWidth(width); - this->setMaximumWidth(width); - this->setMinimumHeight(height); - this->setMaximumHeight(height); - return; + QString searchName = QString(*itemName); + bool bNewName = false; + + // Replace word delimiters with spaces + for (int i = searchName.length() - 1; i >= 0; --i) { + if (isDelimiter( searchName[i].unicode() )) { + searchName.replace(i, 1, QLatin1Char(' ')); + if (!bNewName) { + bNewName = true; + } + } + // Avoid duplicate delimiters/spaces + if (bNewName && i < searchName.length() - 1 && searchName[i].isSpace() && searchName[i + 1].isSpace()) { + searchName.remove(i + 1, 1); + } + } + + if (bNewName) { + if (searchName[searchName.length() - 1].isSpace()) { + searchName.remove(searchName.length() - 1, 1); + } + if (searchName[0].isSpace()) { + searchName.remove(0, 1); + } + m_searchName = new QString(searchName); + return true; + } else { + m_searchName = itemName; + } + return false; +} + + +bool KateModeMenuListData::ListItem::matchExtension(const QString &text) const +{ + if (!hasMode() || m_type->wildcards.count() == 0) { + return false; + } + + /* + * Only file extensions and full names are matched. Files like "Kconfig*" + * aren't considered. It's also assumed that "text" doesn't contain '*'. + */ + for (const auto &ext : m_type->wildcards) { + // File extension + if (ext.startsWith(QLatin1String("*."))) { + if (text.length() == ext.length() - 2 && text.compare(ext.mid(2), Qt::CaseInsensitive) == 0) { + return true; + } + } else if (text.length() != ext.length() || ext.endsWith(QLatin1Char('*'))) { + continue; + // Full name + } else if (text.compare(&ext, Qt::CaseInsensitive) == 0) { + return true; + } + } + return false; } -void KateModeMenuList::ModeListWidget::keyPressEvent(QKeyEvent *event) +void KateModeMenuListData::ListView::keyPressEvent(QKeyEvent *event) { - // Ctrl/Alt/Shift/Meta + Return selects an item, but without hiding the menu + // Ctrl/Alt/Shift/Meta + Return/Enter selects an item, but without hiding the menu if (( event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return ) && ( event->modifiers().testFlag(Qt::ControlModifier) || event->modifiers().testFlag(Qt::AltModifier) || event->modifiers().testFlag(Qt::ShiftModifier) || event->modifiers().testFlag(Qt::MetaModifier) )) { - m_parentMenu->selectHighlightingSetVisibility(currentItem(), false); + m_parentMenu->selectHighlightingSetVisibility(m_parentMenu->m_list->currentItem(), false); } // Return/Enter selects an item and hide the menu else if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { - m_parentMenu->selectHighlightingSetVisibility(currentItem(), true); + m_parentMenu->selectHighlightingSetVisibility(m_parentMenu->m_list->currentItem(), true); + } else { + QListView::keyPressEvent(event); + } +} + + +void KateModeMenuListData::SearchLine::keyPressEvent(QKeyEvent *event) +{ + if (m_parentMenu->m_list && + ( event->matches(QKeySequence::MoveToNextLine) || event->matches(QKeySequence::SelectNextLine) || + event->matches(QKeySequence::MoveToPreviousLine) || event->matches(QKeySequence::SelectPreviousLine) || + event->matches(QKeySequence::MoveToNextPage) || event->matches(QKeySequence::SelectNextPage) || + event->matches(QKeySequence::MoveToPreviousPage) || event->matches(QKeySequence::SelectPreviousPage) || + event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter )) { + QApplication::sendEvent(m_parentMenu->m_list, event); + } else { + QLineEdit::keyPressEvent(event); + } +} + + +void KateModeMenuListData::SearchLine::init() +{ + connect(this, &KateModeMenuListData::SearchLine::textChanged, + this, &KateModeMenuListData::SearchLine::_k_queueSearch); + + setEnabled(true); + setClearButtonEnabled(true); +} + + +void KateModeMenuListData::SearchLine::clear() +{ + m_queuedSearches = 0; + m_bSearchStateAutoScroll = (text().trimmed().isEmpty()) ? false : true; + // NOTE: This calls "SearchLine::_k_queueSearch()" with an empty string. + // The search clearing should be done without delays. + QLineEdit::clear(); +} + + +void KateModeMenuListData::SearchLine::_k_queueSearch(const QString &s) +{ + m_queuedSearches++; + m_search = s; + + if (m_search.isEmpty()) { + _k_activateSearch(); // Clear search without delay } else { - QListWidget::keyPressEvent(event); + QTimer::singleShot(m_searchDelay, this, &KateModeMenuListData::SearchLine::_k_activateSearch); } - return; } -void KateModeMenuList::ModeLineEdit::clear() +void KateModeMenuListData::SearchLine::_k_activateSearch() { - m_bSearchStateClear = false; - m_bSearchStateAutoScroll = ( text().trimmed().isEmpty() ) ? false : true; - KListWidgetSearchLine::clear(); - return; + m_queuedSearches--; + + if (m_queuedSearches <= 0) { + updateSearch(m_search); + m_queuedSearches = 0; + } } -inline void KateModeMenuList::ModeLineEdit::updateSearch(const QString &s) +void KateModeMenuListData::SearchLine::updateSearch(const QString &s) { if (m_parentMenu->m_emptyListMsg) { m_parentMenu->m_emptyListMsg->hide(); } if (m_parentMenu->m_scroll->isHidden()) { m_parentMenu->m_scroll->show(); } - KateModeMenuList::ModeListWidget *listWidget = m_parentMenu->m_list; + KateModeMenuListData::ListView *listView = m_parentMenu->m_list; + QStandardItemModel *listModel = m_parentMenu->m_model; const QString searchText = (s.isNull() ? text() : s).simplified(); /* * Empty search bar. - * Show all items and scroll to the selected item. + * Show all items and scroll to the selected item or to the first item. */ if ( searchText.isEmpty() || (searchText.size() == 1 && searchText[0].isSpace()) ) { - // This indicates that the search has already been cleared by clear() and - // that all the items are already visible. Also see: KListWidgetSearchLine::clear() - if (m_bSearchStateClear) { - for (int i = 0; i < listWidget->count(); ++i) { - if (listWidget->item(i)->isHidden()) { - listWidget->item(i)->setHidden(false); - } + for (int i = 0; i < listModel->rowCount(); ++i) { + if (listView->isRowHidden(i)) { + listView->setRowHidden(i, false); } } // Don't auto-scroll if the search is already clear if (m_bSearchStateAutoScroll) { - listWidget->setCurrentItem(m_parentMenu->m_selectedItem); - listWidget->scrollToItem(m_parentMenu->m_selectedItem, QAbstractItemView::PositionAtCenter); + m_parentMenu->autoScroll(); } - m_bSearchStateClear = true; m_bSearchStateAutoScroll = false; return; } /* * Prepare item filter. */ int lastItem = -1; int lastSection = -1; bool bEmptySection = true; bool bSectionSeparator = false; bool bSectionName = false; bool bSearchExtensions = true; bool bExactMatch = false; // If the search name will not be used /* * It's used for two purposes, it's true if searchText is a * single alphanumeric character or if it starts with a point. * Both cases don't conflict, so a single bool is used. */ bool bIsAlphaOrPointExt = false; /* * Don't search for extensions if the search text has only one character, * to avoid unwanted results. In this case, the items that start with * that character are displayed. */ if (searchText.length() < 2) { bSearchExtensions = false; if (searchText[0].isLetterOrNumber()) { bIsAlphaOrPointExt = true; } } // If the search text has a point at the beginning, match extensions else if (searchText.length() > 1 && searchText[0].toLatin1() == 46) { bIsAlphaOrPointExt = true; bSearchExtensions = true; bExactMatch = true; } // Two characters: search using the normal name of the items else if (searchText.length() == 2) { bExactMatch = true; // if it contains the '*' character, don't match extensions if (searchText[1].toLatin1() == 42 || searchText[0].toLatin1() == 42) { bSearchExtensions = false; } } /* * Don't use the search name if the search text has delimiters. * Don't search in extensions if it contains the '*' character. */ else { - for (const auto &cc : searchText) { - const ushort c = cc.unicode(); - if (c == 42) { + QString::const_iterator srcText = searchText.constBegin(); + QString::const_iterator endText = searchText.constEnd(); + + for (int it = 0; it < searchText.length() / 2 + searchText.length() % 2; ++it) { + --endText; + const ushort ucsrc = srcText->unicode(); + const ushort ucend = endText->unicode(); + + // If searchText contains "*" + if (ucsrc == 42 || ucend == 42) { bSearchExtensions = false; bExactMatch = true; break; } - if (!bExactMatch && isDelimiter(c)) { + if (!bExactMatch && ( isDelimiter(ucsrc) || (ucsrc != ucend && isDelimiter(ucend)) )) { bExactMatch = true; } + ++srcText; } } /* * Filter items. */ - for (int i = 0; i < listWidget->count(); ++i) { - QString itemName = listWidget->item(i)->text(); + for (int i = 0; i < listModel->rowCount(); ++i) { + QString itemName = listModel->item(i, 0)->text(); /* * Hide/show the name of the section. If the text of the item * is empty, then it corresponds to the name of the section. */ if (itemName.isEmpty()) { - listWidget->item(i)->setHidden(false); + listView->setRowHidden(i, false); if (bSectionSeparator) { bSectionName = true; } else { bSectionSeparator = true; } /* * This hides the name of the previous section * (and the separator) if this section has no items. */ if (bSectionName && bEmptySection && lastSection > 0) { - listWidget->item(lastSection)->setHidden(true); - listWidget->item(lastSection - 1)->setHidden(true); + listView->setRowHidden(lastSection, true); + listView->setRowHidden(lastSection - 1, true); } // Find the section name if (bSectionName) { bSectionName = false; bSectionSeparator = false; bEmptySection = true; lastSection = i; } continue; } /* * Start filtering items. */ - ModeListWidgetItem *item = static_cast(listWidget->item(i)); + KateModeMenuListData::ListItem *item = static_cast( listModel->item(i, 0) ); + + if (!item->hasMode()) { + listView->setRowHidden(i, true); + continue; + } + if (!item->getSearchName()) { + item->generateSearchName( item->getMode()->translatedName.isEmpty() ? &item->getMode()->name : &item->getMode()->translatedName ); + } // Only a character is written in the search bar if (searchText.length() == 1) { if (bIsAlphaOrPointExt) { // CASE 1: All the items that start with that character will be displayed. - if (item->getSearchName()->startsWith(searchText, Qt::CaseInsensitive) ) { + if (item->getSearchName()->startsWith(searchText, m_caseSensitivity) ) { setSearchResult(i, bEmptySection, lastSection, lastItem); continue; } // CASE 2: Matches considering delimiters. For example, when writing "c", // "Objective-C" will be displayed in the results, but not "Yacc/Bison". - if (QString( QLatin1Char(' ') + *(item->getSearchName()) + QLatin1Char(' ') ).contains( QLatin1Char(' ') + searchText + QLatin1Char(' '), Qt::CaseInsensitive )) { + if (QString( QLatin1Char(' ') + *(item->getSearchName()) + QLatin1Char(' ') ).contains( QLatin1Char(' ') + searchText + QLatin1Char(' '), m_caseSensitivity )) { setSearchResult(i, bEmptySection, lastSection, lastItem); continue; } } // CASE 3: The character isn't a letter or number, do an exact search. - else if ( item->getMode()->nameTranslated().contains(searchText[0], Qt::CaseInsensitive) ) { + else if ( item->getMode()->nameTranslated().contains(searchText[0], m_caseSensitivity) ) { setSearchResult(i, bEmptySection, lastSection, lastItem); continue; } } // CASE 4: Search text, using the search name or the normal name. - else if (!bExactMatch && item->getSearchName()->contains(searchText, Qt::CaseInsensitive)) { + else if (!bExactMatch && item->getSearchName()->contains(searchText, m_caseSensitivity)) { setSearchResult(i, bEmptySection, lastSection, lastItem); continue; } - else if (bExactMatch && item->getMode()->nameTranslated().contains(searchText, Qt::CaseInsensitive)) { + else if (bExactMatch && item->getMode()->nameTranslated().contains(searchText, m_caseSensitivity)) { setSearchResult(i, bEmptySection, lastSection, lastItem); continue; } // CASE 5: Exact matches in extensions. if (bSearchExtensions) { if (bIsAlphaOrPointExt && item->matchExtension(searchText.mid(1))) { setSearchResult(i, bEmptySection, lastSection, lastItem); continue; } else if (item->matchExtension(searchText)) { setSearchResult(i, bEmptySection, lastSection, lastItem); continue; } } // Item not found, hide - listWidget->item(i)->setHidden(true); + listView->setRowHidden(i, true); } // Remove last section name, if it's empty. - if ( bEmptySection && lastSection > 0 && !listWidget->item( listWidget->count() - 1 )->text().isEmpty() ) { - listWidget->item(lastSection)->setHidden(true); - listWidget->item(lastSection - 1)->setHidden(true); + if ( bEmptySection && lastSection > 0 && !listModel->item( listModel->rowCount() - 1, 0 )->text().isEmpty() ) { + listView->setRowHidden(lastSection, true); + listView->setRowHidden(lastSection - 1, true); } - listWidget->scrollToTop(); + listView->scrollToTop(); // Show message of empty list if (lastItem == -1) { if (m_parentMenu->m_emptyListMsg == nullptr) { m_parentMenu->loadEmptyMsg(); } m_parentMenu->m_scroll->hide(); m_parentMenu->m_emptyListMsg->show(); } // Hide scroll bar if it isn't necessary - else if ( listWidget->visualItemRect( listWidget->item(lastItem) ).bottom() <= listWidget->geometry().height() ) { + else if ( listView->visualRect( listModel->index(lastItem, 0) ).bottom() <= listView->geometry().height() ) { m_parentMenu->m_scroll->hide(); } - m_bSearchStateClear = true; m_bSearchStateAutoScroll = true; - return; } -void KateModeMenuList::ModeLineEdit::setSearchResult(const int rowItem, bool &bEmptySection, int &lastSection, int &lastItem) +void KateModeMenuListData::SearchLine::setSearchResult(const int rowItem, bool &bEmptySection, int &lastSection, int &lastItem) { if (lastItem == -1) { /* * Detect the first result of the search and "select" it. * This allows you to scroll through the list using * the Up/Down keys after entering a search. */ - m_parentMenu->m_list->setCurrentItem( m_parentMenu->m_list->item(rowItem) ); + m_parentMenu->m_list->setCurrentItem(rowItem); /* * This avoids showing the separator line in the name * of the first section, in the search results. */ if (lastSection > 0) { - m_parentMenu->m_list->item(lastSection - 1)->setHidden(true); + m_parentMenu->m_list->setRowHidden(lastSection - 1, true); } } if (bEmptySection) { bEmptySection = false; } lastItem = rowItem; - if ( m_parentMenu->m_list->item(rowItem)->isHidden() ) { - m_parentMenu->m_list->item(rowItem)->setHidden(false); + if ( m_parentMenu->m_list->isRowHidden(rowItem) ) { + m_parentMenu->m_list->setRowHidden(rowItem, false); } - return; -} - - -bool KateModeMenuList::ModeListWidgetItem::generateSearchName(const QString *itemName) -{ - QString searchName = QString(*itemName); - bool bNewName = false; - - // Replace word delimiters with spaces - for (int i = searchName.length() - 1; i >= 0; --i) { - if (isDelimiter( searchName[i].unicode() )) { - searchName.replace(i, 1, QLatin1Char(' ')); - if (!bNewName) { - bNewName = true; - } - } - // Avoid duplicate delimiters/spaces - if (bNewName && i < searchName.length() - 1 && searchName[i].isSpace() && searchName[i + 1].isSpace()) { - searchName.remove(i + 1, 1); - } - } - - if (bNewName) { - if (searchName[searchName.length() - 1].isSpace()) { - searchName.remove(searchName.length() - 1, 1); - } - if (searchName[0].isSpace()) { - searchName.remove(0, 1); - } - m_searchName = new QString(searchName); - return true; - } else { - m_searchName = itemName; - } - return false; -} - - -bool KateModeMenuList::ModeListWidgetItem::matchExtension(const QString &text) -{ - if (!hasMode() || m_type->wildcards.count() == 0) { - return false; - } - - /* - * Only file extensions and full names are matched. Files like "Kconfig*" - * aren't considered. It's also assumed that "text" doesn't contain '*'. - */ - for (auto &ext : m_type->wildcards) { - // File extension - if (ext.startsWith(QLatin1String("*."))) { - if (text.length() == ext.length() - 2 && text.compare(ext.mid(2), Qt::CaseInsensitive) == 0) { - return true; - } - } else if (text.length() != ext.length() || ext.endsWith(QLatin1Char('*'))) { - continue; - // Full name - } else if (text.compare(&ext, Qt::CaseInsensitive) == 0) { - return true; - } - } - return false; } void KateModeMenuList::updateMenu(KTextEditor::Document *doc) { m_doc = static_cast(doc); } diff --git a/src/mode/katemodemenulist.h b/src/mode/katemodemenulist.h index c63bc5ae..edd4f375 100644 --- a/src/mode/katemodemenulist.h +++ b/src/mode/katemodemenulist.h @@ -1,341 +1,419 @@ -/* This file is part of the KDE libraries and the Kate part. +/* This file is part of the KDE libraries and the KTextEditor project. * * Copyright (C) 2019 Nibaldo González S. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. + * + * --------------------------------------------------------------------- + * NOTE: The KateModeMenuListData::SearchLine class is based on + * KListWidgetSearchLine, by Scott Wheeler and + * Gustavo Sverzut Barbieri . + * See: https://api.kde.org/frameworks/kitemviews/html/classKListWidgetSearchLine.html + * + * TODO: Add keyboard shortcut to show the menu. Put the menu in + * the center of the window if the status bar is hidden. + * See: KateModeMenuList::showEvent() */ #ifndef KATEMODEMENULIST_H #define KATEMODEMENULIST_H #include -#include -#include +#include +#include +#include #include +#include #include #include #include #include #include -#include - #include "katemodemanager.h" namespace KTextEditor { class DocumentPrivate; } +namespace KateModeMenuListData { class ListView; class ListItem; class SearchLine; } + /** * Class of menu to select the * syntax highlighting language (mode menu). * Provides a menu with a scrollable list plus search bar. * * This is an alternative to the classic mode menu of the KateModeMenu class. * * @see KateModeManager, KateFileType, KateModeMenu */ class KateModeMenuList : public QMenu { Q_OBJECT public: /** * Alignment with respect to the trigger button. * "Default" is the normal alignment (left alignment in Left-to-right layouts). * "Inverse" uses right alignment in Left-to-right layouts and left - * alignment in Right-to-left layouts (used in some languages). - * "Left" or "Right" forces the alignment. + * alignment in Right-to-left layouts (used in some languages). + * "Left" and "Right" forces the alignment. * @see setButton(), QWidget::layoutDirection(), Qt::LayoutDirection */ enum AlignmentButton { Default, Inverse, Left, Right }; /** * Search bar position, above or below the list. */ enum SearchBarPosition { Top, Bottom }; + /** + * Defines where the list will scroll after clearing the search or changing the view. + * @see setAutoScroll(), autoScroll() + */ + enum AutoScroll { + ScrollToSelectedItem, + ScrollToTop + }; /** * @param searchBarPos Search bar position, can be top or bottom. * @see SearchBarPosition */ KateModeMenuList(const SearchBarPosition searchBarPos = Bottom) : QMenu() { init(searchBarPos); } KateModeMenuList(const QString &title, const SearchBarPosition searchBarPos = Bottom) : QMenu(title) { init(searchBarPos); } KateModeMenuList(QWidget *parent, const SearchBarPosition searchBarPos = Bottom) : QMenu(parent) { init(searchBarPos); } KateModeMenuList(const QString &title, QWidget *parent, const SearchBarPosition searchBarPos = Bottom) : QMenu(title, parent) { init(searchBarPos); } ~KateModeMenuList() { } /** * Update the selected item in the list widget, but without changing * the syntax highlighting in the document. * This is useful for updating this menu, when changing the syntax highlighting * from another menu, or from an external one. This doesn't hide or show the menu. * @param nameMode Raw name of the syntax highlight definition. If it's empty, * the "Normal" mode will be used. */ void selectHighlightingFromExternal(const QString &nameMode); /** * Update the selected item in the list widget, but without changing * the syntax highlighting in the document. This doesn't hide or show the menu. * The menu is kept updated according to the active syntax highlighting, * obtained from the KTextEditor::DocumentPrivate class. * @see KTextEditor::DocumentPrivate::fileType() */ void selectHighlightingFromExternal(); /** * Set the button that shows this menu. It allows to update the label * of the button and define the alignment of the menu with respect to it. * @param button Trigger button. * @param bAutoUpdateTextButton Determines whether the text of the button should be * changed when selecting an item from the menu. * @param position Position of the menu with respect to the trigger button. * See KateModeMenuList::AlignmentButton. * * @see AlignmentButton */ void setButton(QPushButton *button, const bool bAutoUpdateTextButton = false, AlignmentButton position = Inverse); /** * Define the size of the list widget, in pixels. */ - inline void setSizeList(const int height, const int width = 260) + inline void setSizeList(const int height, const int width = 260); + + /** + * Define the scroll when cleaning the search or changing the view. + * The default value is AutoScroll::ScrollToSelectedItem. + * @see AutoScroll + */ + void setAutoScroll(AutoScroll scroll) { - m_list->setSizeList(height, width); + m_autoScroll = scroll; } /** * Set document to apply the syntax highlighting. * @see KTextEditor::DocumentPrivate */ void updateMenu(KTextEditor::Document *doc); protected: + friend KateModeMenuListData::ListView; + friend KateModeMenuListData::ListItem; + friend KateModeMenuListData::SearchLine; + /** + * Action when displaying the menu. + * Override from QWidget. + */ + void showEvent(QShowEvent *event) override; + +private: + void init(const SearchBarPosition searchBarPos); + + /** + * Load the data model with the syntax highlighting definitions to show in the list. + */ + void loadHighlightingModel(); + + /** + * Scroll the list, according to AutoScroll. + * @see AutoScroll + */ + void autoScroll(); + + /** + * Set a custom word wrap on a text line, according to a maximum width (in pixels). + * @param text Line of text + * @param maxWidth Width of the text container, in pixels. + * @param fontMetrics Font metrics. See QWidget::fontMetrics() + */ + QString setWordWrap(const QString &text, const int maxWidth, const QFontMetrics &fontMetrics) const; + + /** + * Update the selected item in the list, with the active syntax highlighting. + * This method only changes the selected item, doesn't apply + * syntax highlighting in the document, or hides the menu. + * @see selectHighlighting(), selectHighlightingFromExternal(), selectHighlightingSetVisibility() + */ + void updateSelectedItem(KateModeMenuListData::ListItem *item); + + /** + * Select an item from the list and apply the syntax highlighting in the document. + * This is equivalent to KateModeMenuList::selectHighlighting(). + * @param bHideMenu If the menu should be hidden after applying the highlight. + * @see selectHighlighting() + */ + void selectHighlightingSetVisibility(QStandardItem *pItem, const bool bHideMenu); + + /** + * Load message when the list is empty in the search. + */ + inline void loadEmptyMsg(); + + AutoScroll m_autoScroll = ScrollToSelectedItem; + AlignmentButton m_position; + bool m_bAutoUpdateTextButton; + + QPushButton *m_pushButton = nullptr; + QLabel *m_emptyListMsg = nullptr; + QGridLayout *m_layoutList = nullptr; + QScrollBar *m_scroll = nullptr; + + KateModeMenuListData::SearchLine *m_searchBar = nullptr; + KateModeMenuListData::ListView *m_list = nullptr; + QStandardItemModel *m_model = nullptr; + + /** + * Item with active syntax highlighting. + */ + KateModeMenuListData::ListItem *m_selectedItem = nullptr; + + /** + * Icon for selected/active item (checkbox). + * NOTE: Selected and inactive items show an icon with incorrect color, + * however, this isn't a problem, since the list widget is never inactive. + */ + const QIcon m_checkIcon = QIcon::fromTheme(QStringLiteral("checkbox")); + static const int m_iconSize = 16; + + QPointer m_doc; + +private Q_SLOTS: + /** + * Action when selecting a item in the list. This also applies + * the syntax highlighting in the document and hides the menu. + * This is equivalent to KateModeMenuList::selectHighlightingSetVisibility(). + * @see selectHighlightingSetVisibility(), updateSelectedItem() + */ + void selectHighlighting(const QModelIndex &index); +}; + + +namespace KateModeMenuListData +{ /** * Class of List Widget. */ - class ModeListWidget : public QListWidget + class ListView : public QListView { - public: - ModeListWidget(KateModeMenuList *menu) : QListWidget(menu) + Q_OBJECT + + private: + ListView(KateModeMenuList *menu) : QListView(menu) { m_parentMenu = menu; } - /** - * Add item, setting the default properties. - */ - void addDefaultItem(QListWidgetItem *item); - + public: /** * Define the size of the widget list. * @p height and @p width are values in pixels. */ void setSizeList(const int height, const int width = 260); + inline void setCurrentItem(const int rowItem) + { + selectionModel()->setCurrentIndex(m_parentMenu->m_model->index(rowItem, 0), QItemSelectionModel::ClearAndSelect); + } + inline QStandardItem* currentItem() const + { + return m_parentMenu->m_model->item(currentIndex().row(), 0); + } + + inline void scrollToItem(const int rowItem, QAbstractItemView::ScrollHint hint = QAbstractItemView::PositionAtCenter) + { + scrollTo(m_parentMenu->m_model->index(rowItem, 0), hint); + } + protected: /** - * Override from QListWidget. + * Override from QListView. */ void keyPressEvent(QKeyEvent *event) override; private: KateModeMenuList *m_parentMenu = nullptr; + friend KateModeMenuList; }; /** - * Class of an Item of the List Widget. - * @see ModeListWidget, KateFileType + * Class of an Item of the Data Model of the List. + * @see KateModeMenuListData::ListView, KateFileType, QStandardItemModel */ - class ModeListWidgetItem : public QListWidgetItem + class ListItem : public QStandardItem { - public: - ModeListWidgetItem() : QListWidgetItem() { } + private: + ListItem() : QStandardItem() { } + const KateFileType *m_type = nullptr; + const QString *m_searchName = nullptr; + + friend KateModeMenuList; + + public: /** * Associate this item with a KateFileType object. */ inline void setMode(KateFileType *type) { m_type = type; - return; } - inline const KateFileType* getMode() + const KateFileType* getMode() const { return m_type; } - inline bool hasMode() const + bool hasMode() const { return m_type; } /** * Generate name of the item used for the search. * @param itemName Pointer to the item name, can be an attribute of a KateFileType object. * @return True if a new name is generated for the search. */ bool generateSearchName(const QString *itemName); /** * Find matches in the extensions of the item mode, with a @p text. * @param text Text to match, without dots or asterisks. For example, in * a common extension, it corresponds to the text after "*." * @return True if a match is found, false if not. */ - bool matchExtension(const QString &text); + bool matchExtension(const QString &text) const; - inline const QString* getSearchName() + const QString* getSearchName() const { return m_searchName; } - - private: - const KateFileType *m_type = nullptr; - const QString *m_searchName = nullptr; }; /** - * Class of Search Bar Widget. + * Class of Search Bar. + * Based on the KListWidgetSearchLine class. */ - class ModeLineEdit : public KListWidgetSearchLine + class SearchLine : public QLineEdit { - public: - ModeLineEdit(KateModeMenuList *menu, QListWidget *listWidget) : KListWidgetSearchLine(menu, listWidget) + Q_OBJECT + + private: + SearchLine(KateModeMenuList *menu) : QLineEdit(menu) { m_parentMenu = menu; + init(); } - ~ModeLineEdit() { }; + ~SearchLine() { }; - /** - * Override from KListWidgetSearchLine. - */ - void updateSearch(const QString &s = QString()) override; - void clear(); + void init(); - private: /** * Select result of the items search. - * Used only by ModeLineEdit::updateSearch(). + * Used only by KateModeMenuListData::SearchLine::updateSearch(). */ void setSearchResult(const int rowItem, bool &bEmptySection, int &lastSection, int &lastItem); - KateModeMenuList *m_parentMenu = nullptr; + /** + * Delay in search results after typing, in milliseconds. + * Default value: 200 + */ + static const int m_searchDelay = 170; - bool m_bSearchStateClear = true; bool m_bSearchStateAutoScroll = false; - }; + QString m_search = QString(); + int m_queuedSearches = 0; + Qt::CaseSensitivity m_caseSensitivity = Qt::CaseInsensitive; + KateModeMenuList *m_parentMenu = nullptr; + friend KateModeMenuList; - /** - * Action when displaying the menu. - * Override from QWidget. - */ - void showEvent(QShowEvent *event) override; - -private: - void init(const SearchBarPosition searchBarPos); - - /** - * Load the syntax highlighting definitions in the widget of list of items. - */ - void loadHighlightingList(); - - /** - * Set a custom word wrap on a text line, according to a maximum width (in pixels). - * @param text Line of text - * @param maxWidth Width of the text container, in pixels. - * @param fontMetrics Font metrics. See QWidget::fontMetrics() - */ - QString setWordWrap(const QString &text, const int maxWidth, const QFontMetrics &fontMetrics) const; - - /** - * Update the selected item in the list widget. - * This method only changes the selected item, doesn't apply - * syntax highlighting in the document, or hides the menu. - * @see selectHighlighting(), selectHighlightingFromExternal(), selectHighlightingSetVisibility() - */ - void updateSelectedItem(ModeListWidgetItem *item); - - /** - * Select an item from the list and apply the syntax highlighting in the document. - * This is equivalent to KateModeMenuList::selectHighlighting(). - * @param bHideMenu If the menu should be hidden after applying the highlight. - * @see selectHighlighting() - */ - void selectHighlightingSetVisibility(QListWidgetItem *pItem, const bool bHideMenu); - - /** - * Load message when the list is empty in the search. - */ - inline void loadEmptyMsg(); - - QPushButton *m_pushButton = nullptr; - AlignmentButton m_position; - bool m_bAutoUpdateTextButton; - - QGridLayout *m_layoutList = nullptr; - QLabel *m_emptyListMsg = nullptr; - - ModeLineEdit *m_searchBar = nullptr; - ModeListWidget *m_list = nullptr; - QScrollBar *m_scroll = nullptr; - - /** - * Item with active syntax highlighting. - */ - ModeListWidgetItem *m_selectedItem = nullptr; - - /** - * Icon for selected item (checkbox). - * NOTE: Selected and inactive items show an icon with incorrect color, - * however, this isn't a problem, since the list widget is never inactive. - */ - const QIcon m_checkIcon = QIcon::fromTheme(QStringLiteral("checkbox")); - static const int m_iconSize = 16; + protected: + /** + * Override from QLineEdit. This allows you to navigate through + * the menu and write in the search bar simultaneously with the keyboard. + */ + void keyPressEvent(QKeyEvent *event) override; - QPointer m_doc; + public Q_SLOTS: + virtual void clear(); + virtual void updateSearch(const QString &s = QString()); -private Q_SLOTS: - /** - * Action when selecting a item in the list. This also applies - * the syntax highlighting in the document and hides the menu. - * This is equivalent to KateModeMenuList::selectHighlightingSetVisibility(). - * @see selectHighlightingSetVisibility(), updateSelectedItem() - */ - void selectHighlighting(QListWidgetItem *pItem); -}; + private Q_SLOTS: + void _k_queueSearch(const QString &s); + void _k_activateSearch(); + }; +} #endif // KATEMODEMENULIST_H