]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiSelectionManager.cpp
Disable CheckTeX while buffer is processed
[lyx.git] / src / frontends / qt4 / GuiSelectionManager.cpp
1 /**
2  * \file GuiSelectionManager.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Richard Heck
7  * \author Et Alia
8  *
9  * Some of the material in this file previously appeared in
10  * GuiCitationDialog.cpp.
11  *
12  * Full author contact details are available in file CREDITS.
13  */
14
15 #include <config.h>
16
17 #include "GuiSelectionManager.h"
18 #include "qt_helpers.h"
19
20 #include "support/debug.h"
21
22 #include <QAbstractItemModel>
23 #include <QAbstractListModel>
24 #include <QItemSelection>
25 #include <QListView>
26 #include <QKeyEvent>
27 #include <QPushButton>
28
29 #ifdef KeyPress
30 #undef KeyPress
31 #endif
32
33 #ifdef ControlModifier
34 #undef ControlModifier
35 #endif
36
37 #ifdef FocusIn
38 #undef FocusIn
39 #endif
40
41
42 namespace lyx {
43 namespace frontend {
44
45 GuiSelectionManager::GuiSelectionManager(QObject * parent,
46                                          QAbstractItemView * avail,
47                                          QAbstractItemView * sel,
48                                          QPushButton * add,
49                                          QPushButton * del,
50                                          QPushButton * up,
51                                          QPushButton * down,
52                                          QAbstractListModel * 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 {
60         selectedLV->setModel(smod);
61         availableLV->setModel(amod);
62         selectedLV->setSelectionBehavior(QAbstractItemView::SelectRows);
63         selectedLV->setSelectionMode(QAbstractItemView::SingleSelection);
64
65         connect(availableLV->selectionModel(),
66                 SIGNAL(currentChanged(QModelIndex, QModelIndex)),
67                 this, SLOT(availableChanged(QModelIndex, QModelIndex)));
68         connect(selectedLV->selectionModel(),
69                 SIGNAL(currentChanged(QModelIndex, QModelIndex)),
70                 this, SLOT(selectedChanged(QModelIndex, QModelIndex)));
71         connect(availableLV->selectionModel(),
72                 SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
73                 this, SLOT(availableChanged(QItemSelection, QItemSelection)));
74         connect(selectedLV->selectionModel(),
75                 SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
76                 this, SLOT(selectedChanged(QItemSelection, QItemSelection)));
77         connect(selectedLV->itemDelegate(), SIGNAL(commitData(QWidget*)),
78                 this, SLOT(selectedEdited()));
79         connect(addPB, SIGNAL(clicked()),
80                 this, SLOT(addPB_clicked()));
81         connect(deletePB, SIGNAL(clicked()),
82                 this, SLOT(deletePB_clicked()));
83         connect(upPB, SIGNAL(clicked()),
84                 this, SLOT(upPB_clicked()));
85         connect(downPB, SIGNAL(clicked()),
86                 this, SLOT(downPB_clicked()));
87         connect(availableLV, SIGNAL(doubleClicked(QModelIndex)),
88                 this, SLOT(availableLV_doubleClicked(QModelIndex)));
89
90         availableLV->installEventFilter(this);
91         selectedLV->installEventFilter(this);
92 }
93
94
95 void GuiSelectionManager::update()
96 {
97         updateAddPB();
98         updateDelPB();
99         updateDownPB();
100         updateUpPB();
101 }
102
103
104 QModelIndex GuiSelectionManager::getSelectedIndex(int const c) const
105 {
106         QModelIndexList avail = availableLV->selectionModel()->selectedIndexes();
107         QModelIndexList sel   = selectedLV->selectionModel()->selectedRows(c);
108         bool const have_avl = !avail.isEmpty();
109         bool const have_sel = !sel.isEmpty();
110
111         if (selectedFocused()) {
112                 if (have_sel)
113                         return sel.front();
114                 if (have_avl)
115                         return avail.front();
116         }
117         else { // available has focus
118                 if (have_avl)
119                         return avail.front();
120                 if (have_sel)
121                         return sel.front();
122         }
123         return QModelIndex();
124 }
125
126
127 void GuiSelectionManager::updateAddPB()
128 {
129         int const arows = availableModel->rowCount();
130         QModelIndexList const availSels =
131                 availableLV->selectionModel()->selectedIndexes();
132         addPB->setEnabled(arows > 0 &&
133                 !availSels.isEmpty() &&
134                 !isSelected(availSels.first()));
135 }
136
137
138 void GuiSelectionManager::updateDelPB()
139 {
140         int const srows = selectedModel->rowCount();
141         if (srows == 0) {
142                 deletePB->setEnabled(false);
143                 return;
144         }
145         QModelIndexList const selSels =
146                 selectedLV->selectionModel()->selectedIndexes();
147         int const sel_nr = selSels.empty() ? -1 : selSels.first().row();
148         deletePB->setEnabled(sel_nr >= 0);
149 }
150
151
152 void GuiSelectionManager::updateUpPB()
153 {
154         int const srows = selectedModel->rowCount();
155         if (srows == 0) {
156                 upPB->setEnabled(false);
157                 return;
158         }
159         QModelIndexList const selSels =
160                         selectedLV->selectionModel()->selectedIndexes();
161         int const sel_nr = selSels.empty() ? -1 : selSels.first().row();
162         upPB->setEnabled(sel_nr > 0);
163 }
164
165
166 void GuiSelectionManager::updateDownPB()
167 {
168         int const srows = selectedModel->rowCount();
169         if (srows == 0) {
170                 downPB->setEnabled(false);
171                 return;
172         }
173         QModelIndexList const selSels =
174                         selectedLV->selectionModel()->selectedIndexes();
175         int const sel_nr = selSels.empty() ? -1 : selSels.first().row();
176         downPB->setEnabled(sel_nr >= 0 && sel_nr < srows - 1);
177 }
178
179
180 bool GuiSelectionManager::isSelected(const QModelIndex & idx)
181 {
182         if (selectedModel->rowCount() == 0)
183                 return false;
184         QVariant const & str = availableModel->data(idx, Qt::DisplayRole);
185         QModelIndexList qmil =
186                         selectedModel->match(selectedModel->index(0, main_sel_col_),
187                                              Qt::DisplayRole, str, 1,
188                                              Qt::MatchFlags(Qt::MatchExactly | Qt::MatchWrap));
189         return !qmil.empty();
190 }
191
192
193 void GuiSelectionManager::availableChanged(QItemSelection const & qis, QItemSelection const &)
194 {
195         QModelIndexList il = qis.indexes();
196         if (il.empty())
197                 return;
198         availableChanged(il.front(), QModelIndex());
199 }
200
201
202 void GuiSelectionManager::availableChanged(const QModelIndex & idx, const QModelIndex &)
203 {
204         if (!idx.isValid())
205                 return;
206
207         selectedHasFocus_ = false;
208         updateHook();
209 }
210
211
212 void GuiSelectionManager::selectedChanged(QItemSelection const & qis, QItemSelection const &)
213 {
214         QModelIndexList il = qis.indexes();
215         if (il.empty())
216                 return;
217         selectedChanged(il.front(), QModelIndex());
218 }
219
220
221 void GuiSelectionManager::selectedChanged(const QModelIndex & idx, const QModelIndex &)
222 {
223         if (!idx.isValid())
224                 return;
225
226         selectedHasFocus_ = true;
227         updateHook();
228 }
229
230
231 void GuiSelectionManager::selectedEdited()
232 {
233         selectionChanged();
234 }
235
236
237 bool GuiSelectionManager::insertRowToSelected(int i,
238                 QMap<int, QVariant> const & itemData)
239 {
240         if (i <= -1)
241                 i = 0;
242         if (i > selectedModel->rowCount())
243                 i = selectedModel->rowCount();
244         if (!selectedModel->insertRow(i))
245                 return false;
246         return selectedModel->setItemData(selectedModel->index(i, main_sel_col_), itemData);
247 }
248
249
250 bool GuiSelectionManager::insertRowToSelected(int i, QMap<int, QMap<int, QVariant>> & qms)
251 {
252         if (i <= -1)
253                 i = 0;
254         if (i > selectedModel->rowCount())
255                 i = selectedModel->rowCount();
256         if (!selectedModel->insertRow(i))
257                 return false;
258         bool res = true;
259         QMap<int, QMap<int, QVariant>>::const_iterator it = qms.constBegin();
260         for (; it != qms.constEnd(); ++it)
261                 res &= selectedModel->setItemData(selectedModel->index(i, it.key()), it.value());
262         return res;
263 }
264
265
266 void GuiSelectionManager::addPB_clicked()
267 {
268         QModelIndexList selIdx =
269                 availableLV->selectionModel()->selectedIndexes();
270         if (selIdx.isEmpty())
271                 return;
272
273         QModelIndex const idxToAdd = selIdx.first();
274         QModelIndex const idx = selectedLV->currentIndex();
275         int const srows = selectedModel->rowCount();
276
277         QMap<int, QVariant> qm = availableModel->itemData(idxToAdd);
278         insertRowToSelected(srows, qm);
279
280         selectionChanged(); //signal
281
282         if (idx.isValid())
283                 selectedLV->setCurrentIndex(idx);
284
285         updateHook();
286 }
287
288
289 void GuiSelectionManager::deletePB_clicked()
290 {
291         QModelIndexList selIdx =
292                 selectedLV->selectionModel()->selectedIndexes();
293         if (selIdx.isEmpty())
294                 return;
295         QModelIndex idx = selIdx.first();
296         selectedModel->removeRow(idx.row());
297         selectionChanged(); //signal
298
299         int nrows = selectedLV->model()->rowCount();
300         if (idx.row() == nrows) //was last item on list
301                 idx = idx.sibling(idx.row() - 1, idx.column());
302
303         if (nrows > 1)
304                 selectedLV->setCurrentIndex(idx);
305         else if (nrows == 1)
306                 selectedLV->setCurrentIndex(selectedLV->model()->index(0, 0));
307         selectedHasFocus_ = (nrows > 0);
308         updateHook();
309 }
310
311
312 void GuiSelectionManager::upPB_clicked()
313 {
314         QModelIndexList selIdx =
315                 selectedLV->selectionModel()->selectedIndexes();
316         if (selIdx.isEmpty())
317                 return;
318         QModelIndex idx = selIdx.first();
319
320         int const pos = idx.row();
321         if (pos <= 0)
322                 return;
323
324         QMap<int, QMap<int, QVariant>> qms;
325         QList<QModelIndex>::const_iterator it = selIdx.constBegin();
326         for (; it != selIdx.constEnd(); ++it)
327                 qms[it->column()] = selectedModel->itemData(*it);
328
329         selectedModel->removeRow(pos);
330         insertRowToSelected(pos - 1, qms);
331
332         selectionChanged(); //signal
333
334         selectedLV->setCurrentIndex(idx.sibling(idx.row() - 1, idx.column()));
335         selectedHasFocus_ = true;
336         updateHook();
337 }
338
339
340 void GuiSelectionManager::downPB_clicked()
341 {
342         QModelIndexList selIdx =
343                 selectedLV->selectionModel()->selectedIndexes();
344         if (selIdx.isEmpty())
345                 return;
346         QModelIndex idx = selIdx.first();
347
348         int const pos = idx.row();
349         if (pos >= selectedModel->rowCount() - 1)
350                 return;
351
352         QMap<int, QMap<int, QVariant>> qms;
353         QList<QModelIndex>::const_iterator it = selIdx.constBegin();
354         for (; it != selIdx.constEnd(); ++it)
355                 qms[it->column()] = selectedModel->itemData(*it);
356
357         selectedModel->removeRow(pos);
358         insertRowToSelected(pos + 1, qms);
359
360         selectionChanged(); //signal
361
362         selectedLV->setCurrentIndex(idx.sibling(idx.row() + 1, idx.column()));
363         selectedHasFocus_ = true;
364         updateHook();
365 }
366
367
368 void GuiSelectionManager::availableLV_doubleClicked(const QModelIndex & idx)
369 {
370         if (isSelected(idx) || !addPB->isEnabled())
371                 return;
372
373         if (idx.isValid())
374                 selectedHasFocus_ = false;
375         addPB_clicked();
376         //updateHook() will be emitted there
377 }
378
379
380 bool GuiSelectionManager::eventFilter(QObject * obj, QEvent * event)
381 {
382         QEvent::Type etype = event->type();
383         if (obj == availableLV) {
384                 if (etype == QEvent::KeyPress) {
385                         QKeyEvent * keyEvent = static_cast<QKeyEvent *>(event);
386                         int const keyPressed = keyEvent->key();
387                         Qt::KeyboardModifiers const keyModifiers = keyEvent->modifiers();
388                         // Enter key without modifier will add current item.
389                         // Ctrl-Enter will add it and close the dialog.
390                         // This is designed to work both with the main enter key
391                         // and the one on the numeric keypad.
392                         if (keyPressed == Qt::Key_Enter || keyPressed == Qt::Key_Return) {
393                                 if (!keyModifiers ||
394                                     keyModifiers == Qt::ControlModifier ||
395                                     keyModifiers == Qt::KeypadModifier  ||
396                                     keyModifiers == (Qt::ControlModifier
397                                                      | Qt::KeypadModifier)) {
398                                         if (addPB->isEnabled()) {
399                                                 addPB_clicked();
400                                         }
401                                         if (keyModifiers)
402                                                 okHook(); //signal
403                                 }
404                                 event->accept();
405                                 return true;
406                         }
407                         else if (keyPressed == Qt::Key_Right) {
408                                 focusAndHighlight(selectedLV);
409                                 event->accept();
410                                 return true;
411                         }
412                 } else if (etype == QEvent::FocusIn) {
413                         if (selectedHasFocus_) {
414                                 selectedHasFocus_ = false;
415                                 updateHook();
416                         }
417                         return false;
418                 }
419         } else if (obj == selectedLV) {
420                 if (etype == QEvent::KeyPress) {
421                         QKeyEvent * keyEvent = static_cast<QKeyEvent *>(event);
422                         int const keyPressed = keyEvent->key();
423                         Qt::KeyboardModifiers const keyModifiers = keyEvent->modifiers();
424                         // Delete or backspace key will delete current item
425                         // ...with control modifier will clear the list
426                         if (keyPressed == Qt::Key_Delete || keyPressed == Qt::Key_Backspace) {
427                                 if (keyModifiers == Qt::NoModifier && deletePB->isEnabled()) {
428                                         deletePB_clicked();
429                                         updateHook();
430                                 } else if (keyModifiers == Qt::ControlModifier) {
431                                         selectedModel->removeRows(0, selectedModel->rowCount());
432                                         updateHook();
433                                 } else
434                                         return QObject::eventFilter(obj, event);
435                         }
436                         // Ctrl-Up activates upPB
437                         else if (keyPressed == Qt::Key_Up) {
438                                 if (keyModifiers == Qt::ControlModifier) {
439                                         if (upPB->isEnabled())
440                                                 upPB_clicked();
441                                         event->accept();
442                                         return true;
443                                 }
444                         }
445                         // Ctrl-Down activates downPB
446                         else if (keyPressed == Qt::Key_Down) {
447                                 if (keyModifiers == Qt::ControlModifier) {
448                                         if (downPB->isEnabled())
449                                                 downPB_clicked();
450                                         event->accept();
451                                         return true;
452                                 }
453                         }
454                         else if (keyPressed == Qt::Key_Left) {
455                                 focusAndHighlight(availableLV);
456                                 event->accept();
457                                 return true;
458                         }
459                 } else if (etype == QEvent::FocusIn) {
460                         if (!selectedHasFocus_) {
461                                 selectedHasFocus_ = true;
462                                 updateHook();
463                         }
464                         return false;
465                 }
466         }
467         return QObject::eventFilter(obj, event);
468 }
469
470 } // namespace frontend
471 } // namespace lyx
472
473 #include "moc_GuiSelectionManager.cpp"