]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiSelectionManager.cpp
72ba407c3e5da308dd2c657ef91e9d035aa079f7
[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::updateView()
71 {
72         int const arows = availableLV->model()->rowCount();
73         QModelIndexList const availSels = 
74                         availableLV->selectionModel()->selectedIndexes();
75         addPB->setEnabled(arows > 0 &&
76                         !availSels.isEmpty() &&
77                         !isSelected(availSels.first()));
78
79         int const srows = selectedLV->model()->rowCount();
80         QModelIndexList const selSels = 
81                         selectedLV->selectionModel()->selectedIndexes();
82         int const sel_nr =      selSels.empty() ? -1 : selSels.first().row();
83         deletePB->setEnabled(sel_nr >= 0);
84         upPB->setEnabled(sel_nr > 0);
85         downPB->setEnabled(sel_nr >= 0 && sel_nr < srows - 1);
86 }
87
88
89 bool GuiSelectionManager::isSelected(const QModelIndex & idx)
90 {
91         QString const str = idx.data().toString();
92         return selectedModel->stringList().contains(str);
93 }
94
95
96 void GuiSelectionManager::availableChanged(const QModelIndex & idx, const QModelIndex &)
97 {
98         if (!idx.isValid())
99                 return;
100
101         selectedHasFocus_ = false;
102         updateHook();
103 }
104
105
106 void GuiSelectionManager::selectedChanged(const QModelIndex & idx, const QModelIndex &)
107 {
108         if (!idx.isValid())
109                 return;
110
111         selectedHasFocus_ = true;
112         updateHook();
113 }
114
115
116 static QModelIndex getSelectedIndex(QListView * lv)
117 {
118         QModelIndex retval = QModelIndex();
119         QModelIndexList selIdx = 
120                         lv->selectionModel()->selectedIndexes();
121         if (!selIdx.empty())
122                 retval = selIdx.first();
123         return retval;
124 }
125
126
127 void GuiSelectionManager::addPB_clicked()
128 {
129         QModelIndex const idxToAdd = getSelectedIndex(availableLV);
130         if (!idxToAdd.isValid())
131                 return;
132         QModelIndex idx = selectedLV->currentIndex();
133         
134         QStringList keys = selectedModel->stringList();
135         keys.append(idxToAdd.data().toString());
136         selectedModel->setStringList(keys);
137         selectionChanged(); //signal
138         
139         if (idx.isValid())
140                 selectedLV->setCurrentIndex(idx);
141         updateHook();
142 }
143
144
145 void GuiSelectionManager::deletePB_clicked()
146 {
147         QModelIndex idx = getSelectedIndex(selectedLV);
148         if (!idx.isValid())
149                 return;
150
151         QStringList keys = selectedModel->stringList();
152         keys.removeAt(idx.row());
153         selectedModel->setStringList(keys);
154         selectionChanged(); //signal
155
156         int nrows = selectedLV->model()->rowCount();
157         if (idx.row() == nrows) //was last item on list
158                 idx = idx.sibling(idx.row() - 1, idx.column());
159
160         if (nrows > 1)
161                 selectedLV->setCurrentIndex(idx);
162         else if (nrows == 1)
163                 selectedLV->setCurrentIndex(selectedLV->model()->index(0,0));
164         selectedHasFocus_ = (nrows > 0);
165         updateHook();
166 }
167
168
169 void GuiSelectionManager::upPB_clicked()
170 {
171         QModelIndex idx = selectedLV->currentIndex();
172         
173         int const pos = idx.row();
174         QStringList keys = selectedModel->stringList();
175         keys.swap(pos, pos - 1);
176         selectedModel->setStringList(keys);
177         selectionChanged(); //signal
178         
179         selectedLV->setCurrentIndex(idx.sibling(idx.row() - 1, idx.column()));
180         selectedHasFocus_ = true;
181         updateHook();
182 }
183
184
185 void GuiSelectionManager::downPB_clicked()
186 {
187         QModelIndex idx = selectedLV->currentIndex();
188         
189         int const pos = idx.row();
190         QStringList keys = selectedModel->stringList();
191         keys.swap(pos, pos + 1);
192         selectedModel->setStringList(keys);
193         selectionChanged(); //signal
194         
195         selectedLV->setCurrentIndex(idx.sibling(idx.row() + 1, idx.column()));
196         selectedHasFocus_ = true;
197         updateHook();
198 }
199
200
201 //FIXME These slots do not really do what they need to do, since focus
202 //can enter the QListView in other ways. But there are no signals sent
203 //in that case. We need to reimplement focusInEvent() to capture those,
204 //which means subclassing QListView. (rgh)
205 void GuiSelectionManager::availableLV_clicked(const QModelIndex &)
206 {
207         selectedHasFocus_ = false;
208         updateHook();
209 }
210
211
212 void GuiSelectionManager::availableLV_doubleClicked(const QModelIndex & idx)
213 {
214         if (isSelected(idx))
215                 return;
216
217         if (idx.isValid())
218                 selectedHasFocus_ = false;
219         addPB_clicked();
220         //updateHook() will be emitted there
221 }
222
223
224 void GuiSelectionManager::selectedLV_clicked(const QModelIndex &)
225 {
226         selectedHasFocus_ = true;
227         updateHook();
228 }
229
230
231 bool GuiSelectionManager::eventFilter(QObject * obj, QEvent * event) 
232 {
233         if (obj == availableLV) {
234                 if (event->type() != QEvent::KeyPress)
235                         return QObject::eventFilter(obj, event);
236                 QKeyEvent * keyEvent = static_cast<QKeyEvent *>(event);
237                 int const keyPressed = keyEvent->key();
238                 Qt::KeyboardModifiers const keyModifiers = keyEvent->modifiers();
239                 //Enter key without modifier will add current item.
240                 //Ctrl-Enter will add it and close the dialog.
241                 //This is designed to work both with the main enter key
242                 //and the one on the numeric keypad.
243                 if ((keyPressed == Qt::Key_Enter || keyPressed == Qt::Key_Return) &&
244                                 //We want one or both of Control and Keypad, and nothing else
245                                 //(KeypadModifier is what you get if you use the Enter key on the
246                                 //numeric keypad.)
247                                          (!keyModifiers || 
248                                          (keyModifiers == Qt::ControlModifier) ||
249                                          (keyModifiers == Qt::KeypadModifier)  ||
250                                          (keyModifiers == (Qt::ControlModifier | Qt::KeypadModifier))
251                                          )
252                          ) {
253                         if (addPB->isEnabled()) {
254                                 addPB_clicked();
255                                 okHook(); //signal
256                         }
257                         event->accept();
258                         return true;
259                          } 
260         } else if (obj == selectedLV) {
261                 //Delete or backspace key will delete current item
262                 //...with control modifier will clear the list
263                 if (event->type() != QEvent::KeyPress)
264                         return QObject::eventFilter(obj, event);
265                 QKeyEvent * keyEvent = static_cast<QKeyEvent *>(event);
266                 int const keyPressed = keyEvent->key();
267                 Qt::KeyboardModifiers const keyModifiers = keyEvent->modifiers();
268                 if (keyPressed == Qt::Key_Delete || keyPressed == Qt::Key_Backspace) {
269                         if (keyModifiers == Qt::NoModifier && deletePB->isEnabled())
270                                 deletePB_clicked();
271                         else if (keyModifiers == Qt::ControlModifier) {
272                                 QStringList list = selectedModel->stringList();
273                                 list.clear();
274                                 selectedModel->setStringList(list);
275                                 updateHook();
276                         } else
277                                 //ignore it otherwise
278                                 return QObject::eventFilter(obj, event);
279                                 event->accept();
280                                 return true;
281                 }
282         }
283         return QObject::eventFilter(obj, event);
284 }
285
286 }//namespace frontend
287 }//namespace lyx
288
289 #include "GuiSelectionManager_moc.cpp"