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