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