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 int const srows = selectedModel->rowCount();
290 QMap<int, QVariant> qm = availableModel->itemData(idxToAdd);
291 insertRowToSelected(srows, qm);
293 selectionChanged(); //signal
295 QModelIndex const idx = selectedLV->currentIndex();
297 selectedLV->setCurrentIndex(idx);
303 void GuiSelectionManager::deletePB_clicked()
305 QModelIndexList selIdx =
306 selectedLV->selectionModel()->selectedIndexes();
307 if (selIdx.isEmpty())
309 QModelIndex idx = selIdx.first();
311 int const row = idx.row();
312 int nrows = selectedLV->model()->rowCount();
314 selectedModel->removeRow(row);
315 selectionChanged(); //signal
317 // select previous item
319 selectedLV->setCurrentIndex(selectedLV->model()->index(row - 1, 0));
321 selectedLV->setCurrentIndex(selectedLV->model()->index(0, 0));
322 selectedHasFocus_ = (nrows > 1);
327 void GuiSelectionManager::upPB_clicked()
329 QModelIndexList selIdx =
330 selectedLV->selectionModel()->selectedIndexes();
331 if (selIdx.isEmpty())
333 QModelIndex idx = selIdx.first();
335 int const pos = idx.row();
339 QMap<int, QMap<int, QVariant>> qms;
340 QList<QModelIndex>::const_iterator it = selIdx.constBegin();
341 for (; it != selIdx.constEnd(); ++it)
342 qms[it->column()] = selectedModel->itemData(*it);
344 selectedModel->removeRow(pos);
345 insertRowToSelected(pos - 1, qms);
347 idx = selIdx.first();
348 selectedLV->setCurrentIndex(idx.sibling(idx.row() - 1, idx.column()));
349 selectedHasFocus_ = true;
354 void GuiSelectionManager::downPB_clicked()
356 QModelIndexList selIdx =
357 selectedLV->selectionModel()->selectedIndexes();
358 if (selIdx.isEmpty())
360 QModelIndex idx = selIdx.first();
362 int const pos = idx.row();
363 if (pos >= selectedModel->rowCount() - 1)
366 QMap<int, QMap<int, QVariant>> qms;
367 QList<QModelIndex>::const_iterator it = selIdx.constBegin();
368 for (; it != selIdx.constEnd(); ++it)
369 qms[it->column()] = selectedModel->itemData(*it);
371 selectedModel->removeRow(pos);
372 insertRowToSelected(pos + 1, qms);
374 idx = selIdx.first();
375 selectedLV->setCurrentIndex(idx.sibling(idx.row() + 1, idx.column()));
376 selectedHasFocus_ = true;
381 void GuiSelectionManager::availableLV_doubleClicked(const QModelIndex & idx)
383 if (isSelected(idx) || !addPB->isEnabled())
387 selectedHasFocus_ = false;
389 //updateHook() will be emitted there
393 bool GuiSelectionManager::eventFilter(QObject * obj, QEvent * event)
395 QEvent::Type etype = event->type();
396 if (obj == availableLV) {
397 if (etype == QEvent::KeyPress) {
398 QKeyEvent * keyEvent = static_cast<QKeyEvent *>(event);
399 int const keyPressed = keyEvent->key();
400 Qt::KeyboardModifiers const keyModifiers = keyEvent->modifiers();
401 // Enter key without modifier will add current item.
402 // Ctrl-Enter will add it and close the dialog.
403 // This is designed to work both with the main enter key
404 // and the one on the numeric keypad.
405 if (keyPressed == Qt::Key_Enter || keyPressed == Qt::Key_Return) {
407 keyModifiers == Qt::ControlModifier ||
408 keyModifiers == Qt::KeypadModifier ||
409 keyModifiers == (Qt::ControlModifier
410 | Qt::KeypadModifier)) {
411 if (addPB->isEnabled()) {
420 else if (keyPressed == Qt::Key_Right) {
421 QModelIndex const idx = availableLV->currentIndex();
422 if (availableLV->model()->hasChildren(idx)) { // skip for headers
425 focusAndHighlight(selectedLV);
429 } else if (etype == QEvent::FocusIn) {
430 if (selectedHasFocus_) {
431 selectedHasFocus_ = false;
436 } else if (obj == selectedLV) {
437 if (etype == QEvent::KeyPress) {
438 QKeyEvent * keyEvent = static_cast<QKeyEvent *>(event);
439 int const keyPressed = keyEvent->key();
440 Qt::KeyboardModifiers const keyModifiers = keyEvent->modifiers();
441 // Delete or backspace key will delete current item
442 // ...with control modifier will clear the list
443 if (keyPressed == Qt::Key_Delete || keyPressed == Qt::Key_Backspace) {
444 if (keyModifiers == Qt::NoModifier && deletePB->isEnabled()) {
447 } else if (keyModifiers == Qt::ControlModifier) {
448 selectedModel->removeRows(0, selectedModel->rowCount());
451 return QObject::eventFilter(obj, event);
453 // Ctrl-Up activates upPB
454 else if (keyPressed == Qt::Key_Up) {
455 if (keyModifiers == Qt::ControlModifier) {
456 if (upPB->isEnabled())
462 // Ctrl-Down activates downPB
463 else if (keyPressed == Qt::Key_Down) {
464 if (keyModifiers == Qt::ControlModifier) {
465 if (downPB->isEnabled())
471 else if (keyPressed == Qt::Key_Left) {
472 focusAndHighlight(availableLV);
476 } else if (etype == QEvent::FocusIn) {
477 if (!selectedHasFocus_) {
478 selectedHasFocus_ = true;
484 return QObject::eventFilter(obj, event);
487 } // namespace frontend
490 #include "moc_GuiSelectionManager.cpp"