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;
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 = KEditListBox::Buttons();
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 
246 KEditListBox::KEditListBox(QWidget *parent, const char *name,
247  bool checkAtEntering, Buttons buttons)
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 
262 KEditListBox::KEditListBox(const QString &title, const CustomEditor &custom,
263  QWidget *parent, const char *name,
264  bool checkAtEntering, Buttons buttons)
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 
639 QStringList KEditListBox::items() const
640 {
641  return d->model->stringList();
642 }
643 
645 {
646  d->model->setStringList(items);
647 }
648 
649 KEditListBox::Buttons KEditListBox::buttons() const
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 }
676 
677 #include "moc_keditlistbox.cpp"
QLineEdit * lineEdit() const const
void append(const T &value)
void setParent(QWidget *parent)
DisplayRole
QString text(int index) const
See Q3ListBox::text()
CaseSensitive
QString currentText() const
See Q3ListBox::currentText()
void setSizePolicy(QSizePolicy)
void clear()
Clears both the listbox and the line edit.
QCA_EXPORT void init()
int count(const T &value) const const
int column() const const
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
QIcon fromTheme(const QString &name)
int count() const
See Q3ListBox::count()
KIOFILEWIDGETS_EXPORT QStringList list(const QString &fileClass)
int currentItem() const
See Q3ListBox::currentItem()
void setButtons(Buttons buttons)
Specifies which buttons should be visible.
bool eventFilter(QObject *o, QEvent *e) override
Reimplented for interal reasons.
void addStretch(int stretch)
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...
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void setItems(const QStringList &items)
Clears the listbox and sets the contents to items.
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
QPushButton * upButton() const
Return a pointer to the Up button.
QString i18n(const char *text, const TYPE &arg...)
OtherFocusReason
QPushButton * downButton() const
Return a pointer to the Down button.
bool checkAtEntering()
Returns true if check at entering is enabled.
void setTabOrder(QWidget *first, QWidget *second)
void clicked(bool checked)
bool isEmpty() const const
void removed(const QString &text)
This signal is emitted when the user removes a string from the list, the parameter is the removed str...
QListView * listView() const
Return a pointer to the embedded QListView.
Custom editor class.
Definition: keditlistbox.h:54
Key_Down
static void beep(const QString &reason=QString(), QWidget *widget=nullptr)
bool isValid() const const
void insert(int i, const T &value)
int row() const const
An editable listbox.
Definition: keditlistbox.h:40
void insertItem(const QString &text, int index=-1)
See Q3ListBox::insertItem()
QPushButton * removeButton() const
Return a pointer to the Remove button.
QEvent::Type type() const const
void setObjectName(const QString &name)
virtual bool event(QEvent *e) override
void addLayout(QLayout *layout, int stretch)
KEditListBox(QWidget *parent=nullptr)
Create an editable listbox.
void insertStringList(const QStringList &list, int index=-1)
See Q3ListBox::insertStringList()
void setCustomEditor(const CustomEditor &editor)
Allows to use a custom editing widget instead of the standard KLineEdit widget.
KLineEdit * lineEdit() const
Return a pointer to the embedded KLineEdit.
QObject * parent() const const
QPushButton * addButton() const
Return a pointer to the Add button.
void setCheckAtEntering(bool check)
If check is true, after every character you type in the line edit KEditListBox will enable or disable...
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Sep 26 2023 03:54:13 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.