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