2 * \file GuiSelectionManager.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Richard Kimberly Heck
9 * Some of the material in this file previously appeared in
10 * GuiCitationDialog.cpp.
12 * Full author contact details are available in file CREDITS.
17 #include "GuiSelectionManager.h"
18 #include "qt_helpers.h"
20 #include "support/debug.h"
22 #include <QAbstractItemModel>
23 #include <QAbstractListModel>
24 #include <QItemSelection>
27 #include <QPushButton>
33 #ifdef ControlModifier
34 #undef ControlModifier
45 GuiSelectionManager::GuiSelectionManager(QObject * parent,
46 QAbstractItemView * avail,
47 QAbstractItemView * sel,
52 QAbstractItemModel * amod,
53 QAbstractItemModel * smod,
54 int const main_sel_col)
55 : QObject(parent), availableLV(avail), selectedLV(sel),
56 addPB(add), deletePB(del), upPB(up), downPB(down),
57 availableModel(amod), selectedModel(smod),
58 selectedHasFocus_(false), main_sel_col_(main_sel_col),
59 allow_multi_selection_(false)
61 selectedLV->setModel(smod);
62 availableLV->setModel(amod);
63 selectedLV->setSelectionBehavior(QAbstractItemView::SelectRows);
64 selectedLV->setSelectionMode(QAbstractItemView::SingleSelection);
66 connect(availableLV->selectionModel(),
67 SIGNAL(currentChanged(QModelIndex, QModelIndex)),
68 this, SLOT(availableChanged(QModelIndex, QModelIndex)));
69 connect(selectedLV->selectionModel(),
70 SIGNAL(currentChanged(QModelIndex, QModelIndex)),
71 this, SLOT(selectedChanged(QModelIndex, QModelIndex)));
72 connect(availableLV->selectionModel(),
73 SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
74 this, SLOT(availableChanged(QItemSelection, QItemSelection)));
75 connect(availableLV->selectionModel(),
76 SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
77 this, SLOT(updateButtons()));
78 connect(selectedLV->selectionModel(),
79 SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
80 this, SLOT(selectedChanged(QItemSelection, QItemSelection)));
81 connect(selectedLV->selectionModel(),
82 SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
83 this, SLOT(updateButtons()));
84 connect(selectedLV->itemDelegate(), SIGNAL(commitData(QWidget*)),
85 this, SLOT(selectedEdited()));
86 connect(addPB, SIGNAL(clicked()),
87 this, SLOT(addPB_clicked()));
88 connect(deletePB, SIGNAL(clicked()),
89 this, SLOT(deletePB_clicked()));
90 connect(upPB, SIGNAL(clicked()),
91 this, SLOT(upPB_clicked()));
92 connect(downPB, SIGNAL(clicked()),
93 this, SLOT(downPB_clicked()));
94 connect(availableLV, SIGNAL(doubleClicked(QModelIndex)),
95 this, SLOT(availableLV_doubleClicked(QModelIndex)));
97 availableLV->installEventFilter(this);
98 selectedLV->installEventFilter(this);
102 void GuiSelectionManager::update()
111 void GuiSelectionManager::updateButtons()
118 QModelIndex GuiSelectionManager::getSelectedIndex(int const c) const
120 QModelIndexList avail = availableLV->selectionModel()->selectedIndexes();
121 QModelIndexList sel = selectedLV->selectionModel()->selectedRows(c);
122 bool const have_avl = !avail.isEmpty();
123 bool const have_sel = !sel.isEmpty();
125 if (selectedFocused()) {
129 return avail.first();
131 else { // available has focus
133 return avail.first();
137 return QModelIndex();
141 void GuiSelectionManager::updateAddPB()
143 int const arows = availableModel->rowCount();
144 QModelIndexList const availSels =
145 availableLV->selectionModel()->selectedIndexes();
146 addPB->setEnabled(arows > 0 &&
147 !availSels.isEmpty() &&
148 (allow_multi_selection_ || !isSelected(availSels.first())));
152 void GuiSelectionManager::updateDelPB()
154 int const srows = selectedModel->rowCount();
156 deletePB->setEnabled(false);
159 QModelIndexList const selSels =
160 selectedLV->selectionModel()->selectedIndexes();
161 int const sel_nr = selSels.empty() ? -1 : selSels.first().row();
162 deletePB->setEnabled(sel_nr >= 0);
166 void GuiSelectionManager::updateUpPB()
168 int const srows = selectedModel->rowCount();
170 upPB->setEnabled(false);
173 QModelIndexList const selSels =
174 selectedLV->selectionModel()->selectedIndexes();
175 int const sel_nr = selSels.empty() ? -1 : selSels.first().row();
176 upPB->setEnabled(sel_nr > 0);
180 void GuiSelectionManager::updateDownPB()
182 int const srows = selectedModel->rowCount();
184 downPB->setEnabled(false);
187 QModelIndexList const selSels =
188 selectedLV->selectionModel()->selectedIndexes();
189 int const sel_nr = selSels.empty() ? -1 : selSels.first().row();
190 downPB->setEnabled(sel_nr >= 0 && sel_nr < srows - 1);
194 bool GuiSelectionManager::isSelected(const QModelIndex & idx)
196 if (selectedModel->rowCount() == 0)
198 QVariant const & str = availableModel->data(idx, Qt::DisplayRole);
199 QModelIndexList qmil =
200 selectedModel->match(selectedModel->index(0, main_sel_col_),
201 Qt::DisplayRole, str, 1,
202 Qt::MatchFlags(Qt::MatchExactly | Qt::MatchWrap));
203 return !qmil.empty();
207 void GuiSelectionManager::availableChanged(QItemSelection const & qis, QItemSelection const &)
209 QModelIndexList il = qis.indexes();
212 availableChanged(il.front(), QModelIndex());
216 void GuiSelectionManager::availableChanged(const QModelIndex & idx, const QModelIndex &)
221 selectedHasFocus_ = false;
226 void GuiSelectionManager::selectedChanged(QItemSelection const & qis, QItemSelection const &)
228 QModelIndexList il = qis.indexes();
231 selectedChanged(il.front(), QModelIndex());
235 void GuiSelectionManager::selectedChanged(const QModelIndex & idx, const QModelIndex &)
240 selectedHasFocus_ = true;
245 void GuiSelectionManager::selectedEdited()
251 bool GuiSelectionManager::insertRowToSelected(int i,
252 QMap<int, QVariant> const & itemData)
256 if (i > selectedModel->rowCount())
257 i = selectedModel->rowCount();
258 if (!selectedModel->insertRow(i))
260 return selectedModel->setItemData(selectedModel->index(i, main_sel_col_), itemData);
264 bool GuiSelectionManager::insertRowToSelected(int i, QMap<int, QMap<int, QVariant>> & qms)
268 if (i > selectedModel->rowCount())
269 i = selectedModel->rowCount();
270 if (!selectedModel->insertRow(i))
273 QMap<int, QMap<int, QVariant>>::const_iterator it = qms.constBegin();
274 for (; it != qms.constEnd(); ++it)
275 res &= selectedModel->setItemData(selectedModel->index(i, it.key()), it.value());
280 void GuiSelectionManager::addPB_clicked()
282 QModelIndexList selIdx =
283 availableLV->selectionModel()->selectedIndexes();
284 if (selIdx.isEmpty())
287 QModelIndex const idxToAdd = selIdx.first();
288 // Add item after selected item
289 int const currentRow = selectedLV->currentIndex().row();
290 int const srows = currentRow == -1 ? selectedModel->rowCount() :
293 QMap<int, QVariant> qm = availableModel->itemData(idxToAdd);
294 bool const isAdded = insertRowToSelected(srows, qm);
296 selectionChanged(); //signal
298 QModelIndex const idx = selectedLV->currentIndex();
300 selectedLV->setCurrentIndex(idx);
302 // select and show last added item
304 QModelIndex idx = selectedModel->index(srows, 0);
305 selectedLV->setCurrentIndex(idx);
312 void GuiSelectionManager::deletePB_clicked()
314 QModelIndexList selIdx =
315 selectedLV->selectionModel()->selectedIndexes();
316 if (selIdx.isEmpty())
318 QModelIndex idx = selIdx.first();
320 int const row = idx.row();
322 selectedModel->removeRow(row);
323 selectionChanged(); //signal
325 int nrows = selectedLV->model()->rowCount();
327 // select following item if one follows,
328 // otherwise use previous one if we have one
330 selectedLV->setCurrentIndex(selectedLV->model()->index(row, 0));
331 else if (nrows > 0 && row > 0)
332 selectedLV->setCurrentIndex(selectedLV->model()->index(row - 1, 0));
333 selectedHasFocus_ = (nrows > 0);
338 void GuiSelectionManager::upPB_clicked()
340 QModelIndexList selIdx =
341 selectedLV->selectionModel()->selectedIndexes();
342 if (selIdx.isEmpty())
344 QModelIndex idx = selIdx.first();
346 int const pos = idx.row();
350 QMap<int, QMap<int, QVariant>> qms;
351 QList<QModelIndex>::const_iterator it = selIdx.constBegin();
352 for (; it != selIdx.constEnd(); ++it)
353 qms[it->column()] = selectedModel->itemData(*it);
355 selectedModel->removeRow(pos);
356 insertRowToSelected(pos - 1, qms);
358 idx = selIdx.first();
359 selectedLV->setCurrentIndex(idx.sibling(idx.row() - 1, idx.column()));
360 selectedHasFocus_ = true;
365 void GuiSelectionManager::downPB_clicked()
367 QModelIndexList selIdx =
368 selectedLV->selectionModel()->selectedIndexes();
369 if (selIdx.isEmpty())
371 QModelIndex idx = selIdx.first();
373 int const pos = idx.row();
374 if (pos >= selectedModel->rowCount() - 1)
377 QMap<int, QMap<int, QVariant>> qms;
378 QList<QModelIndex>::const_iterator it = selIdx.constBegin();
379 for (; it != selIdx.constEnd(); ++it)
380 qms[it->column()] = selectedModel->itemData(*it);
382 selectedModel->removeRow(pos);
383 insertRowToSelected(pos + 1, qms);
385 idx = selIdx.first();
386 selectedLV->setCurrentIndex(idx.sibling(idx.row() + 1, idx.column()));
387 selectedHasFocus_ = true;
392 void GuiSelectionManager::availableLV_doubleClicked(const QModelIndex & idx)
394 if (isSelected(idx) || !addPB->isEnabled())
398 selectedHasFocus_ = false;
400 //updateHook() will be emitted there
404 bool GuiSelectionManager::eventFilter(QObject * obj, QEvent * event)
406 QEvent::Type etype = event->type();
407 if (obj == availableLV) {
408 if (etype == QEvent::KeyPress) {
409 QKeyEvent * keyEvent = static_cast<QKeyEvent *>(event);
410 int const keyPressed = keyEvent->key();
411 Qt::KeyboardModifiers const keyModifiers = keyEvent->modifiers();
412 // Enter key without modifier will add current item.
413 // Ctrl-Enter will add it and close the dialog.
414 // This is designed to work both with the main enter key
415 // and the one on the numeric keypad.
416 if (keyPressed == Qt::Key_Enter || keyPressed == Qt::Key_Return) {
418 keyModifiers == Qt::ControlModifier ||
419 keyModifiers == Qt::KeypadModifier ||
420 keyModifiers == (Qt::ControlModifier
421 | Qt::KeypadModifier)) {
422 if (addPB->isEnabled()) {
431 else if (keyPressed == Qt::Key_Right) {
432 QModelIndex const idx = availableLV->currentIndex();
433 if (availableLV->model()->hasChildren(idx)) { // skip for headers
436 focusAndHighlight(selectedLV);
440 } else if (etype == QEvent::FocusIn) {
441 if (selectedHasFocus_) {
442 selectedHasFocus_ = false;
447 } else if (obj == selectedLV) {
448 if (etype == QEvent::KeyPress) {
449 QKeyEvent * keyEvent = static_cast<QKeyEvent *>(event);
450 int const keyPressed = keyEvent->key();
451 Qt::KeyboardModifiers const keyModifiers = keyEvent->modifiers();
452 // Delete or backspace key will delete current item
453 // ...with control modifier will clear the list
454 if (keyPressed == Qt::Key_Delete || keyPressed == Qt::Key_Backspace) {
455 if (keyModifiers == Qt::NoModifier && deletePB->isEnabled()) {
458 } else if (keyModifiers == Qt::ControlModifier) {
459 selectedModel->removeRows(0, selectedModel->rowCount());
462 return QObject::eventFilter(obj, event);
464 // Ctrl-Up activates upPB
465 else if (keyPressed == Qt::Key_Up) {
466 if (keyModifiers == Qt::ControlModifier) {
467 if (upPB->isEnabled())
473 // Ctrl-Down activates downPB
474 else if (keyPressed == Qt::Key_Down) {
475 if (keyModifiers == Qt::ControlModifier) {
476 if (downPB->isEnabled())
482 else if (keyPressed == Qt::Key_Left) {
483 focusAndHighlight(availableLV);
487 } else if (etype == QEvent::FocusIn) {
488 if (!selectedHasFocus_) {
489 selectedHasFocus_ = true;
495 return QObject::eventFilter(obj, event);
498 } // namespace frontend
501 #include "moc_GuiSelectionManager.cpp"