File indexing completed on 2024-04-14 03:55:12
0001 /* 0002 SPDX-FileCopyrightText: 2019-2020 Nibaldo González S. <nibgonz@gmail.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 /* 0008 * NOTE: The KateModeMenuListData::SearchLine class is based on 0009 * KListWidgetSearchLine, by Scott Wheeler <wheeler@kde.org> and 0010 * Gustavo Sverzut Barbieri <gsbarbieri@users.sourceforge.net>. 0011 * See: https://api.kde.org/frameworks/kitemviews/html/classKListWidgetSearchLine.html 0012 * 0013 * TODO: Add keyboard shortcut to show the menu. 0014 * See: KateModeMenuList::showEvent() 0015 */ 0016 0017 #ifndef KATEMODEMENULIST_H 0018 #define KATEMODEMENULIST_H 0019 0020 #include <QGridLayout> 0021 #include <QIcon> 0022 #include <QKeyEvent> 0023 #include <QLabel> 0024 #include <QLineEdit> 0025 #include <QListView> 0026 #include <QMenu> 0027 #include <QPointer> 0028 #include <QPushButton> 0029 #include <QScrollBar> 0030 #include <QStandardItemModel> 0031 #include <QString> 0032 0033 namespace KTextEditor 0034 { 0035 class DocumentPrivate; 0036 class Document; 0037 } 0038 0039 namespace KateModeMenuListData 0040 { 0041 class ListView; 0042 class ListItem; 0043 class SearchLine; 0044 class Factory; 0045 } 0046 0047 class KateFileType; 0048 /** 0049 * Class of menu to select the 0050 * syntax highlighting language (mode menu). 0051 * Provides a menu with a scrollable list plus search bar. 0052 * 0053 * This is an alternative to the classic mode menu of the KateModeMenu class. 0054 * 0055 * @see KateModeManager, KateFileType, KateModeMenu 0056 */ 0057 class KateModeMenuList : public QMenu 0058 { 0059 public: 0060 /** 0061 * Horizontal Alignment with respect to the trigger button. 0062 * "AlignHDefault" is the normal alignment. 0063 * "AlignHInverse" uses right alignment in Left-to-right layouts and 0064 * left alignmentnin Right-to-left layouts (used in some languages). 0065 * "AlignLeft" and "AlignRight" forces the alignment, regardless of the layout direction. 0066 * @see setButton(), QWidget::layoutDirection(), Qt::LayoutDirection 0067 */ 0068 enum AlignmentHButton { AlignHDefault, AlignHInverse, AlignLeft, AlignRight }; 0069 /** 0070 * Vertical Alignment with respect to the trigger button. 0071 * "AlignVDefault" uses normal alignment (below the button) and "AlignTop" 0072 * forces the alignment above the trigger button. 0073 * @see setButton(), KateStatusBarOpenUpMenu::setVisible() 0074 */ 0075 enum AlignmentVButton { AlignVDefault, AlignTop }; 0076 /** 0077 * Define if the trigger button label must be updated when selecting an item. 0078 * @see setButton() 0079 */ 0080 enum class AutoUpdateTextButton : bool; 0081 /** 0082 * Search bar position, above or below the list. 0083 */ 0084 enum SearchBarPosition { Top, Bottom }; 0085 /** 0086 * Defines where the list will scroll after clearing the search or changing the view. 0087 * @see setAutoScroll(), autoScroll() 0088 */ 0089 enum AutoScroll { ScrollToSelectedItem, ScrollToTop }; 0090 0091 KateModeMenuList(const QString &title, QWidget *parent) 0092 : QMenu(title, parent) 0093 { 0094 init(); 0095 } 0096 0097 /** 0098 * Reload all items. 0099 * @see KateModeManager::update() 0100 */ 0101 void reloadItems(); 0102 0103 /** 0104 * Update the selected item in the list widget, but without changing 0105 * the syntax highlighting in the document. 0106 * This is useful for updating this menu, when changing the syntax highlighting 0107 * from another menu, or from an external one. This doesn't hide or show the menu. 0108 * @param nameMode Raw name of the syntax highlight definition. If it's empty, 0109 * the "Normal" mode will be used. 0110 * @return True if @p nameMode exists and is selected. 0111 */ 0112 bool selectHighlightingFromExternal(const QString &nameMode); 0113 /** 0114 * Update the selected item in the list widget, but without changing 0115 * the syntax highlighting in the document. This doesn't hide or show the menu. 0116 * The menu is kept updated according to the active syntax highlighting, 0117 * obtained from the KTextEditor::DocumentPrivate class. 0118 * @return True if the item is selected correctly. 0119 * @see KTextEditor::DocumentPrivate::fileType() 0120 */ 0121 bool selectHighlightingFromExternal(); 0122 0123 /** 0124 * Set the button that shows this menu. It allows to update the label 0125 * of the button and define the alignment of the menu with respect to it. 0126 * This function doesn't call QPushButton::setMenu(). 0127 * @param button Trigger button. 0128 * @param positionX Horizontal position of the menu with respect to the trigger button. 0129 * @param positionY Vertical position of the menu with respect to the trigger button. 0130 * @param autoUpdateTextButton Determines whether the text of the button should be 0131 * changed when selecting an item from the menu. 0132 * 0133 * @see AlignmentHButton, AlignmentVButton, AutoUpdateTextButton 0134 */ 0135 void setButton(QPushButton *button, 0136 AlignmentHButton positionX = AlignHDefault, 0137 AlignmentVButton positionY = AlignTop, 0138 AutoUpdateTextButton autoUpdateTextButton = AutoUpdateTextButton(false)); 0139 0140 /** 0141 * Define the scroll when cleaning the search or changing the view. 0142 * The default value is AutoScroll::ScrollToSelectedItem. 0143 * @see AutoScroll 0144 */ 0145 void setAutoScroll(AutoScroll scroll) 0146 { 0147 m_autoScroll = scroll; 0148 } 0149 0150 /** 0151 * Set document to apply the syntax highlighting. 0152 * @see KTextEditor::DocumentPrivate 0153 */ 0154 void updateMenu(KTextEditor::Document *doc); 0155 0156 protected: 0157 friend KateModeMenuListData::ListView; 0158 friend KateModeMenuListData::ListItem; 0159 friend KateModeMenuListData::SearchLine; 0160 0161 /** 0162 * Action when displaying the menu. 0163 * Override from QWidget. 0164 */ 0165 void showEvent(QShowEvent *event) override; 0166 0167 private: 0168 void init(); 0169 0170 void onAboutToShowMenu(); 0171 0172 /** 0173 * Define the size of the list widget, in pixels. The @p width is also 0174 * applied to the search bar. This does not recalculate the word wrap in items. 0175 */ 0176 inline void setSizeList(const int height, const int width = 266); 0177 0178 /** 0179 * Load the data model with the syntax highlighting definitions to show in the list. 0180 */ 0181 void loadHighlightingModel(); 0182 0183 /** 0184 * Scroll the list, according to AutoScroll. 0185 * @see AutoScroll 0186 */ 0187 void autoScroll(); 0188 0189 /** 0190 * Set a custom word wrap on a text line, according to a maximum width (in pixels). 0191 * @param text Line of text 0192 * @param maxWidth Width of the text container, in pixels. 0193 * @param fontMetrics Font metrics. See QWidget::fontMetrics() 0194 */ 0195 QString setWordWrap(const QString &text, const int maxWidth, const QFontMetrics &fontMetrics) const; 0196 0197 /** 0198 * Update the selected item in the list, with the active syntax highlighting. 0199 * This method only changes the selected item, with the checkbox icon, doesn't apply 0200 * syntax highlighting in the document or hides the menu. 0201 * @see selectHighlighting(), selectHighlightingFromExternal(), selectHighlightingSetVisibility() 0202 */ 0203 void updateSelectedItem(KateModeMenuListData::ListItem *item); 0204 0205 /** 0206 * Select an item from the list and apply the syntax highlighting in the document. 0207 * This is equivalent to the slot: KateModeMenuList::selectHighlighting(). 0208 * @param bHideMenu If the menu should be hidden after applying the highlight. 0209 * @see selectHighlighting() 0210 */ 0211 void selectHighlightingSetVisibility(QStandardItem *pItem, const bool bHideMenu); 0212 0213 /** 0214 * Create a new section in the list of items and add it to the model. 0215 * It corresponds to a separator line and a title. 0216 * @param sectionName Section title. 0217 * @param background Background color is generally transparent. 0218 * @param bSeparator True if a separation line will also be created before the section title. 0219 * @param modelPosition Position in the model where to insert the new section. If the value is 0220 * less than zero, the section is added to the end of the list/model. 0221 * @return A pointer to the item created with the section title. 0222 */ 0223 KateModeMenuListData::ListItem *createSectionList(const QString §ionName, const QBrush &background, bool bSeparator = true, int modelPosition = -1); 0224 0225 /** 0226 * Load message when the list is empty in the search. 0227 */ 0228 void loadEmptyMsg(); 0229 0230 AutoScroll m_autoScroll = ScrollToSelectedItem; 0231 AlignmentHButton m_positionX; 0232 AlignmentVButton m_positionY; 0233 AutoUpdateTextButton m_autoUpdateTextButton; 0234 0235 QPointer<QPushButton> m_pushButton = nullptr; 0236 QLabel *m_emptyListMsg = nullptr; 0237 QGridLayout *m_layoutList = nullptr; 0238 QScrollBar *m_scroll = nullptr; 0239 0240 KateModeMenuListData::SearchLine *m_searchBar = nullptr; 0241 KateModeMenuListData::ListView *m_list = nullptr; 0242 QStandardItemModel *m_model = nullptr; 0243 0244 /** 0245 * Item with active syntax highlighting. 0246 */ 0247 KateModeMenuListData::ListItem *m_selectedItem = nullptr; 0248 0249 /** 0250 * Icon for selected/active item (checkbox). 0251 * NOTE: Selected and inactive items show an icon with incorrect color, 0252 * however, this isn't a problem, since the list widget is never inactive. 0253 */ 0254 const QIcon m_checkIcon = QIcon::fromTheme(QStringLiteral("checkbox")); 0255 QIcon m_emptyIcon; 0256 int m_iconSize = 16; 0257 0258 int m_defaultHeightItemSection; 0259 0260 QPointer<KTextEditor::DocumentPrivate> m_doc; 0261 0262 bool m_initialized = false; 0263 0264 private: 0265 /** 0266 * Action when selecting a item in the list. This also applies 0267 * the syntax highlighting in the document and hides the menu. 0268 * This is equivalent to KateModeMenuList::selectHighlightingSetVisibility(). 0269 * @see selectHighlightingSetVisibility(), updateSelectedItem() 0270 */ 0271 void selectHighlighting(const QModelIndex &index); 0272 }; 0273 0274 namespace KateModeMenuListData 0275 { 0276 /** 0277 * Class of List Widget. 0278 */ 0279 class ListView : public QListView 0280 { 0281 private: 0282 ListView(KateModeMenuList *menu) 0283 : QListView(menu) 0284 { 0285 m_parentMenu = menu; 0286 } 0287 0288 public: 0289 ~ListView() override 0290 { 0291 } 0292 0293 /** 0294 * Define the size of the widget list. 0295 * @p height and @p width are values in pixels. 0296 */ 0297 void setSizeList(const int height, const int width = 266); 0298 0299 /** 0300 * Get the width of the list, in pixels. 0301 * @see QAbstractScrollArea::sizeHint() 0302 */ 0303 inline int getWidth() const; 0304 0305 /** 0306 * Get the width of the contents of the list (in pixels), that is, 0307 * the list minus the scroll bar and margins. 0308 */ 0309 int getContentWidth() const; 0310 0311 /** 0312 * Get the width of the contents of the list (in pixels), that is, the list minus 0313 * the scroll bar and margins. The parameter allows you to specify additional margins 0314 * according to the scroll bar, which can be superimposed or fixed depending to the 0315 * desktop environment or operating system. 0316 * @param overlayScrollbarMargin Additional margin for the scroll bar, if it is 0317 * superimposed on the list. 0318 * @param classicScrollbarMargin Additional margin for the scroll bar, if fixed in the list. 0319 */ 0320 inline int getContentWidth(const int overlayScrollbarMargin, const int classicScrollbarMargin) const; 0321 0322 inline void setCurrentItem(const int rowItem) 0323 { 0324 selectionModel()->setCurrentIndex(m_parentMenu->m_model->index(rowItem, 0), QItemSelectionModel::ClearAndSelect); 0325 } 0326 0327 inline QStandardItem *currentItem() const 0328 { 0329 return m_parentMenu->m_model->item(currentIndex().row(), 0); 0330 } 0331 0332 inline void scrollToItem(const int rowItem, QAbstractItemView::ScrollHint hint = QAbstractItemView::PositionAtCenter) 0333 { 0334 scrollTo(m_parentMenu->m_model->index(rowItem, 0), hint); 0335 } 0336 0337 inline void scrollToFirstItem() 0338 { 0339 setCurrentItem(1); 0340 scrollToTop(); 0341 } 0342 0343 protected: 0344 /** 0345 * Override from QListView. 0346 */ 0347 void keyPressEvent(QKeyEvent *event) override; 0348 0349 private: 0350 KateModeMenuList *m_parentMenu = nullptr; 0351 friend Factory; 0352 }; 0353 0354 /** 0355 * Class of an Item of the Data Model of the List. 0356 * @see KateModeMenuListData::ListView, KateFileType, QStandardItemModel 0357 */ 0358 class ListItem : public QStandardItem 0359 { 0360 private: 0361 ListItem() 0362 : QStandardItem() 0363 { 0364 } 0365 0366 const KateFileType *m_type = nullptr; 0367 QString m_searchName; 0368 0369 friend Factory; 0370 0371 public: 0372 ~ListItem() override 0373 { 0374 } 0375 0376 /** 0377 * Associate this item with a KateFileType object. 0378 */ 0379 inline void setMode(KateFileType *type) 0380 { 0381 m_type = type; 0382 } 0383 const KateFileType *getMode() const 0384 { 0385 return m_type; 0386 } 0387 bool hasMode() const 0388 { 0389 return m_type; 0390 } 0391 0392 /** 0393 * Generate name of the item used for the search. 0394 * @param itemName The item name. 0395 * @return True if a new name is generated for the search. 0396 */ 0397 bool generateSearchName(const QString &itemName); 0398 0399 /** 0400 * Find matches in the extensions of the item mode, with a @p text. 0401 * @param text Text to match, without dots or asterisks. For example, in 0402 * a common extension, it corresponds to the text after "*." 0403 * @return True if a match is found, false if not. 0404 */ 0405 bool matchExtension(const QString &text) const; 0406 0407 const QString &getSearchName() const 0408 { 0409 return m_searchName; 0410 } 0411 }; 0412 0413 /** 0414 * Class of Search Bar. 0415 * Based on the KListWidgetSearchLine class. 0416 */ 0417 class SearchLine : public QLineEdit 0418 { 0419 public: 0420 ~SearchLine() override 0421 { 0422 m_bestResults.clear(); 0423 } 0424 0425 /** 0426 * Define the width of the search bar, in pixels. 0427 */ 0428 void setWidth(const int width); 0429 0430 private: 0431 SearchLine(KateModeMenuList *menu) 0432 : QLineEdit(menu) 0433 { 0434 m_parentMenu = menu; 0435 init(); 0436 } 0437 0438 void init(); 0439 0440 /** 0441 * Select result of the items search. 0442 * Used only by KateModeMenuListData::SearchLine::updateSearch(). 0443 */ 0444 void setSearchResult(const int rowItem, bool &bEmptySection, int &lastSection, int &firstSection, int &lastItem); 0445 0446 /** 0447 * Delay in search results after typing, in milliseconds. 0448 * Default value: 200 0449 */ 0450 static const int m_searchDelay = 170; 0451 0452 /** 0453 * This prevents auto-scrolling when the search is kept clean. 0454 */ 0455 bool m_bSearchStateAutoScroll = false; 0456 0457 QString m_search = QString(); 0458 int m_queuedSearches = 0; 0459 Qt::CaseSensitivity m_caseSensitivity = Qt::CaseInsensitive; 0460 0461 /** 0462 * List of items to display in the "Best Search Matches" section. The integer value 0463 * corresponds to the original position of the item in the model. The purpose of this 0464 * is to restore the position of the items when starting or cleaning a search. 0465 */ 0466 QList<QPair<ListItem *, int>> m_bestResults; 0467 0468 KateModeMenuList *m_parentMenu = nullptr; 0469 friend Factory; 0470 friend void KateModeMenuList::reloadItems(); 0471 0472 protected: 0473 /** 0474 * Override from QLineEdit. This allows you to navigate through 0475 * the menu and write in the search bar simultaneously with the keyboard. 0476 */ 0477 void keyPressEvent(QKeyEvent *event) override; 0478 0479 public: 0480 virtual void clear(); 0481 virtual void updateSearch(const QString &s = QString()); 0482 0483 private: 0484 void _k_queueSearch(const QString &s); 0485 void _k_activateSearch(); 0486 }; 0487 0488 class Factory 0489 { 0490 private: 0491 friend KateModeMenuList; 0492 Factory(){}; 0493 static ListView *createListView(KateModeMenuList *parentMenu) 0494 { 0495 return new ListView(parentMenu); 0496 } 0497 static ListItem *createListItem() 0498 { 0499 return new ListItem(); 0500 } 0501 static SearchLine *createSearchLine(KateModeMenuList *parentMenu) 0502 { 0503 return new SearchLine(parentMenu); 0504 } 0505 }; 0506 } 0507 0508 #endif // KATEMODEMENULIST_H