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