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