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