KTextEditor

katemodemenulist.cpp
1/*
2 SPDX-FileCopyrightText: 2019-2020 Nibaldo González S. <nibgonz@gmail.com>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6#include "katemodemenulist.h"
7
8#include "kateconfig.h"
9#include "katedocument.h"
10#include "kateglobal.h"
11#include "katemodemanager.h"
12#include "katepartdebug.h"
13
14#include <QAbstractItemView>
15#include <QApplication>
16#include <QFrame>
17#include <QHBoxLayout>
18#include <QVBoxLayout>
19#include <QWidgetAction>
20
21#include <KLocalizedString>
22
23namespace
24{
25/**
26 * Detect words delimiters:
27 * ! " # $ % & ' ( ) * + , - . / : ;
28 * < = > ? [ \ ] ^ ` { | } ~ « »
29 */
30static bool isDelimiter(const ushort c)
31{
32 return (c <= 126 && c >= 33 && (c >= 123 || c <= 47 || (c <= 96 && c >= 58 && c != 95 && (c >= 91 || c <= 63)))) || c == 171 || c == 187;
33}
34
35/**
36 * Overlay scroll bar on the list according to the operating system
37 * and/or the desktop environment. In some desktop themes the scroll bar
38 * isn't transparent, so it's better not to overlap it on the list.
39 * NOTE: Currently, in the Breeze theme, the scroll bar does not overlap
40 * the content. See: https://phabricator.kde.org/T9126
41 */
42inline static bool overlapScrollBar()
43{
44 return false;
45}
46}
47
48void KateModeMenuList::init()
49{
50 connect(this, &QMenu::aboutToShow, this, &KateModeMenuList::onAboutToShowMenu);
51}
52
53void KateModeMenuList::onAboutToShowMenu()
54{
55 if (m_initialized) {
56 return;
57 }
58 /*
59 * Fix font size & font style: display the font correctly when changing it from the
60 * KDE Plasma preferences. For example, the font type "Menu" is displayed, but "font()"
61 * and "fontMetrics()" return the font type "General". Therefore, this overwrites the
62 * "General" font. This makes it possible to correctly apply word wrapping on items,
63 * when changing the font or its size.
64 */
65 QFont font = this->font();
66 font.setFamily(font.family());
67 font.setStyle(font.style());
68 font.setStyleName(font.styleName());
69 font.setBold(font.bold());
70 font.setItalic(font.italic());
71 font.setUnderline(font.underline());
72 font.setStrikeOut(font.strikeOut());
73 font.setPointSize(font.pointSize());
75
76 /*
77 * Calculate the size of the list and the checkbox icon (in pixels) according
78 * to the font size. From font 12pt to 26pt increase the list size.
79 */
80 int menuWidth = 266;
81 int menuHeight = 428;
82 const int fontSize = font.pointSize();
83 if (fontSize >= 12) {
84 const int increaseSize = (fontSize - 11) * 10;
85 if (increaseSize >= 150) { // Font size: 26pt
86 menuWidth += 150;
87 menuHeight += 150;
88 } else {
89 menuWidth += increaseSize;
90 menuHeight += increaseSize;
91 }
92
93 if (fontSize >= 22) {
94 m_iconSize = 32;
95 } else if (fontSize >= 18) {
96 m_iconSize = 24;
97 } else if (fontSize >= 14) {
98 m_iconSize = 22;
99 } else if (fontSize >= 12) {
100 m_iconSize = 18;
101 }
102 }
103
104 // Create list and search bar
105 m_list = KateModeMenuListData::Factory::createListView(this);
106 m_searchBar = KateModeMenuListData::Factory::createSearchLine(this);
107
108 // Empty icon for items.
109 QPixmap emptyIconPixmap(m_iconSize, m_iconSize);
110 emptyIconPixmap.fill(Qt::transparent);
111 m_emptyIcon = QIcon(emptyIconPixmap);
112
113 /*
114 * Load list widget, scroll bar and items.
115 */
116 if (overlapScrollBar()) {
117 // The vertical scroll bar will be added in another layout
118 m_scroll = new QScrollBar(Qt::Vertical, this);
119 m_list->setVerticalScrollBar(m_scroll);
122 } else {
125 }
126 m_list->setIconSize(QSize(m_iconSize, m_iconSize));
128 // Size of the list widget and search bar.
129 setSizeList(menuHeight, menuWidth);
130
131 // Data model (items).
132 // couple model to view to let it be deleted with the view
133 m_model = new QStandardItemModel(0, 0, m_list);
134 loadHighlightingModel();
135
136 /*
137 * Search bar widget.
138 */
139 m_searchBar->setPlaceholderText(i18nc("Placeholder in search bar", "Search..."));
140 m_searchBar->setToolTip(i18nc("ToolTip of the search bar of modes of syntax highlighting",
141 "Search for syntax highlighting modes by language name or file extension (for example, C++ or .cpp)"));
142 m_searchBar->setMaxLength(200);
143
144 m_list->setFocusProxy(m_searchBar);
145
146 /*
147 * Set layouts and widgets.
148 * container (QWidget)
149 * └── layoutContainer (QVBoxLayout)
150 * ├── m_layoutList (QGridLayout)
151 * │ ├── m_list (ListView)
152 * │ ├── layoutScrollBar (QHBoxLayout) --> m_scroll (QScrollBar)
153 * │ └── m_emptyListMsg (QLabel)
154 * └── layoutSearchBar (QHBoxLayout) --> m_searchBar (SearchLine)
155 */
156 QWidget *container = new QWidget(this);
157 QVBoxLayout *layoutContainer = new QVBoxLayout(container);
158 m_layoutList = new QGridLayout();
159 QHBoxLayout *layoutSearchBar = new QHBoxLayout();
160
161 m_layoutList->addWidget(m_list, 0, 0, Qt::AlignLeft);
162
163 // Add scroll bar and set margin.
164 // Overlap scroll bar above the list widget.
165 if (overlapScrollBar()) {
166 QHBoxLayout *layoutScrollBar = new QHBoxLayout();
167 layoutScrollBar->addWidget(m_scroll);
168 layoutScrollBar->setContentsMargins(1, 2, 2, 2); // ScrollBar Margin = 2, Also see: KateModeMenuListData::ListView::getContentWidth()
169 m_layoutList->addLayout(layoutScrollBar, 0, 0, Qt::AlignRight);
170 }
171
172 layoutSearchBar->addWidget(m_searchBar);
173 layoutContainer->addLayout(m_layoutList);
174 layoutContainer->addLayout(layoutSearchBar);
175
176 QWidgetAction *widAct = new QWidgetAction(this);
177 widAct->setDefaultWidget(container);
178 addAction(widAct);
179
180 /*
181 * Detect selected item with one click.
182 * This also applies to double-clicks.
183 */
184 connect(m_list, &KateModeMenuListData::ListView::clicked, this, &KateModeMenuList::selectHighlighting);
185
186 m_initialized = true;
187}
188
190{
191 // We aren't initialized, nothing to reload
192 if (!m_initialized) {
193 return;
194 }
195
196 const QString searchText = m_searchBar->text().trimmed();
197 m_searchBar->m_bestResults.clear();
198 if (!isHidden()) {
199 hide();
200 }
201 /*
202 * Clear model.
203 * NOTE: This deletes the item objects and widgets indexed to items.
204 * That is, the QLabel & QFrame objects of the section titles are also deleted.
205 * See: QAbstractItemView::setIndexWidget(), QObject::deleteLater()
206 */
207 m_model->clear();
208 m_list->selectionModel()->clear();
209 m_selectedItem = nullptr;
210
211 loadHighlightingModel();
212
213 // Restore search text, if there is.
214 m_searchBar->m_bSearchStateAutoScroll = false;
215 if (!searchText.isEmpty()) {
217 m_searchBar->updateSearch(searchText);
218 m_searchBar->setText(searchText);
219 }
220}
221
222void KateModeMenuList::loadHighlightingModel()
223{
224 m_list->setModel(m_model);
225
226 QString *prevHlSection = nullptr;
227 /*
228 * The width of the text container in the item, in pixels. This is used to make
229 * a custom word wrap and prevent the item's text from passing under the scroll bar.
230 * NOTE: 8 = Icon margin
231 */
232 const int maxWidthText = m_list->getContentWidth(1, 8) - m_iconSize - 8;
233
234 // Transparent color used as background in the sections.
235 QPixmap transparentPixmap = QPixmap(m_iconSize / 2, m_iconSize / 2);
236 transparentPixmap.fill(Qt::transparent);
237 QBrush transparentBrush(transparentPixmap);
238
239 /*
240 * The first item on the list is the "Best Search Matches" section,
241 * which will remain hidden and will only be shown when necessary.
242 */
243 createSectionList(QString(), transparentBrush, false);
244 m_defaultHeightItemSection = m_list->visualRect(m_model->index(0, 0)).height();
245 m_list->setRowHidden(0, true);
246
247 /*
248 * Get list of modes from KateModeManager::list().
249 * We assume that the modes are arranged according to sections, alphabetically;
250 * and the attribute "translatedSection" isn't empty if "section" has a value.
251 */
252 for (auto *hl : KTextEditor::EditorPrivate::self()->modeManager()->list()) {
253 if (hl->name.isEmpty()) {
254 continue;
255 }
256
257 // Detects a new section.
258 if (!hl->translatedSection.isEmpty() && (prevHlSection == nullptr || hl->translatedSection != *prevHlSection)) {
259 createSectionList(hl->sectionTranslated(), transparentBrush);
260 }
261 prevHlSection = hl->translatedSection.isNull() ? nullptr : &hl->translatedSection;
262
263 // Create item in the list with the language name.
264 KateModeMenuListData::ListItem *item = KateModeMenuListData::Factory::createListItem();
265 /*
266 * NOTE:
267 * - (If the scroll bar is not overlapped) In QListView::setWordWrap(),
268 * when the scroll bar is hidden, the word wrap changes, but the size
269 * of the items is keeped, causing display problems in some items.
270 * KateModeMenuList::setWordWrap() applies a fixed word wrap.
271 * - Search names generated in: KateModeMenuListData::SearchLine::updateSearch()
272 */
273 item->setText(setWordWrap(hl->nameTranslated(), maxWidthText, m_list->fontMetrics()));
274 item->setMode(hl);
275
276 item->setIcon(m_emptyIcon);
277 item->setEditable(false);
278 // Add item
279 m_model->appendRow(item);
280 }
281}
282
283KateModeMenuListData::ListItem *KateModeMenuList::createSectionList(const QString &sectionName, const QBrush &background, bool bSeparator, int modelPosition)
284{
285 /*
286 * Add a separator to the list.
287 */
288 if (bSeparator) {
289 KateModeMenuListData::ListItem *separator = KateModeMenuListData::Factory::createListItem();
290 separator->setFlags(Qt::NoItemFlags);
291 separator->setEnabled(false);
292 separator->setEditable(false);
293 separator->setSelectable(false);
294
295 separator->setSizeHint(QSize(separator->sizeHint().width() - 2, 4));
296 separator->setBackground(background);
297
298 QFrame *line = new QFrame(m_list);
300
301 if (modelPosition < 0) {
302 m_model->appendRow(separator);
303 } else {
304 m_model->insertRow(modelPosition, separator);
305 }
306 m_list->setIndexWidget(m_model->index(separator->row(), 0), line);
308 }
309
310 /*
311 * Add the section name to the list.
312 */
313 KateModeMenuListData::ListItem *section = KateModeMenuListData::Factory::createListItem();
314 section->setFlags(Qt::NoItemFlags);
315 section->setEnabled(false);
316 section->setEditable(false);
317 section->setSelectable(false);
318
319 QLabel *label = new QLabel(sectionName, m_list);
320 if (m_list->layoutDirection() == Qt::RightToLeft) {
321 label->setAlignment(Qt::AlignRight);
322 }
323 label->setTextFormat(Qt::PlainText);
324 label->setIndent(6);
325
326 /*
327 * NOTE: Names of sections in bold. The font color
328 * should change according to Kate's color theme.
329 */
330 QFont font = label->font();
331 font.setWeight(QFont::Bold);
332 label->setFont(font);
333
334 section->setBackground(background);
335
336 if (modelPosition < 0) {
337 m_model->appendRow(section);
338 } else {
339 m_model->insertRow(modelPosition + 1, section);
340 }
341 m_list->setIndexWidget(m_model->index(section->row(), 0), label);
343
344 // Apply word wrap in sections, for long labels.
345 const int containerTextWidth = m_list->getContentWidth(2, 4);
346 int heightSectionMargin = m_list->visualRect(m_model->index(section->row(), 0)).height() - label->sizeHint().height();
347
348 if (label->sizeHint().width() > containerTextWidth) {
349 label->setText(setWordWrap(label->text(), containerTextWidth - label->indent(), label->fontMetrics()));
350 if (heightSectionMargin < 2) {
351 heightSectionMargin = 2;
352 }
353 section->setSizeHint(QSize(section->sizeHint().width(), label->sizeHint().height() + heightSectionMargin));
354 } else if (heightSectionMargin < 2) {
355 section->setSizeHint(QSize(section->sizeHint().width(), label->sizeHint().height() + 2));
356 }
357
358 return section;
359}
360
361void KateModeMenuList::setButton(QPushButton *button, AlignmentHButton positionX, AlignmentVButton positionY, AutoUpdateTextButton autoUpdateTextButton)
362{
363 if (positionX == AlignHInverse) {
365 m_positionX = KateModeMenuList::AlignLeft;
366 } else {
367 m_positionX = KateModeMenuList::AlignRight;
368 }
369 } else if (positionX == AlignLeft && layoutDirection() != Qt::RightToLeft) {
370 m_positionX = KateModeMenuList::AlignHDefault;
371 } else {
372 m_positionX = positionX;
373 }
374
375 m_positionY = positionY;
376 m_pushButton = button;
377 m_autoUpdateTextButton = autoUpdateTextButton;
378}
379
380void KateModeMenuList::setSizeList(const int height, const int width)
381{
382 m_list->setSizeList(height, width);
383 m_searchBar->setWidth(width);
384}
385
386void KateModeMenuList::autoScroll()
387{
388 if (m_selectedItem && m_autoScroll == ScrollToSelectedItem) {
389 m_list->setCurrentItem(m_selectedItem->row());
390 m_list->scrollToItem(m_selectedItem->row(), QAbstractItemView::PositionAtCenter);
391 } else {
392 m_list->scrollToFirstItem();
393 }
394}
395
397{
398 Q_UNUSED(event);
399 /*
400 * TODO: Put the menu on the bottom-edge of the window if the status bar is hidden,
401 * to show the menu with keyboard shortcuts. To do this, it's preferable to add a new
402 * function/slot to display the menu, correcting the position. If the trigger button
403 * isn't set or is destroyed, there may be problems detecting Right-to-left layouts.
404 */
405
406 // Set the menu position
407 if (m_pushButton && m_pushButton->isVisible()) {
408 /*
409 * Get vertical position.
410 * NOTE: In KDE Plasma with Wayland, the reference point of the position
411 * is the main window, not the desktop. Therefore, if the window is vertically
412 * smaller than the menu, it will be positioned on the upper edge of the window.
413 */
414 int newMenu_y; // New vertical menu position
415 if (m_positionY == AlignTop) {
416 newMenu_y = m_pushButton->mapToGlobal(QPoint(0, 0)).y() - geometry().height();
417 if (newMenu_y < 0) {
418 newMenu_y = 0;
419 }
420 } else {
421 newMenu_y = pos().y();
422 }
423
424 // Set horizontal position.
425 if (m_positionX == AlignRight) {
426 // New horizontal menu position
427 int newMenu_x = pos().x() - geometry().width() + m_pushButton->geometry().width();
428 // Get position of the right edge of the toggle button
429 const int buttonPositionRight = m_pushButton->mapToGlobal(QPoint(0, 0)).x() + m_pushButton->geometry().width();
430 if (newMenu_x < 0) {
431 newMenu_x = 0;
432 } else if (newMenu_x + geometry().width() < buttonPositionRight) {
433 newMenu_x = buttonPositionRight - geometry().width();
434 }
435 move(newMenu_x, newMenu_y);
436 } else if (m_positionX == AlignLeft) {
437 move(m_pushButton->mapToGlobal(QPoint(0, 0)).x(), newMenu_y);
438 } else if (m_positionY == AlignTop) {
439 // Set vertical position, use the default horizontal position
440 move(pos().x(), newMenu_y);
441 }
442 }
443
444 // Select text from the search bar
445 if (!m_searchBar->text().isEmpty()) {
446 if (m_searchBar->text().trimmed().isEmpty()) {
447 m_searchBar->clear();
448 } else {
449 m_searchBar->selectAll();
450 }
451 }
452
453 // Set focus on the list. The list widget uses focus proxy to the search bar.
455
456 KTextEditor::DocumentPrivate *doc = m_doc;
457 if (!doc) {
458 return;
459 }
460
461 // First show or if an external changed the current syntax highlighting.
462 if (!m_selectedItem || (m_selectedItem->hasMode() && m_selectedItem->getMode()->name != doc->fileType())) {
463 if (!selectHighlightingFromExternal(doc->fileType())) {
464 // Strange case: if the current syntax highlighting does not exist in the list.
465 if (m_selectedItem) {
466 m_selectedItem->setIcon(m_emptyIcon);
467 }
468 if ((m_selectedItem || !m_list->currentItem()) && m_searchBar->text().isEmpty()) {
469 m_list->scrollToFirstItem();
470 }
471 m_selectedItem = nullptr;
472 }
473 }
474}
475
476void KateModeMenuList::updateSelectedItem(KateModeMenuListData::ListItem *item)
477{
478 // Change the previously selected item to empty icon
479 if (m_selectedItem) {
480 m_selectedItem->setIcon(m_emptyIcon);
481 }
482
483 // Update the selected item
484 item->setIcon(m_checkIcon);
485 m_selectedItem = item;
486 m_list->setCurrentItem(item->row());
487
488 // Change text of the trigger button
489 if (bool(m_autoUpdateTextButton) && m_pushButton && item->hasMode()) {
490 m_pushButton->setText(item->getMode()->nameTranslated());
491 }
492}
493
494void KateModeMenuList::selectHighlightingSetVisibility(QStandardItem *pItem, const bool bHideMenu)
495{
496 if (!pItem || !pItem->isSelectable() || !pItem->isEnabled()) {
497 return;
498 }
499
501
502 if (!item->text().isEmpty()) {
503 updateSelectedItem(item);
504 }
505 if (bHideMenu) {
506 hide();
507 }
508
509 // Apply syntax highlighting
510 KTextEditor::DocumentPrivate *doc = m_doc;
511 if (doc && item->hasMode()) {
512 doc->updateFileType(item->getMode()->name, true);
513 }
514}
515
516void KateModeMenuList::selectHighlighting(const QModelIndex &index)
517{
518 selectHighlightingSetVisibility(m_model->item(index.row(), 0), true);
519}
520
522{
523 for (int i = 0; i < m_model->rowCount(); ++i) {
524 KateModeMenuListData::ListItem *item = static_cast<KateModeMenuListData::ListItem *>(m_model->item(i, 0));
525
526 if (!item->hasMode() || m_model->item(i, 0)->text().isEmpty()) {
527 continue;
528 }
529 if (item->getMode()->name == nameMode || (nameMode.isEmpty() && item->getMode()->name == QLatin1String("Normal"))) {
530 updateSelectedItem(item);
531
532 // Clear search
533 if (!m_searchBar->text().isEmpty()) {
534 // Prevent the empty list message from being seen over the items for a short time
535 if (m_emptyListMsg) {
536 m_emptyListMsg->hide();
537 }
538
539 // NOTE: This calls updateSearch(), it's scrolled to the selected item or the first item.
540 m_searchBar->clear();
541 } else if (m_autoScroll == ScrollToSelectedItem) {
542 m_list->scrollToItem(i);
543 } else {
544 // autoScroll()
545 m_list->scrollToFirstItem();
546 }
547 return true;
548 }
549 }
550 return false;
551}
552
554{
555 KTextEditor::DocumentPrivate *doc = m_doc;
556 if (doc) {
557 return selectHighlightingFromExternal(doc->fileType());
558 }
559 return false;
560}
561
562void KateModeMenuList::loadEmptyMsg()
563{
564 m_emptyListMsg = new QLabel(i18nc("A search yielded no results", "No items matching your search"), this);
565 m_emptyListMsg->setMargin(15);
566 m_emptyListMsg->setWordWrap(true);
567
568 const int fontSize = font().pointSize() > 10 ? font().pointSize() + 4 : 14;
569
570 QColor color = m_emptyListMsg->palette().color(QPalette::Text);
571 m_emptyListMsg->setStyleSheet(QLatin1String("font-size: ") + QString::number(fontSize) + QLatin1String("pt; color: rgba(") + QString::number(color.red())
572 + QLatin1Char(',') + QString::number(color.green()) + QLatin1Char(',') + QString::number(color.blue())
573 + QLatin1String(", 0.3);"));
574
575 m_emptyListMsg->setAlignment(Qt::AlignCenter);
576 m_layoutList->addWidget(m_emptyListMsg, 0, 0, Qt::AlignCenter);
577}
578
579QString KateModeMenuList::setWordWrap(const QString &text, const int maxWidth, const QFontMetrics &fontMetrics) const
580{
581 // Get the length of the text, in pixels, and compare it with the container
582 if (fontMetrics.horizontalAdvance(text) <= maxWidth) {
583 return text;
584 }
585
586 // Add line breaks in the text to fit in the container
587 QStringList words = text.split(QLatin1Char(' '));
588 if (words.count() < 1) {
589 return text;
590 }
591 QString newText = QString();
592 QString tmpLineText = QString();
593
594 for (int i = 0; i < words.count() - 1; ++i) {
595 // Elide mode in long words
596 if (fontMetrics.horizontalAdvance(words[i]) > maxWidth) {
597 if (!tmpLineText.isEmpty()) {
598 newText += tmpLineText + QLatin1Char('\n');
599 tmpLineText.clear();
600 }
601 newText +=
602 fontMetrics.elidedText(words[i], m_list->layoutDirection() == Qt::RightToLeft ? Qt::ElideLeft : Qt::ElideRight, maxWidth) + QLatin1Char('\n');
603 continue;
604 } else {
605 tmpLineText += words[i];
606 }
607
608 // This prevents the last line of text from having only one word with 1 or 2 chars
609 if (i == words.count() - 3 && words[i + 2].length() <= 2
610 && fontMetrics.horizontalAdvance(tmpLineText + QLatin1Char(' ') + words[i + 1] + QLatin1Char(' ') + words[i + 2]) > maxWidth) {
611 newText += tmpLineText + QLatin1Char('\n');
612 tmpLineText.clear();
613 }
614 // Add line break if the maxWidth is exceeded with the next word
615 else if (fontMetrics.horizontalAdvance(tmpLineText + QLatin1Char(' ') + words[i + 1]) > maxWidth) {
616 newText += tmpLineText + QLatin1Char('\n');
617 tmpLineText.clear();
618 } else {
619 tmpLineText.append(QLatin1Char(' '));
620 }
621 }
622
623 // Add line breaks in delimiters, if the last word is greater than the container
624 bool bElidedLastWord = false;
625 if (fontMetrics.horizontalAdvance(words[words.count() - 1]) > maxWidth) {
626 bElidedLastWord = true;
627 const int lastw = words.count() - 1;
628 for (int c = words[lastw].length() - 1; c >= 0; --c) {
629 if (isDelimiter(words[lastw][c].unicode()) && fontMetrics.horizontalAdvance(words[lastw].mid(0, c + 1)) <= maxWidth) {
630 bElidedLastWord = false;
631 if (fontMetrics.horizontalAdvance(words[lastw].mid(c + 1)) > maxWidth) {
632 words[lastw] = words[lastw].mid(0, c + 1) + QLatin1Char('\n')
633 + fontMetrics.elidedText(words[lastw].mid(c + 1),
635 maxWidth);
636 } else {
637 words[lastw].insert(c + 1, QLatin1Char('\n'));
638 }
639 break;
640 }
641 }
642 }
643
644 if (!tmpLineText.isEmpty()) {
645 newText += tmpLineText;
646 }
647 if (bElidedLastWord) {
648 newText += fontMetrics.elidedText(words[words.count() - 1], m_list->layoutDirection() == Qt::RightToLeft ? Qt::ElideLeft : Qt::ElideRight, maxWidth);
649 } else {
650 newText += words[words.count() - 1];
651 }
652 return newText;
653}
654
660
661void KateModeMenuListData::ListView::setSizeList(const int height, const int width)
662{
663 setMinimumWidth(width);
664 setMaximumWidth(width);
665 setMinimumHeight(height);
666 setMaximumHeight(height);
667}
668
670{
671 // Equivalent to: sizeHint().width()
672 // But "sizeHint().width()" returns an incorrect value when the menu is large.
673 return size().width() - 4;
674}
675
676int KateModeMenuListData::ListView::getContentWidth(const int overlayScrollbarMargin, const int classicScrollbarMargin) const
677{
678 if (overlapScrollBar()) {
679 return getWidth() - m_parentMenu->m_scroll->sizeHint().width() - 2 - overlayScrollbarMargin; // ScrollBar Margin = 2
680 }
681 return getWidth() - verticalScrollBar()->sizeHint().width() - classicScrollbarMargin;
682}
683
685{
686 return getContentWidth(0, 0);
687}
688
690{
691 QString searchName = QString(itemName);
692 bool bNewName = false;
693
694 // Replace word delimiters with spaces
695 for (int i = searchName.length() - 1; i >= 0; --i) {
696 if (isDelimiter(searchName[i].unicode())) {
697 searchName.replace(i, 1, QLatin1Char(' '));
698 if (!bNewName) {
699 bNewName = true;
700 }
701 }
702 // Avoid duplicate delimiters/spaces
703 if (bNewName && i < searchName.length() - 1 && searchName[i].isSpace() && searchName[i + 1].isSpace()) {
704 searchName.remove(i + 1, 1);
705 }
706 }
707
708 if (bNewName) {
709 if (searchName[searchName.length() - 1].isSpace()) {
710 searchName.remove(searchName.length() - 1, 1);
711 }
712 if (searchName[0].isSpace()) {
713 searchName.remove(0, 1);
714 }
715 m_searchName = searchName;
716 return true;
717 } else {
718 m_searchName = itemName;
719 }
720 return false;
721}
722
724{
725 if (!hasMode() || m_type->wildcards.count() == 0) {
726 return false;
727 }
728
729 /*
730 * Only file extensions and full names are matched. Files like "Kconfig*"
731 * aren't considered. It's also assumed that "text" doesn't contain '*'.
732 */
733 for (const auto &ext : m_type->wildcards) {
734 // File extension
735 if (ext.startsWith(QLatin1String("*."))) {
736 if (text.length() == ext.length() - 2 && text.compare(QStringView(ext).mid(2), Qt::CaseInsensitive) == 0) {
737 return true;
738 }
739 } else if (text.length() != ext.length() || ext.endsWith(QLatin1Char('*'))) {
740 continue;
741 // Full name
742 } else if (text.compare(ext, Qt::CaseInsensitive) == 0) {
743 return true;
744 }
745 }
746 return false;
747}
748
750{
751 // Ctrl/Alt/Shift/Meta + Return/Enter selects an item, but without hiding the menu
752 if ((event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)
753 && (event->modifiers().testFlag(Qt::ControlModifier) || event->modifiers().testFlag(Qt::AltModifier) || event->modifiers().testFlag(Qt::ShiftModifier)
754 || event->modifiers().testFlag(Qt::MetaModifier))) {
755 m_parentMenu->selectHighlightingSetVisibility(m_parentMenu->m_list->currentItem(), false);
756 }
757 // Return/Enter selects an item and hide the menu
758 else if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) {
759 m_parentMenu->selectHighlightingSetVisibility(m_parentMenu->m_list->currentItem(), true);
760 } else {
762 }
763}
764
766{
767 if (m_parentMenu->m_list
768 && (event->matches(QKeySequence::MoveToNextLine) || event->matches(QKeySequence::SelectNextLine) || event->matches(QKeySequence::MoveToPreviousLine)
769 || event->matches(QKeySequence::SelectPreviousLine) || event->matches(QKeySequence::MoveToNextPage) || event->matches(QKeySequence::SelectNextPage)
770 || event->matches(QKeySequence::MoveToPreviousPage) || event->matches(QKeySequence::SelectPreviousPage) || event->key() == Qt::Key_Return
771 || event->key() == Qt::Key_Enter)) {
772 QApplication::sendEvent(m_parentMenu->m_list, event);
773 } else {
775 }
776}
777
778void KateModeMenuListData::SearchLine::init()
779{
780 connect(this, &KateModeMenuListData::SearchLine::textChanged, this, &KateModeMenuListData::SearchLine::_k_queueSearch);
781
782 setEnabled(true);
783 setClearButtonEnabled(true);
784}
785
786void KateModeMenuListData::SearchLine::clear()
787{
788 m_queuedSearches = 0;
789 m_bSearchStateAutoScroll = (text().trimmed().isEmpty()) ? false : true;
790 /*
791 * NOTE: This calls "SearchLine::_k_queueSearch()" with an empty string.
792 * The search clearing should be done without delays.
793 */
795}
796
797void KateModeMenuListData::SearchLine::_k_queueSearch(const QString &s)
798{
799 m_queuedSearches++;
800 m_search = s;
801
802 if (m_search.isEmpty()) {
803 _k_activateSearch(); // Clear search without delay
804 } else {
805 QTimer::singleShot(m_searchDelay, this, &KateModeMenuListData::SearchLine::_k_activateSearch);
806 }
807}
808
809void KateModeMenuListData::SearchLine::_k_activateSearch()
810{
811 m_queuedSearches--;
812
813 if (m_queuedSearches <= 0) {
814 updateSearch(m_search);
815 m_queuedSearches = 0;
816 }
817}
818
819void KateModeMenuListData::SearchLine::updateSearch(const QString &s)
820{
821 if (m_parentMenu->m_emptyListMsg) {
822 m_parentMenu->m_emptyListMsg->hide();
823 }
824 if (m_parentMenu->m_scroll && m_parentMenu->m_scroll->isHidden()) {
825 m_parentMenu->m_scroll->show();
826 }
827
828 KateModeMenuListData::ListView *listView = m_parentMenu->m_list;
829 QStandardItemModel *listModel = m_parentMenu->m_model;
830
831 const QString searchText = (s.isNull() ? text() : s).simplified();
832
833 /*
834 * Clean "Best Search Matches" section, move items to their original places.
835 */
836 if (!listView->isRowHidden(0)) {
837 listView->setRowHidden(0, true);
838 }
839 if (!m_bestResults.isEmpty()) {
840 const int sizeBestResults = m_bestResults.size();
841 for (int i = 0; i < sizeBestResults; ++i) {
842 listModel->takeRow(m_bestResults.at(i).first->index().row());
843 listModel->insertRow(m_bestResults.at(i).second + sizeBestResults - i - 1, m_bestResults.at(i).first);
844 }
845 m_bestResults.clear();
846 }
847
848 /*
849 * Empty search bar.
850 * Show all items and scroll to the selected item or to the first item.
851 */
852 if (searchText.isEmpty() || (searchText.size() == 1 && searchText[0].isSpace())) {
853 for (int i = 1; i < listModel->rowCount(); ++i) {
854 if (listView->isRowHidden(i)) {
855 listView->setRowHidden(i, false);
856 }
857 }
858
859 // Don't auto-scroll if the search is already clear
860 if (m_bSearchStateAutoScroll) {
861 m_parentMenu->autoScroll();
862 }
863 m_bSearchStateAutoScroll = false;
864 return;
865 }
866
867 /*
868 * Prepare item filter.
869 */
870 int lastItem = -1;
871 int lastSection = -1;
872 int firstSection = -1;
873 bool bEmptySection = true;
874 bool bSectionSeparator = false;
875 bool bSectionName = false;
876 bool bNotShowBestResults = false;
877 bool bSearchExtensions = true;
878 bool bExactMatch = false; // If the search name will not be used
879 /*
880 * It's used for two purposes, it's true if searchText is a
881 * single alphanumeric character or if it starts with a point.
882 * Both cases don't conflict, so a single bool is used.
883 */
884 bool bIsAlphaOrPointExt = false;
885
886 /*
887 * Don't search for extensions if the search text has only one character,
888 * to avoid unwanted results. In this case, the items that start with
889 * that character are displayed.
890 */
891 if (searchText.length() < 2) {
892 bSearchExtensions = false;
893 if (searchText[0].isLetterOrNumber()) {
894 bIsAlphaOrPointExt = true;
895 }
896 }
897 // If the search text has a point at the beginning, match extensions
898 else if (searchText.length() > 1 && searchText[0].toLatin1() == 46) {
899 bIsAlphaOrPointExt = true;
900 bSearchExtensions = true;
901 bExactMatch = true;
902 }
903 // Two characters: search using the normal name of the items
904 else if (searchText.length() == 2) {
905 bExactMatch = true;
906 // if it contains the '*' character, don't match extensions
907 if (searchText[1].toLatin1() == 42 || searchText[0].toLatin1() == 42) {
908 bSearchExtensions = false;
909 }
910 }
911 /*
912 * Don't use the search name if the search text has delimiters.
913 * Don't search in extensions if it contains the '*' character.
914 */
915 else {
916 QString::const_iterator srcText = searchText.constBegin();
917 QString::const_iterator endText = searchText.constEnd();
918
919 for (int it = 0; it < searchText.length() / 2 + searchText.length() % 2; ++it) {
920 --endText;
921 const ushort ucsrc = srcText->unicode();
922 const ushort ucend = endText->unicode();
923
924 // If searchText contains "*"
925 if (ucsrc == 42 || ucend == 42) {
926 bSearchExtensions = false;
927 bExactMatch = true;
928 break;
929 }
930 if (!bExactMatch && (isDelimiter(ucsrc) || (ucsrc != ucend && isDelimiter(ucend)))) {
931 bExactMatch = true;
932 }
933 ++srcText;
934 }
935 }
936
937 /*
938 * Filter items.
939 */
940 for (int i = 1; i < listModel->rowCount(); ++i) {
941 QString itemName = listModel->item(i, 0)->text();
942
943 /*
944 * Hide/show the name of the section. If the text of the item
945 * is empty, then it corresponds to the name of the section.
946 */
947 if (itemName.isEmpty()) {
948 listView->setRowHidden(i, false);
949
950 if (bSectionSeparator) {
951 bSectionName = true;
952 } else {
953 bSectionSeparator = true;
954 }
955
956 /*
957 * This hides the name of the previous section
958 * (and the separator) if this section has no items.
959 */
960 if (bSectionName && bEmptySection && lastSection > 0) {
961 listView->setRowHidden(lastSection, true);
962 listView->setRowHidden(lastSection - 1, true);
963 }
964
965 // Find the section name
966 if (bSectionName) {
967 bSectionName = false;
968 bSectionSeparator = false;
969 bEmptySection = true;
970 lastSection = i;
971 }
972 continue;
973 }
974
975 /*
976 * Start filtering items.
977 */
978 KateModeMenuListData::ListItem *item = static_cast<KateModeMenuListData::ListItem *>(listModel->item(i, 0));
979
980 if (!item->hasMode()) {
981 listView->setRowHidden(i, true);
982 continue;
983 }
984 if (item->getSearchName().isEmpty()) {
985 item->generateSearchName(item->getMode()->translatedName.isEmpty() ? item->getMode()->name : item->getMode()->translatedName);
986 }
987
988 /*
989 * Add item to the "Best Search Matches" section if there is an exact match in the search.
990 * However, if the "exact match" is already the first search result, that section will not
991 * be displayed, as it isn't necessary.
992 */
993 if (!bNotShowBestResults
994 && (item->getSearchName().compare(searchText, m_caseSensitivity) == 0
995 || (bExactMatch && item->getMode()->nameTranslated().compare(searchText, m_caseSensitivity) == 0))) {
996 if (lastItem == -1) {
997 bNotShowBestResults = true;
998 } else {
999 m_bestResults.append(qMakePair(item, i));
1000 continue;
1001 }
1002 }
1003
1004 // Only a character is written in the search bar
1005 if (searchText.length() == 1) {
1006 if (bIsAlphaOrPointExt) {
1007 /*
1008 * Add item to the "Best Search Matches" section, if there is a single letter.
1009 * Also look for coincidence in the raw name, some translations use delimiters
1010 * instead of spaces and this can lead to inaccurate results.
1011 */
1012 bool bMatchCharDel = true;
1013 if (item->getMode()->name.startsWith(searchText + QLatin1Char(' '), m_caseSensitivity)) {
1014 if (QString(QLatin1Char(' ') + item->getSearchName() + QLatin1Char(' '))
1015 .contains(QLatin1Char(' ') + searchText + QLatin1Char(' '), m_caseSensitivity)) {
1016 m_bestResults.append(qMakePair(item, i));
1017 continue;
1018 } else {
1019 bMatchCharDel = false;
1020 }
1021 }
1022
1023 // CASE 1: All the items that start with that character will be displayed.
1024 if (item->getSearchName().startsWith(searchText, m_caseSensitivity)) {
1025 setSearchResult(i, bEmptySection, lastSection, firstSection, lastItem);
1026 continue;
1027 }
1028
1029 // CASE 2: Matches considering delimiters. For example, when writing "c",
1030 // "Objective-C" will be displayed in the results, but not "Yacc/Bison".
1031 if (bMatchCharDel
1032 && QString(QLatin1Char(' ') + item->getSearchName() + QLatin1Char(' '))
1033 .contains(QLatin1Char(' ') + searchText + QLatin1Char(' '), m_caseSensitivity)) {
1034 setSearchResult(i, bEmptySection, lastSection, firstSection, lastItem);
1035 continue;
1036 }
1037 }
1038 // CASE 3: The character isn't a letter or number, do an exact search.
1039 else if (item->getMode()->nameTranslated().contains(searchText[0], m_caseSensitivity)) {
1040 setSearchResult(i, bEmptySection, lastSection, firstSection, lastItem);
1041 continue;
1042 }
1043 }
1044 // CASE 4: Search text, using the search name or the normal name.
1045 else if (!bExactMatch && item->getSearchName().contains(searchText, m_caseSensitivity)) {
1046 setSearchResult(i, bEmptySection, lastSection, firstSection, lastItem);
1047 continue;
1048 } else if (bExactMatch && item->getMode()->nameTranslated().contains(searchText, m_caseSensitivity)) {
1049 setSearchResult(i, bEmptySection, lastSection, firstSection, lastItem);
1050 continue;
1051 }
1052
1053 // CASE 5: Exact matches in extensions.
1054 if (bSearchExtensions) {
1055 if (bIsAlphaOrPointExt && item->matchExtension(searchText.mid(1))) {
1056 setSearchResult(i, bEmptySection, lastSection, firstSection, lastItem);
1057 continue;
1058 } else if (item->matchExtension(searchText)) {
1059 setSearchResult(i, bEmptySection, lastSection, firstSection, lastItem);
1060 continue;
1061 }
1062 }
1063
1064 // Item not found, hide
1065 listView->setRowHidden(i, true);
1066 }
1067
1068 // Remove last section name, if it's empty.
1069 if (bEmptySection && lastSection > 0 && !listModel->item(listModel->rowCount() - 1, 0)->text().isEmpty()) {
1070 listView->setRowHidden(lastSection, true);
1071 listView->setRowHidden(lastSection - 1, true);
1072 }
1073
1074 // Hide the separator line in the name of the first section.
1075 if (m_bestResults.isEmpty()) {
1076 listView->setRowHidden(0, true);
1077 if (firstSection > 0) {
1078 listView->setRowHidden(firstSection - 1, true);
1079 }
1080 } else {
1081 /*
1082 * Show "Best Search Matches" section, if there are items.
1083 */
1084
1085 // Show title in singular or plural, depending on the number of items.
1086 QLabel *labelSection = static_cast<QLabel *>(listView->indexWidget(listModel->index(0, 0)));
1087 if (m_bestResults.size() == 1) {
1088 labelSection->setText(
1089 i18nc("Title (in singular) of the best result in an item search. Please, that the translation doesn't have more than 34 characters, since the "
1090 "menu where it's displayed is small and fixed.",
1091 "Best Search Match"));
1092 } else {
1093 labelSection->setText(
1094 i18nc("Title (in plural) of the best results in an item search. Please, that the translation doesn't have more than 34 characters, since the "
1095 "menu where it's displayed is small and fixed.",
1096 "Best Search Matches"));
1097 }
1098
1099 int heightSectionMargin = m_parentMenu->m_defaultHeightItemSection - labelSection->sizeHint().height();
1100 if (heightSectionMargin < 2) {
1101 heightSectionMargin = 2;
1102 }
1103 int maxWidthText = listView->getContentWidth(1, 3);
1104 // NOTE: labelSection->sizeHint().width() == labelSection->indent() + labelSection->fontMetrics().horizontalAdvance(labelSection->text())
1105 const bool bSectionMultiline = labelSection->sizeHint().width() > maxWidthText;
1106 maxWidthText -= labelSection->indent();
1107 if (!bSectionMultiline) {
1108 listModel->item(0, 0)->setSizeHint(QSize(listModel->item(0, 0)->sizeHint().width(), labelSection->sizeHint().height() + heightSectionMargin));
1109 listView->setRowHidden(0, false);
1110 }
1111
1112 /*
1113 * Show items in "Best Search Matches" section.
1114 */
1115 int rowModelBestResults = 0; // New position in the model
1116
1117 // Special Case: always show the "R Script" mode first by typing "r" in the search box
1118 if (searchText.length() == 1 && searchText.compare(QLatin1String("r"), m_caseSensitivity) == 0) {
1119 for (const QPair<ListItem *, int> &itemBestResults : std::as_const(m_bestResults)) {
1120 listModel->takeRow(itemBestResults.second);
1121 ++rowModelBestResults;
1122 if (itemBestResults.first->getMode()->name == QLatin1String("R Script")) {
1123 listModel->insertRow(1, itemBestResults.first);
1124 listView->setRowHidden(1, false);
1125 } else {
1126 listModel->insertRow(rowModelBestResults, itemBestResults.first);
1127 listView->setRowHidden(rowModelBestResults, false);
1128 }
1129 }
1130 } else {
1131 // Move items to the "Best Search Matches" section
1132 for (const QPair<ListItem *, int> &itemBestResults : std::as_const(m_bestResults)) {
1133 listModel->takeRow(itemBestResults.second);
1134 listModel->insertRow(++rowModelBestResults, itemBestResults.first);
1135 listView->setRowHidden(rowModelBestResults, false);
1136 }
1137 }
1138 if (lastItem == -1) {
1139 lastItem = rowModelBestResults;
1140 }
1141
1142 // Add word wrap in long section titles.
1143 if (bSectionMultiline) {
1144 if (listView->visualRect(listModel->index(lastItem, 0)).bottom() + labelSection->sizeHint().height() + heightSectionMargin
1145 > listView->geometry().height()
1146 || labelSection->sizeHint().width() > listView->getWidth() - 1) {
1147 labelSection->setText(m_parentMenu->setWordWrap(labelSection->text(), maxWidthText, labelSection->fontMetrics()));
1148 }
1149 listModel->item(0, 0)->setSizeHint(QSize(listModel->item(0, 0)->sizeHint().width(), labelSection->sizeHint().height() + heightSectionMargin));
1150 listView->setRowHidden(0, false);
1151 }
1152
1153 m_parentMenu->m_list->setCurrentItem(1);
1154 }
1155
1156 listView->scrollToTop();
1157
1158 // Show message of empty list
1159 if (lastItem == -1) {
1160 if (m_parentMenu->m_emptyListMsg == nullptr) {
1161 m_parentMenu->loadEmptyMsg();
1162 }
1163 if (m_parentMenu->m_scroll) {
1164 m_parentMenu->m_scroll->hide();
1165 }
1166 m_parentMenu->m_emptyListMsg->show();
1167 }
1168 // Hide scroll bar if it isn't necessary
1169 else if (m_parentMenu->m_scroll && listView->visualRect(listModel->index(lastItem, 0)).bottom() <= listView->geometry().height()) {
1170 m_parentMenu->m_scroll->hide();
1171 }
1172
1173 m_bSearchStateAutoScroll = true;
1174}
1175
1176void KateModeMenuListData::SearchLine::setSearchResult(const int rowItem, bool &bEmptySection, int &lastSection, int &firstSection, int &lastItem)
1177{
1178 if (lastItem == -1) {
1179 /*
1180 * Detect the first result of the search and "select" it.
1181 * This allows you to scroll through the list using
1182 * the Up/Down keys after entering a search.
1183 */
1184 m_parentMenu->m_list->setCurrentItem(rowItem);
1185
1186 // Position of the first section visible.
1187 if (lastSection > 0) {
1188 firstSection = lastSection;
1189 }
1190 }
1191 if (bEmptySection) {
1192 bEmptySection = false;
1193 }
1194
1195 lastItem = rowItem;
1196 if (m_parentMenu->m_list->isRowHidden(rowItem)) {
1197 m_parentMenu->m_list->setRowHidden(rowItem, false);
1198 }
1199}
1200
1202{
1203 m_doc = static_cast<KTextEditor::DocumentPrivate *>(doc);
1204}
A KParts derived class representing a text document.
Definition document.h:284
Class of an Item of the Data Model of the List.
void setMode(KateFileType *type)
Associate this item with a KateFileType object.
bool generateSearchName(const QString &itemName)
Generate name of the item used for the search.
bool matchExtension(const QString &text) const
Find matches in the extensions of the item mode, with a text.
void setSizeList(const int height, const int width=266)
Define the size of the widget list.
int getContentWidth() const
Get the width of the contents of the list (in pixels), that is, the list minus the scroll bar and mar...
int getWidth() const
Get the width of the list, in pixels.
void keyPressEvent(QKeyEvent *event) override
Override from QListView.
void setWidth(const int width)
Define the width of the search bar, in pixels.
void keyPressEvent(QKeyEvent *event) override
Override from QLineEdit.
bool selectHighlightingFromExternal()
Update the selected item in the list widget, but without changing the syntax highlighting in the docu...
AlignmentVButton
Vertical Alignment with respect to the trigger button.
void updateMenu(KTextEditor::Document *doc)
Set document to apply the syntax highlighting.
AlignmentHButton
Horizontal Alignment with respect to the trigger button.
void reloadItems()
Reload all items.
void showEvent(QShowEvent *event) override
Action when displaying the menu.
void setButton(QPushButton *button, AlignmentHButton positionX=AlignHDefault, AlignmentVButton positionY=AlignTop, AutoUpdateTextButton autoUpdateTextButton=AutoUpdateTextButton(false))
Set the button that shows this menu.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString label(StandardShortcut id)
The KTextEditor namespace contains all the public API that is required to use the KTextEditor compone...
void clicked(const QModelIndex &index)
void setIconSize(const QSize &size)
QWidget * indexWidget(const QModelIndex &index) const const
virtual void keyPressEvent(QKeyEvent *event) override
QItemSelectionModel * selectionModel() const const
void setIndexWidget(const QModelIndex &index, QWidget *widget)
virtual void setModel(QAbstractItemModel *model)
void setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy)
void setVerticalScrollBar(QScrollBar *scrollBar)
void setVerticalScrollBarPolicy(Qt::ScrollBarPolicy)
void addLayout(QLayout *layout, int stretch)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
int blue() const const
int green() const const
int red() const const
bool sendEvent(QObject *receiver, QEvent *event)
void setFamily(const QString &family)
QString elidedText(const QString &text, Qt::TextElideMode mode, int width, int flags) const const
int horizontalAdvance(QChar ch) const const
void setFrameStyle(int style)
void addLayout(QLayout *layout, int row, int column, Qt::Alignment alignment)
void addWidget(QWidget *widget, int fromRow, int fromColumn, int rowSpan, int columnSpan, Qt::Alignment alignment)
virtual void clear()
virtual void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command)
void setAlignment(Qt::Alignment)
void setMargin(int)
virtual QSize sizeHint() const const override
void setText(const QString &)
void setWordWrap(bool on)
void setContentsMargins(const QMargins &margins)
void clear()
virtual void keyPressEvent(QKeyEvent *event) override
void setMaxLength(int)
void setPlaceholderText(const QString &)
void selectAll()
void textChanged(const QString &text)
qsizetype count() const const
iterator insert(const_iterator before, parameter_type value)
QList< T > mid(qsizetype pos, qsizetype length) const const
bool isRowHidden(int row) const const
void setResizeMode(ResizeMode mode)
void setRowHidden(int row, bool hide)
virtual QRect visualRect(const QModelIndex &index) const const override
QAction * addAction(const QIcon &icon, const QString &text, Functor functor, const QKeySequence &shortcut)
void aboutToShow()
virtual bool event(QEvent *e) override
int row() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void fill(const QColor &color)
int bottom() const const
int height() const const
int height() const const
int width() const const
QModelIndex index() const const
bool isEnabled() const const
bool isSelectable() const const
int row() const const
void setBackground(const QBrush &brush)
void setEditable(bool editable)
void setEnabled(bool enabled)
void setFlags(Qt::ItemFlags flags)
void setIcon(const QIcon &icon)
void setSelectable(bool selectable)
void setSizeHint(const QSize &size)
void setText(const QString &text)
QSize sizeHint() const const
QString text() const const
void appendRow(QStandardItem *item)
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
bool insertRow(int row, const QModelIndex &parent)
QStandardItem * item(int row, int column) const const
virtual int rowCount(const QModelIndex &parent) const const override
QList< QStandardItem * > takeRow(int row)
QString & append(QChar ch)
void clear()
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
const_iterator constBegin() const const
const_iterator constEnd() const const
typedef const_iterator
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
bool isNull() const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) const const
QString number(double n, char format, int precision)
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
qsizetype size() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QString trimmed() const const
AlignLeft
CaseInsensitive
ActiveWindowFocusReason
transparent
NoItemFlags
Key_Enter
ControlModifier
RightToLeft
Vertical
ScrollBarAlwaysOff
ElideLeft
PlainText
QWidget(QWidget *parent, Qt::WindowFlags f)
QFontMetrics fontMetrics() const const
void hide()
bool isHidden() const const
void setMaximumWidth(int maxw)
void setMinimumWidth(int minw)
void setFocus()
void setFocusProxy(QWidget *w)
void setStyleSheet(const QString &styleSheet)
void setToolTip(const QString &)
void setDefaultWidget(QWidget *widget)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:15:44 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.