KDELibs4Support

keditlistbox.cpp
1 /* This file is part of the KDE libraries
2  Copyright (C) 2000 David Faure <[email protected]>, Alexander Neundorf <[email protected]>
3  2000, 2002 Carsten Pfeiffer <[email protected]>
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Library General Public
7  License as published by the Free Software Foundation; either
8  version 2 of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Library General Public License for more details.
14 
15  You should have received a copy of the GNU Library General Public License
16  along with this library; see the file COPYING.LIB. If not, write to
17  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  Boston, MA 02110-1301, USA.
19 */
20 
21 #include "keditlistbox.h"
22 
23 #include <QStringList>
24 #include <QKeyEvent>
25 #include <QLabel>
26 #include <QLayout>
27 #include <QListView>
28 #include <QPushButton>
29 
30 #include <kcombobox.h>
31 #include <kdebug.h>
32 #include <klineedit.h>
33 #include <klocalizedstring.h>
34 #include <knotification.h>
35 
36 #include <assert.h>
37 
38 class KEditListBoxPrivate
39 {
40 public:
41  KEditListBoxPrivate(KEditListBox *parent)
42  : lineEdit(nullptr),
43  editingWidget(nullptr),
44  q(parent)
45  {
46  }
47  QListView *listView;
48  QPushButton *servUpButton, *servDownButton;
49  QPushButton *servNewButton, *servRemoveButton;
50  KLineEdit *lineEdit;
51  QWidget *editingWidget;
52  QVBoxLayout *mainLayout;
53  QVBoxLayout *btnsLayout;
54  QStringListModel *model;
55 
56  bool checkAtEntering;
57  KEditListBox::Buttons buttons;
58 
59  void init(bool check = false, KEditListBox::Buttons buttons = KEditListBox::All,
60  QWidget *representationWidget = nullptr);
61  void setEditor(KLineEdit *lineEdit, QWidget *representationWidget = nullptr);
62  void updateButtonState();
63  QModelIndex selectedIndex();
64 
65 private:
66  KEditListBox *q;
67 };
68 
69 void KEditListBoxPrivate::init(bool check, KEditListBox::Buttons newButtons,
70  QWidget *representationWidget)
71 {
72  checkAtEntering = check;
73 
74  servNewButton = servRemoveButton = servUpButton = servDownButton = nullptr;
75  q->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding,
76  QSizePolicy::Preferred));
77 
78  mainLayout = new QVBoxLayout(q);
79 
80  QHBoxLayout *subLayout = new QHBoxLayout;
81  btnsLayout = new QVBoxLayout;
82  btnsLayout->addStretch();
83 
84  model = new QStringListModel();
85  listView = new QListView(q);
86  listView->setModel(model);
87 
88  subLayout->addWidget(listView);
89  subLayout->addLayout(btnsLayout);
90 
91  mainLayout->insertLayout(1, subLayout);
92 
93  setEditor(lineEdit, representationWidget);
94 
95  buttons = nullptr;
96  q->setButtons(newButtons);
97 
98  q->connect(listView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
99  SLOT(slotSelectionChanged(QItemSelection,QItemSelection)));
100 }
101 
102 void KEditListBoxPrivate::setEditor(KLineEdit *newLineEdit, QWidget *representationWidget)
103 {
104  if (editingWidget != lineEdit &&
105  editingWidget != representationWidget) {
106  delete editingWidget;
107  }
108  if (lineEdit != newLineEdit) {
109  delete lineEdit;
110  }
111  lineEdit = newLineEdit ? newLineEdit : new KLineEdit(q);
112  editingWidget = representationWidget ?
113  representationWidget : lineEdit;
114 
115  if (representationWidget) {
116  representationWidget->setParent(q);
117  }
118 
119  mainLayout->insertWidget(0, editingWidget);
120 
121  lineEdit->setTrapReturnKey(true);
122  lineEdit->installEventFilter(q);
123 
124  q->connect(lineEdit, SIGNAL(textChanged(QString)), SLOT(typedSomething(QString)));
125  q->connect(lineEdit, SIGNAL(returnPressed()), SLOT(addItem()));
126 
127  // maybe supplied lineedit has some text already
128  q->typedSomething(lineEdit->text());
129 
130  // fix tab ordering
131  q->setTabOrder(editingWidget, listView);
132  QWidget *w = listView;
133  if (servNewButton) {
134  q->setTabOrder(w, servNewButton);
135  w = servNewButton;
136  }
137  if (servRemoveButton) {
138  q->setTabOrder(w, servRemoveButton);
139  w = servRemoveButton;
140  }
141  if (servUpButton) {
142  q->setTabOrder(w, servUpButton);
143  w = servUpButton;
144  }
145  if (servDownButton) {
146  q->setTabOrder(w, servDownButton);
147  w = servDownButton;
148  }
149 }
150 
151 void KEditListBoxPrivate::updateButtonState()
152 {
153  QModelIndex index = selectedIndex();
154  if (servUpButton) {
155  servUpButton->setEnabled(index.isValid());
156  }
157  if (servDownButton) {
158  servDownButton->setEnabled(index.isValid());
159  }
160  if (servRemoveButton) {
161  servRemoveButton->setEnabled(index.isValid());
162  }
163 }
164 
165 QModelIndex KEditListBoxPrivate::selectedIndex()
166 {
167  QItemSelectionModel *selection = listView->selectionModel();
168  const QModelIndexList selectedIndexes = selection->selectedIndexes();
169  if (!selectedIndexes.isEmpty() && selectedIndexes[0].isValid()) {
170  return selectedIndexes[0];
171  } else {
172  return QModelIndex();
173  }
174 }
175 
176 class Q_DECL_HIDDEN KEditListBox::CustomEditorPrivate
177 {
178 public:
179  CustomEditorPrivate(KEditListBox::CustomEditor *q):
180  q(q),
181  representationWidget(nullptr),
182  lineEdit(nullptr) {}
183 
185  QWidget *representationWidget;
186  KLineEdit *lineEdit;
187 };
188 
189 KEditListBox::CustomEditor::CustomEditor()
190  : d(new CustomEditorPrivate(this))
191 {
192 }
193 
194 KEditListBox::CustomEditor::CustomEditor(QWidget *repWidget, KLineEdit *edit)
195  : d(new CustomEditorPrivate(this))
196 {
197  d->representationWidget = repWidget;
198  d->lineEdit = edit;
199 }
200 
201 KEditListBox::CustomEditor::CustomEditor(KComboBox *combo)
202  : d(new CustomEditorPrivate(this))
203 {
204  d->representationWidget = combo;
205  d->lineEdit = qobject_cast<KLineEdit *>(combo->lineEdit());
206  Q_ASSERT(d->lineEdit);
207 }
208 
209 KEditListBox::CustomEditor::~CustomEditor()
210 {
211  delete d;
212 }
213 
214 void KEditListBox::CustomEditor::setRepresentationWidget(QWidget *repWidget)
215 {
216  d->representationWidget = repWidget;
217 }
218 
219 void KEditListBox::CustomEditor::setLineEdit(KLineEdit *edit)
220 {
221  d->lineEdit = edit;
222 }
223 
224 QWidget *KEditListBox::CustomEditor::representationWidget() const
225 {
226  return d->representationWidget;
227 }
228 
229 KLineEdit *KEditListBox::CustomEditor::lineEdit() const
230 {
231  return d->lineEdit;
232 }
233 
235  : QGroupBox(parent), d(new KEditListBoxPrivate(this))
236 {
237  d->init();
238 }
239 
241  : QGroupBox(title, parent), d(new KEditListBoxPrivate(this))
242 {
243  d->init();
244 }
245 
248  : QGroupBox(parent), d(new KEditListBoxPrivate(this))
249 {
250  setObjectName(name);
251  d->init(checkAtEntering, buttons);
252 }
253 
255  const char *name, bool checkAtEntering, Buttons buttons)
256  : QGroupBox(title, parent), d(new KEditListBoxPrivate(this))
257 {
258  setObjectName(name);
259  d->init(checkAtEntering, buttons);
260 }
261 
263  QWidget *parent, const char *name,
265  : QGroupBox(title, parent), d(new KEditListBoxPrivate(this))
266 {
267  setObjectName(name);
268  d->lineEdit = custom.lineEdit();
269  d->init(checkAtEntering, buttons, custom.representationWidget());
270 }
271 
272 KEditListBox::~KEditListBox()
273 {
274  delete d;
275 }
276 
278 {
279  d->setEditor(editor.lineEdit(), editor.representationWidget());
280 }
281 
283 {
284  return d->listView;
285 }
286 
288 {
289  return d->lineEdit;
290 }
291 
293 {
294  return d->servNewButton;
295 }
296 
298 {
299  return d->servRemoveButton;
300 }
301 
303 {
304  return d->servUpButton;
305 }
306 
308 {
309  return d->servDownButton;
310 }
311 
313 {
314  return int(d->model->rowCount());
315 }
316 
318 {
319  if (d->buttons == buttons) {
320  return;
321  }
322 
323  if ((buttons & Add) && !d->servNewButton) {
324  d->servNewButton = new QPushButton(QIcon::fromTheme("list-add"), i18n("&Add"), this);
325  d->servNewButton->setEnabled(false);
326  d->servNewButton->show();
327  connect(d->servNewButton, SIGNAL(clicked()), SLOT(addItem()));
328 
329  d->btnsLayout->insertWidget(0, d->servNewButton);
330  } else if ((buttons & Add) == 0 && d->servNewButton) {
331  delete d->servNewButton;
332  d->servNewButton = nullptr;
333  }
334 
335  if ((buttons & Remove) && !d->servRemoveButton) {
336  d->servRemoveButton = new QPushButton(QIcon::fromTheme("list-remove"), i18n("&Remove"), this);
337  d->servRemoveButton->setEnabled(false);
338  d->servRemoveButton->show();
339  connect(d->servRemoveButton, SIGNAL(clicked()), SLOT(removeItem()));
340 
341  d->btnsLayout->insertWidget(1, d->servRemoveButton);
342  } else if ((buttons & Remove) == 0 && d->servRemoveButton) {
343  delete d->servRemoveButton;
344  d->servRemoveButton = nullptr;
345  }
346 
347  if ((buttons & UpDown) && !d->servUpButton) {
348  d->servUpButton = new QPushButton(QIcon::fromTheme("arrow-up"), i18n("Move &Up"), this);
349  d->servUpButton->setEnabled(false);
350  d->servUpButton->show();
351  connect(d->servUpButton, SIGNAL(clicked()), SLOT(moveItemUp()));
352 
353  d->servDownButton = new QPushButton(QIcon::fromTheme("arrow-down"), i18n("Move &Down"), this);
354  d->servDownButton->setEnabled(false);
355  d->servDownButton->show();
356  connect(d->servDownButton, SIGNAL(clicked()), SLOT(moveItemDown()));
357 
358  d->btnsLayout->insertWidget(2, d->servUpButton);
359  d->btnsLayout->insertWidget(3, d->servDownButton);
360  } else if ((buttons & UpDown) == 0 && d->servUpButton) {
361  delete d->servUpButton; d->servUpButton = nullptr;
362  delete d->servDownButton; d->servDownButton = nullptr;
363  }
364 
365  d->buttons = buttons;
366 }
367 
369 {
370  d->checkAtEntering = check;
371 }
372 
374 {
375  return d->checkAtEntering;
376 }
377 
378 void KEditListBox::typedSomething(const QString &text)
379 {
380  if (currentItem() >= 0) {
381  if (currentText() != d->lineEdit->text()) {
382  // IMHO changeItem() shouldn't do anything with the value
383  // of currentItem() ... like changing it or emitting signals ...
384  // but TT disagree with me on this one (it's been that way since ages ... grrr)
385  bool block = d->listView->signalsBlocked();
386  d->listView->blockSignals(true);
387  QModelIndex currentIndex = d->selectedIndex();
388  if (currentIndex.isValid()) {
389  d->model->setData(currentIndex, text);
390  }
391  d->listView->blockSignals(block);
392  emit changed();
393  }
394  }
395 
396  if (!d->servNewButton) {
397  return;
398  }
399 
400  if (!d->lineEdit->hasAcceptableInput()) {
401  d->servNewButton->setEnabled(false);
402  return;
403  }
404 
405  if (!d->checkAtEntering) {
406  d->servNewButton->setEnabled(!text.isEmpty());
407  } else {
408  if (text.isEmpty()) {
409  d->servNewButton->setEnabled(false);
410  } else {
411  QStringList list = d->model->stringList();
412  bool enable = !list.contains(text, Qt::CaseSensitive);
413  d->servNewButton->setEnabled(enable);
414  }
415  }
416 }
417 
418 void KEditListBox::moveItemUp()
419 {
420  if (!d->listView->isEnabled()) {
422  return;
423  }
424 
425  QModelIndex index = d->selectedIndex();
426  if (index.isValid()) {
427  if (index.row() == 0) {
429  return;
430  }
431 
432  QModelIndex aboveIndex = d->model->index(index.row() - 1, index.column());
433 
434  QString tmp = d->model->data(aboveIndex, Qt::DisplayRole).toString();
435  d->model->setData(aboveIndex, d->model->data(index, Qt::DisplayRole));
436  d->model->setData(index, tmp);
437 
438  d->listView->selectionModel()->select(index, QItemSelectionModel::Deselect);
439  d->listView->selectionModel()->select(aboveIndex, QItemSelectionModel::Select);
440  }
441 
442  emit changed();
443 }
444 
445 void KEditListBox::moveItemDown()
446 {
447  if (!d->listView->isEnabled()) {
449  return;
450  }
451 
452  QModelIndex index = d->selectedIndex();
453  if (index.isValid()) {
454  if (index.row() == d->model->rowCount() - 1) {
456  return;
457  }
458 
459  QModelIndex belowIndex = d->model->index(index.row() + 1, index.column());
460 
461  QString tmp = d->model->data(belowIndex, Qt::DisplayRole).toString();
462  d->model->setData(belowIndex, d->model->data(index, Qt::DisplayRole));
463  d->model->setData(index, tmp);
464 
465  d->listView->selectionModel()->select(index, QItemSelectionModel::Deselect);
466  d->listView->selectionModel()->select(belowIndex, QItemSelectionModel::Select);
467  }
468 
469  emit changed();
470 }
471 
472 void KEditListBox::addItem()
473 {
474  // when checkAtEntering is true, the add-button is disabled, but this
475  // slot can still be called through Key_Return/Key_Enter. So we guard
476  // against this.
477  if (!d->servNewButton || !d->servNewButton->isEnabled()) {
478  return;
479  }
480 
481  QModelIndex currentIndex = d->selectedIndex();
482 
483  const QString &currentTextLE = d->lineEdit->text();
484  bool alreadyInList(false);
485  //if we didn't check for dupes at the inserting we have to do it now
486  if (!d->checkAtEntering) {
487  // first check current item instead of dumb iterating the entire list
488  if (currentIndex.isValid()) {
489  if (d->model->data(currentIndex, Qt::DisplayRole).toString() == currentTextLE) {
490  alreadyInList = true;
491  }
492  } else {
493  alreadyInList = d->model->stringList().contains(currentTextLE, Qt::CaseSensitive);
494  }
495  }
496  if (d->servNewButton) {
497  d->servNewButton->setEnabled(false);
498  }
499 
500  bool block = d->lineEdit->signalsBlocked();
501  d->lineEdit->blockSignals(true);
502  d->lineEdit->clear();
503  d->lineEdit->blockSignals(block);
504 
505  d->listView->selectionModel()->setCurrentIndex(currentIndex, QItemSelectionModel::Deselect);
506 
507  if (!alreadyInList) {
508  block = d->listView->signalsBlocked();
509 
510  if (currentIndex.isValid()) {
511  d->model->setData(currentIndex, currentTextLE);
512  } else {
513  QStringList lst;
514  lst << currentTextLE;
515  lst << d->model->stringList();
516  d->model->setStringList(lst);
517  }
518  emit changed();
519  emit added(currentTextLE); // TODO: pass the index too
520  }
521 
522  d->updateButtonState();
523 }
524 
526 {
527  QModelIndex selectedIndex = d->selectedIndex();
528  if (selectedIndex.isValid()) {
529  return selectedIndex.row();
530  } else {
531  return -1;
532  }
533 }
534 
535 void KEditListBox::removeItem()
536 {
537  QModelIndex currentIndex = d->selectedIndex();
538  if (!currentIndex.isValid()) {
539  return;
540  }
541 
542  if (currentIndex.row() >= 0) {
543  QString removedText = d->model->data(currentIndex, Qt::DisplayRole).toString();
544 
545  d->model->removeRows(currentIndex.row(), 1);
546 
547  d->listView->selectionModel()->clear();
548 
549  emit changed();
550 
551  emit removed(removedText);
552  }
553 
554  d->updateButtonState();
555 }
556 
557 void KEditListBox::enableMoveButtons(const QModelIndex &newIndex, const QModelIndex &)
558 {
559  int index = newIndex.row();
560 
561  // Update the lineEdit when we select a different line.
562  if (currentText() != d->lineEdit->text()) {
563  d->lineEdit->setText(currentText());
564  }
565 
566  bool moveEnabled = d->servUpButton && d->servDownButton;
567 
568  if (moveEnabled) {
569  if (d->model->rowCount() <= 1) {
570  d->servUpButton->setEnabled(false);
571  d->servDownButton->setEnabled(false);
572  } else if (index == (d->model->rowCount() - 1)) {
573  d->servUpButton->setEnabled(true);
574  d->servDownButton->setEnabled(false);
575  } else if (index == 0) {
576  d->servUpButton->setEnabled(false);
577  d->servDownButton->setEnabled(true);
578  } else {
579  d->servUpButton->setEnabled(true);
580  d->servDownButton->setEnabled(true);
581  }
582  }
583 
584  if (d->servRemoveButton) {
585  d->servRemoveButton->setEnabled(true);
586  }
587 }
588 
590 {
591  d->lineEdit->clear();
592  d->model->setStringList(QStringList());
593  emit changed();
594 }
595 
596 void KEditListBox::insertStringList(const QStringList &list, int index)
597 {
598  QStringList content = d->model->stringList();
599  if (index < 0) {
600  content += list;
601  } else
602  for (int i = 0, j = index; i < list.count(); ++i, ++j) {
603  content.insert(j, list[ i ]);
604  }
605 
606  d->model->setStringList(content);
607 }
608 
609 void KEditListBox::insertItem(const QString &text, int index)
610 {
611  QStringList list = d->model->stringList();
612 
613  if (index < 0) {
614  list.append(text);
615  } else {
616  list.insert(index, text);
617  }
618 
619  d->model->setStringList(list);
620 }
621 
622 QString KEditListBox::text(int index) const
623 {
624  const QStringList list = d->model->stringList();
625 
626  return list[ index ];
627 }
628 
630 {
631  QModelIndex index = d->selectedIndex();
632  if (!index.isValid()) {
633  return QString();
634  } else {
635  return text(index.row());
636  }
637 }
638 
640 {
641  return d->model->stringList();
642 }
643 
645 {
646  d->model->setStringList(items);
647 }
648 
650 {
651  return d->buttons;
652 }
653 
654 void KEditListBox::slotSelectionChanged(const QItemSelection &, const QItemSelection &)
655 {
656  d->updateButtonState();
657  QModelIndex index = d->selectedIndex();
658  enableMoveButtons(index, QModelIndex());
659  if (index.isValid()) {
660  d->lineEdit->setFocus(Qt::OtherFocusReason);
661  }
662 }
663 
665 {
666  if (o == d->lineEdit && e->type() == QEvent::KeyPress) {
667  QKeyEvent *keyEvent = (QKeyEvent *)e;
668  if (keyEvent->key() == Qt::Key_Down ||
669  keyEvent->key() == Qt::Key_Up) {
670  return ((QObject *)d->listView)->event(e);
671  }
672  }
673 
674  return false;
675 }
int currentItem() const
See Q3ListBox::currentItem()
QEvent::Type type() const const
KEditListBox(QWidget *parent=nullptr)
Create an editable listbox.
static void beep(const QString &reason=QString(), QWidget *widget=nullptr)
bool eventFilter(QObject *o, QEvent *e) override
Reimplented for interal reasons.
void clicked(bool checked)
void added(const QString &text)
This signal is emitted when the user adds a new string to the list, the parameter is the added string...
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
QPushButton * upButton() const
Return a pointer to the Up button.
QString currentText() const
See Q3ListBox::currentText()
QString text(int index) const
See Q3ListBox::text()
void setParent(QWidget *parent)
QPushButton * addButton() const
Return a pointer to the Add button.
Custom editor class.
Definition: keditlistbox.h:54
QUICKADDONS_EXPORT void init()
void setCustomEditor(const CustomEditor &editor)
Allows to use a custom editing widget instead of the standard KLineEdit widget.
void insertStringList(const QStringList &list, int index=-1)
See Q3ListBox::insertStringList()
bool isValid() const const
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
int count(const T &value) const const
void append(const T &value)
void setCheckAtEntering(bool check)
If check is true, after every character you type in the line edit KEditListBox will enable or disable...
int count() const
See Q3ListBox::count()
void setObjectName(const QString &name)
void setTabOrder(QWidget *first, QWidget *second)
bool isEmpty() const const
QPushButton * removeButton() const
Return a pointer to the Remove button.
int row() const const
void setButtons(Buttons buttons)
Specifies which buttons should be visible.
void insertItem(const QString &text, int index=-1)
See Q3ListBox::insertItem()
void setSizePolicy(QSizePolicy)
bool checkAtEntering()
Returns true if check at entering is enabled.
int key() const const
QString i18n(const char *text, const TYPE &arg...)
QLineEdit * lineEdit() const const
An editable listbox.
Definition: keditlistbox.h:40
QStringList items() const
void clear()
Clears both the listbox and the line edit.
void removed(const QString &text)
This signal is emitted when the user removes a string from the list, the parameter is the removed str...
void addStretch(int stretch)
KLineEdit * lineEdit() const
Return a pointer to the embedded KLineEdit.
void insert(int i, const T &value)
QString title() const const
int column() const const
void setItems(const QStringList &items)
Clears the listbox and sets the contents to items.
QIcon fromTheme(const QString &name)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
T qobject_cast(QObject *object)
QObject * parent() const const
Buttons buttons() const
Returns which buttons are visible.
virtual bool event(QEvent *e) override
QPushButton * downButton() const
Return a pointer to the Down button.
void addLayout(QLayout *layout, int stretch)
QListView * listView() const
Return a pointer to the embedded QListView.
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Sat May 30 2020 22:59:46 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.