kdelibs/kdeui/widgets/keditlistwidget.cpp
Ivailo Monev 82fc15f54b generic: use CMake moc instead of automoc4 by default
for compatibilty reasons automoc4 support is not removed but it
shall be in the future. automoc4 has not been maintained for a
while (last commit is from 2011) and the stable release is from
2009.

CMake version >= 2.8.6 provides the functionality for mocking so
I see no reason to not make use of it.
2015-02-27 07:40:26 +00:00

666 lines
18 KiB
C++

/* This file is part of the KDE libraries
Copyright (C) 2000 David Faure <faure@kde.org>, Alexander Neundorf <neundorf@kde.org>
(C) 2000, 2002 Carsten Pfeiffer <pfeiffer@kde.org>
(C) 2010 Sebastian Trueg <trueg@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "keditlistwidget.h"
#include <QtCore/QStringList>
#include <QtGui/QKeyEvent>
#include <QtGui/QLabel>
#include <QtGui/QLayout>
#include <QtGui/QListView>
#include <kcombobox.h>
#include <kdebug.h>
#include <kdialog.h>
#include <klineedit.h>
#include <klocale.h>
#include <knotification.h>
#include <kpushbutton.h>
#include <assert.h>
class KEditListWidgetPrivate
{
public:
KEditListWidgetPrivate( KEditListWidget* parent )
: lineEdit(0),
editingWidget(0),
q(parent) {
}
QListView *listView;
QPushButton *servUpButton, *servDownButton;
QPushButton *servNewButton, *servRemoveButton;
KLineEdit *lineEdit;
QWidget* editingWidget;
QVBoxLayout* mainLayout;
QVBoxLayout* btnsLayout;
QStringListModel *model;
bool checkAtEntering;
KEditListWidget::Buttons buttons;
void init( bool check = false, KEditListWidget::Buttons buttons = KEditListWidget::All,
QWidget *representationWidget = 0 );
void setEditor( KLineEdit* lineEdit, QWidget* representationWidget = 0 );
void updateButtonState();
QModelIndex selectedIndex();
private:
KEditListWidget* q;
};
void KEditListWidgetPrivate::init( bool check, KEditListWidget::Buttons newButtons,
QWidget *representationWidget )
{
checkAtEntering = check;
servNewButton = servRemoveButton = servUpButton = servDownButton = 0L;
q->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding,
QSizePolicy::Preferred));
mainLayout = new QVBoxLayout(q);
QHBoxLayout* subLayout = new QHBoxLayout;
btnsLayout = new QVBoxLayout;
btnsLayout->addStretch();
model = new QStringListModel();
listView = new QListView(q);
listView->setModel(model);
subLayout->addWidget(listView);
subLayout->addLayout(btnsLayout);
mainLayout->insertLayout(1, subLayout);
setEditor( lineEdit, representationWidget );
buttons = 0;
q->setButtons( newButtons );
q->connect(listView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
SLOT(slotSelectionChanged(QItemSelection,QItemSelection)));
}
void KEditListWidgetPrivate::setEditor( KLineEdit* newLineEdit, QWidget* representationWidget )
{
if (editingWidget != lineEdit &&
editingWidget != representationWidget) {
delete editingWidget;
}
if (lineEdit != newLineEdit) {
delete lineEdit;
}
lineEdit = newLineEdit ? newLineEdit : new KLineEdit(q);
editingWidget = representationWidget ?
representationWidget : lineEdit;
if ( representationWidget )
representationWidget->setParent(q);
mainLayout->insertWidget(0,editingWidget);
lineEdit->setTrapReturnKey(true);
lineEdit->installEventFilter(q);
q->connect(lineEdit,SIGNAL(textChanged(QString)),SLOT(typedSomething(QString)));
q->connect(lineEdit,SIGNAL(returnPressed()),SLOT(addItem()));
// maybe supplied lineedit has some text already
q->typedSomething( lineEdit->text() );
// fix tab ordering
q->setTabOrder(editingWidget, listView);
QWidget* w = listView;
if (servNewButton) {
q->setTabOrder(w,servNewButton);
w = servNewButton;
}
if (servRemoveButton) {
q->setTabOrder(w,servRemoveButton);
w = servRemoveButton;
}
if (servUpButton) {
q->setTabOrder(w,servUpButton);
w = servUpButton;
}
if (servDownButton) {
q->setTabOrder(w,servDownButton);
w = servDownButton;
}
}
void KEditListWidgetPrivate::updateButtonState()
{
QModelIndex index = selectedIndex();
if (servUpButton) {
servUpButton->setEnabled(index.isValid());
}
if (servDownButton) {
servDownButton->setEnabled(index.isValid());
}
if (servRemoveButton) {
servRemoveButton->setEnabled(index.isValid());
}
}
QModelIndex KEditListWidgetPrivate::selectedIndex()
{
QItemSelectionModel *selection = listView->selectionModel();
const QModelIndexList selectedIndexes = selection->selectedIndexes();
if ( !selectedIndexes.isEmpty() && selectedIndexes[0].isValid() )
return selectedIndexes[0];
else
return QModelIndex();
}
class KEditListWidget::CustomEditorPrivate
{
public:
CustomEditorPrivate(KEditListWidget::CustomEditor *q):
q(q),
representationWidget(0),
lineEdit(0) {}
KEditListWidget::CustomEditor *q;
QWidget *representationWidget;
KLineEdit *lineEdit;
};
KEditListWidget::CustomEditor::CustomEditor()
: d(new CustomEditorPrivate(this))
{
}
KEditListWidget::CustomEditor::CustomEditor( QWidget *repWidget, KLineEdit *edit )
: d(new CustomEditorPrivate(this))
{
d->representationWidget = repWidget;
d->lineEdit = edit;
}
KEditListWidget::CustomEditor::CustomEditor( KComboBox *combo )
: d(new CustomEditorPrivate(this))
{
d->representationWidget = combo;
d->lineEdit = qobject_cast<KLineEdit*>( combo->lineEdit() );
Q_ASSERT( d->lineEdit );
}
KEditListWidget::CustomEditor::~CustomEditor()
{
delete d;
}
void KEditListWidget::CustomEditor::setRepresentationWidget( QWidget *repWidget )
{
d->representationWidget = repWidget;
}
void KEditListWidget::CustomEditor::setLineEdit( KLineEdit *edit )
{
d->lineEdit = edit;
}
QWidget *KEditListWidget::CustomEditor::representationWidget() const
{
return d->representationWidget;
}
KLineEdit *KEditListWidget::CustomEditor::lineEdit() const
{
return d->lineEdit;
}
KEditListWidget::KEditListWidget(QWidget *parent)
: QWidget(parent), d(new KEditListWidgetPrivate(this))
{
d->init();
}
KEditListWidget::KEditListWidget(const CustomEditor& custom,
QWidget *parent,
bool checkAtEntering,
Buttons buttons)
:QWidget(parent), d(new KEditListWidgetPrivate(this))
{
d->lineEdit = custom.lineEdit();
d->init( checkAtEntering, buttons, custom.representationWidget() );
}
KEditListWidget::~KEditListWidget()
{
delete d;
}
void KEditListWidget::setCustomEditor( const CustomEditor& editor )
{
d->setEditor( editor.lineEdit(), editor.representationWidget() );
}
QListView *KEditListWidget::listView() const
{
return d->listView;
}
KLineEdit *KEditListWidget::lineEdit() const
{
return d->lineEdit;
}
QPushButton *KEditListWidget::addButton() const
{
return d->servNewButton;
}
QPushButton *KEditListWidget::removeButton() const
{
return d->servRemoveButton;
}
QPushButton *KEditListWidget::upButton() const
{
return d->servUpButton;
}
QPushButton *KEditListWidget::downButton() const
{
return d->servDownButton;
}
int KEditListWidget::count() const
{
return int(d->model->rowCount());
}
void KEditListWidget::setButtons( Buttons buttons )
{
if ( d->buttons == buttons )
return;
if ( ( buttons & Add ) && !d->servNewButton ) {
d->servNewButton = new KPushButton(KIcon("list-add"), i18n("&Add"), this);
d->servNewButton->setEnabled(false);
d->servNewButton->show();
connect(d->servNewButton, SIGNAL(clicked()), SLOT(addItem()));
d->btnsLayout->insertWidget(0, d->servNewButton);
} else if ( ( buttons & Add ) == 0 && d->servNewButton ) {
delete d->servNewButton;
d->servNewButton = 0;
}
if ( ( buttons & Remove ) && !d->servRemoveButton ) {
d->servRemoveButton = new KPushButton(KIcon("list-remove"), i18n("&Remove"), this);
d->servRemoveButton->setEnabled(false);
d->servRemoveButton->show();
connect(d->servRemoveButton, SIGNAL(clicked()), SLOT(removeItem()));
d->btnsLayout->insertWidget(1, d->servRemoveButton);
} else if ( ( buttons & Remove ) == 0 && d->servRemoveButton ) {
delete d->servRemoveButton;
d->servRemoveButton = 0;
}
if ( ( buttons & UpDown ) && !d->servUpButton ) {
d->servUpButton = new KPushButton(KIcon("arrow-up"), i18n("Move &Up"), this);
d->servUpButton->setEnabled(false);
d->servUpButton->show();
connect(d->servUpButton, SIGNAL(clicked()), SLOT(moveItemUp()));
d->servDownButton = new KPushButton(KIcon("arrow-down"), i18n("Move &Down"), this);
d->servDownButton->setEnabled(false);
d->servDownButton->show();
connect(d->servDownButton, SIGNAL(clicked()), SLOT(moveItemDown()));
d->btnsLayout->insertWidget(2, d->servUpButton);
d->btnsLayout->insertWidget(3, d->servDownButton);
} else if ( ( buttons & UpDown ) == 0 && d->servUpButton ) {
delete d->servUpButton; d->servUpButton = 0;
delete d->servDownButton; d->servDownButton = 0;
}
d->buttons = buttons;
}
void KEditListWidget::setCheckAtEntering(bool check)
{
d->checkAtEntering = check;
}
bool KEditListWidget::checkAtEntering()
{
return d->checkAtEntering;
}
void KEditListWidget::typedSomething(const QString& text)
{
if(currentItem() >= 0) {
if(currentText() != d->lineEdit->text())
{
// IMHO changeItem() shouldn't do anything with the value
// of currentItem() ... like changing it or emitting signals ...
// but TT disagree with me on this one (it's been that way since ages ... grrr)
bool block = d->listView->signalsBlocked();
d->listView->blockSignals( true );
QModelIndex currentIndex = d->selectedIndex();
if ( currentIndex.isValid() )
d->model->setData(currentIndex,text);
d->listView->blockSignals( block );
emit changed();
}
}
if ( !d->servNewButton )
return;
if ( !d->lineEdit->hasAcceptableInput() ) {
d->servNewButton->setEnabled(false);
return;
}
if (!d->checkAtEntering)
d->servNewButton->setEnabled(!text.isEmpty());
else
{
if (text.isEmpty())
{
d->servNewButton->setEnabled(false);
}
else
{
QStringList list = d->model->stringList();
bool enable = !list.contains( text, Qt::CaseSensitive );
d->servNewButton->setEnabled( enable );
}
}
}
void KEditListWidget::moveItemUp()
{
if (!d->listView->isEnabled())
{
KNotification::beep();
return;
}
QModelIndex index = d->selectedIndex();
if ( index.isValid() ) {
if (index.row() == 0) {
KNotification::beep();
return;
}
QModelIndex aboveIndex = d->model->index( index.row() - 1, index.column() );
QString tmp = d->model->data( aboveIndex, Qt::DisplayRole ).toString();
d->model->setData( aboveIndex, d->model->data( index, Qt::DisplayRole ) );
d->model->setData( index, tmp );
d->listView->selectionModel()->select(index, QItemSelectionModel::Deselect);
d->listView->selectionModel()->select(aboveIndex, QItemSelectionModel::Select);
}
emit changed();
}
void KEditListWidget::moveItemDown()
{
if (!d->listView->isEnabled())
{
KNotification::beep();
return;
}
QModelIndex index = d->selectedIndex();
if ( index.isValid() ) {
if (index.row() == d->model->rowCount() - 1) {
KNotification::beep();
return;
}
QModelIndex belowIndex = d->model->index( index.row() + 1, index.column() );
QString tmp = d->model->data( belowIndex, Qt::DisplayRole ).toString();
d->model->setData( belowIndex, d->model->data( index, Qt::DisplayRole ) );
d->model->setData( index, tmp );
d->listView->selectionModel()->select(index, QItemSelectionModel::Deselect);
d->listView->selectionModel()->select(belowIndex, QItemSelectionModel::Select);
}
emit changed();
}
void KEditListWidget::addItem()
{
// when checkAtEntering is true, the add-button is disabled, but this
// slot can still be called through Key_Return/Key_Enter. So we guard
// against this.
if ( !d->servNewButton || !d->servNewButton->isEnabled() )
return;
QModelIndex currentIndex = d->selectedIndex();
const QString& currentTextLE=d->lineEdit->text();
bool alreadyInList(false);
//if we didn't check for dupes at the inserting we have to do it now
if (!d->checkAtEntering)
{
// first check current item instead of dumb iterating the entire list
if ( currentIndex.isValid() ) {
if ( d->model->data( currentIndex, Qt::DisplayRole ).toString() == currentTextLE )
alreadyInList = true;
}
else
{
alreadyInList = d->model->stringList().contains( currentTextLE, Qt::CaseSensitive );
}
}
if ( d->servNewButton )
d->servNewButton->setEnabled(false);
bool block = d->lineEdit->signalsBlocked();
d->lineEdit->blockSignals(true);
d->lineEdit->clear();
d->lineEdit->blockSignals(block);
d->listView->selectionModel()->setCurrentIndex(currentIndex, QItemSelectionModel::Deselect);
if (!alreadyInList)
{
block = d->listView->signalsBlocked();
if ( currentIndex.isValid() ) {
d->model->setData(currentIndex, currentTextLE );
} else {
QStringList lst;
lst<<currentTextLE;
lst<<d->model->stringList();
d->model->setStringList(lst);
}
emit changed();
emit added( currentTextLE ); // TODO: pass the index too
}
d->updateButtonState();
}
int KEditListWidget::currentItem() const
{
QModelIndex selectedIndex = d->selectedIndex();
if ( selectedIndex.isValid() )
return selectedIndex.row();
else
return -1;
}
void KEditListWidget::removeItem()
{
QModelIndex currentIndex = d->selectedIndex();
if ( !currentIndex.isValid() )
return;
if ( currentIndex.row() >= 0 )
{
QString removedText = d->model->data( currentIndex, Qt::DisplayRole ).toString();
d->model->removeRows( currentIndex.row(), 1 );
d->listView->selectionModel()->clear();
emit changed();
emit removed( removedText );
}
d->updateButtonState();
}
void KEditListWidget::enableMoveButtons(const QModelIndex &newIndex, const QModelIndex&)
{
int index = newIndex.row();
// Update the lineEdit when we select a different line.
if(currentText() != d->lineEdit->text())
d->lineEdit->setText(currentText());
bool moveEnabled = d->servUpButton && d->servDownButton;
if (moveEnabled )
{
if (d->model->rowCount() <= 1)
{
d->servUpButton->setEnabled(false);
d->servDownButton->setEnabled(false);
}
else if (index == (d->model->rowCount() - 1))
{
d->servUpButton->setEnabled(true);
d->servDownButton->setEnabled(false);
}
else if (index == 0)
{
d->servUpButton->setEnabled(false);
d->servDownButton->setEnabled(true);
}
else
{
d->servUpButton->setEnabled(true);
d->servDownButton->setEnabled(true);
}
}
if ( d->servRemoveButton )
d->servRemoveButton->setEnabled(true);
}
void KEditListWidget::clear()
{
d->lineEdit->clear();
d->model->setStringList( QStringList() );
emit changed();
}
void KEditListWidget::insertStringList(const QStringList& list, int index)
{
QStringList content = d->model->stringList();
if ( index < 0 )
content += list;
else
for ( int i = 0, j = index; i < list.count(); ++i, ++j )
content.insert( j, list[ i ] );
d->model->setStringList( content );
}
void KEditListWidget::insertItem(const QString& text, int index)
{
QStringList list = d->model->stringList();
if ( index < 0 )
list.append( text );
else
list.insert( index, text );
d->model->setStringList(list);
}
QString KEditListWidget::text(int index) const
{
const QStringList list = d->model->stringList();
return list[ index ];
}
QString KEditListWidget::currentText() const
{
QModelIndex index = d->selectedIndex();
if ( !index.isValid() )
return QString();
else
return text( index.row() );
}
QStringList KEditListWidget::items() const
{
return d->model->stringList();
}
void KEditListWidget::setItems(const QStringList& items)
{
d->model->setStringList(items);
}
KEditListWidget::Buttons KEditListWidget::buttons() const
{
return d->buttons;
}
void KEditListWidget::slotSelectionChanged( const QItemSelection&, const QItemSelection& )
{
d->updateButtonState();
QModelIndex index = d->selectedIndex();
enableMoveButtons(index, QModelIndex());
if (index.isValid()) {
d->lineEdit->setFocus( Qt::OtherFocusReason );
}
}
bool KEditListWidget::eventFilter( QObject* o, QEvent* e )
{
if (o == d->lineEdit && e->type() == QEvent::KeyPress ) {
QKeyEvent* keyEvent = (QKeyEvent*)e;
if (keyEvent->key() == Qt::Key_Down ||
keyEvent->key() == Qt::Key_Up) {
return ((QObject*)d->listView)->event(e);
}
}
return false;
}
#include "moc_keditlistwidget.cpp"