Advance Wayland and KDE package bring-up
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
add_library(KF6Completion)
|
||||
add_library(KF6::Completion ALIAS KF6Completion)
|
||||
|
||||
set_target_properties(KF6Completion PROPERTIES
|
||||
VERSION ${KCOMPLETION_VERSION}
|
||||
SOVERSION ${KCOMPLETION_SOVERSION}
|
||||
EXPORT_NAME Completion
|
||||
)
|
||||
|
||||
ecm_create_qm_loader(KF6Completion kcompletion6_qt)
|
||||
|
||||
target_sources(KF6Completion PRIVATE
|
||||
kcombobox.cpp
|
||||
kcombobox.h
|
||||
kcombobox_p.h
|
||||
kcompletionbase.cpp
|
||||
kcompletionbase.h
|
||||
kcompletionbox.cpp
|
||||
kcompletionbox.h
|
||||
kcompletion.cpp
|
||||
kcompletion.h
|
||||
kcompletionmatches.cpp
|
||||
kcompletionmatches.h
|
||||
kcompletion_p.h
|
||||
kemailvalidator.cpp
|
||||
kemailvalidator.h
|
||||
khistorycombobox.cpp
|
||||
khistorycombobox.h
|
||||
klineedit.cpp
|
||||
klineedit.h
|
||||
klineedit_p.h
|
||||
ksortablelist.h
|
||||
kzoneallocator.cpp
|
||||
kzoneallocator_p.h
|
||||
)
|
||||
|
||||
ecm_qt_declare_logging_category(KF6Completion
|
||||
HEADER kcompletion_debug.h
|
||||
IDENTIFIER KCOMPLETION_LOG
|
||||
CATEGORY_NAME kf.completion
|
||||
DESCRIPTION "KCompletion"
|
||||
EXPORT KCOMPLETION
|
||||
)
|
||||
|
||||
ecm_generate_export_header(KF6Completion
|
||||
BASE_NAME KCompletion
|
||||
GROUP_BASE_NAME KF
|
||||
VERSION ${KF_VERSION}
|
||||
USE_VERSION_HEADER
|
||||
DEPRECATED_BASE_VERSION 0
|
||||
DEPRECATION_VERSIONS
|
||||
EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
|
||||
)
|
||||
|
||||
target_include_directories(KF6Completion INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/KCompletion>")
|
||||
|
||||
target_link_libraries(KF6Completion PUBLIC Qt6::Widgets
|
||||
PRIVATE KF6::ConfigCore # KConfigGroup, used in many places
|
||||
KF6::ConfigGui # KStandardShortcut
|
||||
KF6::WidgetsAddons # KCursor
|
||||
KF6::Codecs # KEmailAddress
|
||||
)
|
||||
|
||||
ecm_generate_headers(KCompletion_HEADERS
|
||||
HEADER_NAMES
|
||||
KComboBox
|
||||
KCompletion
|
||||
KCompletionBase
|
||||
KCompletionBox
|
||||
KEmailValidator
|
||||
KLineEdit
|
||||
KHistoryComboBox
|
||||
KSortableList
|
||||
KCompletionMatches
|
||||
|
||||
REQUIRED_HEADERS KCompletion_HEADERS
|
||||
)
|
||||
|
||||
install(TARGETS KF6Completion EXPORT KF6CompletionTargets ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
|
||||
install(FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/kcompletion_export.h
|
||||
${KCompletion_HEADERS}
|
||||
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KCompletion COMPONENT Devel
|
||||
)
|
||||
|
||||
if(BUILD_DESIGNERPLUGIN)
|
||||
add_subdirectory(designer)
|
||||
endif()
|
||||
|
||||
if(BUILD_QCH)
|
||||
ecm_add_qch(
|
||||
KF6Completion_QCH
|
||||
NAME KCompletion
|
||||
BASE_NAME KF6Completion
|
||||
VERSION ${KF_VERSION}
|
||||
ORG_DOMAIN org.kde
|
||||
SOURCES # using only public headers, to cover only public API
|
||||
${KCompletion_HEADERS}
|
||||
MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md"
|
||||
IMAGE_DIRS "${CMAKE_SOURCE_DIR}/docs/pics"
|
||||
LINK_QCHS
|
||||
Qt6Core_QCH
|
||||
Qt6Gui_QCH
|
||||
Qt6Widgets_QCH
|
||||
INCLUDE_DIRS
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
BLANK_MACROS
|
||||
KCOMPLETION_EXPORT
|
||||
KCOMPLETION_DEPRECATED
|
||||
"KCOMPLETION_DEPRECATED_VERSION(x, y, t)"
|
||||
TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
|
||||
QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
|
||||
COMPONENT Devel
|
||||
)
|
||||
endif()
|
||||
|
||||
ecm_qt_install_logging_categories(
|
||||
EXPORT KCOMPLETION
|
||||
FILE kcompletion.categories
|
||||
DESTINATION "${KDE_INSTALL_LOGGINGCATEGORIESDIR}"
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Extract strings from all source files.
|
||||
# EXTRACT_TR_STRINGS extracts strings with lupdate and convert them to .pot with
|
||||
# lconvert.
|
||||
$EXTRACT_TR_STRINGS `find . -name \*.cpp -o -name \*.h -o -name \*.ui -o -name \*.qml` -o $podir/kcompletion6_qt.pot
|
||||
@@ -0,0 +1,28 @@
|
||||
include(ECMAddQtDesignerPlugin)
|
||||
|
||||
ecm_qtdesignerplugin_widget(KComboBox
|
||||
TOOLTIP "Combo Box (KF6)"
|
||||
GROUP "Input (KF6)"
|
||||
)
|
||||
ecm_qtdesignerplugin_widget(KHistoryComboBox
|
||||
TOOLTIP "A combobox for offering a history and completion (KF6)"
|
||||
WHATSTHIS "A combobox which implements a history like a unix shell"
|
||||
GROUP "Input (KF6)"
|
||||
)
|
||||
ecm_qtdesignerplugin_widget(KLineEdit
|
||||
TOOLTIP "Line Edit (KF6)"
|
||||
GROUP "Input (KF6)"
|
||||
)
|
||||
|
||||
ecm_add_qtdesignerplugin(kcompletionwidgets
|
||||
NAME KCompletionWidgets
|
||||
OUTPUT_NAME kcompletion6widgets
|
||||
WIDGETS
|
||||
KComboBox
|
||||
KHistoryComboBox
|
||||
KLineEdit
|
||||
LINK_LIBRARIES
|
||||
KF6::Completion
|
||||
INSTALL_DESTINATION "${KDE_INSTALL_QTPLUGINDIR}/designer"
|
||||
COMPONENT Devel
|
||||
)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2000, 2001 Dawit Alemayehu <adawit@kde.org>
|
||||
SPDX-FileCopyrightText: 2000, 2001 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
SPDX-FileCopyrightText: 2000 Stefan Schimanski <1Stein@gmx.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "kcombobox.h"
|
||||
#include "kcombobox_p.h"
|
||||
|
||||
#include <kcompletion_debug.h>
|
||||
#include <kcompletionbox.h>
|
||||
|
||||
#include <QUrl>
|
||||
|
||||
void KComboBoxPrivate::init()
|
||||
{
|
||||
Q_Q(KComboBox);
|
||||
}
|
||||
|
||||
void KComboBoxPrivate::slotLineEditDeleted(QLineEdit *sender)
|
||||
{
|
||||
Q_Q(KComboBox);
|
||||
// yes, we need those ugly casts due to the multiple inheritance
|
||||
// "sender" is guaranteed to be a KLineEdit (see the connect() to the
|
||||
// destroyed() signal
|
||||
const KCompletionBase *base = static_cast<const KCompletionBase *>(static_cast<const KLineEdit *>(sender));
|
||||
|
||||
// is it our delegate, that is destroyed?
|
||||
if (base == q->delegate()) {
|
||||
q->setDelegate(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
KComboBox::KComboBox(QWidget *parent)
|
||||
: KComboBox(*new KComboBoxPrivate(this), parent)
|
||||
{
|
||||
}
|
||||
|
||||
KComboBox::KComboBox(KComboBoxPrivate &dd, QWidget *parent)
|
||||
: QComboBox(parent)
|
||||
, d_ptr(&dd)
|
||||
{
|
||||
Q_D(KComboBox);
|
||||
|
||||
d->init();
|
||||
}
|
||||
|
||||
KComboBox::KComboBox(bool rw, QWidget *parent)
|
||||
: KComboBox(*new KComboBoxPrivate(this), parent)
|
||||
{
|
||||
setEditable(rw);
|
||||
}
|
||||
|
||||
KComboBox::~KComboBox()
|
||||
{
|
||||
Q_D(KComboBox);
|
||||
disconnect(d->m_klineEditConnection);
|
||||
}
|
||||
|
||||
bool KComboBox::contains(const QString &text) const
|
||||
{
|
||||
if (text.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int itemCount = count();
|
||||
for (int i = 0; i < itemCount; ++i) {
|
||||
if (itemText(i) == text) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int KComboBox::cursorPosition() const
|
||||
{
|
||||
return (isEditable()) ? lineEdit()->cursorPosition() : -1;
|
||||
}
|
||||
|
||||
void KComboBox::setAutoCompletion(bool autocomplete)
|
||||
{
|
||||
Q_D(KComboBox);
|
||||
if (d->klineEdit) {
|
||||
if (autocomplete) {
|
||||
d->klineEdit->setCompletionMode(KCompletion::CompletionAuto);
|
||||
setCompletionMode(KCompletion::CompletionAuto);
|
||||
} else {
|
||||
d->klineEdit->setCompletionMode(KCompletion::CompletionPopup);
|
||||
setCompletionMode(KCompletion::CompletionPopup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool KComboBox::autoCompletion() const
|
||||
{
|
||||
return completionMode() == KCompletion::CompletionAuto;
|
||||
}
|
||||
|
||||
bool KComboBox::urlDropsEnabled() const
|
||||
{
|
||||
Q_D(const KComboBox);
|
||||
return d->klineEdit && d->klineEdit->urlDropsEnabled();
|
||||
}
|
||||
|
||||
void KComboBox::setCompletedText(const QString &text, bool marked)
|
||||
{
|
||||
Q_D(KComboBox);
|
||||
if (d->klineEdit) {
|
||||
d->klineEdit->setCompletedText(text, marked);
|
||||
}
|
||||
}
|
||||
|
||||
void KComboBox::setCompletedText(const QString &text)
|
||||
{
|
||||
Q_D(KComboBox);
|
||||
if (d->klineEdit) {
|
||||
d->klineEdit->setCompletedText(text);
|
||||
}
|
||||
}
|
||||
|
||||
void KComboBox::makeCompletion(const QString &text)
|
||||
{
|
||||
Q_D(KComboBox);
|
||||
if (d->klineEdit) {
|
||||
d->klineEdit->makeCompletion(text);
|
||||
}
|
||||
|
||||
else { // read-only combo completion
|
||||
if (text.isNull() || !view()) {
|
||||
return;
|
||||
}
|
||||
|
||||
view()->keyboardSearch(text);
|
||||
}
|
||||
}
|
||||
|
||||
void KComboBox::rotateText(KCompletionBase::KeyBindingType type)
|
||||
{
|
||||
Q_D(KComboBox);
|
||||
if (d->klineEdit) {
|
||||
d->klineEdit->rotateText(type);
|
||||
}
|
||||
}
|
||||
|
||||
void KComboBox::setTrapReturnKey(bool trap)
|
||||
{
|
||||
Q_D(KComboBox);
|
||||
d->trapReturnKey = trap;
|
||||
|
||||
if (d->klineEdit) {
|
||||
d->klineEdit->setTrapReturnKey(trap);
|
||||
} else {
|
||||
qCWarning(KCOMPLETION_LOG) << "KComboBox::setTrapReturnKey not supported with a non-KLineEdit.";
|
||||
}
|
||||
}
|
||||
|
||||
bool KComboBox::trapReturnKey() const
|
||||
{
|
||||
Q_D(const KComboBox);
|
||||
return d->trapReturnKey;
|
||||
}
|
||||
|
||||
void KComboBox::setEditUrl(const QUrl &url)
|
||||
{
|
||||
QComboBox::setEditText(url.toDisplayString());
|
||||
}
|
||||
|
||||
void KComboBox::addUrl(const QUrl &url)
|
||||
{
|
||||
QComboBox::addItem(url.toDisplayString());
|
||||
}
|
||||
|
||||
void KComboBox::addUrl(const QIcon &icon, const QUrl &url)
|
||||
{
|
||||
QComboBox::addItem(icon, url.toDisplayString());
|
||||
}
|
||||
|
||||
void KComboBox::insertUrl(int index, const QUrl &url)
|
||||
{
|
||||
QComboBox::insertItem(index, url.toDisplayString());
|
||||
}
|
||||
|
||||
void KComboBox::insertUrl(int index, const QIcon &icon, const QUrl &url)
|
||||
{
|
||||
QComboBox::insertItem(index, icon, url.toDisplayString());
|
||||
}
|
||||
|
||||
void KComboBox::changeUrl(int index, const QUrl &url)
|
||||
{
|
||||
QComboBox::setItemText(index, url.toDisplayString());
|
||||
}
|
||||
|
||||
void KComboBox::changeUrl(int index, const QIcon &icon, const QUrl &url)
|
||||
{
|
||||
QComboBox::setItemIcon(index, icon);
|
||||
QComboBox::setItemText(index, url.toDisplayString());
|
||||
}
|
||||
|
||||
void KComboBox::setCompletedItems(const QStringList &items, bool autoSuggest)
|
||||
{
|
||||
Q_D(KComboBox);
|
||||
if (d->klineEdit) {
|
||||
d->klineEdit->setCompletedItems(items, autoSuggest);
|
||||
}
|
||||
}
|
||||
|
||||
KCompletionBox *KComboBox::completionBox(bool create)
|
||||
{
|
||||
Q_D(KComboBox);
|
||||
if (d->klineEdit) {
|
||||
return d->klineEdit->completionBox(create);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QSize KComboBox::minimumSizeHint() const
|
||||
{
|
||||
Q_D(const KComboBox);
|
||||
QSize size = QComboBox::minimumSizeHint();
|
||||
if (isEditable() && d->klineEdit) {
|
||||
// if it's a KLineEdit and it's editable add the clear button size
|
||||
// to the minimum size hint, otherwise looks ugly because the
|
||||
// clear button will cover the last 2/3 letters of the biggest entry
|
||||
QSize bs = d->klineEdit->clearButtonUsedSize();
|
||||
if (bs.isValid()) {
|
||||
size.rwidth() += bs.width();
|
||||
size.rheight() = qMax(size.height(), bs.height());
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void KComboBox::setLineEdit(QLineEdit *edit)
|
||||
{
|
||||
Q_D(KComboBox);
|
||||
if (!isEditable() && edit && !qstrcmp(edit->metaObject()->className(), "QLineEdit")) {
|
||||
// uic generates code that creates a read-only KComboBox and then
|
||||
// calls combo->setEditable(true), which causes QComboBox to set up
|
||||
// a dumb QLineEdit instead of our nice KLineEdit.
|
||||
// As some KComboBox features rely on the KLineEdit, we reject
|
||||
// this order here.
|
||||
delete edit;
|
||||
KLineEdit *kedit = new KLineEdit(this);
|
||||
|
||||
if (isEditable()) {
|
||||
kedit->setClearButtonEnabled(true);
|
||||
}
|
||||
|
||||
edit = kedit;
|
||||
}
|
||||
|
||||
// reuse an existing completion object, if it does not belong to the previous
|
||||
// line edit and gets destroyed with it
|
||||
QPointer<KCompletion> completion = compObj();
|
||||
|
||||
QComboBox::setLineEdit(edit);
|
||||
edit->setCompleter(nullptr); // remove Qt's builtin completer (set by setLineEdit), we have our own
|
||||
d->klineEdit = qobject_cast<KLineEdit *>(edit);
|
||||
setDelegate(d->klineEdit);
|
||||
|
||||
if (completion && d->klineEdit) {
|
||||
d->klineEdit->setCompletionObject(completion);
|
||||
}
|
||||
|
||||
if (d->klineEdit) {
|
||||
// someone calling KComboBox::setEditable(false) destroys our
|
||||
// line edit without us noticing. And KCompletionBase::delegate would
|
||||
// be a dangling pointer then, so prevent that. Note: only do this
|
||||
// when it is a KLineEdit!
|
||||
d->m_klineEditConnection = connect(edit, &QObject::destroyed, this, [d, edit]() {
|
||||
d->slotLineEditDeleted(edit);
|
||||
});
|
||||
|
||||
connect(d->klineEdit, &KLineEdit::returnKeyPressed, this, qOverload<const QString &>(&KComboBox::returnPressed));
|
||||
|
||||
connect(d->klineEdit, &KLineEdit::completion, this, &KComboBox::completion);
|
||||
|
||||
connect(d->klineEdit, &KLineEdit::substringCompletion, this, &KComboBox::substringCompletion);
|
||||
|
||||
connect(d->klineEdit, &KLineEdit::textRotation, this, &KComboBox::textRotation);
|
||||
|
||||
connect(d->klineEdit, &KLineEdit::completionModeChanged, this, &KComboBox::completionModeChanged);
|
||||
|
||||
connect(d->klineEdit, &KLineEdit::aboutToShowContextMenu, [this](QMenu *menu) {
|
||||
Q_D(KComboBox);
|
||||
d->contextMenu = menu;
|
||||
Q_EMIT aboutToShowContextMenu(menu);
|
||||
});
|
||||
|
||||
connect(d->klineEdit, &KLineEdit::completionBoxActivated, this, &QComboBox::textActivated);
|
||||
|
||||
d->klineEdit->setTrapReturnKey(d->trapReturnKey);
|
||||
}
|
||||
}
|
||||
|
||||
QMenu *KComboBox::contextMenu() const
|
||||
{
|
||||
return d_ptr->contextMenu;
|
||||
}
|
||||
|
||||
void KComboBox::setCurrentItem(const QString &item, bool insert, int index)
|
||||
{
|
||||
int sel = -1;
|
||||
|
||||
const int itemCount = count();
|
||||
for (int i = 0; i < itemCount; ++i) {
|
||||
if (itemText(i) == item) {
|
||||
sel = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sel == -1 && insert) {
|
||||
if (index >= 0) {
|
||||
insertItem(index, item);
|
||||
sel = index;
|
||||
} else {
|
||||
addItem(item);
|
||||
sel = count() - 1;
|
||||
}
|
||||
}
|
||||
setCurrentIndex(sel);
|
||||
}
|
||||
|
||||
void KComboBox::setEditable(bool editable)
|
||||
{
|
||||
if (editable == isEditable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (editable) {
|
||||
// Create a KLineEdit instead of a QLineEdit
|
||||
// Compared to QComboBox::setEditable, we might be missing the SH_ComboBox_Popup code though...
|
||||
// If a style needs this, then we'll need to call QComboBox::setEditable and then setLineEdit again
|
||||
KLineEdit *edit = new KLineEdit(this);
|
||||
edit->setClearButtonEnabled(true);
|
||||
setLineEdit(edit);
|
||||
} else {
|
||||
if (d_ptr->contextMenu) {
|
||||
d_ptr->contextMenu->close();
|
||||
}
|
||||
QComboBox::setEditable(editable);
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_kcombobox.cpp"
|
||||
@@ -0,0 +1,467 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2000, 2001 Dawit Alemayehu <adawit@kde.org>
|
||||
SPDX-FileCopyrightText: 2000, 2001 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCOMBOBOX_H
|
||||
#define KCOMBOBOX_H
|
||||
|
||||
#include <kcompletion.h>
|
||||
|
||||
#include <kcompletion_export.h>
|
||||
#include <kcompletionbase.h>
|
||||
|
||||
#include <QComboBox>
|
||||
#include <memory>
|
||||
|
||||
class KCompletionBox;
|
||||
class KComboBoxPrivate;
|
||||
|
||||
class QLineEdit;
|
||||
class QMenu;
|
||||
|
||||
/**
|
||||
* @class KComboBox kcombobox.h KComboBox
|
||||
*
|
||||
* @short A combo box with completion support.
|
||||
*
|
||||
* This widget inherits from QComboBox and implements the following
|
||||
* additional features:
|
||||
* @li a completion object that provides both automatic
|
||||
* and manual text completion as well as text rotation
|
||||
* @li configurable key bindings to activate these features
|
||||
* @li a popup menu item that can be used to allow the user to change
|
||||
* the text completion mode on the fly.
|
||||
*
|
||||
* To support these additional features, KComboBox emits a few additional signals
|
||||
* such as completion(const QString&) and textRotation(KeyBindingType).
|
||||
*
|
||||
* The completion signal can be connected to a slot that will assist the user in
|
||||
* filling out the remaining text while the rotation signal can be used to traverse
|
||||
* through all possible matches whenever text completion results in multiple matches.
|
||||
* Additionally, the returnPressed(const QString &) signal is emitted when the user
|
||||
* presses the Return or Enter key.
|
||||
*
|
||||
* KCombobox by default creates a completion object when you invoke the
|
||||
* completionObject(bool) member function for the first time or
|
||||
* explicitly use setCompletionObject(KCompletion*, bool) to assign your
|
||||
* own completion object. Additionally, to make this widget more functional,
|
||||
* KComboBox will by default handle text rotation and completion events
|
||||
* internally whenever a completion object is created through either one of the
|
||||
* methods mentioned above. If you do not need this functionality, simply use
|
||||
* KCompletionBase::setHandleSignals(bool) or alternatively set the boolean
|
||||
* parameter in the @c setCompletionObject() call to @c false.
|
||||
*
|
||||
* Beware: The completion object can be deleted on you, especially if a call
|
||||
* such as setEditable(false) is made. Store the pointer at your own risk,
|
||||
* and consider using QPointer<KCompletion>.
|
||||
*
|
||||
* The default key bindings for completion and rotation are determined from the
|
||||
* global settings in KStandardShortcut. These values, however, can be overridden
|
||||
* locally by invoking KCompletionBase::setKeyBinding(). The values can
|
||||
* easily be reverted back to the default settings by calling
|
||||
* useGlobalSettings(). An alternate method would be to default individual
|
||||
* key bindings by using setKeyBinding() with the default second argument.
|
||||
*
|
||||
* A non-editable combo box only has one completion mode, @c CompletionAuto.
|
||||
* Unlike an editable combo box, the CompletionAuto mode works by matching
|
||||
* any typed key with the first letter of entries in the combo box. Please note
|
||||
* that if you call setEditable(false) to change an editable combo box to a
|
||||
* non-editable one, the text completion object associated with the combo box will
|
||||
* no longer exist unless you created the completion object yourself and assigned
|
||||
* it to this widget or you called setAutoDeleteCompletionObject(false). In other
|
||||
* words do not do the following:
|
||||
*
|
||||
* \code
|
||||
* KComboBox* combo = new KComboBox(true, this);
|
||||
* KCompletion* comp = combo->completionObject();
|
||||
* combo->setEditable(false);
|
||||
* comp->clear(); // CRASH: completion object does not exist anymore.
|
||||
* \endcode
|
||||
*
|
||||
*
|
||||
* A read-only KComboBox will have the same background color as a
|
||||
* disabled KComboBox, but its foreground color will be the one used for
|
||||
* the editable mode. This differs from QComboBox's implementation
|
||||
* and is done to give visual distinction between the three different modes:
|
||||
* disabled, read-only, and editable.
|
||||
*
|
||||
* \b Usage
|
||||
*
|
||||
* To enable the basic completion feature:
|
||||
*
|
||||
* \code
|
||||
* KComboBox *combo = new KComboBox(true, this);
|
||||
* KCompletion *comp = combo->completionObject();
|
||||
* // Connect to the Return pressed signal - optional
|
||||
* connect(combo, &KComboBox::returnPressed, comp, [this](const QString &text) { addItem(text); });
|
||||
*
|
||||
* // Provide the to be completed strings. Note that those are separate from the combo's
|
||||
* // contents.
|
||||
* comp->insertItems(someQStringList);
|
||||
* \endcode
|
||||
*
|
||||
* To use your own completion object:
|
||||
*
|
||||
* \code
|
||||
* KComboBox *combo = new KComboBox(this);
|
||||
* KUrlCompletion *comp = new KUrlCompletion();
|
||||
* // You can either delete the allocated completion object manually when you
|
||||
* // don't need it anymore, or call setAutoDeleteCompletionObject(true) and it
|
||||
* // will be deleted automatically
|
||||
* comp->setAutoDeleteCompletionObject(true);
|
||||
* combo->setCompletionObject(comp);
|
||||
* // Connect to the return pressed signal - optional
|
||||
* connect(combo, &KComboBox::returnPressed, comp, [this](const QString &text) { addItem(text); });
|
||||
* \endcode
|
||||
*
|
||||
* Miscellaneous function calls:
|
||||
*
|
||||
* \code
|
||||
* // Tell the widget not to handle completion and rotation
|
||||
* combo->setHandleSignals(false);
|
||||
* // Set your own completion key for manual completions.
|
||||
* combo->setKeyBinding(KCompletionBase::TextCompletion, Qt::End);
|
||||
* \endcode
|
||||
*
|
||||
* \image html kcombobox.png "KComboBox widgets, one non-editable, one editable with KUrlCompletion"
|
||||
*
|
||||
* @author Dawit Alemayehu <adawit@kde.org>
|
||||
*/
|
||||
class KCOMPLETION_EXPORT KComboBox : public QComboBox, public KCompletionBase // krazy:exclude=qclasses
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool autoCompletion READ autoCompletion WRITE setAutoCompletion)
|
||||
Q_PROPERTY(bool trapReturnKey READ trapReturnKey WRITE setTrapReturnKey)
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructs a read-only (or rather select-only) combo box.
|
||||
*
|
||||
* @param parent The parent object of this widget
|
||||
*/
|
||||
explicit KComboBox(QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Constructs an editable or read-only combo box.
|
||||
*
|
||||
* @param rw When @c true, widget will be editable.
|
||||
* @param parent The parent object of this widget.
|
||||
*/
|
||||
explicit KComboBox(bool rw, QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~KComboBox() override;
|
||||
|
||||
/**
|
||||
* Sets @p url into the edit field of the combo box.
|
||||
*
|
||||
* It uses QUrl::toDisplayString() so that the url is properly decoded for
|
||||
* displaying.
|
||||
*/
|
||||
void setEditUrl(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Appends @p url to the combo box.
|
||||
*
|
||||
* QUrl::toDisplayString() is used so that the url is properly decoded
|
||||
* for displaying.
|
||||
*/
|
||||
void addUrl(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Appends @p url with the @p icon to the combo box.
|
||||
*
|
||||
* QUrl::toDisplayString() is used so that the url is properly decoded
|
||||
* for displaying.
|
||||
*/
|
||||
void addUrl(const QIcon &icon, const QUrl &url);
|
||||
|
||||
/**
|
||||
* Inserts @p url at position @p index into the combo box.
|
||||
*
|
||||
* QUrl::toDisplayString() is used so that the url is properly decoded
|
||||
* for displaying.
|
||||
*/
|
||||
void insertUrl(int index, const QUrl &url);
|
||||
|
||||
/**
|
||||
* Inserts @p url with the @p icon at position @p index into
|
||||
* the combo box.
|
||||
*
|
||||
* QUrl::toDisplayString() is used so that the url is
|
||||
* properly decoded for displaying.
|
||||
*/
|
||||
void insertUrl(int index, const QIcon &icon, const QUrl &url);
|
||||
|
||||
/**
|
||||
* Replaces the item at position @p index with @p url.
|
||||
*
|
||||
* QUrl::toDisplayString() is used so that the url is properly decoded
|
||||
* for displaying.
|
||||
*/
|
||||
void changeUrl(int index, const QUrl &url);
|
||||
|
||||
/**
|
||||
* Replaces the item at position @p index with @p url and @p icon.
|
||||
*
|
||||
* QUrl::toDisplayString() is used so that the url is properly decoded
|
||||
* for displaying.
|
||||
*/
|
||||
void changeUrl(int index, const QIcon &icon, const QUrl &url);
|
||||
|
||||
/**
|
||||
* Returns the current cursor position.
|
||||
*
|
||||
* This method always returns a -1 if the combo box is @em not
|
||||
* editable (read-only).
|
||||
*
|
||||
* @return Current cursor position.
|
||||
*/
|
||||
int cursorPosition() const;
|
||||
|
||||
/**
|
||||
* Reimplemented from QComboBox.
|
||||
*
|
||||
* If @c true, the completion mode will be set to automatic.
|
||||
* Otherwise, it is defaulted to the global setting. This
|
||||
* method has been replaced by the more comprehensive
|
||||
* setCompletionMode().
|
||||
*
|
||||
* @param autocomplete Flag to enable/disable automatic completion mode.
|
||||
*/
|
||||
virtual void setAutoCompletion(bool autocomplete);
|
||||
|
||||
/**
|
||||
* Reimplemented from QComboBox.
|
||||
*
|
||||
* Returns @c true if the current completion mode is set
|
||||
* to automatic. See its more comprehensive replacement
|
||||
* completionMode().
|
||||
*
|
||||
* @return @c true when completion mode is automatic.
|
||||
*/
|
||||
bool autoCompletion() const;
|
||||
|
||||
/**
|
||||
* Returns @c true when decoded URL drops are enabled
|
||||
*/
|
||||
bool urlDropsEnabled() const;
|
||||
|
||||
/**
|
||||
* Convenience method which iterates over all items and checks if
|
||||
* any of them is equal to @p text.
|
||||
*
|
||||
* If @p text is an empty string, @c false
|
||||
* is returned.
|
||||
*
|
||||
* @return @c true if an item with the string @p text is in the combo box.
|
||||
*/
|
||||
bool contains(const QString &text) const;
|
||||
|
||||
/**
|
||||
* By default, KComboBox recognizes Key_Return and Key_Enter and emits the
|
||||
* returnPressed(const QString &) signal, but it also lets the event pass,
|
||||
* for example causing a dialog's default button to be called.
|
||||
*
|
||||
* Call this method with @p trap set to true to make KComboBox stop these
|
||||
* events. The signals will still be emitted of course.
|
||||
*
|
||||
* @note This only affects editable combo boxes.
|
||||
*
|
||||
* @see setTrapReturnKey()
|
||||
*/
|
||||
void setTrapReturnKey(bool trap);
|
||||
|
||||
/**
|
||||
* @return @c true if Key_Return or Key_Enter input events will be stopped or
|
||||
* @c false if they will be propagated.
|
||||
*
|
||||
* @see setTrapReturnKey()
|
||||
*/
|
||||
bool trapReturnKey() const;
|
||||
|
||||
/**
|
||||
* This method will create a completion box by calling
|
||||
* KLineEdit::completionBox, if none is there yet.
|
||||
*
|
||||
* @param create Set this to false if you don't want the box to be created
|
||||
* i.e. to test if it is available.
|
||||
* @returns the completion box that is used in completion mode
|
||||
* CompletionPopup and CompletionPopupAuto.
|
||||
*/
|
||||
KCompletionBox *completionBox(bool create = true);
|
||||
|
||||
/**
|
||||
* Reimplemented for internal reasons. API remains unaffected.
|
||||
* Note that QComboBox::setLineEdit is not virtual in Qt4, do not
|
||||
* use a KComboBox in a QComboBox pointer.
|
||||
*
|
||||
* NOTE: Only editable combo boxes can have a line editor. As such
|
||||
* any attempt to assign a line edit to a non-editable combo box will
|
||||
* simply be ignored.
|
||||
*/
|
||||
virtual void setLineEdit(QLineEdit *);
|
||||
|
||||
/**
|
||||
* Reimplemented so that setEditable(true) creates a KLineEdit
|
||||
* instead of QLineEdit.
|
||||
*
|
||||
* Note that QComboBox::setEditable is not virtual, so do not
|
||||
* use a KComboBox in a QComboBox pointer.
|
||||
*/
|
||||
void setEditable(bool editable);
|
||||
|
||||
/**
|
||||
* Pointer to KLineEdit's context menu, or nullptr if it does not exist at
|
||||
* the given moment.
|
||||
*
|
||||
* @since 5.78
|
||||
*/
|
||||
QMenu *contextMenu() const;
|
||||
|
||||
QSize minimumSizeHint() const override;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted when the user presses the Return or Enter key.
|
||||
*
|
||||
* The argument is the current text being edited.
|
||||
*
|
||||
* @note This signal is only emitted when the widget is editable.
|
||||
*
|
||||
*/
|
||||
void returnPressed(const QString &text); // clazy:exclude=overloaded-signal
|
||||
|
||||
/**
|
||||
* Emitted when the completion key is pressed.
|
||||
*
|
||||
* The argument is the current text being edited.
|
||||
*
|
||||
* Note that this signal is @em not available when the widget is non-editable
|
||||
* or the completion mode is set to @c CompletionNone.
|
||||
*/
|
||||
void completion(const QString &);
|
||||
|
||||
/**
|
||||
* Emitted when the shortcut for substring completion is pressed.
|
||||
*/
|
||||
void substringCompletion(const QString &);
|
||||
|
||||
/**
|
||||
* Emitted when the text rotation key bindings are pressed.
|
||||
*
|
||||
* The argument indicates which key binding was pressed. In this case this
|
||||
* can be either one of four values: @c PrevCompletionMatch,
|
||||
* @c NextCompletionMatch, @c RotateUp or @c RotateDown.
|
||||
*
|
||||
* Note that this signal is @em not emitted if the completion
|
||||
* mode is set to CompletionNone.
|
||||
*
|
||||
* @see KCompletionBase::setKeyBinding() for details
|
||||
*/
|
||||
void textRotation(KCompletionBase::KeyBindingType);
|
||||
|
||||
/**
|
||||
* Emitted whenever the completion mode is changed by the user
|
||||
* through the context menu.
|
||||
*/
|
||||
void completionModeChanged(KCompletion::CompletionMode);
|
||||
|
||||
/**
|
||||
* Emitted before the context menu is displayed.
|
||||
*
|
||||
* The signal allows you to add your own entries into the context menu.
|
||||
* Note that you <em>must not</em> store the pointer to the QPopupMenu since it is
|
||||
* created and deleted on demand. Otherwise, you can crash your app.
|
||||
*
|
||||
* @param contextMenu the context menu about to be displayed
|
||||
*/
|
||||
void aboutToShowContextMenu(QMenu *contextMenu);
|
||||
|
||||
public Q_SLOTS:
|
||||
|
||||
/**
|
||||
* Iterates through all possible matches of the completed text
|
||||
* or the history list.
|
||||
*
|
||||
* Depending on the value of the argument, this function either
|
||||
* iterates through the history list of this widget or all the
|
||||
* possible matches in whenever multiple matches result from a
|
||||
* text completion request. Note that the all-possible-match
|
||||
* iteration will not work if there are no previous matches, i.e.
|
||||
* no text has been completed and the *nix shell history list
|
||||
* rotation is only available if the insertion policy for this
|
||||
* widget is set either @c QComobBox::AtTop or @c QComboBox::AtBottom.
|
||||
* For other insertion modes whatever has been typed by the user
|
||||
* when the rotation event was initiated will be lost.
|
||||
*
|
||||
* @param type The key binding invoked.
|
||||
*/
|
||||
void rotateText(KCompletionBase::KeyBindingType type);
|
||||
|
||||
/**
|
||||
* Sets the completed text in the line edit appropriately.
|
||||
*
|
||||
* This function is an implementation for
|
||||
* KCompletionBase::setCompletedText.
|
||||
*/
|
||||
void setCompletedText(const QString &) override;
|
||||
|
||||
/**
|
||||
* Sets @p items into the completion box if completionMode() is
|
||||
* CompletionPopup. The popup will be shown immediately.
|
||||
*/
|
||||
void setCompletedItems(const QStringList &items, bool autoSuggest = true) override;
|
||||
|
||||
/**
|
||||
* Selects the first item that matches @p item.
|
||||
*
|
||||
* If there is no such item, it is inserted at position @p index
|
||||
* if @p insert is true. Otherwise, no item is selected.
|
||||
*/
|
||||
void setCurrentItem(const QString &item, bool insert = false, int index = -1);
|
||||
|
||||
protected Q_SLOTS:
|
||||
|
||||
/**
|
||||
* Completes text according to the completion mode.
|
||||
*
|
||||
* @note This method is not invoked if the completion mode is
|
||||
* set to @c CompletionNone. Also if the mode is set to @c CompletionShell
|
||||
* and multiple matches are found, this method will complete the
|
||||
* text to the first match with a beep to indicate that there are
|
||||
* more matches. Then any successive completion key event iterates
|
||||
* through the remaining matches. This way the rotation functionality
|
||||
* is left to iterate through the list as usual.
|
||||
*/
|
||||
virtual void makeCompletion(const QString &);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* This function sets the line edit text and
|
||||
* highlights the text appropriately if the boolean
|
||||
* value is set to true.
|
||||
*
|
||||
* @param text The text to be set in the line edit
|
||||
* @param marked Whether the text inserted should be highlighted
|
||||
*/
|
||||
virtual void setCompletedText(const QString &text, bool marked);
|
||||
|
||||
protected:
|
||||
KCOMPLETION_NO_EXPORT KComboBox(KComboBoxPrivate &dd, QWidget *parent);
|
||||
|
||||
protected:
|
||||
std::unique_ptr<KComboBoxPrivate> const d_ptr;
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(KComboBox)
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2000, 2001 Dawit Alemayehu <adawit@kde.org>
|
||||
SPDX-FileCopyrightText: 2000, 2001 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
SPDX-FileCopyrightText: 2000 Stefan Schimanski <1Stein@gmx.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCOMBOBOX_P_H
|
||||
#define KCOMBOBOX_P_H
|
||||
|
||||
#include "kcombobox.h"
|
||||
|
||||
#include "klineedit.h"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QPointer>
|
||||
|
||||
class KComboBoxPrivate
|
||||
{
|
||||
Q_DECLARE_PUBLIC(KComboBox)
|
||||
|
||||
public:
|
||||
explicit KComboBoxPrivate(KComboBox *q)
|
||||
: q_ptr(q)
|
||||
{
|
||||
}
|
||||
virtual ~KComboBoxPrivate() = default;
|
||||
|
||||
/**
|
||||
* Initializes the variables upon construction.
|
||||
*/
|
||||
void init();
|
||||
|
||||
void slotLineEditDeleted(QLineEdit *sender);
|
||||
|
||||
KComboBox *const q_ptr;
|
||||
|
||||
KLineEdit *klineEdit = nullptr;
|
||||
bool trapReturnKey = false;
|
||||
QPointer<QMenu> contextMenu;
|
||||
QMetaObject::Connection m_klineEditConnection;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,519 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999, 2000, 2001 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kcompletion.h"
|
||||
#include "kcompletion_p.h"
|
||||
#include <kcompletion_debug.h>
|
||||
|
||||
#include <QCollator>
|
||||
|
||||
void KCompletionPrivate::addWeightedItem(const QString &item)
|
||||
{
|
||||
Q_Q(KCompletion);
|
||||
if (order != KCompletion::Weighted) {
|
||||
q->addItem(item, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
int len = item.length();
|
||||
uint weight = 0;
|
||||
|
||||
// find out the weighting of this item (appended to the string as ":num")
|
||||
int index = item.lastIndexOf(QLatin1Char(':'));
|
||||
if (index > 0) {
|
||||
bool ok;
|
||||
weight = QStringView(item).mid(index + 1).toUInt(&ok);
|
||||
if (!ok) {
|
||||
weight = 0;
|
||||
}
|
||||
|
||||
len = index; // only insert until the ':'
|
||||
}
|
||||
|
||||
q->addItem(item.left(len), weight);
|
||||
return;
|
||||
}
|
||||
|
||||
// tries to complete "string" from the tree-root
|
||||
QString KCompletionPrivate::findCompletion(const QString &string)
|
||||
{
|
||||
QString completion;
|
||||
const KCompTreeNode *node = m_treeRoot.get();
|
||||
|
||||
// start at the tree-root and try to find the search-string
|
||||
for (const auto ch : string) {
|
||||
node = node->find(ch);
|
||||
|
||||
if (node) {
|
||||
completion += ch;
|
||||
} else {
|
||||
return QString(); // no completion
|
||||
}
|
||||
}
|
||||
|
||||
// Now we have the last node of the to be completed string.
|
||||
// Follow it as long as it has exactly one child (= longest possible
|
||||
// completion)
|
||||
|
||||
while (node->childrenCount() == 1) {
|
||||
node = node->firstChild();
|
||||
if (!node->isNull()) {
|
||||
completion += *node;
|
||||
}
|
||||
}
|
||||
// if multiple matches and auto-completion mode
|
||||
// -> find the first complete match
|
||||
if (node && node->childrenCount() > 1) {
|
||||
hasMultipleMatches = true;
|
||||
|
||||
if (completionMode == KCompletion::CompletionAuto) {
|
||||
rotationIndex = 1;
|
||||
if (order != KCompletion::Weighted) {
|
||||
while ((node = node->firstChild())) {
|
||||
if (!node->isNull()) {
|
||||
completion += *node;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// don't just find the "first" match, but the one with the
|
||||
// highest priority
|
||||
|
||||
const KCompTreeNode *temp_node = nullptr;
|
||||
while (1) {
|
||||
int count = node->childrenCount();
|
||||
temp_node = node->firstChild();
|
||||
uint weight = temp_node->weight();
|
||||
const KCompTreeNode *hit = temp_node;
|
||||
for (int i = 1; i < count; i++) {
|
||||
temp_node = node->childAt(i);
|
||||
if (temp_node->weight() > weight) {
|
||||
hit = temp_node;
|
||||
weight = hit->weight();
|
||||
}
|
||||
}
|
||||
// 0x0 has the highest priority -> we have the best match
|
||||
if (hit->isNull()) {
|
||||
break;
|
||||
}
|
||||
|
||||
node = hit;
|
||||
completion += *node;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return completion;
|
||||
}
|
||||
|
||||
void KCompletionPrivate::defaultSort(QStringList &stringList)
|
||||
{
|
||||
QCollator c;
|
||||
c.setCaseSensitivity(Qt::CaseSensitive);
|
||||
std::stable_sort(stringList.begin(), stringList.end(), c);
|
||||
}
|
||||
|
||||
KCompletion::KCompletion()
|
||||
: d_ptr(new KCompletionPrivate(this))
|
||||
{
|
||||
setOrder(Insertion);
|
||||
}
|
||||
|
||||
KCompletion::~KCompletion()
|
||||
{
|
||||
}
|
||||
|
||||
void KCompletion::setOrder(CompOrder order)
|
||||
{
|
||||
Q_D(KCompletion);
|
||||
d->order = order;
|
||||
d->matches.setSorting(order);
|
||||
}
|
||||
|
||||
KCompletion::CompOrder KCompletion::order() const
|
||||
{
|
||||
Q_D(const KCompletion);
|
||||
return d->order;
|
||||
}
|
||||
|
||||
void KCompletion::setIgnoreCase(bool ignoreCase)
|
||||
{
|
||||
Q_D(KCompletion);
|
||||
d->ignoreCase = ignoreCase;
|
||||
}
|
||||
|
||||
bool KCompletion::ignoreCase() const
|
||||
{
|
||||
Q_D(const KCompletion);
|
||||
return d->ignoreCase;
|
||||
}
|
||||
|
||||
void KCompletion::setItems(const QStringList &itemList)
|
||||
{
|
||||
clear();
|
||||
insertItems(itemList);
|
||||
}
|
||||
|
||||
void KCompletion::insertItems(const QStringList &items)
|
||||
{
|
||||
Q_D(KCompletion);
|
||||
for (const auto &str : items) {
|
||||
if (d->order == Weighted) {
|
||||
d->addWeightedItem(str);
|
||||
} else {
|
||||
addItem(str, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QStringList KCompletion::items() const
|
||||
{
|
||||
Q_D(const KCompletion);
|
||||
KCompletionMatchesWrapper list(d->sorterFunction); // unsorted
|
||||
list.extractStringsFromNode(d->m_treeRoot.get(), QString(), d->order == Weighted);
|
||||
return list.list();
|
||||
}
|
||||
|
||||
bool KCompletion::isEmpty() const
|
||||
{
|
||||
Q_D(const KCompletion);
|
||||
return (d->m_treeRoot->childrenCount() == 0);
|
||||
}
|
||||
|
||||
void KCompletion::postProcessMatch(QString *) const
|
||||
{
|
||||
}
|
||||
|
||||
void KCompletion::postProcessMatches(QStringList *) const
|
||||
{
|
||||
}
|
||||
|
||||
void KCompletion::postProcessMatches(KCompletionMatches *) const
|
||||
{
|
||||
}
|
||||
|
||||
void KCompletion::addItem(const QString &item)
|
||||
{
|
||||
Q_D(KCompletion);
|
||||
d->matches.clear();
|
||||
d->rotationIndex = 0;
|
||||
d->lastString.clear();
|
||||
|
||||
addItem(item, 0);
|
||||
}
|
||||
|
||||
void KCompletion::addItem(const QString &item, uint weight)
|
||||
{
|
||||
Q_D(KCompletion);
|
||||
if (item.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
KCompTreeNode *node = d->m_treeRoot.get();
|
||||
int len = item.length();
|
||||
|
||||
bool sorted = (d->order == Sorted);
|
||||
bool weighted = ((d->order == Weighted) && weight > 1);
|
||||
|
||||
// knowing the weight of an item, we simply add this weight to all of its
|
||||
// nodes.
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
node = node->insert(item.at(i), sorted);
|
||||
if (weighted) {
|
||||
node->confirm(weight - 1); // node->insert() sets weighting to 1
|
||||
}
|
||||
}
|
||||
|
||||
// add 0x0-item as delimiter with evtl. weight
|
||||
node = node->insert(QChar(0x0), true);
|
||||
if (weighted) {
|
||||
node->confirm(weight - 1);
|
||||
}
|
||||
// qDebug("*** added: %s (%i)", item.toLatin1().constData(), node->weight());
|
||||
}
|
||||
|
||||
void KCompletion::removeItem(const QString &item)
|
||||
{
|
||||
Q_D(KCompletion);
|
||||
d->matches.clear();
|
||||
d->rotationIndex = 0;
|
||||
d->lastString.clear();
|
||||
|
||||
d->m_treeRoot->remove(item);
|
||||
}
|
||||
|
||||
void KCompletion::clear()
|
||||
{
|
||||
Q_D(KCompletion);
|
||||
d->matches.clear();
|
||||
d->rotationIndex = 0;
|
||||
d->lastString.clear();
|
||||
|
||||
d->m_treeRoot.reset(new KCompTreeNode);
|
||||
}
|
||||
|
||||
QString KCompletion::makeCompletion(const QString &string)
|
||||
{
|
||||
Q_D(KCompletion);
|
||||
if (d->completionMode == CompletionNone) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
// qDebug() << "KCompletion: completing: " << string;
|
||||
|
||||
d->matches.clear();
|
||||
d->rotationIndex = 0;
|
||||
d->hasMultipleMatches = false;
|
||||
d->lastMatch = d->currentMatch;
|
||||
|
||||
// in Shell-completion-mode, emit all matches when we get the same
|
||||
// complete-string twice
|
||||
if (d->completionMode == CompletionShell && string == d->lastString) {
|
||||
// Don't use d->matches since calling postProcessMatches()
|
||||
// on d->matches here would interfere with call to
|
||||
// postProcessMatch() during rotation
|
||||
|
||||
d->matches.findAllCompletions(d->m_treeRoot.get(), string, d->ignoreCase, d->hasMultipleMatches);
|
||||
QStringList l = d->matches.list();
|
||||
postProcessMatches(&l);
|
||||
Q_EMIT matches(l);
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString completion;
|
||||
// in case-insensitive popup mode, we search all completions at once
|
||||
if (d->completionMode == CompletionPopup || d->completionMode == CompletionPopupAuto) {
|
||||
d->matches.findAllCompletions(d->m_treeRoot.get(), string, d->ignoreCase, d->hasMultipleMatches);
|
||||
if (!d->matches.isEmpty()) {
|
||||
completion = d->matches.first();
|
||||
}
|
||||
} else {
|
||||
completion = d->findCompletion(string);
|
||||
}
|
||||
|
||||
if (d->hasMultipleMatches) {
|
||||
Q_EMIT multipleMatches();
|
||||
}
|
||||
|
||||
d->lastString = string;
|
||||
d->currentMatch = completion;
|
||||
|
||||
postProcessMatch(&completion);
|
||||
|
||||
if (!string.isEmpty()) { // only emit match when string is not empty
|
||||
// qDebug() << "KCompletion: Match: " << completion;
|
||||
Q_EMIT match(completion);
|
||||
}
|
||||
|
||||
return completion;
|
||||
}
|
||||
|
||||
QStringList KCompletion::substringCompletion(const QString &string) const
|
||||
{
|
||||
Q_D(const KCompletion);
|
||||
// get all items in the tree, eventually in sorted order
|
||||
KCompletionMatchesWrapper allItems(d->sorterFunction, d->order);
|
||||
allItems.extractStringsFromNode(d->m_treeRoot.get(), QString(), false);
|
||||
|
||||
QStringList list = allItems.list();
|
||||
|
||||
// subStringMatches is invoked manually, via a shortcut
|
||||
if (list.isEmpty()) {
|
||||
return list;
|
||||
}
|
||||
|
||||
if (!string.isEmpty()) { // If it's empty, nothing to compare
|
||||
auto it = std::remove_if(list.begin(), list.end(), [&string](const QString &item) {
|
||||
return !item.contains(string, Qt::CaseInsensitive); // always case insensitive
|
||||
});
|
||||
list.erase(it, list.end());
|
||||
}
|
||||
|
||||
postProcessMatches(&list);
|
||||
return list;
|
||||
}
|
||||
|
||||
void KCompletion::setCompletionMode(CompletionMode mode)
|
||||
{
|
||||
Q_D(KCompletion);
|
||||
d->completionMode = mode;
|
||||
}
|
||||
|
||||
KCompletion::CompletionMode KCompletion::completionMode() const
|
||||
{
|
||||
Q_D(const KCompletion);
|
||||
return d->completionMode;
|
||||
}
|
||||
|
||||
void KCompletion::setShouldAutoSuggest(const bool shouldAutoSuggest)
|
||||
{
|
||||
Q_D(KCompletion);
|
||||
d->shouldAutoSuggest = shouldAutoSuggest;
|
||||
}
|
||||
|
||||
bool KCompletion::shouldAutoSuggest() const
|
||||
{
|
||||
Q_D(const KCompletion);
|
||||
return d->shouldAutoSuggest;
|
||||
}
|
||||
|
||||
void KCompletion::setSorterFunction(SorterFunction sortFunc)
|
||||
{
|
||||
Q_D(KCompletion);
|
||||
d->sorterFunction = sortFunc ? sortFunc : KCompletionPrivate::defaultSort;
|
||||
}
|
||||
|
||||
QStringList KCompletion::allMatches()
|
||||
{
|
||||
Q_D(KCompletion);
|
||||
// Don't use d->matches since calling postProcessMatches()
|
||||
// on d->matches here would interfere with call to
|
||||
// postProcessMatch() during rotation
|
||||
KCompletionMatchesWrapper matches(d->sorterFunction, d->order);
|
||||
bool dummy;
|
||||
matches.findAllCompletions(d->m_treeRoot.get(), d->lastString, d->ignoreCase, dummy);
|
||||
QStringList l = matches.list();
|
||||
postProcessMatches(&l);
|
||||
return l;
|
||||
}
|
||||
|
||||
KCompletionMatches KCompletion::allWeightedMatches()
|
||||
{
|
||||
Q_D(KCompletion);
|
||||
// Don't use d->matches since calling postProcessMatches()
|
||||
// on d->matches here would interfere with call to
|
||||
// postProcessMatch() during rotation
|
||||
KCompletionMatchesWrapper matches(d->sorterFunction, d->order);
|
||||
bool dummy;
|
||||
matches.findAllCompletions(d->m_treeRoot.get(), d->lastString, d->ignoreCase, dummy);
|
||||
KCompletionMatches ret(matches);
|
||||
postProcessMatches(&ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
QStringList KCompletion::allMatches(const QString &string)
|
||||
{
|
||||
Q_D(KCompletion);
|
||||
KCompletionMatchesWrapper matches(d->sorterFunction, d->order);
|
||||
bool dummy;
|
||||
matches.findAllCompletions(d->m_treeRoot.get(), string, d->ignoreCase, dummy);
|
||||
QStringList l = matches.list();
|
||||
postProcessMatches(&l);
|
||||
return l;
|
||||
}
|
||||
|
||||
KCompletionMatches KCompletion::allWeightedMatches(const QString &string)
|
||||
{
|
||||
Q_D(KCompletion);
|
||||
KCompletionMatchesWrapper matches(d->sorterFunction, d->order);
|
||||
bool dummy;
|
||||
matches.findAllCompletions(d->m_treeRoot.get(), string, d->ignoreCase, dummy);
|
||||
KCompletionMatches ret(matches);
|
||||
postProcessMatches(&ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void KCompletion::setSoundsEnabled(bool enable)
|
||||
{
|
||||
Q_D(KCompletion);
|
||||
d->beep = enable;
|
||||
}
|
||||
|
||||
bool KCompletion::soundsEnabled() const
|
||||
{
|
||||
Q_D(const KCompletion);
|
||||
return d->beep;
|
||||
}
|
||||
|
||||
bool KCompletion::hasMultipleMatches() const
|
||||
{
|
||||
Q_D(const KCompletion);
|
||||
return d->hasMultipleMatches;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
///////////////// tree operations ///////////////////
|
||||
|
||||
QString KCompletion::nextMatch()
|
||||
{
|
||||
Q_D(KCompletion);
|
||||
QString completion;
|
||||
d->lastMatch = d->currentMatch;
|
||||
|
||||
if (d->matches.isEmpty()) {
|
||||
d->matches.findAllCompletions(d->m_treeRoot.get(), d->lastString, d->ignoreCase, d->hasMultipleMatches);
|
||||
if (!d->matches.isEmpty()) {
|
||||
completion = d->matches.first();
|
||||
}
|
||||
d->currentMatch = completion;
|
||||
d->rotationIndex = 0;
|
||||
postProcessMatch(&completion);
|
||||
Q_EMIT match(completion);
|
||||
return completion;
|
||||
}
|
||||
|
||||
QStringList matches = d->matches.list();
|
||||
d->lastMatch = matches[d->rotationIndex++];
|
||||
|
||||
if (d->rotationIndex == matches.count()) {
|
||||
d->rotationIndex = 0;
|
||||
}
|
||||
|
||||
completion = matches[d->rotationIndex];
|
||||
d->currentMatch = completion;
|
||||
postProcessMatch(&completion);
|
||||
Q_EMIT match(completion);
|
||||
return completion;
|
||||
}
|
||||
|
||||
const QString &KCompletion::lastMatch() const
|
||||
{
|
||||
Q_D(const KCompletion);
|
||||
return d->lastMatch;
|
||||
}
|
||||
|
||||
QString KCompletion::previousMatch()
|
||||
{
|
||||
Q_D(KCompletion);
|
||||
QString completion;
|
||||
d->lastMatch = d->currentMatch;
|
||||
|
||||
if (d->matches.isEmpty()) {
|
||||
d->matches.findAllCompletions(d->m_treeRoot.get(), d->lastString, d->ignoreCase, d->hasMultipleMatches);
|
||||
if (!d->matches.isEmpty()) {
|
||||
completion = d->matches.last();
|
||||
}
|
||||
d->currentMatch = completion;
|
||||
d->rotationIndex = 0;
|
||||
postProcessMatch(&completion);
|
||||
Q_EMIT match(completion);
|
||||
return completion;
|
||||
}
|
||||
|
||||
QStringList matches = d->matches.list();
|
||||
d->lastMatch = matches[d->rotationIndex];
|
||||
|
||||
if (d->rotationIndex == 0) {
|
||||
d->rotationIndex = matches.count();
|
||||
}
|
||||
|
||||
d->rotationIndex--;
|
||||
|
||||
completion = matches[d->rotationIndex];
|
||||
d->currentMatch = completion;
|
||||
postProcessMatch(&completion);
|
||||
Q_EMIT match(completion);
|
||||
return completion;
|
||||
}
|
||||
|
||||
QSharedPointer<KZoneAllocator> KCompTreeNode::m_alloc(new KZoneAllocator(8 * 1024));
|
||||
|
||||
#include "moc_kcompletion.cpp"
|
||||
@@ -0,0 +1,559 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999, 2000 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCOMPLETION_H
|
||||
#define KCOMPLETION_H
|
||||
|
||||
#include <kcompletion_export.h>
|
||||
|
||||
#include <QKeySequence>
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
#include <QStringList>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
class KCompTreeNode;
|
||||
class KCompletionPrivate;
|
||||
class KCompletionMatchesWrapper;
|
||||
class KCompletionMatches;
|
||||
|
||||
/**
|
||||
* @class KCompletion kcompletion.h KCompletion
|
||||
*
|
||||
* @short A generic class for completing QStrings
|
||||
*
|
||||
* This class offers easy use of "auto completion", "manual completion" or
|
||||
* "shell completion" on QString objects. A common use is completing filenames
|
||||
* or URLs (see KUrlCompletion()).
|
||||
* But it is not limited to URL-completion -- everything should be completable!
|
||||
* The user should be able to complete email addresses, telephone numbers,
|
||||
* commands, SQL queries...
|
||||
* Every time your program knows what the user can type into an edit field, you
|
||||
* should offer completion. With KCompletion, this is very easy, and if you are
|
||||
* using a line edit widget (KLineEdit), it is even easier.
|
||||
* Basically, you tell a KCompletion object what strings should be completable
|
||||
* and, whenever completion should be invoked, you call makeCompletion().
|
||||
* KLineEdit and (an editable) KComboBox even do this automatically for you.
|
||||
*
|
||||
* KCompletion offers the completed string via the signal match() and
|
||||
* all matching strings (when the result is ambiguous) via the method
|
||||
* allMatches().
|
||||
*
|
||||
* Notice: auto completion, shell completion and manual completion work
|
||||
* slightly differently:
|
||||
*
|
||||
* @li auto completion always returns a complete item as match.
|
||||
* When more than one matching item is available, it will deliver just
|
||||
* the first one (depending on sorting order). Iterating over all matches
|
||||
* is possible via nextMatch() and previousMatch().
|
||||
*
|
||||
* @li popup completion works in the same way, the only difference being that
|
||||
* the completed items are not put into the edit widget, but into a
|
||||
* separate popup box.
|
||||
*
|
||||
* @li manual completion works the same way as auto completion, except that
|
||||
* it is not invoked automatically while the user is typing,
|
||||
* but only when the user presses a special key. The difference
|
||||
* of manual and auto completion is therefore only visible in UI classes.
|
||||
* KCompletion needs to know whether to deliver partial matches
|
||||
* (shell completion) or whole matches (auto/manual completion), therefore
|
||||
* KCompletion::CompletionMan and KCompletion::CompletionAuto have the exact
|
||||
* same effect in KCompletion.
|
||||
*
|
||||
* @li shell completion works like "tab completion" in a shell:
|
||||
* when multiple matches are available, the longest possible string of all
|
||||
* matches is returned (i.e. only a partial item).
|
||||
* Iterating over all matching items (complete, not partial) is possible
|
||||
* via nextMatch() and previousMatch().
|
||||
*
|
||||
* As an application programmer, you do not normally have to worry about
|
||||
* the different completion modes; KCompletion handles
|
||||
* that for you, according to the setting setCompletionMode().
|
||||
* The default setting is globally configured by the user and read
|
||||
* from completionMode().
|
||||
*
|
||||
* A short example:
|
||||
* \code
|
||||
* KCompletion completion;
|
||||
* completion.setOrder(KCompletion::Sorted);
|
||||
* completion.addItem("pfeiffer@kde.org");
|
||||
* completion.addItem("coolo@kde.org");
|
||||
* completion.addItem("carpdjih@sp.zrz.tu-berlin.de");
|
||||
* completion.addItem("carp@cs.tu-berlin.de");
|
||||
*
|
||||
* cout << completion.makeCompletion("ca").latin1() << endl;
|
||||
* \endcode
|
||||
*
|
||||
* In shell-completion mode, this will be "carp"; in auto-completion
|
||||
* mode it will be "carp\@cs.tu-berlin.de", as that is alphabetically
|
||||
* smaller.
|
||||
* If setOrder was set to Insertion, "carpdjih\@sp.zrz.tu-berlin.de"
|
||||
* would be completed in auto-completion mode, as that was inserted before
|
||||
* "carp\@cs.tu-berlin.de".
|
||||
*
|
||||
* You can dynamically update the completable items by removing and adding them
|
||||
* whenever you want.
|
||||
* For advanced usage, you could even use multiple KCompletion objects. E.g.
|
||||
* imagine an editor like kwrite with multiple open files. You could store
|
||||
* items of each file in a different KCompletion object, so that you know (and
|
||||
* tell the user) where a completion comes from.
|
||||
*
|
||||
* @note KCompletion does not work with strings that contain 0x0 characters
|
||||
* (unicode null), as this is used internally as a delimiter.
|
||||
*
|
||||
* You may inherit from KCompletion and override makeCompletion() in
|
||||
* special cases (like reading directories or urls and then supplying the
|
||||
* contents to KCompletion, as KUrlCompletion does), but this is usually
|
||||
* not necessary.
|
||||
*
|
||||
*
|
||||
* @author Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
*/
|
||||
class KCOMPLETION_EXPORT KCompletion : public QObject
|
||||
{
|
||||
Q_PROPERTY(CompOrder order READ order WRITE setOrder)
|
||||
Q_PROPERTY(bool ignoreCase READ ignoreCase WRITE setIgnoreCase)
|
||||
Q_PROPERTY(QStringList items READ items WRITE setItems)
|
||||
Q_OBJECT
|
||||
Q_DECLARE_PRIVATE(KCompletion)
|
||||
|
||||
public:
|
||||
/**
|
||||
* This enum describes the completion mode used for by the KCompletion class.
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
enum CompletionMode {
|
||||
/**
|
||||
* No completion is used.
|
||||
*/
|
||||
CompletionNone = 1,
|
||||
/**
|
||||
* Text is automatically filled in whenever possible.
|
||||
*/
|
||||
CompletionAuto,
|
||||
/**
|
||||
* Same as automatic, but shortest match is used for completion.
|
||||
*/
|
||||
CompletionMan,
|
||||
/**
|
||||
* Completes text much in the same way as a typical *nix shell would.
|
||||
*/
|
||||
CompletionShell,
|
||||
/**
|
||||
* Lists all possible matches in a popup list box to choose from.
|
||||
*/
|
||||
CompletionPopup,
|
||||
/**
|
||||
* Lists all possible matches in a popup list box to choose from, and automatically
|
||||
* fills the result whenever possible.
|
||||
*/
|
||||
CompletionPopupAuto,
|
||||
};
|
||||
|
||||
/**
|
||||
* Constants that represent the order in which KCompletion performs
|
||||
* completion lookups.
|
||||
*/
|
||||
enum CompOrder {
|
||||
Sorted, ///< Use alphabetically sorted order or custom sorter logic
|
||||
Insertion, ///< Use order of insertion
|
||||
Weighted, ///< Use weighted order
|
||||
};
|
||||
Q_ENUM(CompOrder)
|
||||
|
||||
/**
|
||||
* The sorter function signature. Deriving classes may provide
|
||||
* custom sorting logic via the setSorterFunction method.
|
||||
*
|
||||
* @since 5.88
|
||||
*/
|
||||
using SorterFunction = std::function<void(QStringList &)>;
|
||||
|
||||
/**
|
||||
* Constructor, nothing special here :)
|
||||
*/
|
||||
KCompletion();
|
||||
|
||||
/**
|
||||
* Destructor, nothing special here, either.
|
||||
*/
|
||||
~KCompletion() override;
|
||||
|
||||
/**
|
||||
* Returns a list of all completion items that contain the given @p string.
|
||||
* @param string the string to complete
|
||||
* @return a list of items which contain @p text as a substring,
|
||||
* i.e. not necessarily at the beginning.
|
||||
*
|
||||
* @see makeCompletion
|
||||
*/
|
||||
QStringList substringCompletion(const QString &string) const;
|
||||
|
||||
/**
|
||||
* Returns the last match. Might be useful if you need to check whether
|
||||
* a completion is different from the last one.
|
||||
* @return the last match. QString() is returned when there is no
|
||||
* last match.
|
||||
*/
|
||||
virtual const QString &lastMatch() const;
|
||||
|
||||
/**
|
||||
* Returns a list of all items inserted into KCompletion. This is useful
|
||||
* if you need to save the state of a KCompletion object and restore it
|
||||
* later.
|
||||
*
|
||||
* @note When order() == Weighted, then every item in the
|
||||
* stringlist has its weight appended, delimited by a colon. E.g. an item
|
||||
* "www.kde.org" might look like "www.kde.org:4", where 4 is the weight.
|
||||
* This is necessary so that you can save the items along with its
|
||||
* weighting on disk and load them back with setItems(), restoring its
|
||||
* weight as well. If you really don't want the appended weightings, call
|
||||
* setOrder( KCompletion::Insertion ) before calling items().
|
||||
*
|
||||
* @return a list of all items
|
||||
* @see setItems
|
||||
*/
|
||||
QStringList items() const;
|
||||
|
||||
/**
|
||||
* Returns true if the completion object contains no entries.
|
||||
*/
|
||||
bool isEmpty() const;
|
||||
|
||||
/**
|
||||
* Sets the completion mode.
|
||||
* @param mode the completion mode
|
||||
* @see CompletionMode
|
||||
*/
|
||||
virtual void setCompletionMode(CompletionMode mode);
|
||||
|
||||
/**
|
||||
* Returns the current completion mode.
|
||||
*
|
||||
* @return the current completion mode, default is CompletionPopup
|
||||
* @see setCompletionMode
|
||||
* @see CompletionMode
|
||||
*/
|
||||
CompletionMode completionMode() const;
|
||||
|
||||
/**
|
||||
* KCompletion offers three different ways in which it offers its items:
|
||||
* @li in the order of insertion
|
||||
* @li sorted alphabetically
|
||||
* @li weighted
|
||||
*
|
||||
* Choosing weighted makes KCompletion perform an implicit weighting based
|
||||
* on how often an item is inserted. Imagine a web browser with a location
|
||||
* bar, where the user enters URLs. The more often a URL is entered, the
|
||||
* higher priority it gets.
|
||||
*
|
||||
* @note Setting the order to sorted only affects new inserted items,
|
||||
* already existing items will stay in the current order. So you probably
|
||||
* want to call setOrder(Sorted) before inserting items if you want
|
||||
* everything sorted.
|
||||
*
|
||||
* Default is insertion order.
|
||||
* @param order the new order
|
||||
* @see order
|
||||
*/
|
||||
virtual void setOrder(CompOrder order);
|
||||
|
||||
/**
|
||||
* Returns the completion order.
|
||||
* @return the current completion order.
|
||||
* @see setOrder
|
||||
*/
|
||||
CompOrder order() const;
|
||||
|
||||
/**
|
||||
* Setting this to true makes KCompletion behave case insensitively.
|
||||
* E.g. makeCompletion("CA"); might return "carp\@cs.tu-berlin.de".
|
||||
* Default is false (case sensitive).
|
||||
* @param ignoreCase true to ignore the case
|
||||
* @see ignoreCase
|
||||
*/
|
||||
virtual void setIgnoreCase(bool ignoreCase);
|
||||
|
||||
/**
|
||||
* Returns whether KCompletion acts case insensitively or not.
|
||||
* Default is false (case sensitive).
|
||||
* @return true if the case will be ignored
|
||||
* @see setIgnoreCase
|
||||
*/
|
||||
bool ignoreCase() const;
|
||||
|
||||
/**
|
||||
* Informs the caller if they should display the auto-suggestion for the last completion operation performed.
|
||||
* Applies for CompletionPopupAuto and CompletionAuto modes.
|
||||
* Defaults to true, but deriving classes may set it to false in special cases via "setShouldAutoSuggest".
|
||||
* @return true if auto-suggestion should be displayed for the last completion operation performed.
|
||||
* @since 5.87
|
||||
*/
|
||||
bool shouldAutoSuggest() const;
|
||||
|
||||
/**
|
||||
* Returns a list of all items matching the last completed string.
|
||||
* It might take some time if you have a @em lot of items.
|
||||
* @return a list of all matches for the last completed string.
|
||||
* @see substringCompletion
|
||||
*/
|
||||
QStringList allMatches();
|
||||
|
||||
/**
|
||||
* Returns a list of all items matching @p string.
|
||||
* @param string the string to match
|
||||
* @return the list of all matches
|
||||
*/
|
||||
QStringList allMatches(const QString &string);
|
||||
|
||||
/**
|
||||
* Returns a list of all items matching the last completed string.
|
||||
* It might take some time if you have a @em lot of items.
|
||||
* The matches are returned as KCompletionMatches, which also
|
||||
* keeps the weight of the matches, allowing
|
||||
* you to modify some matches or merge them with matches
|
||||
* from another call to allWeightedMatches(), and sort the matches
|
||||
* after that in order to have the matches ordered correctly.
|
||||
*
|
||||
* @return a list of all completion matches
|
||||
* @see substringCompletion
|
||||
*/
|
||||
KCompletionMatches allWeightedMatches();
|
||||
|
||||
/**
|
||||
* Returns a list of all items matching @p string.
|
||||
* @param string the string to match
|
||||
* @return a list of all matches
|
||||
*/
|
||||
KCompletionMatches allWeightedMatches(const QString &string);
|
||||
|
||||
/**
|
||||
* Enables/disables emitting a sound when
|
||||
* @li makeCompletion() can't find a match
|
||||
* @li there is a partial completion (= multiple matches in
|
||||
* Shell-completion mode)
|
||||
* @li nextMatch() or previousMatch() hit the last possible
|
||||
* match and the list is rotated
|
||||
*
|
||||
* KNotifyClient() is used to emit the sounds.
|
||||
*
|
||||
* @param enable true to enable sounds
|
||||
* @see soundsEnabled
|
||||
*/
|
||||
virtual void setSoundsEnabled(bool enable);
|
||||
|
||||
/**
|
||||
* Tells you whether KCompletion will emit sounds on certain occasions.
|
||||
* Default is enabled.
|
||||
* @return true if sounds are enabled
|
||||
* @see setSoundsEnabled
|
||||
*/
|
||||
bool soundsEnabled() const;
|
||||
|
||||
/**
|
||||
* Returns true when more than one match is found.
|
||||
* @return true if there is more than one match
|
||||
* @see multipleMatches
|
||||
*/
|
||||
bool hasMultipleMatches() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* Attempts to find an item in the list of available completions
|
||||
* that begins with @p string. Will either return the first matching item
|
||||
* (if there is more than one match) or QString(), if no match is
|
||||
* found.
|
||||
*
|
||||
* In the latter case, a sound will be emitted, depending on
|
||||
* soundsEnabled().
|
||||
* If a match is found, it will be emitted via the signal
|
||||
* match().
|
||||
*
|
||||
* If this is called twice or more with the same string while no
|
||||
* items were added or removed in the meantime, all available completions
|
||||
* will be emitted via the signal matches().
|
||||
* This happens only in shell-completion mode.
|
||||
*
|
||||
* @param string the string to complete
|
||||
* @return the matching item, or QString() if there is no matching
|
||||
* item.
|
||||
* @see substringCompletion
|
||||
*/
|
||||
virtual QString makeCompletion(const QString &string);
|
||||
|
||||
/**
|
||||
* Returns the next item from the list of matching items.
|
||||
* When reaching the beginning, the list is rotated so it will return the
|
||||
* last match and a sound is emitted (depending on soundsEnabled()).
|
||||
* @return the next item from the list of matching items.
|
||||
* When there is no match, QString() is returned and
|
||||
* a sound is emitted.
|
||||
*/
|
||||
QString previousMatch();
|
||||
|
||||
/**
|
||||
* Returns the next item from the list of matching items.
|
||||
* When reaching the last item, the list is rotated, so it will return
|
||||
* the first match and a sound is emitted (depending on
|
||||
* soundsEnabled()).
|
||||
* @return the next item from the list of matching items. When there is no
|
||||
* match, QString() is returned and a sound is emitted.
|
||||
*/
|
||||
QString nextMatch();
|
||||
|
||||
/**
|
||||
* Inserts @p items into the list of possible completions.
|
||||
* It does the same as setItems(), but without calling clear() before.
|
||||
* @param items the items to insert
|
||||
*/
|
||||
void insertItems(const QStringList &items);
|
||||
|
||||
/**
|
||||
* Sets the list of items available for completion. Removes all previous
|
||||
* items.
|
||||
*
|
||||
* @note When order() == Weighted, then the weighting is looked up for
|
||||
* every item in the stringlist. Every item should have ":number" appended,
|
||||
* where number is an unsigned integer, specifying the weighting.
|
||||
* If you don't like this, call
|
||||
* setOrder(KCompletion::Insertion)
|
||||
* before calling setItems().
|
||||
*
|
||||
* @param itemList the list of items that are available for completion
|
||||
* @see items
|
||||
*/
|
||||
virtual void setItems(const QStringList &itemList);
|
||||
|
||||
/**
|
||||
* Adds an item to the list of available completions.
|
||||
* Resets the current item state (previousMatch() and nextMatch()
|
||||
* won't work the next time they are called).
|
||||
* @param item the item to add
|
||||
*/
|
||||
void addItem(const QString &item);
|
||||
|
||||
/**
|
||||
* Adds an item to the list of available completions.
|
||||
* Resets the current item state (previousMatch() and nextMatch()
|
||||
* won't work the next time they are called).
|
||||
*
|
||||
* Sets the weight of the item to @p weight or adds it to the current
|
||||
* weight if the item is already available. The weight has to be greater
|
||||
* than 1 to take effect (default weight is 1).
|
||||
* @param item the item to add
|
||||
* @param weight the weight of the item, default is 1
|
||||
*/
|
||||
void addItem(const QString &item, uint weight);
|
||||
|
||||
/**
|
||||
* Removes an item from the list of available completions.
|
||||
* Resets the current item state (previousMatch() and nextMatch()
|
||||
* won't work the next time they are called).
|
||||
* @param item the item to remove
|
||||
*/
|
||||
void removeItem(const QString &item);
|
||||
|
||||
/**
|
||||
* Removes all inserted items.
|
||||
*/
|
||||
virtual void clear();
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* This signal is emitted when a match is found.
|
||||
*
|
||||
* In particular, makeCompletion(), previousMatch() and nextMatch()
|
||||
* all emit this signal; makeCompletion() will only emit it when a
|
||||
* match is found, but the other methods will always emit it (and so
|
||||
* may emit it with an empty string).
|
||||
*
|
||||
* @param item the matching item, or QString() if there were no more
|
||||
* matching items.
|
||||
*/
|
||||
void match(const QString &item);
|
||||
|
||||
/**
|
||||
* This signal is emitted by makeCompletion() in shell-completion mode
|
||||
* when the same string is passed to makeCompletion() multiple times in
|
||||
* a row.
|
||||
* @param matchlist the list of all matching items
|
||||
*/
|
||||
void matches(const QStringList &matchlist);
|
||||
|
||||
/**
|
||||
* This signal is emitted when calling makeCompletion() and more than
|
||||
* one matching item is found.
|
||||
* @see hasMultipleMatches
|
||||
*/
|
||||
void multipleMatches();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* This method is called after a completion is found and before the
|
||||
* matching string is emitted. You can override this method to modify the
|
||||
* string that will be emitted.
|
||||
* This is necessary e.g. in KUrlCompletion(), where files with spaces
|
||||
* in their names are shown escaped ("filename\ with\ spaces"), but stored
|
||||
* unescaped inside KCompletion.
|
||||
* Never delete that pointer!
|
||||
*
|
||||
* Default implementation does nothing.
|
||||
* @param match the match to process
|
||||
* @see postProcessMatches
|
||||
*/
|
||||
virtual void postProcessMatch(QString *match) const;
|
||||
|
||||
/**
|
||||
* This method is called before a list of all available completions is
|
||||
* emitted via matches(). You can override this method to modify the
|
||||
* found items before match() or matches() are emitted.
|
||||
* Never delete that pointer!
|
||||
*
|
||||
* Default implementation does nothing.
|
||||
* @param matchList the matches to process
|
||||
* @see postProcessMatch
|
||||
*/
|
||||
virtual void postProcessMatches(QStringList *matchList) const;
|
||||
|
||||
/**
|
||||
* This method is called before a list of all available completions is
|
||||
* emitted via #matches(). You can override this method to modify the
|
||||
* found items before #match() or #matches() are emitted.
|
||||
* Never delete that pointer!
|
||||
*
|
||||
* Default implementation does nothing.
|
||||
* @param matches the matches to process
|
||||
* @see postProcessMatch
|
||||
*/
|
||||
virtual void postProcessMatches(KCompletionMatches *matches) const;
|
||||
|
||||
/**
|
||||
* Deriving classes may set this property and control whether the auto-suggestion should be displayed
|
||||
* for the last completion operation performed.
|
||||
*
|
||||
* Applies for CompletionPopupAuto and CompletionAuto modes.
|
||||
* @since 5.87
|
||||
*/
|
||||
void setShouldAutoSuggest(bool shouldAutosuggest);
|
||||
|
||||
/**
|
||||
* Sets a custom function to be used to sort the matches.
|
||||
* Can be set to nullptr to use the default sorting logic.
|
||||
*
|
||||
* Applies for CompOrder::Sorted mode.
|
||||
* @since 5.88
|
||||
*/
|
||||
void setSorterFunction(SorterFunction sortFunc);
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(KCompletion)
|
||||
std::unique_ptr<KCompletionPrivate> const d_ptr;
|
||||
};
|
||||
|
||||
#endif // KCOMPLETION_H
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCOMPLETION_PRIVATE_H
|
||||
#define KCOMPLETION_PRIVATE_H
|
||||
|
||||
#include "kcompletion.h"
|
||||
#include "kcompletionmatcheswrapper_p.h"
|
||||
#include "kcomptreenode_p.h"
|
||||
|
||||
#include <kcompletionmatches.h>
|
||||
|
||||
#include <QSharedPointer>
|
||||
#include <kzoneallocator_p.h>
|
||||
|
||||
class KCompletionPrivate
|
||||
{
|
||||
public:
|
||||
explicit KCompletionPrivate(KCompletion *parent)
|
||||
: q_ptr(parent)
|
||||
, completionMode(KCompletion::CompletionPopup)
|
||||
, treeNodeAllocator(KCompTreeNode::allocator()) // keep strong-ref to allocator instance
|
||||
, m_treeRoot(new KCompTreeNode)
|
||||
, hasMultipleMatches(false)
|
||||
, beep(true)
|
||||
, ignoreCase(false)
|
||||
, shouldAutoSuggest(true)
|
||||
{
|
||||
}
|
||||
|
||||
~KCompletionPrivate() = default;
|
||||
|
||||
void addWeightedItem(const QString &);
|
||||
QString findCompletion(const QString &string);
|
||||
|
||||
// The default sorting function, sorts alphabetically
|
||||
static void defaultSort(QStringList &);
|
||||
|
||||
// Pointer to sorter function
|
||||
KCompletion::SorterFunction sorterFunction{defaultSort};
|
||||
|
||||
// list used for nextMatch() and previousMatch()
|
||||
KCompletionMatchesWrapper matches{sorterFunction};
|
||||
|
||||
KCompletion *const q_ptr;
|
||||
KCompletion::CompletionMode completionMode;
|
||||
|
||||
QSharedPointer<KZoneAllocator> treeNodeAllocator;
|
||||
|
||||
QString lastString;
|
||||
QString lastMatch;
|
||||
QString currentMatch;
|
||||
std::unique_ptr<KCompTreeNode> m_treeRoot;
|
||||
int rotationIndex = 0;
|
||||
// TODO: Change hasMultipleMatches to bitfield after moving findAllCompletions()
|
||||
// to KCompletionMatchesPrivate
|
||||
KCompletion::CompOrder order : 3;
|
||||
bool hasMultipleMatches;
|
||||
bool beep : 1;
|
||||
bool ignoreCase : 1;
|
||||
bool shouldAutoSuggest : 1;
|
||||
Q_DECLARE_PUBLIC(KCompletion)
|
||||
};
|
||||
|
||||
#endif // KCOMPLETION_PRIVATE_H
|
||||
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2000 Dawit Alemayehu <adawit@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include <kcompletionbase.h>
|
||||
|
||||
#include <QKeySequence>
|
||||
#include <QPointer>
|
||||
|
||||
class KCompletionBasePrivate
|
||||
{
|
||||
public:
|
||||
KCompletionBasePrivate(KCompletionBase *parent)
|
||||
: q_ptr(parent)
|
||||
{
|
||||
}
|
||||
|
||||
~KCompletionBasePrivate();
|
||||
|
||||
void init();
|
||||
|
||||
bool autoDeleteCompletionObject;
|
||||
bool handleSignals;
|
||||
bool emitSignals;
|
||||
KCompletion::CompletionMode completionMode;
|
||||
QPointer<KCompletion> completionObject;
|
||||
KCompletionBase::KeyBindingMap keyBindingMap;
|
||||
// we may act as a proxy to another KCompletionBase object
|
||||
KCompletionBase *delegate = nullptr;
|
||||
KCompletionBase *const q_ptr;
|
||||
Q_DECLARE_PUBLIC(KCompletionBase)
|
||||
};
|
||||
|
||||
KCompletionBasePrivate::~KCompletionBasePrivate()
|
||||
{
|
||||
if (autoDeleteCompletionObject && completionObject) {
|
||||
delete completionObject;
|
||||
}
|
||||
}
|
||||
|
||||
void KCompletionBasePrivate::init()
|
||||
{
|
||||
Q_Q(KCompletionBase);
|
||||
completionMode = KCompletion::CompletionPopup;
|
||||
delegate = nullptr;
|
||||
// Initialize all key-bindings to 0 by default so that
|
||||
// the event filter will use the global settings.
|
||||
q->useGlobalKeyBindings();
|
||||
|
||||
q->setAutoDeleteCompletionObject(false);
|
||||
q->setHandleSignals(true);
|
||||
q->setEmitSignals(false);
|
||||
}
|
||||
|
||||
KCompletionBase::KCompletionBase()
|
||||
: d_ptr(new KCompletionBasePrivate(this))
|
||||
{
|
||||
Q_D(KCompletionBase);
|
||||
d->init();
|
||||
}
|
||||
|
||||
KCompletionBase::~KCompletionBase()
|
||||
{
|
||||
}
|
||||
|
||||
void KCompletionBase::setDelegate(KCompletionBase *delegate)
|
||||
{
|
||||
Q_D(KCompletionBase);
|
||||
d->delegate = delegate;
|
||||
|
||||
if (delegate) {
|
||||
delegate->setAutoDeleteCompletionObject(d->autoDeleteCompletionObject);
|
||||
delegate->setHandleSignals(d->handleSignals);
|
||||
delegate->setEmitSignals(d->emitSignals);
|
||||
delegate->setCompletionMode(d->completionMode);
|
||||
delegate->setKeyBindingMap(d->keyBindingMap);
|
||||
}
|
||||
}
|
||||
|
||||
KCompletionBase *KCompletionBase::delegate() const
|
||||
{
|
||||
Q_D(const KCompletionBase);
|
||||
return d->delegate;
|
||||
}
|
||||
|
||||
KCompletion *KCompletionBase::completionObject(bool handleSignals)
|
||||
{
|
||||
Q_D(KCompletionBase);
|
||||
if (d->delegate) {
|
||||
return d->delegate->completionObject(handleSignals);
|
||||
}
|
||||
|
||||
if (!d->completionObject) {
|
||||
setCompletionObject(new KCompletion(), handleSignals);
|
||||
d->autoDeleteCompletionObject = true;
|
||||
}
|
||||
return d->completionObject;
|
||||
}
|
||||
|
||||
void KCompletionBase::setCompletionObject(KCompletion *completionObject, bool handleSignals)
|
||||
{
|
||||
Q_D(KCompletionBase);
|
||||
if (d->delegate) {
|
||||
d->delegate->setCompletionObject(completionObject, handleSignals);
|
||||
return;
|
||||
}
|
||||
|
||||
if (d->autoDeleteCompletionObject && completionObject != d->completionObject) {
|
||||
delete d->completionObject;
|
||||
}
|
||||
|
||||
d->completionObject = completionObject;
|
||||
|
||||
setAutoDeleteCompletionObject(false);
|
||||
setHandleSignals(handleSignals);
|
||||
|
||||
// We emit rotation and completion signals
|
||||
// if completion object is not NULL.
|
||||
setEmitSignals(!d->completionObject.isNull());
|
||||
}
|
||||
|
||||
// BC: Inline this function and possibly rename it to setHandleEvents??? (DA)
|
||||
void KCompletionBase::setHandleSignals(bool handle)
|
||||
{
|
||||
Q_D(KCompletionBase);
|
||||
if (d->delegate) {
|
||||
d->delegate->setHandleSignals(handle);
|
||||
} else {
|
||||
d->handleSignals = handle;
|
||||
}
|
||||
}
|
||||
|
||||
bool KCompletionBase::isCompletionObjectAutoDeleted() const
|
||||
{
|
||||
Q_D(const KCompletionBase);
|
||||
return d->delegate ? d->delegate->isCompletionObjectAutoDeleted() : d->autoDeleteCompletionObject;
|
||||
}
|
||||
|
||||
void KCompletionBase::setAutoDeleteCompletionObject(bool autoDelete)
|
||||
{
|
||||
Q_D(KCompletionBase);
|
||||
if (d->delegate) {
|
||||
d->delegate->setAutoDeleteCompletionObject(autoDelete);
|
||||
} else {
|
||||
d->autoDeleteCompletionObject = autoDelete;
|
||||
}
|
||||
}
|
||||
|
||||
void KCompletionBase::setEnableSignals(bool enable)
|
||||
{
|
||||
Q_D(KCompletionBase);
|
||||
if (d->delegate) {
|
||||
d->delegate->setEnableSignals(enable);
|
||||
} else {
|
||||
d->emitSignals = enable;
|
||||
}
|
||||
}
|
||||
|
||||
bool KCompletionBase::handleSignals() const
|
||||
{
|
||||
Q_D(const KCompletionBase);
|
||||
return d->delegate ? d->delegate->handleSignals() : d->handleSignals;
|
||||
}
|
||||
|
||||
bool KCompletionBase::emitSignals() const
|
||||
{
|
||||
Q_D(const KCompletionBase);
|
||||
return d->delegate ? d->delegate->emitSignals() : d->emitSignals;
|
||||
}
|
||||
|
||||
void KCompletionBase::setEmitSignals(bool emitRotationSignals)
|
||||
{
|
||||
Q_D(KCompletionBase);
|
||||
if (d->delegate) {
|
||||
d->delegate->setEmitSignals(emitRotationSignals);
|
||||
} else {
|
||||
d->emitSignals = emitRotationSignals;
|
||||
}
|
||||
}
|
||||
|
||||
void KCompletionBase::setCompletionMode(KCompletion::CompletionMode mode)
|
||||
{
|
||||
Q_D(KCompletionBase);
|
||||
if (d->delegate) {
|
||||
d->delegate->setCompletionMode(mode);
|
||||
return;
|
||||
}
|
||||
|
||||
d->completionMode = mode;
|
||||
// Always sync up KCompletion mode with ours as long as we
|
||||
// are performing completions.
|
||||
if (d->completionObject && d->completionMode != KCompletion::CompletionNone) {
|
||||
d->completionObject->setCompletionMode(d->completionMode);
|
||||
}
|
||||
}
|
||||
|
||||
KCompletion::CompletionMode KCompletionBase::completionMode() const
|
||||
{
|
||||
Q_D(const KCompletionBase);
|
||||
return d->delegate ? d->delegate->completionMode() : d->completionMode;
|
||||
}
|
||||
|
||||
bool KCompletionBase::setKeyBinding(KeyBindingType item, const QList<QKeySequence> &cut)
|
||||
{
|
||||
Q_D(KCompletionBase);
|
||||
if (d->delegate) {
|
||||
return d->delegate->setKeyBinding(item, cut);
|
||||
}
|
||||
|
||||
if (!cut.isEmpty()) {
|
||||
for (KeyBindingMap::Iterator it = d->keyBindingMap.begin(); it != d->keyBindingMap.end(); ++it) {
|
||||
if (it.value() == cut) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
d->keyBindingMap.insert(item, cut);
|
||||
return true;
|
||||
}
|
||||
|
||||
QList<QKeySequence> KCompletionBase::keyBinding(KeyBindingType item) const
|
||||
{
|
||||
Q_D(const KCompletionBase);
|
||||
return d->delegate ? d->delegate->keyBinding(item) : d->keyBindingMap[item];
|
||||
}
|
||||
|
||||
void KCompletionBase::useGlobalKeyBindings()
|
||||
{
|
||||
Q_D(KCompletionBase);
|
||||
if (d->delegate) {
|
||||
d->delegate->useGlobalKeyBindings();
|
||||
return;
|
||||
}
|
||||
|
||||
d->keyBindingMap.clear();
|
||||
d->keyBindingMap.insert(TextCompletion, QList<QKeySequence>());
|
||||
d->keyBindingMap.insert(PrevCompletionMatch, QList<QKeySequence>());
|
||||
d->keyBindingMap.insert(NextCompletionMatch, QList<QKeySequence>());
|
||||
d->keyBindingMap.insert(SubstringCompletion, QList<QKeySequence>());
|
||||
}
|
||||
|
||||
KCompletion *KCompletionBase::compObj() const
|
||||
{
|
||||
Q_D(const KCompletionBase);
|
||||
return d->delegate ? d->delegate->compObj() : static_cast<KCompletion *>(d->completionObject);
|
||||
}
|
||||
|
||||
KCompletionBase::KeyBindingMap KCompletionBase::keyBindingMap() const
|
||||
{
|
||||
Q_D(const KCompletionBase);
|
||||
return d->delegate ? d->delegate->keyBindingMap() : d->keyBindingMap;
|
||||
}
|
||||
|
||||
void KCompletionBase::setKeyBindingMap(KCompletionBase::KeyBindingMap keyBindingMap)
|
||||
{
|
||||
Q_D(KCompletionBase);
|
||||
if (d->delegate) {
|
||||
d->delegate->setKeyBindingMap(keyBindingMap);
|
||||
return;
|
||||
}
|
||||
|
||||
d->keyBindingMap = keyBindingMap;
|
||||
}
|
||||
|
||||
void KCompletionBase::virtual_hook(int, void *)
|
||||
{
|
||||
/*BASE::virtual_hook( id, data );*/
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999, 2000 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCOMPLETIONBASE_H
|
||||
#define KCOMPLETIONBASE_H
|
||||
|
||||
#include <kcompletion.h>
|
||||
#include <kcompletion_export.h>
|
||||
|
||||
#include <QMap>
|
||||
#include <memory>
|
||||
|
||||
class KCompletionBasePrivate;
|
||||
|
||||
/**
|
||||
* @class KCompletionBase kcompletionbase.h KCompletionBase
|
||||
*
|
||||
* An abstract base class for adding a completion feature
|
||||
* into widgets.
|
||||
*
|
||||
* This is a convenience class that provides the basic functions
|
||||
* needed to add text completion support into widgets. All that
|
||||
* is required is an implementation for the pure virtual function
|
||||
* setCompletedText(). Refer to KLineEdit or KComboBox
|
||||
* to see how easily such support can be added using this as a base
|
||||
* class.
|
||||
*
|
||||
* @short An abstract class for adding text completion support to widgets.
|
||||
* @author Dawit Alemayehu <adawit@kde.org>
|
||||
*/
|
||||
|
||||
class KCOMPLETION_EXPORT KCompletionBase
|
||||
{
|
||||
public:
|
||||
Q_DECLARE_PRIVATE(KCompletionBase)
|
||||
/**
|
||||
* Constants that represent the items whose shortcut
|
||||
* key binding is programmable. The default key bindings
|
||||
* for these items are defined in KStandardShortcut.
|
||||
*/
|
||||
enum KeyBindingType {
|
||||
/**
|
||||
* Text completion (by default Ctrl-E).
|
||||
*/
|
||||
TextCompletion,
|
||||
/**
|
||||
* Switch to previous completion (by default Ctrl-Up).
|
||||
*/
|
||||
PrevCompletionMatch,
|
||||
/**
|
||||
* Switch to next completion (by default Ctrl-Down).
|
||||
*/
|
||||
NextCompletionMatch,
|
||||
/**
|
||||
* Substring completion (by default Ctrl-T).
|
||||
*/
|
||||
SubstringCompletion,
|
||||
};
|
||||
|
||||
// Map for the key binding types mentioned above.
|
||||
typedef QMap<KeyBindingType, QList<QKeySequence>> KeyBindingMap;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
KCompletionBase();
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~KCompletionBase();
|
||||
|
||||
/**
|
||||
* Returns a pointer to the current completion object.
|
||||
*
|
||||
* If the completion object does not exist, it is automatically created and
|
||||
* by default handles all the completion signals internally unless @c handleSignals
|
||||
* is set to false. It is also automatically destroyed when the destructor
|
||||
* is called. You can change this default behavior using the
|
||||
* @ref setAutoDeleteCompletionObject and @ref setHandleSignals member
|
||||
* functions.
|
||||
*
|
||||
* See also @ref compObj.
|
||||
*
|
||||
* @param handleSignals if true, handles completion signals internally.
|
||||
* @return a pointer to the completion object.
|
||||
*/
|
||||
KCompletion *completionObject(bool handleSignals = true);
|
||||
|
||||
/**
|
||||
* Sets up the completion object to be used.
|
||||
*
|
||||
* This method assigns the completion object and sets it up to automatically
|
||||
* handle the completion and rotation signals internally. You should use
|
||||
* this function if you want to share one completion object among your
|
||||
* widgets or need to use a customized completion object.
|
||||
*
|
||||
* The object assigned through this method is not deleted when this object's
|
||||
* destructor is invoked unless you explicitly call @ref setAutoDeleteCompletionObject
|
||||
* after calling this method. Be sure to set the bool argument to false, if
|
||||
* you want to handle the completion signals yourself.
|
||||
*
|
||||
* @param completionObject a KCompletion or a derived child object.
|
||||
* @param handleCompletionSignals if true, handles completion signals internally.
|
||||
*/
|
||||
virtual void setCompletionObject(KCompletion *completionObject, bool handleSignals = true);
|
||||
|
||||
/**
|
||||
* Enables this object to handle completion and rotation
|
||||
* events internally.
|
||||
*
|
||||
* This function simply assigns a boolean value that
|
||||
* indicates whether it should handle rotation and
|
||||
* completion events or not. Note that this does not
|
||||
* stop the object from emitting signals when these
|
||||
* events occur.
|
||||
*
|
||||
* @param handle if true, it handles completion and rotation internally.
|
||||
*/
|
||||
virtual void setHandleSignals(bool handle);
|
||||
|
||||
/**
|
||||
* Returns true if the completion object is deleted
|
||||
* upon this widget's destruction.
|
||||
*
|
||||
* See setCompletionObject() and enableCompletion()
|
||||
* for details.
|
||||
*
|
||||
* @return true if the completion object will be deleted
|
||||
* automatically
|
||||
*/
|
||||
bool isCompletionObjectAutoDeleted() const;
|
||||
|
||||
/**
|
||||
* Sets the completion object when this widget's destructor
|
||||
* is called.
|
||||
*
|
||||
* If the argument is set to true, the completion object
|
||||
* is deleted when this widget's destructor is called.
|
||||
*
|
||||
* @param autoDelete if true, delete completion object on destruction.
|
||||
*/
|
||||
void setAutoDeleteCompletionObject(bool autoDelete);
|
||||
|
||||
/**
|
||||
* Sets the widget's ability to emit text completion and
|
||||
* rotation signals.
|
||||
*
|
||||
* Invoking this function with @p enable set to @c false will
|
||||
* cause the completion and rotation signals not to be emitted.
|
||||
* However, unlike setting the completion object to @c nullptr
|
||||
* using setCompletionObject, disabling the emission of
|
||||
* the signals through this method does not affect the current
|
||||
* completion object.
|
||||
*
|
||||
* There is no need to invoke this function by default. When a
|
||||
* completion object is created through completionObject or
|
||||
* setCompletionObject, these signals are set to emit
|
||||
* automatically. Also note that disabling this signals will not
|
||||
* necessarily interfere with the objects' ability to handle these
|
||||
* events internally. See setHandleSignals.
|
||||
*
|
||||
* @param enable if false, disables the emission of completion and rotation signals.
|
||||
*/
|
||||
void setEnableSignals(bool enable);
|
||||
|
||||
/**
|
||||
* Returns true if the object handles the signals.
|
||||
*
|
||||
* @return true if this signals are handled internally.
|
||||
*/
|
||||
bool handleSignals() const;
|
||||
|
||||
/**
|
||||
* Returns true if the object emits the signals.
|
||||
*
|
||||
* @return true if signals are emitted
|
||||
*/
|
||||
bool emitSignals() const;
|
||||
|
||||
/**
|
||||
* Sets whether the object emits rotation signals.
|
||||
*
|
||||
* @param emitRotationSignals if false, disables the emission of rotation signals.
|
||||
*/
|
||||
void setEmitSignals(bool emitRotationSignals);
|
||||
|
||||
/**
|
||||
* Sets the type of completion to be used.
|
||||
*
|
||||
* @param mode Completion type
|
||||
* @see CompletionMode
|
||||
*/
|
||||
virtual void setCompletionMode(KCompletion::CompletionMode mode);
|
||||
|
||||
/**
|
||||
* Returns the current completion mode.
|
||||
*
|
||||
* @return the completion mode.
|
||||
*/
|
||||
KCompletion::CompletionMode completionMode() const;
|
||||
|
||||
/**
|
||||
* Sets the key binding to be used for manual text
|
||||
* completion, text rotation in a history list as
|
||||
* well as a completion list.
|
||||
*
|
||||
*
|
||||
* When the keys set by this function are pressed, a
|
||||
* signal defined by the inheriting widget will be activated.
|
||||
* If the default value or 0 is specified by the second
|
||||
* parameter, then the key binding as defined in the global
|
||||
* setting should be used. This method returns false
|
||||
* when @p key is negative or the supplied key binding conflicts
|
||||
* with another one set for another feature.
|
||||
*
|
||||
* NOTE: To use a modifier key (Shift, Ctrl, Alt) as part of
|
||||
* the key binding simply @p sum up the values of the
|
||||
* modifier and the actual key. For example, to use CTRL+E, supply
|
||||
* @c "Qt::CtrlButton | Qt::Key_E" as the second argument to this
|
||||
* function.
|
||||
*
|
||||
* @param item the feature whose key binding needs to be set:
|
||||
* @li TextCompletion the manual completion key binding.
|
||||
* @li PrevCompletionMatch the previous match key for multiple completion.
|
||||
* @li NextCompletionMatch the next match key for for multiple completion.
|
||||
* @li SubstringCompletion the key for substring completion
|
||||
* @param key key binding used to rotate down in a list.
|
||||
* @return true if key binding is successfully set.
|
||||
* @see keyBinding
|
||||
*/
|
||||
bool setKeyBinding(KeyBindingType item, const QList<QKeySequence> &key);
|
||||
|
||||
/**
|
||||
* Returns the key binding used for the specified item.
|
||||
*
|
||||
* This method returns the key binding used to activate
|
||||
* the feature given by @p item. If the binding
|
||||
* contains modifier key(s), the sum of the modifier key
|
||||
* and the actual key code is returned.
|
||||
*
|
||||
* @param item the item to check
|
||||
* @return the key binding used for the feature given by @p item.
|
||||
* @see setKeyBinding
|
||||
* @since 5.0
|
||||
*/
|
||||
QList<QKeySequence> keyBinding(KeyBindingType item) const;
|
||||
|
||||
/**
|
||||
* Sets this object to use global values for key bindings.
|
||||
*
|
||||
* This method changes the values of the key bindings for
|
||||
* rotation and completion features to the default values
|
||||
* provided in KGlobalSettings.
|
||||
*
|
||||
* NOTE: By default, inheriting widgets should use the
|
||||
* global key bindings so that there is no need to
|
||||
* call this method.
|
||||
*/
|
||||
void useGlobalKeyBindings();
|
||||
|
||||
/**
|
||||
* A pure virtual function that must be implemented by
|
||||
* all inheriting classes.
|
||||
*
|
||||
* This function is intended to allow external completion
|
||||
* implementations to set completed text appropriately. It
|
||||
* is mostly relevant when the completion mode is set to
|
||||
* CompletionAuto and CompletionManual modes. See
|
||||
* KCompletionBase::setCompletedText.
|
||||
* Does nothing in CompletionPopup mode, as all available
|
||||
* matches will be shown in the popup.
|
||||
*
|
||||
* @param text the completed text to be set in the widget.
|
||||
*/
|
||||
virtual void setCompletedText(const QString &text) = 0;
|
||||
|
||||
/**
|
||||
* A pure virtual function that must be implemented by
|
||||
* all inheriting classes.
|
||||
* @param items the list of completed items
|
||||
* @param autoSuggest if @c true, the first element of @p items
|
||||
* is automatically completed (i.e. preselected).
|
||||
*/
|
||||
virtual void setCompletedItems(const QStringList &items, bool autoSuggest = true) = 0;
|
||||
|
||||
/**
|
||||
* Returns a pointer to the completion object.
|
||||
*
|
||||
* This method is only different from completionObject()
|
||||
* in that it does not create a new KCompletion object even if
|
||||
* the internal pointer is @c NULL. Use this method to get the
|
||||
* pointer to a completion object when inheriting so that you
|
||||
* will not inadvertently create it.
|
||||
*
|
||||
* @return the completion object or @c NULL if one does not exist.
|
||||
*/
|
||||
KCompletion *compObj() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Returns a key binding map.
|
||||
*
|
||||
* This method is the same as getKeyBinding(), except that it
|
||||
* returns the whole keymap containing the key bindings.
|
||||
*
|
||||
* @return the key binding used for the feature given by @p item.
|
||||
* @since 5.0
|
||||
*/
|
||||
KeyBindingMap keyBindingMap() const;
|
||||
|
||||
/**
|
||||
* Sets the keymap.
|
||||
*
|
||||
* @param keyBindingMap
|
||||
*/
|
||||
void setKeyBindingMap(KeyBindingMap keyBindingMap);
|
||||
|
||||
/**
|
||||
* Sets or removes the delegation object. If a delegation object is
|
||||
* set, all function calls will be forwarded to the delegation object.
|
||||
* @param delegate the delegation object, or @c nullptr to remove it
|
||||
*/
|
||||
void setDelegate(KCompletionBase *delegate);
|
||||
|
||||
/**
|
||||
* Returns the delegation object.
|
||||
* @return the delegation object, or @c nullptr if there is none
|
||||
* @see setDelegate()
|
||||
*/
|
||||
KCompletionBase *delegate() const;
|
||||
|
||||
/** Virtual hook, used to add new "virtual" functions while maintaining
|
||||
binary compatibility. Unused in this class.
|
||||
*/
|
||||
virtual void virtual_hook(int id, void *data);
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(KCompletionBase)
|
||||
std::unique_ptr<KCompletionBasePrivate> const d_ptr;
|
||||
};
|
||||
|
||||
#endif // KCOMPLETIONBASE_H
|
||||
@@ -0,0 +1,493 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2000, 2001, 2002 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
SPDX-FileCopyrightText: 2000 Stefan Schimanski <1Stein@gmx.de>
|
||||
SPDX-FileCopyrightText: 2000, 2001, 2002, 2003, 2004 Dawit Alemayehu <adawit@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kcompletionbox.h"
|
||||
#include "klineedit.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QKeyEvent>
|
||||
#include <QScreen>
|
||||
#include <QScrollBar>
|
||||
|
||||
class KCompletionBoxPrivate
|
||||
{
|
||||
public:
|
||||
QWidget *m_parent = nullptr; // necessary to set the focus back
|
||||
QString cancelText;
|
||||
bool tabHandling = true;
|
||||
bool upwardBox = false;
|
||||
bool emitSelected = true;
|
||||
};
|
||||
|
||||
KCompletionBox::KCompletionBox(QWidget *parent)
|
||||
: QListWidget(parent)
|
||||
, d(new KCompletionBoxPrivate)
|
||||
{
|
||||
d->m_parent = parent;
|
||||
|
||||
// we can't link to QXcbWindowFunctions::Combo
|
||||
// also, q->setAttribute(Qt::WA_X11NetWmWindowTypeCombo); is broken in Qt xcb
|
||||
setProperty("_q_xcb_wm_window_type", 0x001000);
|
||||
setAttribute(Qt::WA_ShowWithoutActivating);
|
||||
|
||||
// on wayland, we need an xdg-popup but we don't want it to grab
|
||||
// calls setVisible, so must be done after initializations
|
||||
if (qGuiApp->platformName() == QLatin1String("wayland")) {
|
||||
setWindowFlags(Qt::ToolTip | Qt::FramelessWindowHint | Qt::BypassWindowManagerHint);
|
||||
} else {
|
||||
setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::BypassWindowManagerHint);
|
||||
}
|
||||
setUniformItemSizes(true);
|
||||
|
||||
setLineWidth(1);
|
||||
setFrameStyle(QFrame::Box | QFrame::Plain);
|
||||
|
||||
setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
|
||||
connect(this, &QListWidget::itemDoubleClicked, this, &KCompletionBox::slotActivated);
|
||||
connect(this, &KCompletionBox::itemClicked, this, [this](QListWidgetItem *item) {
|
||||
if (item) {
|
||||
hide();
|
||||
Q_EMIT currentTextChanged(item->text());
|
||||
Q_EMIT textActivated(item->text());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
KCompletionBox::~KCompletionBox()
|
||||
{
|
||||
d->m_parent = nullptr;
|
||||
}
|
||||
|
||||
QStringList KCompletionBox::items() const
|
||||
{
|
||||
QStringList list;
|
||||
list.reserve(count());
|
||||
for (int i = 0; i < count(); i++) {
|
||||
const QListWidgetItem *currItem = item(i);
|
||||
|
||||
list.append(currItem->text());
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void KCompletionBox::slotActivated(QListWidgetItem *item)
|
||||
{
|
||||
if (item) {
|
||||
hide();
|
||||
Q_EMIT textActivated(item->text());
|
||||
}
|
||||
}
|
||||
|
||||
bool KCompletionBox::eventFilter(QObject *o, QEvent *e)
|
||||
{
|
||||
int type = e->type();
|
||||
QWidget *wid = qobject_cast<QWidget *>(o);
|
||||
|
||||
if (o == this) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (wid && wid == d->m_parent //
|
||||
&& (type == QEvent::Move || type == QEvent::Resize)) {
|
||||
resizeAndReposition();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (wid && (wid->windowFlags() & Qt::Window) //
|
||||
&& type == QEvent::Move && wid == d->m_parent->window()) {
|
||||
hide();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type == QEvent::MouseButtonPress && (wid && !isAncestorOf(wid))) {
|
||||
if (!d->emitSelected && currentItem() && !qobject_cast<QScrollBar *>(o)) {
|
||||
Q_ASSERT(currentItem());
|
||||
Q_EMIT currentTextChanged(currentItem()->text());
|
||||
}
|
||||
hide();
|
||||
e->accept();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (wid && wid->isAncestorOf(d->m_parent) && isVisible()) {
|
||||
if (type == QEvent::KeyPress) {
|
||||
QKeyEvent *ev = static_cast<QKeyEvent *>(e);
|
||||
switch (ev->key()) {
|
||||
case Qt::Key_Backtab:
|
||||
if (d->tabHandling && (ev->modifiers() == Qt::NoButton || (ev->modifiers() & Qt::ShiftModifier))) {
|
||||
up();
|
||||
ev->accept();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case Qt::Key_Tab:
|
||||
if (d->tabHandling && (ev->modifiers() == Qt::NoButton)) {
|
||||
down();
|
||||
// #65877: Key_Tab should complete using the first
|
||||
// (or selected) item, and then offer completions again
|
||||
if (count() == 1) {
|
||||
KLineEdit *parent = qobject_cast<KLineEdit *>(d->m_parent);
|
||||
if (parent) {
|
||||
parent->doCompletion(currentItem()->text());
|
||||
} else {
|
||||
hide();
|
||||
}
|
||||
}
|
||||
ev->accept();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case Qt::Key_Down:
|
||||
down();
|
||||
ev->accept();
|
||||
return true;
|
||||
case Qt::Key_Up:
|
||||
// If there is no selected item and we've popped up above
|
||||
// our parent, select the first item when they press up.
|
||||
if (!selectedItems().isEmpty() //
|
||||
|| mapToGlobal(QPoint(0, 0)).y() > d->m_parent->mapToGlobal(QPoint(0, 0)).y()) {
|
||||
up();
|
||||
} else {
|
||||
down();
|
||||
}
|
||||
ev->accept();
|
||||
return true;
|
||||
case Qt::Key_PageUp:
|
||||
pageUp();
|
||||
ev->accept();
|
||||
return true;
|
||||
case Qt::Key_PageDown:
|
||||
pageDown();
|
||||
ev->accept();
|
||||
return true;
|
||||
case Qt::Key_Escape:
|
||||
if (!d->cancelText.isNull()) {
|
||||
Q_EMIT userCancelled(d->cancelText);
|
||||
}
|
||||
if (isVisible()) {
|
||||
hide();
|
||||
}
|
||||
ev->accept();
|
||||
return true;
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
if (ev->modifiers() & Qt::ShiftModifier) {
|
||||
hide();
|
||||
ev->accept(); // Consume the Enter event
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case Qt::Key_End:
|
||||
if (ev->modifiers() & Qt::ControlModifier) {
|
||||
end();
|
||||
ev->accept();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case Qt::Key_Home:
|
||||
if (ev->modifiers() & Qt::ControlModifier) {
|
||||
home();
|
||||
ev->accept();
|
||||
return true;
|
||||
}
|
||||
Q_FALLTHROUGH();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (type == QEvent::ShortcutOverride) {
|
||||
// Override any accelerators that match
|
||||
// the key sequences we use here...
|
||||
QKeyEvent *ev = static_cast<QKeyEvent *>(e);
|
||||
switch (ev->key()) {
|
||||
case Qt::Key_Down:
|
||||
case Qt::Key_Up:
|
||||
case Qt::Key_PageUp:
|
||||
case Qt::Key_PageDown:
|
||||
case Qt::Key_Escape:
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
ev->accept();
|
||||
return true;
|
||||
case Qt::Key_Tab:
|
||||
case Qt::Key_Backtab:
|
||||
if (ev->modifiers() == Qt::NoButton || (ev->modifiers() & Qt::ShiftModifier)) {
|
||||
ev->accept();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case Qt::Key_Home:
|
||||
case Qt::Key_End:
|
||||
if (ev->modifiers() & Qt::ControlModifier) {
|
||||
ev->accept();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (type == QEvent::FocusOut) {
|
||||
QFocusEvent *event = static_cast<QFocusEvent *>(e);
|
||||
if (event->reason() != Qt::PopupFocusReason
|
||||
#ifdef Q_OS_WIN
|
||||
&& (event->reason() != Qt::ActiveWindowFocusReason || QApplication::activeWindow() != this)
|
||||
#endif
|
||||
) {
|
||||
hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QListWidget::eventFilter(o, e);
|
||||
}
|
||||
|
||||
void KCompletionBox::popup()
|
||||
{
|
||||
if (count() == 0) {
|
||||
hide();
|
||||
} else {
|
||||
bool block = signalsBlocked();
|
||||
blockSignals(true);
|
||||
setCurrentRow(-1);
|
||||
blockSignals(block);
|
||||
clearSelection();
|
||||
if (!isVisible()) {
|
||||
show();
|
||||
} else if (size().height() != sizeHint().height()) {
|
||||
resizeAndReposition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KCompletionBox::resizeAndReposition()
|
||||
{
|
||||
int currentGeom = height();
|
||||
QPoint currentPos = pos();
|
||||
QRect geom = calculateGeometry();
|
||||
resize(geom.size());
|
||||
|
||||
int x = currentPos.x();
|
||||
int y = currentPos.y();
|
||||
if (d->m_parent) {
|
||||
if (!isVisible()) {
|
||||
const QPoint orig = globalPositionHint();
|
||||
QScreen *screen = QGuiApplication::screenAt(orig);
|
||||
if (screen) {
|
||||
const QRect screenSize = screen->geometry();
|
||||
|
||||
x = orig.x() + geom.x();
|
||||
y = orig.y() + geom.y();
|
||||
|
||||
if (x + width() > screenSize.right()) {
|
||||
x = screenSize.right() - width();
|
||||
}
|
||||
if (y + height() > screenSize.bottom()) {
|
||||
y = y - height() - d->m_parent->height();
|
||||
d->upwardBox = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Are we above our parent? If so we must keep bottom edge anchored.
|
||||
if (d->upwardBox) {
|
||||
y += (currentGeom - height());
|
||||
}
|
||||
}
|
||||
move(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
QPoint KCompletionBox::globalPositionHint() const
|
||||
{
|
||||
if (!d->m_parent) {
|
||||
return QPoint();
|
||||
}
|
||||
return d->m_parent->mapToGlobal(QPoint(0, d->m_parent->height()));
|
||||
}
|
||||
|
||||
void KCompletionBox::setVisible(bool visible)
|
||||
{
|
||||
if (visible) {
|
||||
d->upwardBox = false;
|
||||
if (d->m_parent) {
|
||||
resizeAndReposition();
|
||||
qApp->installEventFilter(this);
|
||||
}
|
||||
|
||||
// FIXME: Is this comment still valid or can it be deleted? Is a patch already sent to Qt?
|
||||
// Following lines are a workaround for a bug (not sure whose this is):
|
||||
// If this KCompletionBox' parent is in a layout, that layout will detect the
|
||||
// insertion of a new child (posting a ChildInserted event). Then it will trigger relayout
|
||||
// (posting a LayoutHint event).
|
||||
//
|
||||
// QWidget::show() then sends also posted ChildInserted events for the parent,
|
||||
// and later all LayoutHint events, which cause layout updating.
|
||||
// The problem is that KCompletionBox::eventFilter() detects the resizing
|
||||
// of the parent, calls hide() and this hide() happens in the middle
|
||||
// of show(), causing inconsistent state. I'll try to submit a Qt patch too.
|
||||
qApp->sendPostedEvents();
|
||||
} else {
|
||||
if (d->m_parent) {
|
||||
qApp->removeEventFilter(this);
|
||||
}
|
||||
d->cancelText.clear();
|
||||
}
|
||||
|
||||
QListWidget::setVisible(visible);
|
||||
}
|
||||
|
||||
QRect KCompletionBox::calculateGeometry() const
|
||||
{
|
||||
QRect visualRect;
|
||||
if (count() == 0 || !(visualRect = visualItemRect(item(0))).isValid()) {
|
||||
return QRect();
|
||||
}
|
||||
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int ih = visualRect.height();
|
||||
int h = qMin(15 * ih, count() * ih) + 2 * frameWidth();
|
||||
|
||||
int w = (d->m_parent) ? d->m_parent->width() : QListWidget::minimumSizeHint().width();
|
||||
w = qMax(QListWidget::minimumSizeHint().width(), w);
|
||||
return QRect(x, y, w, h);
|
||||
}
|
||||
|
||||
QSize KCompletionBox::sizeHint() const
|
||||
{
|
||||
return calculateGeometry().size();
|
||||
}
|
||||
|
||||
void KCompletionBox::down()
|
||||
{
|
||||
const int row = currentRow();
|
||||
const int lastRow = count() - 1;
|
||||
if (row < lastRow) {
|
||||
setCurrentRow(row + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (lastRow > -1) {
|
||||
setCurrentRow(0);
|
||||
}
|
||||
}
|
||||
|
||||
void KCompletionBox::up()
|
||||
{
|
||||
const int row = currentRow();
|
||||
if (row > 0) {
|
||||
setCurrentRow(row - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
const int lastRow = count() - 1;
|
||||
if (lastRow > 0) {
|
||||
setCurrentRow(lastRow);
|
||||
}
|
||||
}
|
||||
|
||||
void KCompletionBox::pageDown()
|
||||
{
|
||||
selectionModel()->setCurrentIndex(moveCursor(QAbstractItemView::MovePageDown, Qt::NoModifier), QItemSelectionModel::SelectCurrent);
|
||||
}
|
||||
|
||||
void KCompletionBox::pageUp()
|
||||
{
|
||||
selectionModel()->setCurrentIndex(moveCursor(QAbstractItemView::MovePageUp, Qt::NoModifier), QItemSelectionModel::SelectCurrent);
|
||||
}
|
||||
|
||||
void KCompletionBox::home()
|
||||
{
|
||||
setCurrentRow(0);
|
||||
}
|
||||
|
||||
void KCompletionBox::end()
|
||||
{
|
||||
setCurrentRow(count() - 1);
|
||||
}
|
||||
|
||||
void KCompletionBox::setTabHandling(bool enable)
|
||||
{
|
||||
d->tabHandling = enable;
|
||||
}
|
||||
|
||||
bool KCompletionBox::isTabHandling() const
|
||||
{
|
||||
return d->tabHandling;
|
||||
}
|
||||
|
||||
void KCompletionBox::setCancelledText(const QString &text)
|
||||
{
|
||||
d->cancelText = text;
|
||||
}
|
||||
|
||||
QString KCompletionBox::cancelledText() const
|
||||
{
|
||||
return d->cancelText;
|
||||
}
|
||||
|
||||
void KCompletionBox::insertItems(const QStringList &items, int index)
|
||||
{
|
||||
bool block = signalsBlocked();
|
||||
blockSignals(true);
|
||||
QListWidget::insertItems(index, items);
|
||||
blockSignals(block);
|
||||
setCurrentRow(-1);
|
||||
}
|
||||
|
||||
void KCompletionBox::setItems(const QStringList &items)
|
||||
{
|
||||
bool block = signalsBlocked();
|
||||
blockSignals(true);
|
||||
|
||||
int rowIndex = 0;
|
||||
|
||||
if (!count()) {
|
||||
addItems(items);
|
||||
} else {
|
||||
for (const auto &text : items) {
|
||||
if (rowIndex < count()) {
|
||||
auto item = this->item(rowIndex);
|
||||
if (item->text() != text) {
|
||||
item->setText(text);
|
||||
}
|
||||
} else {
|
||||
addItem(text);
|
||||
}
|
||||
rowIndex++;
|
||||
}
|
||||
|
||||
// remove unused items with an index >= rowIndex
|
||||
for (; rowIndex < count();) {
|
||||
QListWidgetItem *item = takeItem(rowIndex);
|
||||
Q_ASSERT(item);
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
if (isVisible() && size().height() != sizeHint().height()) {
|
||||
resizeAndReposition();
|
||||
}
|
||||
|
||||
blockSignals(block);
|
||||
}
|
||||
|
||||
void KCompletionBox::setActivateOnSelect(bool doEmit)
|
||||
{
|
||||
d->emitSelected = doEmit;
|
||||
}
|
||||
|
||||
bool KCompletionBox::activateOnSelect() const
|
||||
{
|
||||
return d->emitSelected;
|
||||
}
|
||||
|
||||
#include "moc_kcompletionbox.cpp"
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2000 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
SPDX-FileCopyrightText: 2000 Stefan Schimanski <1Stein@gmx.de>
|
||||
SPDX-FileCopyrightText: 2000, 2001, 2002, 2003, 2004 Dawit Alemayehu <adawit@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCOMPLETIONBOX_H
|
||||
#define KCOMPLETIONBOX_H
|
||||
|
||||
#include "kcompletion_export.h"
|
||||
#include <QListWidget>
|
||||
#include <memory>
|
||||
|
||||
class KCompletionBoxPrivate;
|
||||
class QEvent;
|
||||
|
||||
/**
|
||||
* @class KCompletionBox kcompletionbox.h KCompletionBox
|
||||
*
|
||||
* @short A helper widget for "completion-widgets" (KLineEdit, KComboBox))
|
||||
*
|
||||
* A little utility class for "completion-widgets", like KLineEdit or
|
||||
* KComboBox. KCompletionBox is a listbox, displayed as a rectangle without
|
||||
* any window decoration, usually directly under the lineedit or combobox.
|
||||
* It is filled with all possible matches for a completion, so the user
|
||||
* can select the one he wants.
|
||||
*
|
||||
* It is used when KCompletion::CompletionMode == CompletionPopup or CompletionPopupAuto.
|
||||
*
|
||||
* @author Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
*/
|
||||
class KCOMPLETION_EXPORT KCompletionBox : public QListWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool isTabHandling READ isTabHandling WRITE setTabHandling)
|
||||
Q_PROPERTY(QString cancelledText READ cancelledText WRITE setCancelledText)
|
||||
Q_PROPERTY(bool activateOnSelect READ activateOnSelect WRITE setActivateOnSelect)
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructs a KCompletionBox.
|
||||
*
|
||||
* The parent widget is used to give the focus back when pressing the
|
||||
* up-button on the very first item.
|
||||
*/
|
||||
explicit KCompletionBox(QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Destroys the box
|
||||
*/
|
||||
~KCompletionBox() override;
|
||||
|
||||
QSize sizeHint() const override;
|
||||
|
||||
/**
|
||||
* @returns true if selecting an item results in the emission of the selected() signal.
|
||||
*/
|
||||
bool activateOnSelect() const;
|
||||
|
||||
/**
|
||||
* Returns a list of all items currently in the box.
|
||||
*/
|
||||
QStringList items() const;
|
||||
|
||||
/**
|
||||
* @returns true if this widget is handling Tab-key events to traverse the
|
||||
* items in the dropdown list, otherwise false.
|
||||
*
|
||||
* Default is true.
|
||||
*
|
||||
* @see setTabHandling
|
||||
*/
|
||||
bool isTabHandling() const;
|
||||
|
||||
/**
|
||||
* @returns the text set via setCancelledText() or QString().
|
||||
*/
|
||||
QString cancelledText() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* Inserts @p items into the box. Does not clear the items before.
|
||||
* @p index determines at which position @p items will be inserted.
|
||||
* (defaults to appending them at the end)
|
||||
*/
|
||||
void insertItems(const QStringList &items, int index = -1);
|
||||
|
||||
/**
|
||||
* Clears the box and inserts @p items.
|
||||
*/
|
||||
void setItems(const QStringList &items);
|
||||
|
||||
/**
|
||||
* Adjusts the size of the box to fit the width of the parent given in the
|
||||
* constructor and pops it up at the most appropriate place, relative to
|
||||
* the parent.
|
||||
*
|
||||
* Depending on the screensize and the position of the parent, this may
|
||||
* be a different place, however the default is to pop it up and the
|
||||
* lower left corner of the parent.
|
||||
*
|
||||
* Make sure to hide() the box when appropriate.
|
||||
*/
|
||||
virtual void popup();
|
||||
|
||||
/**
|
||||
* Makes this widget (when visible) capture Tab-key events to traverse the
|
||||
* items in the dropdown list (Tab goes down, Shift+Tab goes up).
|
||||
*
|
||||
* On by default, but should be turned off when used in combination with KUrlCompletion.
|
||||
* When off, KLineEdit handles Tab itself, making it select the current item from the completion box,
|
||||
* which is particularly useful when using KUrlCompletion.
|
||||
*
|
||||
* @see isTabHandling
|
||||
*/
|
||||
void setTabHandling(bool enable);
|
||||
|
||||
/**
|
||||
* Sets the text to be emitted if the user chooses not to
|
||||
* pick from the available matches.
|
||||
*
|
||||
* If the cancelled text is not set through this function, the
|
||||
* userCancelled signal will not be emitted.
|
||||
*
|
||||
* @see userCancelled( const QString& )
|
||||
* @param text the text to be emitted if the user cancels this box
|
||||
*/
|
||||
void setCancelledText(const QString &text);
|
||||
|
||||
/**
|
||||
* Set whether or not the selected signal should be emitted when an
|
||||
* item is selected. By default the selected() signal is emitted.
|
||||
*
|
||||
* @param doEmit false if the signal should not be emitted.
|
||||
*/
|
||||
void setActivateOnSelect(bool doEmit);
|
||||
|
||||
/**
|
||||
* Moves the selection one line down or select the first item if nothing is selected yet.
|
||||
*/
|
||||
void down();
|
||||
|
||||
/**
|
||||
* Moves the selection one line up or select the first item if nothing is selected yet.
|
||||
*/
|
||||
void up();
|
||||
|
||||
/**
|
||||
* Moves the selection one page down.
|
||||
*/
|
||||
void pageDown();
|
||||
|
||||
/**
|
||||
* Moves the selection one page up.
|
||||
*/
|
||||
void pageUp();
|
||||
|
||||
/**
|
||||
* Moves the selection up to the first item.
|
||||
*/
|
||||
void home();
|
||||
|
||||
/**
|
||||
* Moves the selection down to the last item.
|
||||
*/
|
||||
void end();
|
||||
|
||||
/**
|
||||
* Reimplemented for internal reasons. API is unaffected.
|
||||
* Call it only if you really need it (i.e. the widget was hidden before) to have better performance.
|
||||
*/
|
||||
void setVisible(bool visible) override;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted when an item is selected, @p text is the text of the selected item.
|
||||
*
|
||||
* @since 5.81
|
||||
*/
|
||||
void textActivated(const QString &text);
|
||||
|
||||
/**
|
||||
* Emitted whenever the user chooses to ignore the available
|
||||
* selections and closes this box.
|
||||
*/
|
||||
void userCancelled(const QString &);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* This calculates the size of the dropdown and the relative position of the top
|
||||
* left corner with respect to the parent widget. This matches the geometry and position
|
||||
* normally used by K/QComboBox when used with one.
|
||||
*/
|
||||
QRect calculateGeometry() const;
|
||||
|
||||
/**
|
||||
* This properly resizes and repositions the listbox.
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
void resizeAndReposition();
|
||||
|
||||
/**
|
||||
* Reimplemented from QListWidget to get events from the viewport (to hide
|
||||
* this widget on mouse-click, Escape-presses, etc.
|
||||
*/
|
||||
bool eventFilter(QObject *, QEvent *) override;
|
||||
|
||||
/**
|
||||
* The preferred global coordinate at which the completion box's top left corner
|
||||
* should be positioned.
|
||||
*/
|
||||
virtual QPoint globalPositionHint() const;
|
||||
|
||||
protected Q_SLOTS:
|
||||
/**
|
||||
* Called when an item is activated. Emits KCompletionBox::textActivated(const QString &) with the item text.
|
||||
*
|
||||
* @note For releases <= 5.81, this slot emitted KCompletionBox::activated(const QString &) with the item text.
|
||||
*/
|
||||
virtual void slotActivated(QListWidgetItem *);
|
||||
|
||||
private:
|
||||
std::unique_ptr<KCompletionBoxPrivate> const d;
|
||||
};
|
||||
|
||||
#endif // KCOMPLETIONBOX_H
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999, 2000, 2001 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kcompletionmatches.h"
|
||||
|
||||
#include <kcompletion.h>
|
||||
#include <kcompletion_p.h> // for KCompletionMatchesWrapper
|
||||
|
||||
class KCompletionMatchesPrivate
|
||||
{
|
||||
public:
|
||||
KCompletionMatchesPrivate(bool sort, KCompletionMatches *parent)
|
||||
: sorting(sort)
|
||||
, q_ptr(parent)
|
||||
{
|
||||
}
|
||||
|
||||
bool sorting;
|
||||
KCompletionMatches *const q_ptr;
|
||||
|
||||
Q_DECLARE_PUBLIC(KCompletionMatches)
|
||||
};
|
||||
|
||||
KCompletionMatches::KCompletionMatches(const KCompletionMatches &o)
|
||||
: KSortableList<QString, int>()
|
||||
, d_ptr(new KCompletionMatchesPrivate(o.sorting(), this))
|
||||
{
|
||||
*this = KCompletionMatches::operator=(o);
|
||||
}
|
||||
|
||||
KCompletionMatches &KCompletionMatches::operator=(const KCompletionMatches &o)
|
||||
{
|
||||
Q_D(KCompletionMatches);
|
||||
if (*this == o) {
|
||||
return *this;
|
||||
}
|
||||
KCompletionMatchesList::operator=(o);
|
||||
d->sorting = o.sorting();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
KCompletionMatches::KCompletionMatches(bool sort_P)
|
||||
: d_ptr(new KCompletionMatchesPrivate(sort_P, this))
|
||||
{
|
||||
}
|
||||
|
||||
KCompletionMatches::KCompletionMatches(const KCompletionMatchesWrapper &matches)
|
||||
: d_ptr(new KCompletionMatchesPrivate(matches.sorting(), this))
|
||||
{
|
||||
if (matches.m_sortedListPtr) {
|
||||
KCompletionMatchesList::operator=(*matches.m_sortedListPtr);
|
||||
} else {
|
||||
const QStringList list = matches.list();
|
||||
reserve(list.size());
|
||||
std::transform(list.crbegin(), list.crend(), std::back_inserter(*this), [](const QString &str) {
|
||||
return KSortableItem<QString, int>(1, str);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
KCompletionMatches::~KCompletionMatches()
|
||||
{
|
||||
}
|
||||
|
||||
QStringList KCompletionMatches::list(bool sort_P) const
|
||||
{
|
||||
Q_D(const KCompletionMatches);
|
||||
if (d->sorting && sort_P) {
|
||||
const_cast<KCompletionMatches *>(this)->sort();
|
||||
}
|
||||
QStringList stringList;
|
||||
stringList.reserve(size());
|
||||
// high weight == sorted last -> reverse the sorting here
|
||||
std::transform(crbegin(), crend(), std::back_inserter(stringList), [](const KSortableItem<QString> &item) {
|
||||
return item.value();
|
||||
});
|
||||
return stringList;
|
||||
}
|
||||
|
||||
bool KCompletionMatches::sorting() const
|
||||
{
|
||||
Q_D(const KCompletionMatches);
|
||||
return d->sorting;
|
||||
}
|
||||
|
||||
void KCompletionMatches::removeDuplicates()
|
||||
{
|
||||
for (auto it1 = begin(); it1 != end(); ++it1) {
|
||||
auto it2 = it1;
|
||||
++it2;
|
||||
while (it2 != end()) {
|
||||
if ((*it1).value() == (*it2).value()) {
|
||||
// Use the max weight
|
||||
(*it1).first = std::max((*it1).key(), (*it2).key());
|
||||
it2 = erase(it2);
|
||||
continue;
|
||||
}
|
||||
++it2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999, 2000 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCOMPLETIONMATCHES_H
|
||||
#define KCOMPLETIONMATCHES_H
|
||||
|
||||
#include <kcompletion_export.h>
|
||||
#include <ksortablelist.h>
|
||||
|
||||
#include <QStringList>
|
||||
#include <memory>
|
||||
|
||||
class KCompletionMatchesWrapper;
|
||||
class KCompletionMatchesPrivate;
|
||||
|
||||
typedef KSortableList<QString> KCompletionMatchesList;
|
||||
|
||||
/**
|
||||
* @class KCompletionMatches kcompletionmatches.h KCompletionMatches
|
||||
*
|
||||
* This structure is returned by KCompletion::allWeightedMatches().
|
||||
* It also keeps the weight of the matches, allowing
|
||||
* you to modify some matches or merge them with matches
|
||||
* from another call to allWeightedMatches(), and sort the matches
|
||||
* after that in order to have the matches ordered correctly.
|
||||
*
|
||||
* Example (a simplified example of what Konqueror's completion does):
|
||||
* \code
|
||||
* KCompletionMatches matches = completion->allWeightedMatches(location);
|
||||
* if(!location.startsWith("www."))
|
||||
matches += completion->allWeightedmatches("www." + location");
|
||||
* matches.removeDuplicates();
|
||||
* QStringList list = matches.list();
|
||||
* \endcode
|
||||
*
|
||||
* @short List for keeping matches returned from KCompletion
|
||||
*/
|
||||
class KCOMPLETION_EXPORT KCompletionMatches : public KCompletionMatchesList
|
||||
{
|
||||
public:
|
||||
Q_DECLARE_PRIVATE(KCompletionMatches)
|
||||
/**
|
||||
* Default constructor.
|
||||
* @param sort if false, the matches won't be sorted before the conversion,
|
||||
* use only if you're sure the sorting is not needed
|
||||
*/
|
||||
KCompletionMatches(bool sort);
|
||||
|
||||
/**
|
||||
* copy constructor.
|
||||
*/
|
||||
KCompletionMatches(const KCompletionMatches &);
|
||||
|
||||
/**
|
||||
* assignment operator.
|
||||
*/
|
||||
KCompletionMatches &operator=(const KCompletionMatches &);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
KCompletionMatches(const KCompletionMatchesWrapper &matches);
|
||||
|
||||
/**
|
||||
* default destructor.
|
||||
*/
|
||||
~KCompletionMatches();
|
||||
/**
|
||||
* Removes duplicate matches. Needed only when you merged several matches
|
||||
* results and there's a possibility of duplicates.
|
||||
*/
|
||||
void removeDuplicates();
|
||||
/**
|
||||
* Returns the matches as a QStringList.
|
||||
* @param sort if false, the matches won't be sorted before the conversion,
|
||||
* use only if you're sure the sorting is not needed
|
||||
* @return the list of matches
|
||||
*/
|
||||
QStringList list(bool sort = true) const;
|
||||
/**
|
||||
* If sorting() returns false, the matches aren't sorted by their weight,
|
||||
* even if true is passed to list().
|
||||
* @return true if the matches won't be sorted
|
||||
*/
|
||||
bool sorting() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<KCompletionMatchesPrivate> const d_ptr;
|
||||
};
|
||||
|
||||
#endif // KCOMPLETIONMATCHES_H
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCOMPLETIONMATCHESWRAPPER_P_H
|
||||
#define KCOMPLETIONMATCHESWRAPPER_P_H
|
||||
|
||||
#include "kcompletion.h"
|
||||
#include "kcomptreenode_p.h"
|
||||
|
||||
#include <kcompletionmatches.h>
|
||||
|
||||
class KCOMPLETION_EXPORT KCompletionMatchesWrapper
|
||||
{
|
||||
public:
|
||||
explicit KCompletionMatchesWrapper(KCompletion::SorterFunction const &sorterFunction, KCompletion::CompOrder compOrder = KCompletion::Insertion)
|
||||
: m_sortedListPtr(compOrder == KCompletion::Weighted ? new KCompletionMatchesList : nullptr)
|
||||
, m_dirty(false)
|
||||
, m_compOrder(compOrder)
|
||||
, m_sorterFunction(sorterFunction)
|
||||
{
|
||||
}
|
||||
|
||||
KCompletionMatchesWrapper(const KCompletionMatchesWrapper &) = delete;
|
||||
KCompletionMatchesWrapper &operator=(const KCompletionMatchesWrapper &) = delete;
|
||||
|
||||
void setSorting(KCompletion::CompOrder compOrder)
|
||||
{
|
||||
if (compOrder == KCompletion::Weighted && !m_sortedListPtr) {
|
||||
m_sortedListPtr = std::make_unique<KCompletionMatchesList>();
|
||||
} else if (compOrder != KCompletion::Weighted) {
|
||||
m_sortedListPtr.reset();
|
||||
}
|
||||
m_compOrder = compOrder;
|
||||
m_stringList.clear();
|
||||
m_dirty = false;
|
||||
}
|
||||
|
||||
KCompletion::CompOrder sorting() const
|
||||
{
|
||||
return m_compOrder;
|
||||
}
|
||||
|
||||
void append(int i, const QString &string)
|
||||
{
|
||||
if (m_sortedListPtr) {
|
||||
m_sortedListPtr->insert(i, string);
|
||||
} else {
|
||||
m_stringList.append(string);
|
||||
}
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
if (m_sortedListPtr) {
|
||||
m_sortedListPtr->clear();
|
||||
}
|
||||
m_stringList.clear();
|
||||
m_dirty = false;
|
||||
}
|
||||
|
||||
uint size() const
|
||||
{
|
||||
if (m_sortedListPtr) {
|
||||
return m_sortedListPtr->size();
|
||||
}
|
||||
return m_stringList.size();
|
||||
}
|
||||
|
||||
bool isEmpty() const
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
QString first() const
|
||||
{
|
||||
return list().constFirst();
|
||||
}
|
||||
|
||||
QString last() const
|
||||
{
|
||||
return list().constLast();
|
||||
}
|
||||
|
||||
inline QStringList list() const;
|
||||
|
||||
inline void findAllCompletions(const KCompTreeNode *, const QString &, bool ignoreCase, bool &hasMultipleMatches);
|
||||
|
||||
inline void extractStringsFromNode(const KCompTreeNode *, const QString &beginning, bool addWeight = false);
|
||||
|
||||
inline void extractStringsFromNodeCI(const KCompTreeNode *, const QString &beginning, const QString &restString);
|
||||
|
||||
mutable QStringList m_stringList;
|
||||
std::unique_ptr<KCompletionMatchesList> m_sortedListPtr;
|
||||
mutable bool m_dirty;
|
||||
KCompletion::CompOrder m_compOrder;
|
||||
KCompletion::SorterFunction const &m_sorterFunction;
|
||||
};
|
||||
|
||||
void KCompletionMatchesWrapper::findAllCompletions(const KCompTreeNode *treeRoot, const QString &string, bool ignoreCase, bool &hasMultipleMatches)
|
||||
{
|
||||
// qDebug() << "*** finding all completions for " << string;
|
||||
|
||||
if (string.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ignoreCase) { // case insensitive completion
|
||||
extractStringsFromNodeCI(treeRoot, QString(), string);
|
||||
hasMultipleMatches = (size() > 1);
|
||||
return;
|
||||
}
|
||||
|
||||
QString completion;
|
||||
const KCompTreeNode *node = treeRoot;
|
||||
|
||||
// start at the tree-root and try to find the search-string
|
||||
for (const QChar ch : string) {
|
||||
node = node->find(ch);
|
||||
|
||||
if (node) {
|
||||
completion += ch;
|
||||
} else {
|
||||
return; // no completion -> return empty list
|
||||
}
|
||||
}
|
||||
|
||||
// Now we have the last node of the to be completed string.
|
||||
// Follow it as long as it has exactly one child (= longest possible
|
||||
// completion)
|
||||
|
||||
while (node->childrenCount() == 1) {
|
||||
node = node->firstChild();
|
||||
if (!node->isNull()) {
|
||||
completion += *node;
|
||||
}
|
||||
// qDebug() << completion << node->latin1();
|
||||
}
|
||||
|
||||
// there is just one single match)
|
||||
if (node->childrenCount() == 0) {
|
||||
append(node->weight(), completion);
|
||||
}
|
||||
|
||||
else {
|
||||
// node has more than one child
|
||||
// -> recursively find all remaining completions
|
||||
hasMultipleMatches = true;
|
||||
extractStringsFromNode(node, completion);
|
||||
}
|
||||
}
|
||||
|
||||
QStringList KCompletionMatchesWrapper::list() const
|
||||
{
|
||||
if (m_sortedListPtr && m_dirty) {
|
||||
m_sortedListPtr->sort();
|
||||
m_dirty = false;
|
||||
|
||||
m_stringList.clear();
|
||||
m_stringList.reserve(m_sortedListPtr->size());
|
||||
// high weight == sorted last -> reverse the sorting here
|
||||
std::transform(m_sortedListPtr->crbegin(), m_sortedListPtr->crend(), std::back_inserter(m_stringList), [](const KSortableItem<QString> &item) {
|
||||
return item.value();
|
||||
});
|
||||
} else if (m_compOrder == KCompletion::Sorted) {
|
||||
m_sorterFunction(m_stringList);
|
||||
}
|
||||
|
||||
return m_stringList;
|
||||
}
|
||||
|
||||
void KCompletionMatchesWrapper::extractStringsFromNode(const KCompTreeNode *node, const QString &beginning, bool addWeight)
|
||||
{
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
// qDebug() << "Beginning: " << beginning;
|
||||
const KCompTreeChildren *list = node->children();
|
||||
QString string;
|
||||
QString w;
|
||||
|
||||
// loop thru all children
|
||||
for (KCompTreeNode *cur = list->begin(); cur; cur = cur->m_next) {
|
||||
string = beginning;
|
||||
node = cur;
|
||||
if (!node->isNull()) {
|
||||
string += *node;
|
||||
}
|
||||
|
||||
while (node && node->childrenCount() == 1) {
|
||||
node = node->firstChild();
|
||||
if (node->isNull()) {
|
||||
break;
|
||||
}
|
||||
string += *node;
|
||||
}
|
||||
|
||||
if (node && node->isNull()) { // we found a leaf
|
||||
if (addWeight) {
|
||||
// add ":num" to the string to store the weighting
|
||||
string += QLatin1Char(':');
|
||||
w.setNum(node->weight());
|
||||
string.append(w);
|
||||
}
|
||||
append(node->weight(), string);
|
||||
}
|
||||
|
||||
// recursively find all other strings.
|
||||
if (node && node->childrenCount() > 1) {
|
||||
extractStringsFromNode(node, string, addWeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KCompletionMatchesWrapper::extractStringsFromNodeCI(const KCompTreeNode *node, const QString &beginning, const QString &restString)
|
||||
{
|
||||
if (restString.isEmpty()) {
|
||||
extractStringsFromNode(node, beginning, false /*noweight*/);
|
||||
return;
|
||||
}
|
||||
|
||||
QChar ch1 = restString.at(0);
|
||||
QString newRest = restString.mid(1);
|
||||
KCompTreeNode *child1;
|
||||
KCompTreeNode *child2;
|
||||
|
||||
child1 = node->find(ch1); // the correct match
|
||||
if (child1) {
|
||||
extractStringsFromNodeCI(child1, beginning + QChar(*child1), newRest);
|
||||
}
|
||||
|
||||
// append the case insensitive matches, if available
|
||||
if (ch1.isLetter()) {
|
||||
// find out if we have to lower or upper it. Is there a better way?
|
||||
QChar ch2 = ch1.toLower();
|
||||
if (ch1 == ch2) {
|
||||
ch2 = ch1.toUpper();
|
||||
}
|
||||
if (ch1 != ch2) {
|
||||
child2 = node->find(ch2);
|
||||
if (child2) {
|
||||
extractStringsFromNodeCI(child2, beginning + QChar(*child2), newRest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // KCOMPLETIONMATCHESWRAPPER_P_H
|
||||
@@ -0,0 +1,362 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCOMPTREENODE_P_H
|
||||
#define KCOMPTREENODE_P_H
|
||||
|
||||
#include "kcompletion_export.h"
|
||||
|
||||
#include <QSharedPointer>
|
||||
#include <kzoneallocator_p.h>
|
||||
|
||||
class KCompTreeNode;
|
||||
|
||||
class KCOMPLETION_EXPORT KCompTreeChildren
|
||||
{
|
||||
public:
|
||||
KCompTreeChildren()
|
||||
: m_first(nullptr)
|
||||
, m_last(nullptr)
|
||||
, m_count(0)
|
||||
{
|
||||
}
|
||||
|
||||
KCompTreeNode *begin() const
|
||||
{
|
||||
return m_first;
|
||||
}
|
||||
|
||||
KCompTreeNode *end() const
|
||||
{
|
||||
return m_last;
|
||||
}
|
||||
|
||||
inline KCompTreeNode *at(uint index) const;
|
||||
inline void append(KCompTreeNode *item);
|
||||
inline void prepend(KCompTreeNode *item);
|
||||
inline void insert(KCompTreeNode *after, KCompTreeNode *item);
|
||||
inline KCompTreeNode *remove(KCompTreeNode *item);
|
||||
|
||||
uint count() const
|
||||
{
|
||||
return m_count;
|
||||
}
|
||||
|
||||
private:
|
||||
KCompTreeNode *m_first;
|
||||
KCompTreeNode *m_last;
|
||||
uint m_count;
|
||||
};
|
||||
|
||||
/**
|
||||
* A helper class for KCompletion. Implements a tree of QChar.
|
||||
* Every node is a QChar and has a list of children, which are Nodes as well.
|
||||
*
|
||||
* QChar( 0x0 ) is used as the delimiter of a string; the last child of each
|
||||
* inserted string is 0x0.
|
||||
*
|
||||
* The tree looks like this (containing the items "kde", "kde-ui",
|
||||
* "kde-core" and "pfeiffer". Every item is delimited with QChar( 0x0 )
|
||||
*
|
||||
* some_root_node
|
||||
* / \
|
||||
* k p
|
||||
* | |
|
||||
* d f
|
||||
* | |
|
||||
* e e
|
||||
* /| |
|
||||
* 0x0 - i
|
||||
* / \ |
|
||||
* u c f
|
||||
* | | |
|
||||
* i o f
|
||||
* | | |
|
||||
* 0x0 r e
|
||||
* | |
|
||||
* e r
|
||||
* | |
|
||||
* 0x0 0x0
|
||||
*
|
||||
* @author Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
* @internal
|
||||
*/
|
||||
class KCOMPLETION_EXPORT KCompTreeNode : public QChar
|
||||
{
|
||||
public:
|
||||
KCompTreeNode()
|
||||
: QChar()
|
||||
, m_next(nullptr)
|
||||
, m_weight(0)
|
||||
{
|
||||
}
|
||||
|
||||
explicit KCompTreeNode(const QChar &ch, uint weight = 0)
|
||||
: QChar(ch)
|
||||
, m_next(nullptr)
|
||||
, m_weight(weight)
|
||||
{
|
||||
}
|
||||
|
||||
~KCompTreeNode()
|
||||
{
|
||||
// delete all children
|
||||
KCompTreeNode *cur = m_children.begin();
|
||||
while (cur) {
|
||||
KCompTreeNode *next = cur->m_next;
|
||||
delete m_children.remove(cur);
|
||||
cur = next;
|
||||
}
|
||||
}
|
||||
|
||||
KCompTreeNode(const KCompTreeNode &) = delete;
|
||||
KCompTreeNode &operator=(const KCompTreeNode &) = delete;
|
||||
|
||||
void *operator new(size_t s)
|
||||
{
|
||||
Q_ASSERT(m_alloc);
|
||||
return m_alloc->allocate(s);
|
||||
}
|
||||
|
||||
void operator delete(void *s)
|
||||
{
|
||||
Q_ASSERT(m_alloc);
|
||||
m_alloc->deallocate(s);
|
||||
}
|
||||
|
||||
// Returns a child of this node matching ch, if available.
|
||||
// Otherwise, returns 0L
|
||||
KCompTreeNode *find(const QChar &ch) const
|
||||
{
|
||||
KCompTreeNode *cur = m_children.begin();
|
||||
while (cur && (*cur != ch)) {
|
||||
cur = cur->m_next;
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
|
||||
// Adds a child-node "ch" to this node. If such a node is already existent,
|
||||
// it will not be created. Returns the new/existing node.
|
||||
inline KCompTreeNode *insert(const QChar &ch, bool sorted);
|
||||
|
||||
// Iteratively removes a string from the tree. The nicer recursive
|
||||
// version apparently was a little memory hungry (see #56757)
|
||||
inline void remove(const QString &str);
|
||||
|
||||
int childrenCount() const
|
||||
{
|
||||
return m_children.count();
|
||||
}
|
||||
|
||||
void confirm()
|
||||
{
|
||||
m_weight++;
|
||||
}
|
||||
|
||||
void confirm(uint w)
|
||||
{
|
||||
m_weight += w;
|
||||
}
|
||||
|
||||
void decline()
|
||||
{
|
||||
m_weight--;
|
||||
}
|
||||
|
||||
uint weight() const
|
||||
{
|
||||
return m_weight;
|
||||
}
|
||||
|
||||
const KCompTreeChildren *children() const
|
||||
{
|
||||
return &m_children;
|
||||
}
|
||||
|
||||
const KCompTreeNode *childAt(int index) const
|
||||
{
|
||||
return m_children.at(index);
|
||||
}
|
||||
|
||||
const KCompTreeNode *firstChild() const
|
||||
{
|
||||
return m_children.begin();
|
||||
}
|
||||
|
||||
const KCompTreeNode *lastChild() const
|
||||
{
|
||||
return m_children.end();
|
||||
}
|
||||
|
||||
/* We want to handle a list of KCompTreeNodes on our own, to not
|
||||
need to use QValueList<>. And to make it even faster we don't
|
||||
use an accessor, but just a public member. */
|
||||
KCompTreeNode *m_next;
|
||||
|
||||
/**
|
||||
* Custom allocator used for all KCompTreeNode instances
|
||||
*/
|
||||
static QSharedPointer<KZoneAllocator> allocator()
|
||||
{
|
||||
return m_alloc;
|
||||
}
|
||||
|
||||
private:
|
||||
uint m_weight;
|
||||
KCompTreeChildren m_children;
|
||||
static QSharedPointer<KZoneAllocator> m_alloc;
|
||||
};
|
||||
|
||||
KCompTreeNode *KCompTreeNode::insert(const QChar &ch, bool sorted)
|
||||
{
|
||||
KCompTreeNode *child = find(ch);
|
||||
if (!child) {
|
||||
child = new KCompTreeNode(ch);
|
||||
|
||||
// FIXME, first (slow) sorted insertion implementation
|
||||
if (sorted) {
|
||||
KCompTreeNode *prev = nullptr;
|
||||
KCompTreeNode *cur = m_children.begin();
|
||||
while (cur) {
|
||||
if (ch > *cur) {
|
||||
prev = cur;
|
||||
cur = cur->m_next;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (prev) {
|
||||
m_children.insert(prev, child);
|
||||
} else {
|
||||
m_children.prepend(child);
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
m_children.append(child);
|
||||
}
|
||||
}
|
||||
|
||||
// implicit weighting: the more often an item is inserted, the higher
|
||||
// priority it gets.
|
||||
child->confirm();
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
void KCompTreeNode::remove(const QString &str)
|
||||
{
|
||||
QString string = str;
|
||||
string += QChar(0x0);
|
||||
|
||||
QList<KCompTreeNode *> deletables(string.length() + 1);
|
||||
|
||||
KCompTreeNode *child = nullptr;
|
||||
KCompTreeNode *parent = this;
|
||||
deletables.replace(0, parent);
|
||||
|
||||
int i = 0;
|
||||
for (; i < string.length(); i++) {
|
||||
child = parent->find(string.at(i));
|
||||
if (child) {
|
||||
deletables.replace(i + 1, child);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
parent = child;
|
||||
}
|
||||
|
||||
for (; i >= 1; i--) {
|
||||
parent = deletables.at(i - 1);
|
||||
child = deletables.at(i);
|
||||
if (child->m_children.count() == 0) {
|
||||
delete parent->m_children.remove(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KCompTreeNode *KCompTreeChildren::at(uint index) const
|
||||
{
|
||||
KCompTreeNode *cur = m_first;
|
||||
while (index-- && cur) {
|
||||
cur = cur->m_next;
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
|
||||
void KCompTreeChildren::append(KCompTreeNode *item)
|
||||
{
|
||||
m_count++;
|
||||
if (!m_last) {
|
||||
m_last = item;
|
||||
m_last->m_next = nullptr;
|
||||
m_first = item;
|
||||
return;
|
||||
}
|
||||
m_last->m_next = item;
|
||||
item->m_next = nullptr;
|
||||
m_last = item;
|
||||
}
|
||||
|
||||
void KCompTreeChildren::prepend(KCompTreeNode *item)
|
||||
{
|
||||
m_count++;
|
||||
if (!m_last) {
|
||||
m_last = item;
|
||||
m_last->m_next = nullptr;
|
||||
m_first = item;
|
||||
return;
|
||||
}
|
||||
item->m_next = m_first;
|
||||
m_first = item;
|
||||
}
|
||||
|
||||
void KCompTreeChildren::insert(KCompTreeNode *after, KCompTreeNode *item)
|
||||
{
|
||||
if (!after) {
|
||||
append(item);
|
||||
return;
|
||||
}
|
||||
|
||||
m_count++;
|
||||
|
||||
item->m_next = after->m_next;
|
||||
after->m_next = item;
|
||||
|
||||
if (after == m_last) {
|
||||
m_last = item;
|
||||
}
|
||||
}
|
||||
|
||||
KCompTreeNode *KCompTreeChildren::remove(KCompTreeNode *item)
|
||||
{
|
||||
if (!m_first || !item) {
|
||||
return nullptr;
|
||||
}
|
||||
KCompTreeNode *cur = nullptr;
|
||||
|
||||
if (item == m_first) {
|
||||
m_first = m_first->m_next;
|
||||
} else {
|
||||
cur = m_first;
|
||||
while (cur && cur->m_next != item) {
|
||||
cur = cur->m_next;
|
||||
}
|
||||
if (!cur) {
|
||||
return nullptr;
|
||||
}
|
||||
cur->m_next = item->m_next;
|
||||
}
|
||||
if (item == m_last) {
|
||||
m_last = cur;
|
||||
}
|
||||
m_count--;
|
||||
return item;
|
||||
}
|
||||
|
||||
#endif // KCOMPTREENODE_P_H
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2010 Casey Link <unnamedrambler@gmail.com>
|
||||
SPDX-FileCopyrightText: 2009-2010 Klaralvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kemailvalidator.h"
|
||||
|
||||
#include <KEmailAddress>
|
||||
|
||||
KEmailValidator::KEmailValidator(QObject *parent)
|
||||
: QValidator(parent)
|
||||
{
|
||||
}
|
||||
|
||||
KEmailValidator::~KEmailValidator() = default;
|
||||
|
||||
QValidator::State KEmailValidator::validate(QString &str, int &pos) const
|
||||
{
|
||||
Q_UNUSED(pos)
|
||||
|
||||
if (KEmailAddress::isValidSimpleAddress(str)) {
|
||||
return QValidator::Acceptable;
|
||||
}
|
||||
const auto containsSpace = std::any_of(str.begin(), str.end(), [](QChar c) {
|
||||
return c.isSpace();
|
||||
});
|
||||
if (containsSpace) {
|
||||
return QValidator::Invalid;
|
||||
}
|
||||
return QValidator::Intermediate;
|
||||
}
|
||||
|
||||
void KEmailValidator::fixup(QString &str) const
|
||||
{
|
||||
str = str.trimmed();
|
||||
}
|
||||
|
||||
#include "moc_kemailvalidator.cpp"
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2010 Casey Link <unnamedrambler@gmail.com>
|
||||
SPDX-FileCopyrightText: 2009-2010 Klaralvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KEMAILVALIDATOR_H
|
||||
#define KEMAILVALIDATOR_H
|
||||
|
||||
#include "kcompletion_export.h"
|
||||
|
||||
#include <QValidator>
|
||||
|
||||
/**
|
||||
An input validator that checks for valid email addresses.
|
||||
@see KEmailAddress::isValidSimpleAddress
|
||||
@since 6.0
|
||||
*/
|
||||
class KCOMPLETION_EXPORT KEmailValidator : public QValidator
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit KEmailValidator(QObject *parent = nullptr);
|
||||
~KEmailValidator();
|
||||
|
||||
State validate(QString &str, int &pos) const override;
|
||||
void fixup(QString &str) const override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,461 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2000, 2001 Dawit Alemayehu <adawit@kde.org>
|
||||
SPDX-FileCopyrightText: 2000, 2001 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
SPDX-FileCopyrightText: 2000 Stefan Schimanski <1Stein@gmx.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "khistorycombobox.h"
|
||||
#include "kcombobox_p.h"
|
||||
|
||||
#include <KStandardShortcut>
|
||||
|
||||
#include <QAbstractItemView>
|
||||
#include <QApplication>
|
||||
#include <QComboBox>
|
||||
#include <QMenu>
|
||||
#include <QWheelEvent>
|
||||
|
||||
class KHistoryComboBoxPrivate : public KComboBoxPrivate
|
||||
{
|
||||
Q_DECLARE_PUBLIC(KHistoryComboBox)
|
||||
|
||||
public:
|
||||
KHistoryComboBoxPrivate(KHistoryComboBox *q)
|
||||
: KComboBoxPrivate(q)
|
||||
{
|
||||
}
|
||||
|
||||
void init(bool useCompletion);
|
||||
void rotateUp();
|
||||
void rotateDown();
|
||||
|
||||
/**
|
||||
* Called from the popupmenu,
|
||||
* calls clearHistory() and emits cleared()
|
||||
*/
|
||||
void _k_clear();
|
||||
|
||||
/**
|
||||
* Appends our own context menu entry.
|
||||
*/
|
||||
void _k_addContextMenuItems(QMenu *);
|
||||
|
||||
/**
|
||||
* Used to emit the activated(QString) signal when enter is pressed
|
||||
*/
|
||||
void _k_simulateActivated(const QString &);
|
||||
|
||||
/**
|
||||
* The text typed before Up or Down was pressed.
|
||||
*/
|
||||
QString typedText;
|
||||
|
||||
/**
|
||||
* The current index in the combobox, used for Up and Down
|
||||
*/
|
||||
int currentIndex;
|
||||
|
||||
/**
|
||||
* Indicates that the user at least once rotated Up through the entire list
|
||||
* Needed to allow going back after rotation.
|
||||
*/
|
||||
bool rotated = false;
|
||||
|
||||
std::function<QIcon(QString)> iconProvider;
|
||||
};
|
||||
|
||||
void KHistoryComboBoxPrivate::init(bool useCompletion)
|
||||
{
|
||||
Q_Q(KHistoryComboBox);
|
||||
// Set a default history size to something reasonable, Qt sets it to INT_MAX by default
|
||||
q->setMaxCount(50);
|
||||
|
||||
if (useCompletion) {
|
||||
q->completionObject()->setOrder(KCompletion::Weighted);
|
||||
}
|
||||
|
||||
q->setInsertPolicy(KHistoryComboBox::NoInsert);
|
||||
currentIndex = -1;
|
||||
rotated = false;
|
||||
|
||||
// obey HISTCONTROL setting
|
||||
QByteArray histControl = qgetenv("HISTCONTROL");
|
||||
if (histControl == "ignoredups" || histControl == "ignoreboth") {
|
||||
q->setDuplicatesEnabled(false);
|
||||
}
|
||||
|
||||
q->connect(q, &KComboBox::aboutToShowContextMenu, q, [this](QMenu *menu) {
|
||||
_k_addContextMenuItems(menu);
|
||||
});
|
||||
QObject::connect(q, qOverload<int>(&QComboBox::activated), q, &KHistoryComboBox::reset);
|
||||
QObject::connect(q, qOverload<const QString &>(&KComboBox::returnPressed), q, [q]() {
|
||||
q->reset();
|
||||
});
|
||||
// We want _k_simulateActivated to be called _after_ QComboBoxPrivate::_q_returnPressed
|
||||
// otherwise there's a risk of emitting activated twice (_k_simulateActivated will find
|
||||
// the item, after some app's slotActivated inserted the item into the combo).
|
||||
q->connect(
|
||||
q,
|
||||
qOverload<const QString &>(&KComboBox::returnPressed),
|
||||
q,
|
||||
[this](const QString &text) {
|
||||
_k_simulateActivated(text);
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
// we are always read-write
|
||||
KHistoryComboBox::KHistoryComboBox(QWidget *parent)
|
||||
: KComboBox(*new KHistoryComboBoxPrivate(this), parent)
|
||||
{
|
||||
Q_D(KHistoryComboBox);
|
||||
d->init(true); // using completion
|
||||
setEditable(true);
|
||||
}
|
||||
|
||||
// we are always read-write
|
||||
KHistoryComboBox::KHistoryComboBox(bool useCompletion, QWidget *parent)
|
||||
: KComboBox(*new KHistoryComboBoxPrivate(this), parent)
|
||||
{
|
||||
Q_D(KHistoryComboBox);
|
||||
d->init(useCompletion);
|
||||
setEditable(true);
|
||||
}
|
||||
|
||||
KHistoryComboBox::~KHistoryComboBox()
|
||||
{
|
||||
}
|
||||
|
||||
void KHistoryComboBox::setHistoryItems(const QStringList &items)
|
||||
{
|
||||
setHistoryItems(items, false);
|
||||
}
|
||||
|
||||
void KHistoryComboBox::setHistoryItems(const QStringList &items, bool setCompletionList)
|
||||
{
|
||||
QStringList insertingItems = items;
|
||||
KComboBox::clear();
|
||||
|
||||
// limit to maxCount()
|
||||
const int itemCount = insertingItems.count();
|
||||
const int toRemove = itemCount - maxCount();
|
||||
|
||||
if (toRemove >= itemCount) {
|
||||
insertingItems.clear();
|
||||
} else {
|
||||
for (int i = 0; i < toRemove; ++i) {
|
||||
insertingItems.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
insertItems(insertingItems);
|
||||
|
||||
if (setCompletionList && useCompletion()) {
|
||||
// we don't have any weighting information here ;(
|
||||
KCompletion *comp = completionObject();
|
||||
comp->setOrder(KCompletion::Insertion);
|
||||
comp->setItems(insertingItems);
|
||||
comp->setOrder(KCompletion::Weighted);
|
||||
}
|
||||
|
||||
clearEditText();
|
||||
}
|
||||
|
||||
QStringList KHistoryComboBox::historyItems() const
|
||||
{
|
||||
QStringList list;
|
||||
const int itemCount = count();
|
||||
list.reserve(itemCount);
|
||||
for (int i = 0; i < itemCount; ++i) {
|
||||
list.append(itemText(i));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
bool KHistoryComboBox::useCompletion() const
|
||||
{
|
||||
return compObj();
|
||||
}
|
||||
|
||||
void KHistoryComboBox::clearHistory()
|
||||
{
|
||||
const QString temp = currentText();
|
||||
KComboBox::clear();
|
||||
if (useCompletion()) {
|
||||
completionObject()->clear();
|
||||
}
|
||||
setEditText(temp);
|
||||
}
|
||||
|
||||
void KHistoryComboBoxPrivate::_k_addContextMenuItems(QMenu *menu)
|
||||
{
|
||||
Q_Q(KHistoryComboBox);
|
||||
if (menu) {
|
||||
menu->addSeparator();
|
||||
QAction *clearHistory =
|
||||
menu->addAction(QIcon::fromTheme(QStringLiteral("edit-clear-history")), KHistoryComboBox::tr("Clear &History", "@action:inmenu"), q, [this]() {
|
||||
_k_clear();
|
||||
});
|
||||
if (!q->count()) {
|
||||
clearHistory->setEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KHistoryComboBox::addToHistory(const QString &item)
|
||||
{
|
||||
Q_D(KHistoryComboBox);
|
||||
if (item.isEmpty() || (count() > 0 && item == itemText(0))) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool wasCurrent = false;
|
||||
// remove all existing items before adding
|
||||
if (!duplicatesEnabled()) {
|
||||
int i = 0;
|
||||
int itemCount = count();
|
||||
while (i < itemCount) {
|
||||
if (itemText(i) == item) {
|
||||
if (!wasCurrent) {
|
||||
wasCurrent = (i == currentIndex());
|
||||
}
|
||||
removeItem(i);
|
||||
--itemCount;
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now add the item
|
||||
if (d->iconProvider) {
|
||||
insertItem(0, d->iconProvider(item), item);
|
||||
} else {
|
||||
insertItem(0, item);
|
||||
}
|
||||
|
||||
if (wasCurrent) {
|
||||
setCurrentIndex(0);
|
||||
}
|
||||
|
||||
const bool useComp = useCompletion();
|
||||
|
||||
const int last = count() - 1; // last valid index
|
||||
const int mc = maxCount();
|
||||
const int stopAt = qMax(mc, 0);
|
||||
|
||||
for (int rmIndex = last; rmIndex >= stopAt; --rmIndex) {
|
||||
// remove the last item, as long as we are longer than maxCount()
|
||||
// remove the removed item from the completionObject if it isn't
|
||||
// anymore available at all in the combobox.
|
||||
const QString rmItem = itemText(rmIndex);
|
||||
removeItem(rmIndex);
|
||||
if (useComp && !contains(rmItem)) {
|
||||
completionObject()->removeItem(rmItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (useComp) {
|
||||
completionObject()->addItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
bool KHistoryComboBox::removeFromHistory(const QString &item)
|
||||
{
|
||||
if (item.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool removed = false;
|
||||
const QString temp = currentText();
|
||||
int i = 0;
|
||||
int itemCount = count();
|
||||
while (i < itemCount) {
|
||||
if (item == itemText(i)) {
|
||||
removed = true;
|
||||
removeItem(i);
|
||||
--itemCount;
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
if (removed && useCompletion()) {
|
||||
completionObject()->removeItem(item);
|
||||
}
|
||||
|
||||
setEditText(temp);
|
||||
return removed;
|
||||
}
|
||||
|
||||
// going up in the history, rotating when reaching QListBox::count()
|
||||
//
|
||||
// Note: this differs from QComboBox because "up" means ++index here,
|
||||
// to simulate the way shell history works (up goes to the most
|
||||
// recent item). In QComboBox "down" means ++index, to match the popup...
|
||||
//
|
||||
void KHistoryComboBoxPrivate::rotateUp()
|
||||
{
|
||||
Q_Q(KHistoryComboBox);
|
||||
// save the current text in the lineedit
|
||||
// (This is also where this differs from standard up/down in QComboBox,
|
||||
// where a single keypress can make you lose your typed text)
|
||||
if (currentIndex == -1) {
|
||||
typedText = q->currentText();
|
||||
}
|
||||
|
||||
++currentIndex;
|
||||
|
||||
// skip duplicates/empty items
|
||||
const int last = q->count() - 1; // last valid index
|
||||
const QString currText = q->currentText();
|
||||
|
||||
while (currentIndex < last && (currText == q->itemText(currentIndex) || q->itemText(currentIndex).isEmpty())) {
|
||||
++currentIndex;
|
||||
}
|
||||
|
||||
if (currentIndex >= q->count()) {
|
||||
rotated = true;
|
||||
currentIndex = -1;
|
||||
|
||||
// if the typed text is the same as the first item, skip the first
|
||||
if (q->count() > 0 && typedText == q->itemText(0)) {
|
||||
currentIndex = 0;
|
||||
}
|
||||
|
||||
q->setEditText(typedText);
|
||||
} else {
|
||||
q->setCurrentIndex(currentIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// going down in the history, no rotation possible. Last item will be
|
||||
// the text that was in the lineedit before Up was called.
|
||||
void KHistoryComboBoxPrivate::rotateDown()
|
||||
{
|
||||
Q_Q(KHistoryComboBox);
|
||||
// save the current text in the lineedit
|
||||
if (currentIndex == -1) {
|
||||
typedText = q->currentText();
|
||||
}
|
||||
|
||||
--currentIndex;
|
||||
|
||||
const QString currText = q->currentText();
|
||||
// skip duplicates/empty items
|
||||
while (currentIndex >= 0 //
|
||||
&& (currText == q->itemText(currentIndex) || q->itemText(currentIndex).isEmpty())) {
|
||||
--currentIndex;
|
||||
}
|
||||
|
||||
if (currentIndex < 0) {
|
||||
if (rotated && currentIndex == -2) {
|
||||
rotated = false;
|
||||
currentIndex = q->count() - 1;
|
||||
q->setEditText(q->itemText(currentIndex));
|
||||
} else { // bottom of history
|
||||
currentIndex = -1;
|
||||
if (q->currentText() != typedText) {
|
||||
q->setEditText(typedText);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
q->setCurrentIndex(currentIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void KHistoryComboBox::keyPressEvent(QKeyEvent *e)
|
||||
{
|
||||
Q_D(KHistoryComboBox);
|
||||
int event_key = e->key() | e->modifiers();
|
||||
|
||||
if (KStandardShortcut::rotateUp().contains(event_key)) {
|
||||
d->rotateUp();
|
||||
} else if (KStandardShortcut::rotateDown().contains(event_key)) {
|
||||
d->rotateDown();
|
||||
} else {
|
||||
KComboBox::keyPressEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
void KHistoryComboBox::wheelEvent(QWheelEvent *ev)
|
||||
{
|
||||
Q_D(KHistoryComboBox);
|
||||
// Pass to poppable listbox if it's up
|
||||
QAbstractItemView *const iv = view();
|
||||
if (iv && iv->isVisible()) {
|
||||
QApplication::sendEvent(iv, ev);
|
||||
return;
|
||||
}
|
||||
// Otherwise make it change the text without emitting activated
|
||||
if (ev->angleDelta().y() > 0) {
|
||||
d->rotateUp();
|
||||
} else {
|
||||
d->rotateDown();
|
||||
}
|
||||
ev->accept();
|
||||
}
|
||||
|
||||
void KHistoryComboBox::setIconProvider(std::function<QIcon(const QString &)> providerFunction)
|
||||
{
|
||||
Q_D(KHistoryComboBox);
|
||||
d->iconProvider = providerFunction;
|
||||
}
|
||||
|
||||
void KHistoryComboBox::insertItems(const QStringList &items)
|
||||
{
|
||||
Q_D(KHistoryComboBox);
|
||||
|
||||
for (const QString &item : items) {
|
||||
if (item.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (d->iconProvider) {
|
||||
addItem(d->iconProvider(item), item);
|
||||
} else {
|
||||
addItem(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KHistoryComboBoxPrivate::_k_clear()
|
||||
{
|
||||
Q_Q(KHistoryComboBox);
|
||||
q->clearHistory();
|
||||
Q_EMIT q->cleared();
|
||||
}
|
||||
|
||||
void KHistoryComboBoxPrivate::_k_simulateActivated(const QString &text)
|
||||
{
|
||||
Q_Q(KHistoryComboBox);
|
||||
/* With the insertion policy NoInsert, which we use by default,
|
||||
Qt doesn't emit activated on typed text if the item is not already there,
|
||||
which is perhaps reasonable. Generate the signal ourselves if that's the case.
|
||||
*/
|
||||
if ((q->insertPolicy() == q->NoInsert && q->findText(text, Qt::MatchFixedString | Qt::MatchCaseSensitive) == -1)) {
|
||||
Q_EMIT q->textActivated(text);
|
||||
}
|
||||
|
||||
/*
|
||||
Qt also doesn't emit it if the box is full, and policy is not
|
||||
InsertAtCurrent
|
||||
*/
|
||||
else if (q->insertPolicy() != q->InsertAtCurrent && q->count() >= q->maxCount()) {
|
||||
Q_EMIT q->textActivated(text);
|
||||
}
|
||||
}
|
||||
|
||||
void KHistoryComboBox::reset()
|
||||
{
|
||||
Q_D(KHistoryComboBox);
|
||||
d->currentIndex = -1;
|
||||
d->rotated = false;
|
||||
}
|
||||
|
||||
#include "moc_khistorycombobox.cpp"
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2000, 2001 Dawit Alemayehu <adawit@kde.org>
|
||||
SPDX-FileCopyrightText: 2000, 2001 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#ifndef KHistoryComboBoxBOX_H
|
||||
#define KHistoryComboBoxBOX_H
|
||||
|
||||
#include <kcombobox.h>
|
||||
#include <kcompletion_export.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
class KHistoryComboBoxPrivate;
|
||||
|
||||
/**
|
||||
* @class KHistoryComboBox khistorycombobox.h KHistoryComboBox
|
||||
*
|
||||
* @short A combobox for offering a history and completion
|
||||
*
|
||||
* A combobox which implements a history like a unix shell. You can navigate
|
||||
* through all the items by using the Up or Down arrows (configurable of
|
||||
* course). Additionally, weighted completion is available. So you should
|
||||
* load and save the completion list to preserve the weighting between
|
||||
* sessions.
|
||||
*
|
||||
* KHistoryComboBox obeys the HISTCONTROL environment variable to determine
|
||||
* whether duplicates in the history should be tolerated in
|
||||
* addToHistory() or not. During construction of KHistoryComboBox,
|
||||
* duplicates will be disabled when HISTCONTROL is set to "ignoredups" or
|
||||
* "ignoreboth". Otherwise, duplicates are enabled by default.
|
||||
*
|
||||
* \image html khistorycombobox.png "KHistoryComboBox widget"
|
||||
*
|
||||
* @author Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
*/
|
||||
class KCOMPLETION_EXPORT KHistoryComboBox : public KComboBox
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QStringList historyItems READ historyItems WRITE setHistoryItems)
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructs a "read-write" combobox. A read-only history combobox
|
||||
* doesn't make much sense, so it is only available as read-write.
|
||||
* Completion will be used automatically for the items in the combo.
|
||||
*
|
||||
* The insertion-policy is set to NoInsert, you have to add the items
|
||||
* yourself via the slot addToHistory. If you want every item added,
|
||||
* use
|
||||
*
|
||||
* \code
|
||||
* connect( combo, SIGNAL( activated( const QString& )),
|
||||
* combo, SLOT( addToHistory( const QString& )));
|
||||
* \endcode
|
||||
*
|
||||
* Use QComboBox::setMaxCount() to limit the history.
|
||||
*
|
||||
* @p parent the parent object of this widget.
|
||||
*/
|
||||
explicit KHistoryComboBox(QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Same as the previous constructor, but additionally has the option
|
||||
* to specify whether you want to let KHistoryComboBox handle completion
|
||||
* or not. If set to @c true, KHistoryComboBox will sync the completion to the
|
||||
* contents of the combobox.
|
||||
*/
|
||||
explicit KHistoryComboBox(bool useCompletion, QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Destructs the combo and the completion-object
|
||||
*/
|
||||
~KHistoryComboBox() override;
|
||||
|
||||
/**
|
||||
* Inserts @p items into the combobox. @p items might get
|
||||
* truncated if it is longer than maxCount()
|
||||
*
|
||||
* @see historyItems
|
||||
*/
|
||||
void setHistoryItems(const QStringList &items);
|
||||
|
||||
/**
|
||||
* Inserts @p items into the combobox. @p items might get
|
||||
* truncated if it is longer than maxCount()
|
||||
*
|
||||
* Set @c setCompletionList to true, if you don't have a list of
|
||||
* completions. This tells KHistoryComboBox to use all the items for the
|
||||
* completion object as well.
|
||||
* You won't have the benefit of weighted completion though, so normally
|
||||
* you should do something like
|
||||
* \code
|
||||
* KConfigGroup config(KSharedConfig::openConfig(), "somegroup");
|
||||
*
|
||||
* // load the history and completion list after creating the history combo
|
||||
* QStringList list;
|
||||
* list = config.readEntry("Completion list", QStringList());
|
||||
* combo->completionObject()->setItems(list);
|
||||
* list = config.readEntry("History list", QStringList());
|
||||
* combo->setHistoryItems(list);
|
||||
*
|
||||
* [...]
|
||||
*
|
||||
* // save the history and completion list when the history combo is
|
||||
* // destroyed
|
||||
* QStringList list;
|
||||
* KConfigGroup config(KSharedConfig::openConfig(), "somegroup");
|
||||
* list = combo->completionObject()->items();
|
||||
* config.writeEntry("Completion list", list);
|
||||
* list = combo->historyItems();
|
||||
* config.writeEntry("History list", list);
|
||||
* \endcode
|
||||
*
|
||||
* Be sure to use different names for saving with KConfig if you have more
|
||||
* than one KHistoryComboBox.
|
||||
*
|
||||
* @note When @c setCompletionList is true, the items are inserted into the
|
||||
* KCompletion object with mode KCompletion::Insertion and the mode is set
|
||||
* to KCompletion::Weighted afterwards.
|
||||
*
|
||||
* @see historyItems
|
||||
* @see KComboBox::completionObject
|
||||
* @see KCompletion::setItems
|
||||
* @see KCompletion::items
|
||||
*/
|
||||
void setHistoryItems(const QStringList &items, bool setCompletionList);
|
||||
|
||||
/**
|
||||
* Returns the list of history items. Empty, when this is not a read-write
|
||||
* combobox.
|
||||
*
|
||||
* @see setHistoryItems
|
||||
*/
|
||||
QStringList historyItems() const;
|
||||
|
||||
/**
|
||||
* Removes all items named @p item.
|
||||
*
|
||||
* @return @c true if at least one item was removed.
|
||||
*
|
||||
* @see addToHistory
|
||||
*/
|
||||
bool removeFromHistory(const QString &item);
|
||||
|
||||
/**
|
||||
* Sets an icon provider, so that items in the combobox can have an icon.
|
||||
* The provider is a function that takes a QString and returns a QIcon
|
||||
* @since 5.66
|
||||
*/
|
||||
void setIconProvider(std::function<QIcon(const QString &)> providerFunction);
|
||||
|
||||
using QComboBox::insertItems;
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* Adds an item to the end of the history list and to the completion list.
|
||||
* If maxCount() is reached, the first item of the list will be
|
||||
* removed.
|
||||
*
|
||||
* If the last inserted item is the same as @p item, it will not be
|
||||
* inserted again.
|
||||
*
|
||||
* If duplicatesEnabled() is false, any equal existing item will be
|
||||
* removed before @p item is added.
|
||||
*
|
||||
* @note By using this method and not the Q and KComboBox insertItem()
|
||||
* methods, you make sure that the combobox stays in sync with the
|
||||
* completion. It would be annoying if completion would give an item
|
||||
* not in the combobox, and vice versa.
|
||||
*
|
||||
* @see removeFromHistory
|
||||
* @see QComboBox::setDuplicatesEnabled
|
||||
*/
|
||||
void addToHistory(const QString &item);
|
||||
|
||||
/**
|
||||
* Clears the history and the completion list.
|
||||
*/
|
||||
void clearHistory();
|
||||
|
||||
/**
|
||||
* Resets the current position of the up/down history. Call this
|
||||
* when you manually call setCurrentItem() or clearEdit().
|
||||
*/
|
||||
void reset();
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted when the history was cleared by the entry in the popup menu.
|
||||
*/
|
||||
void cleared();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Handling key-events, the shortcuts to rotate the items.
|
||||
*/
|
||||
void keyPressEvent(QKeyEvent *) override;
|
||||
|
||||
/**
|
||||
* Handling wheel-events, to rotate the items.
|
||||
*/
|
||||
void wheelEvent(QWheelEvent *ev) override;
|
||||
|
||||
/**
|
||||
* Inserts @p items into the combo, honoring setIconProvider()
|
||||
* Does not update the completionObject.
|
||||
*
|
||||
* @note duplicatesEnabled() is not honored here.
|
||||
*
|
||||
* Called from setHistoryItems()
|
||||
*/
|
||||
void insertItems(const QStringList &items);
|
||||
|
||||
/**
|
||||
* @returns if we can modify the completion object or not.
|
||||
*/
|
||||
bool useCompletion() const;
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(KHistoryComboBox)
|
||||
|
||||
Q_DISABLE_COPY(KHistoryComboBox)
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,508 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
This class was originally inspired by Torben Weis'
|
||||
fileentry.cpp for KFM II.
|
||||
|
||||
SPDX-FileCopyrightText: 1997 Sven Radej <sven.radej@iname.com>
|
||||
SPDX-FileCopyrightText: 1999 Patrick Ward <PAT_WARD@HP-USA-om5.om.hp.com>
|
||||
SPDX-FileCopyrightText: 1999 Preston Brown <pbrown@kde.org>
|
||||
|
||||
Completely re-designed:
|
||||
SPDX-FileCopyrightText: 2000, 2001 Dawit Alemayehu <adawit@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KLINEEDIT_H
|
||||
#define KLINEEDIT_H
|
||||
|
||||
#include <kcompletion.h>
|
||||
#include <kcompletion_export.h>
|
||||
#include <kcompletionbase.h>
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <memory>
|
||||
|
||||
class QAction;
|
||||
class QMenu;
|
||||
class KCompletionBox;
|
||||
class QUrl;
|
||||
class KLineEditPrivate;
|
||||
|
||||
/**
|
||||
* @class KLineEdit klineedit.h KLineEdit
|
||||
*
|
||||
* An enhanced QLineEdit widget for inputting text.
|
||||
*
|
||||
* \b Detail \n
|
||||
*
|
||||
* This widget inherits from QLineEdit and implements the following
|
||||
* additional functionalities:
|
||||
* @li a completion object that provides both automatic and manual text
|
||||
* completion as well as multiple match iteration features
|
||||
* @li configurable key-bindings to activate these features
|
||||
* @li a popup-menu item that can be used to allow the user to set text
|
||||
* completion modes on the fly based on their preference
|
||||
*
|
||||
* To support these features KLineEdit also emits a few more additional
|
||||
* signals:
|
||||
* @li @c completion(const QString &): this signal can be connected to
|
||||
* a slot that will assist the user in filling out the remaining text
|
||||
* @li @c textRotation(KeyBindingType): this signal is intended to be
|
||||
* used to iterate through the list of all possible matches whenever
|
||||
* there is more than one match for the entered text
|
||||
* @li @c returnKeyPressed(const QString &): this signal provides the
|
||||
* current text in the widget as its argument whenever appropriate (this
|
||||
* is in addition to the @c QLineEdit::returnPressed() signal which KLineEdit
|
||||
* inherits from QLineEdit).
|
||||
*
|
||||
* This widget by default creates a completion object when you invoke
|
||||
* the @c completionObject(bool) member function for the first time or
|
||||
* use @c setCompletionObject(KCompletion *, bool) to assign your own
|
||||
* completion object. Additionally, to make this widget more functional,
|
||||
* KLineEdit will by default handle the text rotation and completion
|
||||
* events internally when a completion object is created through either one
|
||||
* of the methods mentioned above. If you do not need this functionality,
|
||||
* simply use @c KCompletionBase::setHandleSignals(bool) or set the boolean
|
||||
* parameter in the above functions to false.
|
||||
*
|
||||
* The default key-bindings for completion and rotation is determined
|
||||
* from the global settings in KStandardShortcut. These values, however,
|
||||
* can be overridden locally by invoking @c KCompletionBase::setKeyBinding().
|
||||
* The values can easily be reverted back to the default setting, by simply
|
||||
* calling @c useGlobalSettings(). An alternate method would be to default
|
||||
* individual key-bindings by using setKeyBinding() with the default
|
||||
* second argument.
|
||||
*
|
||||
* If @c EchoMode for this widget is set to something other than @c QLineEdit::Normal,
|
||||
* the completion mode will always be defaulted to CompletionNone.
|
||||
* This is done purposefully to guard against protected entries, such as
|
||||
* passwords, being cached in KCompletion's list. Hence, if the @c EchoMode
|
||||
* is not @c QLineEdit::Normal, the completion mode is automatically disabled.
|
||||
*
|
||||
* A read-only KLineEdit will have the same background color as a disabled
|
||||
* KLineEdit, but its foreground color will be the one used for the read-write
|
||||
* mode. This differs from QLineEdit's implementation and is done to give visual
|
||||
* distinction between the three different modes: disabled, read-only, and read-write.
|
||||
*
|
||||
* @b Usage
|
||||
*
|
||||
* To enable the basic completion feature:
|
||||
*
|
||||
* @code
|
||||
* KLineEdit *edit = new KLineEdit(this);
|
||||
* KCompletion *comp = edit->completionObject();
|
||||
* // Connect to the Return pressed signal - optional
|
||||
* connect(edit, &KLineEdit::returnKeyPressed, comp, [this](const QString &text) { addItem(text); });
|
||||
* @endcode
|
||||
*
|
||||
* To use a customized completion object or your own completion object:
|
||||
*
|
||||
* @code
|
||||
* KLineEdit *edit = new KLineEdit(this);
|
||||
* KUrlCompletion *comp = new KUrlCompletion();
|
||||
* edit->setCompletionObject(comp);
|
||||
* // Connect to the return pressed signal - optional
|
||||
* connect(edit, &KLineEdit::returnKeyPressed, comp, [this](const QString &text) { addItem(text); });
|
||||
* @endcode
|
||||
*
|
||||
* Note if you specify your own completion object you have to either delete
|
||||
* it when you don't need it anymore, or you can tell KLineEdit to delete it
|
||||
* for you:
|
||||
* @code
|
||||
* edit->setAutoDeleteCompletionObject(true);
|
||||
* @endcode
|
||||
*
|
||||
* <b>Miscellaneous function calls :</b>\n
|
||||
*
|
||||
* @code
|
||||
* // Tell the widget to not handle completion and iteration automatically
|
||||
* edit->setHandleSignals(false);
|
||||
*
|
||||
* // Set your own key-bindings for a text completion mode
|
||||
* edit->setKeyBinding(KCompletionBase::TextCompletion, Qt::End);
|
||||
*
|
||||
* // Hide the context (popup) menu
|
||||
* edit->setContextMenuPolicy(Qt::NoContextMenu);
|
||||
*
|
||||
* // Default the key-bindings back to the default system settings
|
||||
* edit->useGlobalKeyBindings();
|
||||
* @endcode
|
||||
*
|
||||
* @image html klineedit.png "KLineEdit widgets with clear-button"
|
||||
*
|
||||
* @author Dawit Alemayehu <adawit@kde.org>
|
||||
*/
|
||||
|
||||
class KCOMPLETION_EXPORT KLineEdit : public QLineEdit, public KCompletionBase // krazy:exclude=qclasses
|
||||
{
|
||||
friend class KComboBox;
|
||||
friend class KLineEditStyle;
|
||||
|
||||
Q_OBJECT
|
||||
Q_DECLARE_PRIVATE(KLineEdit)
|
||||
Q_PROPERTY(bool trapEnterKeyEvent READ trapReturnKey WRITE setTrapReturnKey)
|
||||
Q_PROPERTY(bool squeezedTextEnabled READ isSqueezedTextEnabled WRITE setSqueezedTextEnabled)
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructs a KLineEdit object with a default text, a parent,
|
||||
* and a name.
|
||||
*
|
||||
* @param string Text to be shown in the edit widget.
|
||||
* @param parent The parent widget of the line edit.
|
||||
*/
|
||||
explicit KLineEdit(const QString &string, QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Constructs a line edit
|
||||
* @param parent The parent widget of the line edit.
|
||||
*/
|
||||
explicit KLineEdit(QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~KLineEdit() override;
|
||||
|
||||
/**
|
||||
* Sets @p url into the lineedit. It uses QUrl::toDisplayString() so
|
||||
* that the url is properly decoded for displaying.
|
||||
*/
|
||||
void setUrl(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Reimplemented from KCompletionBase for internal reasons.
|
||||
*
|
||||
* This function is re-implemented in order to make sure that
|
||||
* the EchoMode is acceptable before we set the completion mode.
|
||||
*
|
||||
* See KCompletionBase::setCompletionMode
|
||||
*/
|
||||
void setCompletionMode(KCompletion::CompletionMode mode) override;
|
||||
|
||||
/**
|
||||
* Disables completion modes by making them non-checkable.
|
||||
*
|
||||
* The context menu allows to change the completion mode.
|
||||
* This method allows to disable some modes.
|
||||
*/
|
||||
void setCompletionModeDisabled(KCompletion::CompletionMode mode, bool disable = true);
|
||||
|
||||
/**
|
||||
* Returns @c true when decoded URL drops are enabled
|
||||
*/
|
||||
bool urlDropsEnabled() const;
|
||||
|
||||
/**
|
||||
* By default, KLineEdit recognizes @c Key_Return and @c Key_Enter and emits
|
||||
* the returnPressed() signals, but it also lets the event pass,
|
||||
* for example causing a dialog's default-button to be called.
|
||||
*
|
||||
* Call this method with @p trap = @c true to make @c KLineEdit stop these
|
||||
* events. The signals will still be emitted of course.
|
||||
*
|
||||
* @see trapReturnKey()
|
||||
*/
|
||||
void setTrapReturnKey(bool trap);
|
||||
|
||||
/**
|
||||
* @returns @c true if keyevents of @c Key_Return or
|
||||
* @c Key_Enter will be stopped or if they will be propagated.
|
||||
*
|
||||
* @see setTrapReturnKey ()
|
||||
*/
|
||||
bool trapReturnKey() const;
|
||||
|
||||
/**
|
||||
* This method will create a completion-box if none is there, yet.
|
||||
*
|
||||
* @param create Set this to false if you don't want the box to be created
|
||||
* i.e. to test if it is available.
|
||||
* @returns the completion-box, that is used in completion mode
|
||||
* CompletionPopup.
|
||||
*/
|
||||
virtual KCompletionBox *completionBox(bool create = true);
|
||||
|
||||
/**
|
||||
* Reimplemented for internal reasons, the API is not affected.
|
||||
*/
|
||||
void setCompletionObject(KCompletion *, bool handle = true) override;
|
||||
|
||||
/**
|
||||
* Reimplemented for internal reasons, the API is not affected.
|
||||
*/
|
||||
virtual void copy() const;
|
||||
|
||||
/**
|
||||
* Enable text squeezing whenever the supplied text is too long.
|
||||
* Only works for "read-only" mode.
|
||||
*
|
||||
* Note that once text squeezing is enabled, QLineEdit::text()
|
||||
* and QLineEdit::displayText() return the squeezed text. If
|
||||
* you want the original text, use @ref originalText.
|
||||
*
|
||||
* @see QLineEdit
|
||||
*/
|
||||
void setSqueezedTextEnabled(bool enable);
|
||||
|
||||
/**
|
||||
* Returns true if text squeezing is enabled.
|
||||
* This is only valid when the widget is in read-only mode.
|
||||
*/
|
||||
bool isSqueezedTextEnabled() const;
|
||||
|
||||
/**
|
||||
* Returns the original text if text squeezing is enabled.
|
||||
* If the widget is not in "read-only" mode, this function
|
||||
* returns the same thing as QLineEdit::text().
|
||||
*
|
||||
* @see QLineEdit
|
||||
*/
|
||||
QString originalText() const;
|
||||
|
||||
/**
|
||||
* Returns the text as given by the user (i.e. not autocompleted)
|
||||
* if the widget has autocompletion disabled, this function
|
||||
* returns the same as QLineEdit::text().
|
||||
* @since 4.2.2
|
||||
*/
|
||||
QString userText() const;
|
||||
|
||||
/**
|
||||
* Set the completion-box to be used in completion mode
|
||||
* CompletionPopup.
|
||||
* This will do nothing if a completion-box already exists.
|
||||
*
|
||||
* @param box The KCompletionBox to set
|
||||
*/
|
||||
void setCompletionBox(KCompletionBox *box);
|
||||
|
||||
/**
|
||||
* @return the size used by the clear button
|
||||
* @since 4.1
|
||||
*/
|
||||
QSize clearButtonUsedSize() const;
|
||||
|
||||
/**
|
||||
* Do completion now. This is called automatically when typing a key for instance.
|
||||
* Emits completion() and/or calls makeCompletion(), depending on
|
||||
* emitSignals and handleSignals.
|
||||
*
|
||||
* @since 4.2.1
|
||||
*/
|
||||
void doCompletion(const QString &text);
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted whenever the completion box is activated.
|
||||
*/
|
||||
void completionBoxActivated(const QString &);
|
||||
|
||||
/**
|
||||
* Emitted when the user presses the Return or Enter key.
|
||||
*
|
||||
* The argument is the current text. Note that this signal is @em not emitted
|
||||
* if the widget's @c EchoMode is set to QLineEdit::EchoMode.
|
||||
*
|
||||
* @since 5.81
|
||||
*/
|
||||
void returnKeyPressed(const QString &text);
|
||||
|
||||
/**
|
||||
* Emitted when the completion key is pressed.
|
||||
*
|
||||
* Please note that this signal is @em not emitted if the
|
||||
* completion mode is set to @c CompletionNone or @c EchoMode is
|
||||
* @em normal.
|
||||
*/
|
||||
void completion(const QString &);
|
||||
|
||||
/**
|
||||
* Emitted when the shortcut for substring completion is pressed.
|
||||
*/
|
||||
void substringCompletion(const QString &);
|
||||
|
||||
/**
|
||||
* Emitted when the text rotation key-bindings are pressed.
|
||||
*
|
||||
* The argument indicates which key-binding was pressed.
|
||||
* In KLineEdit's case this can be either one of two values:
|
||||
* PrevCompletionMatch or NextCompletionMatch. See
|
||||
* KCompletionBase::setKeyBinding for details.
|
||||
*
|
||||
* Note that this signal is @em not emitted if the completion
|
||||
* mode is set to @c CompletionNone or @c echoMode() is @em not normal.
|
||||
*/
|
||||
void textRotation(KCompletionBase::KeyBindingType);
|
||||
|
||||
/**
|
||||
* Emitted when the user changed the completion mode by using the
|
||||
* popupmenu.
|
||||
*/
|
||||
void completionModeChanged(KCompletion::CompletionMode);
|
||||
|
||||
/**
|
||||
* Emitted before the context menu is displayed.
|
||||
*
|
||||
* The signal allows you to add your own entries into the
|
||||
* the context menu that is created on demand.
|
||||
*
|
||||
* NOTE: Do not store the pointer to the QMenu
|
||||
* provided through since it is created and deleted
|
||||
* on demand.
|
||||
*
|
||||
* @param contextMenu the context menu about to be displayed
|
||||
*/
|
||||
void aboutToShowContextMenu(QMenu *contextMenu);
|
||||
|
||||
/**
|
||||
* Emitted when the user clicked on the clear button
|
||||
*/
|
||||
void clearButtonClicked();
|
||||
|
||||
public Q_SLOTS:
|
||||
|
||||
/**
|
||||
* Sets the lineedit to read-only. Similar to QLineEdit::setReadOnly
|
||||
* but also takes care of the background color, and the clear button.
|
||||
*/
|
||||
virtual void setReadOnly(bool);
|
||||
|
||||
/**
|
||||
* Iterates through all possible matches of the completed text or
|
||||
* the history list.
|
||||
*
|
||||
* This function simply iterates over all possible matches in case
|
||||
* multiple matches are found as a result of a text completion request.
|
||||
* It will have no effect if only a single match is found.
|
||||
*
|
||||
* @param type The key-binding invoked.
|
||||
*/
|
||||
void rotateText(KCompletionBase::KeyBindingType type);
|
||||
|
||||
/**
|
||||
* See KCompletionBase::setCompletedText.
|
||||
*/
|
||||
void setCompletedText(const QString &) override;
|
||||
|
||||
/**
|
||||
* Same as the above function except it allows you to temporarily
|
||||
* turn off text completion in CompletionPopupAuto mode.
|
||||
*
|
||||
*
|
||||
* @param items list of completion matches to be shown in the completion box.
|
||||
* @param autoSuggest true if you want automatic text completion (suggestion) enabled.
|
||||
*/
|
||||
void setCompletedItems(const QStringList &items, bool autoSuggest = true) override;
|
||||
|
||||
/**
|
||||
* Squeezes @p text into the line edit.
|
||||
* This can only be used with read-only line-edits.
|
||||
*/
|
||||
void setSqueezedText(const QString &text);
|
||||
|
||||
/**
|
||||
* Reimplemented to enable text squeezing. API is not affected.
|
||||
*/
|
||||
virtual void setText(const QString &);
|
||||
|
||||
protected Q_SLOTS:
|
||||
|
||||
/**
|
||||
* Completes the remaining text with a matching one from
|
||||
* a given list.
|
||||
*/
|
||||
virtual void makeCompletion(const QString &);
|
||||
|
||||
/**
|
||||
* Resets the current displayed text.
|
||||
* Call this function to revert a text completion if the user
|
||||
* cancels the request. Mostly applies to popup completions.
|
||||
*/
|
||||
void userCancelled(const QString &cancelText);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Reimplemented for internal reasons. API not affected.
|
||||
*/
|
||||
bool event(QEvent *) override;
|
||||
|
||||
/**
|
||||
* Reimplemented for internal reasons. API not affected.
|
||||
*
|
||||
* See QLineEdit::resizeEvent().
|
||||
*/
|
||||
void resizeEvent(QResizeEvent *) override;
|
||||
|
||||
/**
|
||||
* Reimplemented for internal reasons. API not affected.
|
||||
*
|
||||
* See QLineEdit::keyPressEvent().
|
||||
*/
|
||||
void keyPressEvent(QKeyEvent *) override;
|
||||
|
||||
/**
|
||||
* Reimplemented for internal reasons. API not affected.
|
||||
*
|
||||
* See QLineEdit::mousePressEvent().
|
||||
*/
|
||||
void mousePressEvent(QMouseEvent *) override;
|
||||
|
||||
/**
|
||||
* Reimplemented for internal reasons. API not affected.
|
||||
*
|
||||
* See QLineEdit::mouseReleaseEvent().
|
||||
*/
|
||||
void mouseReleaseEvent(QMouseEvent *) override;
|
||||
|
||||
/**
|
||||
* Reimplemented for internal reasons. API not affected.
|
||||
*
|
||||
* See QWidget::mouseDoubleClickEvent().
|
||||
*/
|
||||
void mouseDoubleClickEvent(QMouseEvent *) override;
|
||||
|
||||
/**
|
||||
* Reimplemented for internal reasons. API not affected.
|
||||
*
|
||||
* See QLineEdit::contextMenuEvent().
|
||||
*/
|
||||
void contextMenuEvent(QContextMenuEvent *) override;
|
||||
|
||||
/**
|
||||
* Reimplemented for internal reasons. API not affected.
|
||||
*
|
||||
* See QLineEdit::createStandardContextMenu().
|
||||
*/
|
||||
QMenu *createStandardContextMenu();
|
||||
|
||||
/**
|
||||
* This function simply sets the lineedit text and
|
||||
* highlights the text appropriately if the boolean
|
||||
* value is set to true.
|
||||
*
|
||||
* @param text
|
||||
* @param marked
|
||||
*/
|
||||
virtual void setCompletedText(const QString & /*text*/, bool /*marked*/);
|
||||
|
||||
/**
|
||||
* Sets the widget in userSelection mode or in automatic completion
|
||||
* selection mode. This changes the colors of selections.
|
||||
*/
|
||||
void setUserSelection(bool userSelection);
|
||||
|
||||
/**
|
||||
* Whether in current state text should be auto-suggested
|
||||
*/
|
||||
bool autoSuggest() const;
|
||||
|
||||
void paintEvent(QPaintEvent *ev) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<KLineEditPrivate> const d_ptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2007 Aaron Seigo <aseigo@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KLINEEDIT_P_H
|
||||
#define KLINEEDIT_P_H
|
||||
|
||||
#include "klineedit.h"
|
||||
|
||||
class KCompletionBox;
|
||||
class KLineEditUrlDropEventFilter;
|
||||
|
||||
class KLineEditPrivate
|
||||
{
|
||||
public:
|
||||
explicit KLineEditPrivate(KLineEdit *parent)
|
||||
: q_ptr(parent)
|
||||
{
|
||||
}
|
||||
|
||||
~KLineEditPrivate();
|
||||
|
||||
void _k_textChanged(const QString &text);
|
||||
void _k_completionMenuActivated(QAction *act);
|
||||
void _k_tripleClickTimeout(); // resets possibleTripleClick
|
||||
void _k_restoreSelectionColors();
|
||||
void _k_completionBoxTextChanged(const QString &text);
|
||||
|
||||
void updateUserText(const QString &text);
|
||||
|
||||
/**
|
||||
* Checks whether we should/should not consume a key used as a shortcut.
|
||||
* This makes it possible to handle shortcuts in the focused widget before any
|
||||
* window-global QAction is triggered.
|
||||
*/
|
||||
bool overrideShortcut(const QKeyEvent *e);
|
||||
|
||||
void init();
|
||||
|
||||
bool copySqueezedText(bool copy) const;
|
||||
|
||||
/**
|
||||
* Properly sets the squeezed text whenever the widget is
|
||||
* created or resized.
|
||||
*/
|
||||
void setSqueezedText();
|
||||
|
||||
QMap<KCompletion::CompletionMode, bool> disableCompletionMap;
|
||||
|
||||
QColor previousHighlightColor;
|
||||
QColor previousHighlightedTextColor;
|
||||
|
||||
QPalette::ColorRole bgRole;
|
||||
|
||||
QString squeezedText;
|
||||
QString userText;
|
||||
QString lastStyleClass;
|
||||
|
||||
QMetaObject::Connection m_matchesConnection;
|
||||
KCompletionBox *completionBox;
|
||||
|
||||
KLineEditUrlDropEventFilter *urlDropEventFilter;
|
||||
|
||||
QAction *noCompletionAction;
|
||||
QAction *shellCompletionAction;
|
||||
QAction *autoCompletionAction;
|
||||
QAction *popupCompletionAction;
|
||||
QAction *shortAutoCompletionAction;
|
||||
QAction *popupAutoCompletionAction;
|
||||
QAction *defaultAction;
|
||||
|
||||
KLineEdit *const q_ptr;
|
||||
|
||||
int squeezedEnd;
|
||||
int squeezedStart;
|
||||
|
||||
static bool s_initialized;
|
||||
static bool s_backspacePerformsCompletion; // Configuration option
|
||||
|
||||
bool userSelection : 1;
|
||||
bool autoSuggest : 1;
|
||||
bool disableRestoreSelection : 1;
|
||||
bool handleURLDrops : 1;
|
||||
bool trapReturnKeyEvents : 1;
|
||||
bool enableSqueezedText : 1;
|
||||
bool completionRunning : 1;
|
||||
bool italicizePlaceholder : 1;
|
||||
bool threeStars : 1;
|
||||
bool possibleTripleClick : 1; // set in mousePressEvent, deleted in tripleClickTimeout
|
||||
Q_DECLARE_PUBLIC(KLineEdit)
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2001 Carsten Pfeiffer <pfeiffer@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KSORTABLELIST_H
|
||||
#define KSORTABLELIST_H
|
||||
|
||||
#include <kcompletion_export.h>
|
||||
|
||||
#include <QList>
|
||||
#include <QPair>
|
||||
#include <algorithm>
|
||||
|
||||
/**
|
||||
* \class KSortableItem ksortablelist.h <KSortableItem>
|
||||
*
|
||||
* KSortableItem is a QPair that provides several operators
|
||||
* for sorting.
|
||||
* @see KSortableList
|
||||
*/
|
||||
template<typename T, typename Key = int>
|
||||
class KSortableItem : public QPair<Key, T>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates a new KSortableItem with the given values.
|
||||
* @param i the first value (the key)
|
||||
* @param t the second value (the item)
|
||||
*/
|
||||
KSortableItem(Key i, const T &t)
|
||||
: QPair<Key, T>(i, t)
|
||||
{
|
||||
}
|
||||
/**
|
||||
* Creates a new KSortableItem that copies another one.
|
||||
* @param rhs the other item to copy
|
||||
*/
|
||||
KSortableItem(const KSortableItem<T, Key> &rhs)
|
||||
: QPair<Key, T>(rhs.first, rhs.second)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new KSortableItem with uninitialized values.
|
||||
*/
|
||||
KSortableItem()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Assignment operator, just copies the item.
|
||||
*/
|
||||
KSortableItem<T, Key> &operator=(const KSortableItem<T, Key> &i)
|
||||
{
|
||||
this->first = i.first;
|
||||
this->second = i.second;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// operators for sorting
|
||||
/**
|
||||
* Compares the two items. This implementation only compares
|
||||
* the first value.
|
||||
*/
|
||||
bool operator>(const KSortableItem<T, Key> &i2) const
|
||||
{
|
||||
return (i2.first < this->first);
|
||||
}
|
||||
/**
|
||||
* Compares the two items. This implementation only compares
|
||||
* the first value.
|
||||
*/
|
||||
bool operator<(const KSortableItem<T, Key> &i2) const
|
||||
{
|
||||
return (this->first < i2.first);
|
||||
}
|
||||
/**
|
||||
* Compares the two items. This implementation only compares
|
||||
* the first value.
|
||||
*/
|
||||
bool operator>=(const KSortableItem<T, Key> &i2) const
|
||||
{
|
||||
return (this->first >= i2.first);
|
||||
}
|
||||
/**
|
||||
* Compares the two items. This implementation only compares
|
||||
* the first value.
|
||||
*/
|
||||
bool operator<=(const KSortableItem<T, Key> &i2) const
|
||||
{
|
||||
return !(i2.first < this->first);
|
||||
}
|
||||
/**
|
||||
* Compares the two items. This implementation only compares
|
||||
* the first value.
|
||||
*/
|
||||
bool operator==(const KSortableItem<T, Key> &i2) const
|
||||
{
|
||||
return (this->first == i2.first);
|
||||
}
|
||||
/**
|
||||
* Compares the two items. This implementation only compares
|
||||
* the first value.
|
||||
*/
|
||||
bool operator!=(const KSortableItem<T, Key> &i2) const
|
||||
{
|
||||
return (this->first != i2.first);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the second value (the item)
|
||||
*/
|
||||
T &value()
|
||||
{
|
||||
return this->second;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the second value (the item)
|
||||
*/
|
||||
const T &value() const
|
||||
{
|
||||
return this->second;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the first value.
|
||||
*/
|
||||
Key key() const
|
||||
{
|
||||
return this->first;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \class KSortableList ksortablelist.h <KSortableList>
|
||||
*
|
||||
* KSortableList is a QList which associates a key with each item in the list.
|
||||
* This key is used for sorting when calling sort().
|
||||
*
|
||||
* This allows to temporarily calculate a key and use it for sorting, without having
|
||||
* to store that key in the items, or calculate that key many times for the same item
|
||||
* during sorting if that calculation is expensive.
|
||||
*/
|
||||
template<typename T, typename Key = int>
|
||||
class KSortableList : public QList<KSortableItem<T, Key>>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Insert a KSortableItem with the given values.
|
||||
* @param i the first value
|
||||
* @param t the second value
|
||||
*/
|
||||
void insert(Key i, const T &t)
|
||||
{
|
||||
QList<KSortableItem<T, Key>>::append(KSortableItem<T, Key>(i, t));
|
||||
}
|
||||
// add more as you please...
|
||||
|
||||
/**
|
||||
* Returns the first value of the KSortableItem at the given position.
|
||||
* @return the first value of the KSortableItem
|
||||
*/
|
||||
T &operator[](Key i)
|
||||
{
|
||||
return QList<KSortableItem<T, Key>>::operator[](i).value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first value of the KSortableItem at the given position.
|
||||
* @return the first value of the KSortableItem
|
||||
*/
|
||||
const T &operator[](Key i) const
|
||||
{
|
||||
return QList<KSortableItem<T, Key>>::operator[](i).value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the KSortableItems.
|
||||
*/
|
||||
void sort()
|
||||
{
|
||||
std::sort(this->begin(), this->end());
|
||||
}
|
||||
};
|
||||
|
||||
#endif // KSORTABLELIST_H
|
||||
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 1999 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2002 Michael Matz <matz@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
/* Fast zone memory allocator with deallocation support, for use as obstack
|
||||
or as general purpose allocator. It does no compaction. If the usage
|
||||
pattern is non-optimal it might waste some memory while running. E.g.
|
||||
allocating many small things at once, and then deallocating only every
|
||||
second one, there is a high chance, that actually no memory is freed.
|
||||
*/
|
||||
|
||||
#include "kzoneallocator_p.h"
|
||||
|
||||
#include <kcompletion_debug.h>
|
||||
|
||||
#include <QList>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
class KZoneAllocator::MemBlock
|
||||
{
|
||||
public:
|
||||
MemBlock(size_t s)
|
||||
: size(s)
|
||||
, ref(0)
|
||||
, older(nullptr)
|
||||
, newer(nullptr)
|
||||
{
|
||||
begin = new char[s];
|
||||
}
|
||||
~MemBlock()
|
||||
{
|
||||
delete[] begin;
|
||||
}
|
||||
MemBlock(const MemBlock &) = delete;
|
||||
MemBlock &operator=(const MemBlock &) = delete;
|
||||
bool is_in(void *ptr) const
|
||||
{
|
||||
return !(begin > (char *)ptr || (begin + size) <= (char *)ptr);
|
||||
}
|
||||
size_t size;
|
||||
unsigned int ref;
|
||||
char *begin;
|
||||
MemBlock *older;
|
||||
MemBlock *newer;
|
||||
};
|
||||
|
||||
class KZoneAllocator::Private
|
||||
{
|
||||
public:
|
||||
Private()
|
||||
: currentBlock(nullptr)
|
||||
, blockSize(1)
|
||||
, blockOffset(0)
|
||||
, log2(0)
|
||||
, num_blocks(0)
|
||||
, hashList(nullptr)
|
||||
, hashSize(0)
|
||||
, hashDirty(true)
|
||||
{
|
||||
}
|
||||
|
||||
/** One block is 'current' to satisfy requests. @internal */
|
||||
MemBlock *currentBlock;
|
||||
/** Store block size from constructor. @internal */
|
||||
quintptr blockSize;
|
||||
/** Store offset into current block; size-offset is free. @internal */
|
||||
quintptr blockOffset;
|
||||
/** base-2 log of the block size. @internal */
|
||||
unsigned int log2;
|
||||
/** Count total number of allocated blocks. @internal */
|
||||
unsigned int num_blocks;
|
||||
/** Collection of lists of blocks, for lookups. @internal */
|
||||
MemList **hashList;
|
||||
/** Count of hashes. @internal */
|
||||
unsigned int hashSize;
|
||||
/** Flag the hashes as in need of reorganization. @internal */
|
||||
bool hashDirty;
|
||||
};
|
||||
|
||||
KZoneAllocator::KZoneAllocator(unsigned long _blockSize)
|
||||
: d(new Private)
|
||||
{
|
||||
while (d->blockSize < _blockSize) {
|
||||
d->blockSize <<= 1;
|
||||
d->log2++;
|
||||
}
|
||||
|
||||
/* Make sure, that a block is allocated at the first time allocate()
|
||||
is called (even with a 0 size). */
|
||||
d->blockOffset = d->blockSize + 1;
|
||||
}
|
||||
|
||||
KZoneAllocator::~KZoneAllocator()
|
||||
{
|
||||
unsigned int count = 0;
|
||||
if (d->hashList) {
|
||||
/* No need to maintain the different lists in d->hashList[] anymore.
|
||||
I.e. no need to use delBlock(). */
|
||||
for (unsigned int i = 0; i < d->hashSize; i++) {
|
||||
delete d->hashList[i];
|
||||
}
|
||||
delete[] d->hashList;
|
||||
d->hashList = nullptr;
|
||||
}
|
||||
MemBlock *next;
|
||||
for (; d->currentBlock; d->currentBlock = next) {
|
||||
next = d->currentBlock->older;
|
||||
delete d->currentBlock;
|
||||
count++;
|
||||
}
|
||||
#ifndef NDEBUG // as this is called quite late in the app, we don't care
|
||||
// to use qDebug
|
||||
if (count > 1) {
|
||||
fprintf(stderr, "zone still contained %u blocks", count);
|
||||
}
|
||||
#endif
|
||||
delete d;
|
||||
}
|
||||
|
||||
void KZoneAllocator::insertHash(MemBlock *b)
|
||||
{
|
||||
quintptr adr = ((quintptr)b->begin) & (~(d->blockSize - 1));
|
||||
quintptr end = ((quintptr)b->begin) + d->blockSize;
|
||||
while (adr < end) {
|
||||
quintptr key = adr >> d->log2;
|
||||
key = key & (d->hashSize - 1);
|
||||
if (!d->hashList[key]) {
|
||||
d->hashList[key] = new QList<MemBlock *>;
|
||||
}
|
||||
d->hashList[key]->append(b);
|
||||
adr += d->blockSize;
|
||||
}
|
||||
}
|
||||
|
||||
/** Add a new memory block to the pool of blocks,
|
||||
and reorganize the hash lists if needed.
|
||||
@param b block to add
|
||||
@internal
|
||||
*/
|
||||
void KZoneAllocator::addBlock(MemBlock *b)
|
||||
{
|
||||
b->newer = nullptr;
|
||||
b->older = d->currentBlock;
|
||||
if (d->currentBlock) {
|
||||
b->older->newer = b;
|
||||
}
|
||||
d->currentBlock = b;
|
||||
d->num_blocks++;
|
||||
/* If we either have no d->hashList at all, or since it's last construction
|
||||
there are now many more blocks we reconstruct the list. But don't
|
||||
make it larger than a certain maximum. */
|
||||
if (d->hashList && ((d->num_blocks / 4) > d->hashSize && d->hashSize < 64 * 1024)) {
|
||||
d->hashDirty = true;
|
||||
}
|
||||
/* Only insert this block into the hashlists, if we aren't going to
|
||||
reconstruct them anyway. */
|
||||
if (d->hashList && !d->hashDirty) {
|
||||
insertHash(b);
|
||||
}
|
||||
}
|
||||
|
||||
/** Reinitialize hash list. @internal */
|
||||
void KZoneAllocator::initHash()
|
||||
{
|
||||
if (d->hashList) {
|
||||
for (unsigned int i = 0; i < d->hashSize; i++) {
|
||||
delete d->hashList[i];
|
||||
}
|
||||
delete[] d->hashList;
|
||||
d->hashList = nullptr;
|
||||
}
|
||||
d->hashSize = 1;
|
||||
while (d->hashSize < d->num_blocks) {
|
||||
d->hashSize <<= 1;
|
||||
}
|
||||
if (d->hashSize < 1024) {
|
||||
d->hashSize = 1024;
|
||||
}
|
||||
if (d->hashSize > 64 * 1024) {
|
||||
d->hashSize = 64 * 1024;
|
||||
}
|
||||
d->hashList = new QList<MemBlock *> *[d->hashSize];
|
||||
memset(d->hashList, 0, sizeof(QList<MemBlock *> *) * d->hashSize);
|
||||
d->hashDirty = false;
|
||||
for (MemBlock *b = d->currentBlock; b; b = b->older) {
|
||||
insertHash(b);
|
||||
}
|
||||
}
|
||||
|
||||
/** Delete a memory block. This @em really returns the memory to the heap.
|
||||
@param b block to delete
|
||||
@internal
|
||||
*/
|
||||
void KZoneAllocator::delBlock(MemBlock *b)
|
||||
{
|
||||
/* Update also the hashlists if we aren't going to reconstruct them
|
||||
soon. */
|
||||
if (d->hashList && !d->hashDirty) {
|
||||
quintptr adr = ((quintptr)b->begin) & (~(d->blockSize - 1));
|
||||
quintptr end = ((quintptr)b->begin) + d->blockSize;
|
||||
while (adr < end) {
|
||||
quintptr key = adr >> d->log2;
|
||||
key = key & (d->hashSize - 1);
|
||||
if (d->hashList[key]) {
|
||||
QList<MemBlock *> *list = d->hashList[key];
|
||||
QList<MemBlock *>::Iterator it = list->begin();
|
||||
QList<MemBlock *>::Iterator endit = list->end();
|
||||
for (; it != endit; ++it) {
|
||||
if (*it == b) {
|
||||
list->erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
adr += d->blockSize;
|
||||
}
|
||||
}
|
||||
if (b->older) {
|
||||
b->older->newer = b->newer;
|
||||
}
|
||||
if (b->newer) {
|
||||
b->newer->older = b->older;
|
||||
}
|
||||
if (b == d->currentBlock) {
|
||||
d->currentBlock = nullptr;
|
||||
d->blockOffset = d->blockSize;
|
||||
}
|
||||
delete b;
|
||||
d->num_blocks--;
|
||||
}
|
||||
|
||||
void *KZoneAllocator::allocate(size_t _size)
|
||||
{
|
||||
// Use the size of (void *) as alignment
|
||||
const size_t alignment = sizeof(void *) - 1;
|
||||
_size = (_size + alignment) & ~alignment;
|
||||
|
||||
if ((unsigned long)_size + d->blockOffset > d->blockSize) {
|
||||
if (_size > d->blockSize) {
|
||||
qCDebug(KCOMPLETION_LOG, "KZoneAllocator: allocating more than %zu bytes", (size_t)d->blockSize);
|
||||
return nullptr;
|
||||
}
|
||||
addBlock(new MemBlock(d->blockSize));
|
||||
d->blockOffset = 0;
|
||||
// qDebug ("Allocating block #%d (%x)\n", d->num_blocks, d->currentBlock->begin);
|
||||
}
|
||||
void *result = (void *)(d->currentBlock->begin + d->blockOffset);
|
||||
d->currentBlock->ref++;
|
||||
d->blockOffset += _size;
|
||||
return result;
|
||||
}
|
||||
|
||||
void KZoneAllocator::deallocate(void *ptr)
|
||||
{
|
||||
if (d->hashDirty) {
|
||||
initHash();
|
||||
}
|
||||
|
||||
quintptr key = (((quintptr)ptr) >> d->log2) & (d->hashSize - 1);
|
||||
const QList<MemBlock *> *list = d->hashList[key];
|
||||
if (!list) {
|
||||
/* Can happen with certain usage pattern of intermixed free_since()
|
||||
and deallocate(). */
|
||||
// qDebug("Uhoh");
|
||||
return;
|
||||
}
|
||||
QList<MemBlock *>::ConstIterator it = list->begin();
|
||||
QList<MemBlock *>::ConstIterator endit = list->end();
|
||||
for (; it != endit; ++it) {
|
||||
MemBlock *cur = *it;
|
||||
if (cur->is_in(ptr)) {
|
||||
if (!--cur->ref) {
|
||||
if (cur != d->currentBlock) {
|
||||
delBlock(cur);
|
||||
} else {
|
||||
d->blockOffset = 0;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Can happen with certain usage pattern of intermixed free_since()
|
||||
and deallocate(). */
|
||||
// qDebug("Uhoh2");
|
||||
}
|
||||
|
||||
void KZoneAllocator::free_since(void *ptr)
|
||||
{
|
||||
/* If we have a d->hashList and it's not yet dirty, see, if we will dirty
|
||||
it by removing too many blocks. This will make the below delBlock()s
|
||||
faster. */
|
||||
if (d->hashList && !d->hashDirty) {
|
||||
const MemBlock *b;
|
||||
unsigned int removed = 0;
|
||||
for (b = d->currentBlock; b; b = b->older, removed++) {
|
||||
if (b->is_in(ptr)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (d->hashSize >= 4 * (d->num_blocks - removed)) {
|
||||
d->hashDirty = true;
|
||||
}
|
||||
}
|
||||
while (d->currentBlock && !d->currentBlock->is_in(ptr)) {
|
||||
d->currentBlock = d->currentBlock->older;
|
||||
delBlock(d->currentBlock->newer);
|
||||
}
|
||||
d->blockOffset = ((char *)ptr) - d->currentBlock->begin;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 1999 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2002 Michael Matz <matz@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// KDE Memory Allocator
|
||||
|
||||
#ifndef KZONEALLOCATOR_P_H
|
||||
#define KZONEALLOCATOR_P_H
|
||||
|
||||
#include <cstddef> // size_t
|
||||
|
||||
template<typename T>
|
||||
class QList;
|
||||
|
||||
/**
|
||||
* \class KZoneAllocator kallocator.h <KZoneAllocator>
|
||||
*
|
||||
* Memory allocator for large groups of small objects.
|
||||
* This should be used for large groups of objects that are created and
|
||||
* destroyed together. When used carefully for this purpose it is faster
|
||||
* and more memory efficient than malloc. Additionally to a usual obstack
|
||||
* like allocator you can also free the objects individually. Because it
|
||||
* does no compaction it still is faster than malloc()/free(). Depending
|
||||
* on the exact usage pattern that might come at the expense of some
|
||||
* memory though.
|
||||
* @author Waldo Bastian <bastian@kde.org>, Michael Matz <matz@kde.org>
|
||||
*/
|
||||
class KZoneAllocator
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates a KZoneAllocator object.
|
||||
* @param _blockSize Size in bytes of the blocks requested from malloc.
|
||||
*/
|
||||
explicit KZoneAllocator(unsigned long _blockSize = 8 * 1024);
|
||||
|
||||
/**
|
||||
* Destructs the ZoneAllocator and free all memory allocated by it.
|
||||
*/
|
||||
~KZoneAllocator();
|
||||
|
||||
KZoneAllocator(const KZoneAllocator &) = delete;
|
||||
KZoneAllocator &operator=(const KZoneAllocator &) = delete;
|
||||
|
||||
/**
|
||||
* Allocates a memory block.
|
||||
* @param _size Size in bytes of the memory block. Memory is aligned to
|
||||
* the size of a pointer.
|
||||
*/
|
||||
void *allocate(size_t _size);
|
||||
|
||||
/**
|
||||
* Gives back a block returned by allocate() to the zone
|
||||
* allocator, and possibly deallocates the block holding it (when it's
|
||||
* empty). The first deallocate() after many allocate() calls
|
||||
* (or the first at all) builds an internal data structure for speeding
|
||||
* up deallocation. The consistency of that structure is maintained
|
||||
* from then on (by allocate() and deallocate()) unless many
|
||||
* more objects are allocated without any intervening deallocation, in
|
||||
* which case it's thrown away and rebuilt at the next deallocate().
|
||||
*
|
||||
* The effect of this is, that such initial deallocate() calls take
|
||||
* more time then the normal calls, and that after this list is built, i.e.
|
||||
* generally if deallocate() is used at all, also allocate() is a
|
||||
* little bit slower. This means, that if you want to squeeze out the last
|
||||
* bit performance you would want to use KZoneAllocator as an obstack, i.e.
|
||||
* just use the functions allocate() and free_since(). All the
|
||||
* remaining memory is returned to the system if the zone allocator
|
||||
* is destroyed.
|
||||
* @param ptr Pointer as returned by allocate().
|
||||
*/
|
||||
void deallocate(void *ptr);
|
||||
|
||||
/**
|
||||
* Deallocate many objects at once.
|
||||
* free_since() deallocates all objects allocated after @p ptr,
|
||||
* @em including @p ptr itself.
|
||||
*
|
||||
* The intended use is something along the lines of:
|
||||
* \code
|
||||
* KZoneAllocator alloc(8192);
|
||||
* void *remember_me = alloc.allocate(0);
|
||||
* for (int i = 0; i < 1000; i++)
|
||||
* do_something_with (alloc.allocate(12));
|
||||
* alloc.free_since (remember_me);
|
||||
* \endcode
|
||||
* Note, that we don't need to remember all the pointers to the 12-byte
|
||||
* objects for freeing them. The free_since() does deallocate them
|
||||
* all at once.
|
||||
* @param ptr Pointer as returned by allocate(). It acts like
|
||||
* a kind of mark of a certain position in the stack of all objects,
|
||||
* off which you can throw away everything above that mark.
|
||||
*/
|
||||
void free_since(void *ptr);
|
||||
|
||||
protected:
|
||||
/** A single chunk of memory from the heap. @internal */
|
||||
class MemBlock;
|
||||
/**< A list of chunks. @internal */
|
||||
typedef QList<MemBlock *> MemList;
|
||||
void addBlock(MemBlock *b);
|
||||
void delBlock(MemBlock *b);
|
||||
void insertHash(MemBlock *b);
|
||||
void initHash();
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *const d;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user