KTextEditor

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

KDE's Doxygen guidelines are available online.