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