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