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