*/
#include <config.h>
+
#include "GuiSelectionManager.h"
+#include "qt_helpers.h"
+
+#include "support/debug.h"
+
+#include <QAbstractItemModel>
+#include <QAbstractListModel>
+#include <QItemSelection>
+#include <QListView>
+#include <QKeyEvent>
+#include <QPushButton>
+
+#ifdef KeyPress
+#undef KeyPress
+#endif
+
+#ifdef ControlModifier
+#undef ControlModifier
+#endif
+
+#ifdef FocusIn
+#undef FocusIn
+#endif
namespace lyx {
namespace frontend {
-GuiSelectionManager::GuiSelectionManager(
- QListView * avail,
- QListView * sel,
- QPushButton * add,
- QPushButton * del,
- QPushButton * up,
- QPushButton * down,
- QStringListModel * amod,
- QStringListModel * smod)
+GuiSelectionManager::GuiSelectionManager(QObject * parent,
+ QAbstractItemView * avail,
+ QAbstractItemView * sel,
+ QPushButton * add,
+ QPushButton * del,
+ QPushButton * up,
+ QPushButton * down,
+ QAbstractListModel * amod,
+ QAbstractItemModel * smod,
+ int const main_sel_col)
+: QObject(parent), availableLV(avail), selectedLV(sel),
+ addPB(add), deletePB(del), upPB(up), downPB(down),
+ availableModel(amod), selectedModel(smod),
+ selectedHasFocus_(false), main_sel_col_(main_sel_col)
{
- availableLV = avail;
- selectedLV = sel;
- addPB = add;
- deletePB = del;
- upPB = up;
- downPB = down;
- availableModel = amod;
- selectedModel = smod;
-
selectedLV->setModel(smod);
availableLV->setModel(amod);
+ selectedLV->setSelectionBehavior(QAbstractItemView::SelectRows);
+ selectedLV->setSelectionMode(QAbstractItemView::SingleSelection);
connect(availableLV->selectionModel(),
- SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
- this, SLOT(availableChanged(const QModelIndex &, const QModelIndex &)));
+ SIGNAL(currentChanged(QModelIndex, QModelIndex)),
+ this, SLOT(availableChanged(QModelIndex, QModelIndex)));
+ connect(selectedLV->selectionModel(),
+ SIGNAL(currentChanged(QModelIndex, QModelIndex)),
+ this, SLOT(selectedChanged(QModelIndex, QModelIndex)));
+ connect(availableLV->selectionModel(),
+ SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
+ this, SLOT(availableChanged(QItemSelection, QItemSelection)));
connect(selectedLV->selectionModel(),
- SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
- this, SLOT(selectedChanged(const QModelIndex &, const QModelIndex &)));
- connect(addPB, SIGNAL(clicked()),
- this, SLOT(addPB_clicked()));
- connect(deletePB, SIGNAL(clicked()),
- this, SLOT(deletePB_clicked()));
- connect(upPB, SIGNAL(clicked()),
- this, SLOT(upPB_clicked()));
- connect(downPB, SIGNAL(clicked()),
- this, SLOT(downPB_clicked()));
- connect(availableLV, SIGNAL(clicked(const QModelIndex &)),
- this, SLOT(availableLV_clicked(const QModelIndex &)));
- connect(availableLV, SIGNAL(doubleClicked(const QModelIndex &)),
- this, SLOT(availableLV_doubleClicked(const QModelIndex &)));
- connect(selectedLV, SIGNAL(clicked(const QModelIndex &)),
- this, SLOT(selectedLV_clicked(const QModelIndex &)));
+ SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
+ this, SLOT(selectedChanged(QItemSelection, QItemSelection)));
+ connect(selectedLV->itemDelegate(), SIGNAL(commitData(QWidget*)),
+ this, SLOT(selectedEdited()));
+ connect(addPB, SIGNAL(clicked()),
+ this, SLOT(addPB_clicked()));
+ connect(deletePB, SIGNAL(clicked()),
+ this, SLOT(deletePB_clicked()));
+ connect(upPB, SIGNAL(clicked()),
+ this, SLOT(upPB_clicked()));
+ connect(downPB, SIGNAL(clicked()),
+ this, SLOT(downPB_clicked()));
+ connect(availableLV, SIGNAL(doubleClicked(QModelIndex)),
+ this, SLOT(availableLV_doubleClicked(QModelIndex)));
availableLV->installEventFilter(this);
selectedLV->installEventFilter(this);
void GuiSelectionManager::update()
{
- int const arows = availableLV->model()->rowCount();
+ updateAddPB();
+ updateDelPB();
+ updateDownPB();
+ updateUpPB();
+}
+
+
+QModelIndex GuiSelectionManager::getSelectedIndex(int const c) const
+{
+ QModelIndexList avail = availableLV->selectionModel()->selectedIndexes();
+ QModelIndexList sel = selectedLV->selectionModel()->selectedRows(c);
+ bool const have_avl = !avail.isEmpty();
+ bool const have_sel = !sel.isEmpty();
+
+ if (selectedFocused()) {
+ if (have_sel)
+ return sel.front();
+ if (have_avl)
+ return avail.front();
+ }
+ else { // available has focus
+ if (have_avl)
+ return avail.front();
+ if (have_sel)
+ return sel.front();
+ }
+ return QModelIndex();
+}
+
+
+void GuiSelectionManager::updateAddPB()
+{
+ int const arows = availableModel->rowCount();
QModelIndexList const availSels =
- availableLV->selectionModel()->selectedIndexes();
+ availableLV->selectionModel()->selectedIndexes();
addPB->setEnabled(arows > 0 &&
- !availSels.isEmpty() &&
- !isSelected(availSels.first()));
+ !availSels.isEmpty() &&
+ !isSelected(availSels.first()));
+}
- int const srows = selectedLV->model()->rowCount();
+
+void GuiSelectionManager::updateDelPB()
+{
+ int const srows = selectedModel->rowCount();
+ if (srows == 0) {
+ deletePB->setEnabled(false);
+ return;
+ }
QModelIndexList const selSels =
- selectedLV->selectionModel()->selectedIndexes();
- int const sel_nr = selSels.empty() ? -1 : selSels.first().row();
+ selectedLV->selectionModel()->selectedIndexes();
+ int const sel_nr = selSels.empty() ? -1 : selSels.first().row();
deletePB->setEnabled(sel_nr >= 0);
+}
+
+
+void GuiSelectionManager::updateUpPB()
+{
+ int const srows = selectedModel->rowCount();
+ if (srows == 0) {
+ upPB->setEnabled(false);
+ return;
+ }
+ QModelIndexList const selSels =
+ selectedLV->selectionModel()->selectedIndexes();
+ int const sel_nr = selSels.empty() ? -1 : selSels.first().row();
upPB->setEnabled(sel_nr > 0);
+}
+
+
+void GuiSelectionManager::updateDownPB()
+{
+ int const srows = selectedModel->rowCount();
+ if (srows == 0) {
+ downPB->setEnabled(false);
+ return;
+ }
+ QModelIndexList const selSels =
+ selectedLV->selectionModel()->selectedIndexes();
+ int const sel_nr = selSels.empty() ? -1 : selSels.first().row();
downPB->setEnabled(sel_nr >= 0 && sel_nr < srows - 1);
}
bool GuiSelectionManager::isSelected(const QModelIndex & idx)
{
- QString const str = idx.data().toString();
- return selectedModel->stringList().contains(str);
+ if (selectedModel->rowCount() == 0)
+ return false;
+ QVariant const & str = availableModel->data(idx, Qt::DisplayRole);
+ QModelIndexList qmil =
+ selectedModel->match(selectedModel->index(0, main_sel_col_),
+ Qt::DisplayRole, str, 1,
+ Qt::MatchFlags(Qt::MatchExactly | Qt::MatchWrap));
+ return !qmil.empty();
+}
+
+
+void GuiSelectionManager::availableChanged(QItemSelection const & qis, QItemSelection const &)
+{
+ QModelIndexList il = qis.indexes();
+ if (il.empty())
+ return;
+ availableChanged(il.front(), QModelIndex());
}
}
+void GuiSelectionManager::selectedChanged(QItemSelection const & qis, QItemSelection const &)
+{
+ QModelIndexList il = qis.indexes();
+ if (il.empty())
+ return;
+ selectedChanged(il.front(), QModelIndex());
+}
+
+
void GuiSelectionManager::selectedChanged(const QModelIndex & idx, const QModelIndex &)
{
if (!idx.isValid())
}
-static QModelIndex getSelectedIndex(QListView * lv)
+void GuiSelectionManager::selectedEdited()
+{
+ selectionChanged();
+}
+
+
+bool GuiSelectionManager::insertRowToSelected(int i,
+ QMap<int, QVariant> const & itemData)
{
- QModelIndex retval = QModelIndex();
- QModelIndexList selIdx =
- lv->selectionModel()->selectedIndexes();
- if (!selIdx.empty())
- retval = selIdx.first();
- return retval;
+ if (i <= -1)
+ i = 0;
+ if (i > selectedModel->rowCount())
+ i = selectedModel->rowCount();
+ if (!selectedModel->insertRow(i))
+ return false;
+ return selectedModel->setItemData(selectedModel->index(i, main_sel_col_), itemData);
+}
+
+
+bool GuiSelectionManager::insertRowToSelected(int i, QMap<int, QMap<int, QVariant>> & qms)
+{
+ if (i <= -1)
+ i = 0;
+ if (i > selectedModel->rowCount())
+ i = selectedModel->rowCount();
+ if (!selectedModel->insertRow(i))
+ return false;
+ bool res = true;
+ QMap<int, QMap<int, QVariant>>::const_iterator it = qms.constBegin();
+ for (; it != qms.constEnd(); ++it)
+ res &= selectedModel->setItemData(selectedModel->index(i, it.key()), it.value());
+ return res;
}
void GuiSelectionManager::addPB_clicked()
{
- QModelIndex const idxToAdd = getSelectedIndex(availableLV);
- if (!idxToAdd.isValid())
+ QModelIndexList selIdx =
+ availableLV->selectionModel()->selectedIndexes();
+ if (selIdx.isEmpty())
return;
- QModelIndex idx = selectedLV->currentIndex();
-
- QStringList keys = selectedModel->stringList();
- keys.append(idxToAdd.data().toString());
- selectedModel->setStringList(keys);
+
+ QModelIndex const idxToAdd = selIdx.first();
+ QModelIndex const idx = selectedLV->currentIndex();
+ int const srows = selectedModel->rowCount();
+
+ QMap<int, QVariant> qm = availableModel->itemData(idxToAdd);
+ insertRowToSelected(srows, qm);
+
selectionChanged(); //signal
-
+
if (idx.isValid())
selectedLV->setCurrentIndex(idx);
+
updateHook();
}
void GuiSelectionManager::deletePB_clicked()
{
- QModelIndex idx = getSelectedIndex(selectedLV);
- if (!idx.isValid())
+ QModelIndexList selIdx =
+ selectedLV->selectionModel()->selectedIndexes();
+ if (selIdx.isEmpty())
return;
-
- QStringList keys = selectedModel->stringList();
- keys.removeAt(idx.row());
- selectedModel->setStringList(keys);
+ QModelIndex idx = selIdx.first();
+ selectedModel->removeRow(idx.row());
selectionChanged(); //signal
int nrows = selectedLV->model()->rowCount();
if (nrows > 1)
selectedLV->setCurrentIndex(idx);
else if (nrows == 1)
- selectedLV->setCurrentIndex(selectedLV->model()->index(0,0));
+ selectedLV->setCurrentIndex(selectedLV->model()->index(0, 0));
selectedHasFocus_ = (nrows > 0);
updateHook();
}
void GuiSelectionManager::upPB_clicked()
{
- QModelIndex idx = selectedLV->currentIndex();
-
+ QModelIndexList selIdx =
+ selectedLV->selectionModel()->selectedIndexes();
+ if (selIdx.isEmpty())
+ return;
+ QModelIndex idx = selIdx.first();
+
int const pos = idx.row();
- QStringList keys = selectedModel->stringList();
- keys.swap(pos, pos - 1);
- selectedModel->setStringList(keys);
+ if (pos <= 0)
+ return;
+
+ QMap<int, QMap<int, QVariant>> qms;
+ QList<QModelIndex>::const_iterator it = selIdx.constBegin();
+ for (; it != selIdx.constEnd(); ++it)
+ qms[it->column()] = selectedModel->itemData(*it);
+
+ selectedModel->removeRow(pos);
+ insertRowToSelected(pos - 1, qms);
+
selectionChanged(); //signal
-
+
selectedLV->setCurrentIndex(idx.sibling(idx.row() - 1, idx.column()));
selectedHasFocus_ = true;
updateHook();
void GuiSelectionManager::downPB_clicked()
{
- QModelIndex idx = selectedLV->currentIndex();
-
+ QModelIndexList selIdx =
+ selectedLV->selectionModel()->selectedIndexes();
+ if (selIdx.isEmpty())
+ return;
+ QModelIndex idx = selIdx.first();
+
int const pos = idx.row();
- QStringList keys = selectedModel->stringList();
- keys.swap(pos, pos + 1);
- selectedModel->setStringList(keys);
+ if (pos >= selectedModel->rowCount() - 1)
+ return;
+
+ QMap<int, QMap<int, QVariant>> qms;
+ QList<QModelIndex>::const_iterator it = selIdx.constBegin();
+ for (; it != selIdx.constEnd(); ++it)
+ qms[it->column()] = selectedModel->itemData(*it);
+
+ selectedModel->removeRow(pos);
+ insertRowToSelected(pos + 1, qms);
+
selectionChanged(); //signal
-
+
selectedLV->setCurrentIndex(idx.sibling(idx.row() + 1, idx.column()));
selectedHasFocus_ = true;
updateHook();
}
-//FIXME These slots do not really do what they need to do, since focus
-//can enter the QListView in other ways. But there are no signals sent
-//in that case. We need to reimplement focusInEvent() to capture those,
-//which means subclassing QListView. (rgh)
-void GuiSelectionManager::availableLV_clicked(const QModelIndex &)
-{
- selectedHasFocus_ = false;
- updateHook();
-}
-
-
void GuiSelectionManager::availableLV_doubleClicked(const QModelIndex & idx)
{
- if (isSelected(idx))
+ if (isSelected(idx) || !addPB->isEnabled())
return;
if (idx.isValid())
}
-void GuiSelectionManager::selectedLV_clicked(const QModelIndex &)
-{
- selectedHasFocus_ = true;
- updateHook();
-}
-
-
bool GuiSelectionManager::eventFilter(QObject * obj, QEvent * event)
{
+ QEvent::Type etype = event->type();
if (obj == availableLV) {
- if (event->type() != QEvent::KeyPress)
- return QObject::eventFilter(obj, event);
- QKeyEvent * keyEvent = static_cast<QKeyEvent *>(event);
- int const keyPressed = keyEvent->key();
- Qt::KeyboardModifiers const keyModifiers = keyEvent->modifiers();
- //Enter key without modifier will add current item.
- //Ctrl-Enter will add it and close the dialog.
- //This is designed to work both with the main enter key
- //and the one on the numeric keypad.
- if ((keyPressed == Qt::Key_Enter || keyPressed == Qt::Key_Return) &&
- //We want one or both of Control and Keypad, and nothing else
- //(KeypadModifier is what you get if you use the Enter key on the
- //numeric keypad.)
- (!keyModifiers ||
- (keyModifiers == Qt::ControlModifier) ||
- (keyModifiers == Qt::KeypadModifier) ||
- (keyModifiers == (Qt::ControlModifier | Qt::KeypadModifier))
- )
- ) {
- if (addPB->isEnabled()) {
- addPB_clicked();
- okHook(); //signal
+ if (etype == QEvent::KeyPress) {
+ QKeyEvent * keyEvent = static_cast<QKeyEvent *>(event);
+ int const keyPressed = keyEvent->key();
+ Qt::KeyboardModifiers const keyModifiers = keyEvent->modifiers();
+ // Enter key without modifier will add current item.
+ // Ctrl-Enter will add it and close the dialog.
+ // This is designed to work both with the main enter key
+ // and the one on the numeric keypad.
+ if (keyPressed == Qt::Key_Enter || keyPressed == Qt::Key_Return) {
+ if (addPB->isEnabled()) {
+ if (!keyModifiers) {
+ addPB_clicked();
+ } else if (keyModifiers == Qt::ControlModifier ||
+ keyModifiers == Qt::KeypadModifier ||
+ keyModifiers == (Qt::ControlModifier | Qt::KeypadModifier)) {
+ addPB_clicked();
+ okHook(); //signal
+ }
+ }
+ event->accept();
+ return true;
}
- event->accept();
- return true;
- }
- } else if (obj == selectedLV) {
- //Delete or backspace key will delete current item
- //...with control modifier will clear the list
- if (event->type() != QEvent::KeyPress)
- return QObject::eventFilter(obj, event);
- QKeyEvent * keyEvent = static_cast<QKeyEvent *>(event);
- int const keyPressed = keyEvent->key();
- Qt::KeyboardModifiers const keyModifiers = keyEvent->modifiers();
- if (keyPressed == Qt::Key_Delete || keyPressed == Qt::Key_Backspace) {
- if (keyModifiers == Qt::NoModifier && deletePB->isEnabled())
- deletePB_clicked();
- else if (keyModifiers == Qt::ControlModifier) {
- QStringList list = selectedModel->stringList();
- list.clear();
- selectedModel->setStringList(list);
+ else if (keyPressed == Qt::Key_Right) {
+ focusAndHighlight(selectedLV);
+ event->accept();
+ return true;
+ }
+ } else if (etype == QEvent::FocusIn) {
+ if (selectedHasFocus_) {
+ selectedHasFocus_ = false;
updateHook();
- } else
- //ignore it otherwise
- return QObject::eventFilter(obj, event);
+ }
+ return false;
+ }
+ } else if (obj == selectedLV) {
+ if (etype == QEvent::KeyPress) {
+ QKeyEvent * keyEvent = static_cast<QKeyEvent *>(event);
+ int const keyPressed = keyEvent->key();
+ Qt::KeyboardModifiers const keyModifiers = keyEvent->modifiers();
+ // Delete or backspace key will delete current item
+ // ...with control modifier will clear the list
+ if (keyPressed == Qt::Key_Delete || keyPressed == Qt::Key_Backspace) {
+ if (keyModifiers == Qt::NoModifier && deletePB->isEnabled()) {
+ deletePB_clicked();
+ updateHook();
+ } else if (keyModifiers == Qt::ControlModifier) {
+ selectedModel->removeRows(0, selectedModel->rowCount());
+ updateHook();
+ } else
+ return QObject::eventFilter(obj, event);
+ }
+ // Ctrl-Up activates upPB
+ else if (keyPressed == Qt::Key_Up) {
+ if (keyModifiers == Qt::ControlModifier) {
+ if (upPB->isEnabled())
+ upPB_clicked();
+ event->accept();
+ return true;
+ }
+ }
+ // Ctrl-Down activates downPB
+ else if (keyPressed == Qt::Key_Down) {
+ if (keyModifiers == Qt::ControlModifier) {
+ if (downPB->isEnabled())
+ downPB_clicked();
+ event->accept();
+ return true;
+ }
+ }
+ else if (keyPressed == Qt::Key_Left) {
+ focusAndHighlight(availableLV);
event->accept();
return true;
+ }
+ } else if (etype == QEvent::FocusIn) {
+ if (!selectedHasFocus_) {
+ selectedHasFocus_ = true;
+ updateHook();
+ }
+ return false;
}
}
return QObject::eventFilter(obj, event);
}
-}//namespace frontend
-}//namespace lyx
+} // namespace frontend
+} // namespace lyx
-#include "GuiSelectionManager_moc.cpp"
+#include "moc_GuiSelectionManager.cpp"