diff --git a/src/mode/katemodemanager.cpp b/src/mode/katemodemanager.cpp --- a/src/mode/katemodemanager.cpp +++ b/src/mode/katemodemanager.cpp @@ -21,6 +21,7 @@ // BEGIN Includes #include "katemodemanager.h" #include "katewildcardmatcher.h" +#include "katestatusbar.h" #include "kateconfig.h" #include "katedocument.h" @@ -70,6 +71,7 @@ QStringList g(config.groupList()); + KateFileType *normalType = nullptr; qDeleteAll(m_types); m_types.clear(); m_name2Type.clear(); @@ -79,7 +81,6 @@ KateFileType *type = new KateFileType(); type->number = z; type->name = g[z]; - type->section = cg.readEntry(QStringLiteral("Section")); type->wildcards = cg.readXdgListEntry(QStringLiteral("Wildcards")); type->mimetypes = cg.readXdgListEntry(QStringLiteral("Mimetypes")); type->priority = cg.readEntry(QStringLiteral("Priority"), 0); @@ -90,19 +91,34 @@ // only for generated types... type->hlGenerated = cg.readEntry(QStringLiteral("Highlighting Generated"), false); - type->version = cg.readEntry(QStringLiteral("Highlighting Version")); - // insert into the list + hash... - m_types.append(type); + // the "Normal" mode will be added later + if (type->name == QLatin1String("Normal")) { + if (!normalType) { + normalType = type; + } + } else { + type->section = cg.readEntry(QStringLiteral("Section")); + type->version = cg.readEntry(QStringLiteral("Highlighting Version")); + } + + // insert into the hash... + // NOTE: "katemoderc" could have modes that do not exist or are invalid (for example, custom + // XML files that were deleted or renamed), so they will be added to the list "m_types" later m_name2Type.insert(type->name, type); } // try if the hl stuff is up to date... const auto modes = KateHlManager::self()->modeList(); for (int i = 0; i < modes.size(); ++i) { - // filter out hidden languages; and - // filter out "None" hl, we add that later as "normal" mode - if (modes[i].isHidden() || modes[i].name() == QLatin1String("None")) { + /** + * filter out hidden languages; and + * filter out "None" hl, we add that later as "Normal" mode. + * Hl with empty names will also be filtered. The + * KTextEditor::DocumentPrivate::updateFileType() function considers + * hl with empty names as invalid. + */ + if (modes[i].isHidden() || modes[i].name().isEmpty() || modes[i].name() == QLatin1String("None")) { continue; } @@ -115,9 +131,10 @@ type = new KateFileType(); type->name = modes[i].name(); type->priority = 0; - m_types.append(type); m_name2Type.insert(type->name, type); } + // only the types that exist or are valid are added + m_types.append(type); if (newType || type->version != QString::number(modes[i].version())) { type->name = modes[i].name(); @@ -139,15 +156,25 @@ std::sort(m_types.begin(), m_types.end(), compareKateFileType); // add the none type... - KateFileType *t = new KateFileType(); + if (!normalType) { + normalType = new KateFileType(); + } // marked by hlGenerated - t->name = QStringLiteral("Normal"); - t->translatedName = i18n("Normal"); - t->hl = QStringLiteral("None"); - t->hlGenerated = true; - - m_types.prepend(t); + normalType->name = QStringLiteral("Normal"); + normalType->translatedName = i18n("Normal"); + normalType->hl = QStringLiteral("None"); + normalType->hlGenerated = true; + + m_types.prepend(normalType); + + // update the mode menu of the status bar, for all views. + // this menu uses the KateFileType objects + for (auto *view : KTextEditor::EditorPrivate::self()->views()) { + if (view->statusBar() && view->statusBar()->modeMenu()) { + view->statusBar()->modeMenu()->reloadItems(); + } + } } // diff --git a/src/mode/katemodemenu.cpp b/src/mode/katemodemenu.cpp --- a/src/mode/katemodemenu.cpp +++ b/src/mode/katemodemenu.cpp @@ -60,6 +60,9 @@ QString hlName = KTextEditor::EditorPrivate::self()->modeManager()->list().at(z)->nameTranslated(); QString hlSection = KTextEditor::EditorPrivate::self()->modeManager()->list().at(z)->sectionTranslated(); + if (hlName.isEmpty()) { + continue; + } if (!hlSection.isEmpty() && !names.contains(hlName)) { if (!subMenusName.contains(hlSection)) { subMenusName << hlSection; diff --git a/src/mode/katemodemenulist.h b/src/mode/katemodemenulist.h --- a/src/mode/katemodemenulist.h +++ b/src/mode/katemodemenulist.h @@ -141,23 +141,31 @@ { } + /** + * Reload all items. + * @see KateModeManager::update() + */ + void reloadItems(); + /** * 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. + * @return True if @p nameMode exists and is selected. */ - void selectHighlightingFromExternal(const QString &nameMode); + bool 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. + * @return True if the item is selected correctly. * @see KTextEditor::DocumentPrivate::fileType() */ - void selectHighlightingFromExternal(); + bool selectHighlightingFromExternal(); /** * Set the button that shows this menu. It allows to update the label @@ -259,14 +267,14 @@ /** * Load message when the list is empty in the search. */ - inline void loadEmptyMsg(); + void loadEmptyMsg(); AutoScroll m_autoScroll = ScrollToSelectedItem; AlignmentHButton m_positionX; AlignmentVButton m_positionY; AutoUpdateTextButton m_autoUpdateTextButton; - QPushButton *m_pushButton = nullptr; + QPointer m_pushButton = nullptr; QLabel *m_emptyListMsg = nullptr; QGridLayout *m_layoutList = nullptr; QScrollBar *m_scroll = nullptr; @@ -358,6 +366,7 @@ { selectionModel()->setCurrentIndex(m_parentMenu->m_model->index(rowItem, 0), QItemSelectionModel::ClearAndSelect); } + inline QStandardItem *currentItem() const { return m_parentMenu->m_model->item(currentIndex().row(), 0); @@ -368,6 +377,12 @@ scrollTo(m_parentMenu->m_model->index(rowItem, 0), hint); } + inline void scrollToFirstItem() + { + setCurrentItem(1); + scrollToTop(); + } + protected: /** * Override from QListView. @@ -479,7 +494,11 @@ */ static const int m_searchDelay = 170; + /** + * This prevents auto-scrolling when the search is kept clean. + */ bool m_bSearchStateAutoScroll = false; + QString m_search = QString(); int m_queuedSearches = 0; Qt::CaseSensitivity m_caseSensitivity = Qt::CaseInsensitive; @@ -493,6 +512,7 @@ KateModeMenuList *m_parentMenu = nullptr; friend Factory; + friend void KateModeMenuList::reloadItems(); protected: /** diff --git a/src/mode/katemodemenulist.cpp b/src/mode/katemodemenulist.cpp --- a/src/mode/katemodemenulist.cpp +++ b/src/mode/katemodemenulist.cpp @@ -139,6 +139,8 @@ setSizeList(menuHeight, menuWidth); // Data model (items). + // couple model to view to let it be deleted with the view + m_model = new QStandardItemModel(0, 0, m_list); loadHighlightingModel(); /* @@ -212,10 +214,36 @@ connect(m_list, &KateModeMenuListData::ListView::clicked, this, &KateModeMenuList::selectHighlighting); } +void KateModeMenuList::reloadItems() +{ + const QString searchText = m_searchBar->text().trimmed(); + m_searchBar->m_bestResults.clear(); + if (!isHidden()) { + hide(); + } + /* + * Clear model. + * NOTE: This deletes the item objects and widgets indexed to items. + * That is, the QLabel & QFrame objects of the section titles are also deleted. + * See: QAbstractItemView::setIndexWidget(), QObject::deleteLater() + */ + m_model->clear(); + m_list->selectionModel()->clear(); + m_selectedItem = nullptr; + + loadHighlightingModel(); + + // Restore search text, if there is. + m_searchBar->m_bSearchStateAutoScroll = false; + if (!searchText.isEmpty()) { + selectHighlightingFromExternal(); + m_searchBar->updateSearch(searchText); + m_searchBar->setText(searchText); + } +} + void KateModeMenuList::loadHighlightingModel() { - // couple model to view to let it be deleted with the view - m_model = new QStandardItemModel(0, 0, m_list); m_list->setModel(m_model); QString *prevHlSection = nullptr; @@ -245,6 +273,10 @@ * and the attribute "translatedSection" isn't empty if "section" has a value. */ for (auto *hl : KTextEditor::EditorPrivate::self()->modeManager()->list()) { + if (hl->name.isEmpty()) { + continue; + } + // Detects a new section. if (!hl->translatedSection.isEmpty() && (prevHlSection == nullptr || hl->translatedSection != *prevHlSection)) { createSectionList(hl->sectionTranslated(), transparentBrush); @@ -374,12 +406,11 @@ void KateModeMenuList::autoScroll() { - if (m_autoScroll == ScrollToSelectedItem) { + if (m_selectedItem && 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(); + m_list->scrollToFirstItem(); } } @@ -433,7 +464,7 @@ // Select text from the search bar if (!m_searchBar->text().isEmpty()) { - if (m_searchBar->text().simplified().isEmpty()) { + if (m_searchBar->text().trimmed().isEmpty()) { m_searchBar->clear(); } else { m_searchBar->selectAll(); @@ -450,7 +481,16 @@ // 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()); + if (!selectHighlightingFromExternal(doc->fileType())) { + // Strange case: if the current syntax highlighting does not exist in the list. + if (m_selectedItem) { + m_selectedItem->setIcon(m_emptyIcon); + } + if ((m_selectedItem || !m_list->currentItem()) && m_searchBar->text().isEmpty()) { + m_list->scrollToFirstItem(); + } + m_selectedItem = nullptr; + } } } @@ -474,7 +514,7 @@ void KateModeMenuList::selectHighlightingSetVisibility(QStandardItem *pItem, const bool bHideMenu) { - if (!pItem->isSelectable() || !pItem->isEnabled()) { + if (!pItem || !pItem->isSelectable() || !pItem->isEnabled()) { return; } @@ -499,7 +539,7 @@ selectHighlightingSetVisibility(m_model->item(index.row(), 0), true); } -void KateModeMenuList::selectHighlightingFromExternal(const QString &nameMode) +bool KateModeMenuList::selectHighlightingFromExternal(const QString &nameMode) { for (int i = 0; i < m_model->rowCount(); ++i) { KateModeMenuListData::ListItem *item = static_cast(m_model->item(i, 0)); @@ -523,20 +563,21 @@ m_list->scrollToItem(i); } else { // autoScroll() - m_list->setCurrentItem(0); - m_list->scrollToTop(); + m_list->scrollToFirstItem(); } - return; + return true; } } + return false; } -void KateModeMenuList::selectHighlightingFromExternal() +bool KateModeMenuList::selectHighlightingFromExternal() { KTextEditor::DocumentPrivate *doc = m_doc; if (doc) { - selectHighlightingFromExternal(doc->fileType()); + return selectHighlightingFromExternal(doc->fileType()); } + return false; } void KateModeMenuList::loadEmptyMsg() diff --git a/src/syntax/katehighlightmenu.cpp b/src/syntax/katehighlightmenu.cpp --- a/src/syntax/katehighlightmenu.cpp +++ b/src/syntax/katehighlightmenu.cpp @@ -56,7 +56,7 @@ if (hlName == QLatin1String("None")) hlName = i18n("None"); - if (!hl.isHidden()) { + if (!hl.isHidden() && !hlName.isEmpty()) { if (!hlSection.isEmpty() && !names.contains(hlName)) { if (!subMenusName.contains(hlSection)) { subMenusName << hlSection; diff --git a/src/view/katestatusbar.h b/src/view/katestatusbar.h --- a/src/view/katestatusbar.h +++ b/src/view/katestatusbar.h @@ -23,6 +23,7 @@ #include "kateview.h" #include "kateviewhelpers.h" +#include "katemodemenulist.h" #include #include @@ -63,6 +64,8 @@ public: explicit KateStatusBar(KTextEditor::ViewPrivate *view); + KateModeMenuList *modeMenu() const; + public Q_SLOTS: void updateStatus(); @@ -105,6 +108,7 @@ QActionGroup *m_dictionaryGroup = nullptr; KateStatusBarOpenUpMenu *m_dictionaryMenu = nullptr; QMenu *m_indentSettingsMenu; + KateModeMenuList *m_modeMenuList = nullptr; unsigned int m_modifiedStatus; unsigned int m_selectionMode; QActionGroup *m_tabGroup; diff --git a/src/view/katestatusbar.cpp b/src/view/katestatusbar.cpp --- a/src/view/katestatusbar.cpp +++ b/src/view/katestatusbar.cpp @@ -25,7 +25,6 @@ #include "katedocument.h" #include "kateglobal.h" #include "katemodemanager.h" -#include "katemodemenulist.h" #include "wordcounter.h" #include @@ -197,16 +196,16 @@ * load the mode menu, which contains a scrollable list + search bar. * This is an alternative menu to the mode action menu of the view. */ - KateModeMenuList *modeMenuList = new KateModeMenuList(i18n("Mode"), this); - modeMenuList->setWhatsThis(i18n("Here you can choose which mode should be used for the current document. This will influence the highlighting and folding being used, for example.")); - modeMenuList->updateMenu(m_view->doc()); + m_modeMenuList = new KateModeMenuList(i18n("Mode"), this); + m_modeMenuList->setWhatsThis(i18n("Here you can choose which mode should be used for the current document. This will influence the highlighting and folding being used, for example.")); + m_modeMenuList->updateMenu(m_view->doc()); /** * add mode button which allows user to switch mode of document */ m_mode = new StatusBarButton(this); topLayout->addWidget(m_mode); - modeMenuList->setButton(m_mode, KateModeMenuList::AlignHInverse, KateModeMenuList::AlignTop, KateModeMenuList::AutoUpdateTextButton(false)); - m_mode->setMenu(modeMenuList); + m_modeMenuList->setButton(m_mode, KateModeMenuList::AlignHInverse, KateModeMenuList::AlignTop, KateModeMenuList::AutoUpdateTextButton(false)); + m_mode->setMenu(m_modeMenuList); m_mode->setWhatsThis(i18n("Syntax highlighting")); // signals for the statusbar @@ -586,3 +585,8 @@ m_view->doc()->setDefaultDictionary(dictionary); } } + +KateModeMenuList *KateStatusBar::modeMenu() const +{ + return m_modeMenuList; +}