]> git.lyx.org Git - features.git/blob - src/frontends/qt4/QCitationDialog.cpp
0d8d60b50c11c3ff10d6d3e1181e20052bb05fe4
[features.git] / src / frontends / qt4 / QCitationDialog.cpp
1 /**
2  * \file QCitationDialog.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Kalle Dalheimer
7  * \author John Levon
8  * \author Jürgen Spitzmüller
9  * \author Abdelrazak Younes
10  *
11  * Full author contact details are available in file CREDITS.
12  */
13
14 #include <config.h>
15
16 #include "QCitationDialog.h"
17
18 #include "QCitation.h"
19
20 #include "frontends/controllers/frontend_helpers.h"
21 #include "frontends/controllers/ControlCitation.h"
22
23 #include "debug.h"
24 #include "gettext.h"
25
26 #include <algorithm>
27 #include <vector>
28 #include <string>
29
30 #include <QCloseEvent>
31 #include <QKeyEvent>
32
33 using std::vector;
34 using std::string;
35
36 namespace lyx {
37 namespace frontend {
38
39
40 QCitationDialog::QCitationDialog(Dialog & dialog, QCitation * form)
41         : Dialog::View(dialog, _("Citation")), form_(form)
42 {
43         setupUi(this);
44
45         setWindowTitle(toqstr("LyX: " + getTitle()));
46
47         selectedLV->setModel(form_->selected());
48         availableLV->setModel(form_->available());
49
50         connect(citationStyleCO, SIGNAL(activated(int)),
51                 this, SLOT(changed()));
52         connect(fulllistCB, SIGNAL(clicked()),
53                 this, SLOT(changed()));
54         connect(forceuppercaseCB, SIGNAL(clicked()),
55                 this, SLOT(changed()));
56         connect(textBeforeED, SIGNAL(textChanged(const QString&)),
57                 this, SLOT(changed()));
58         connect(textAfterED, SIGNAL(textChanged(const QString&)),
59                 this, SLOT(changed()));
60         connect(clearPB, SIGNAL(clicked()),
61                 findLE, SLOT(clear()));
62         connect(availableLV->selectionModel(),
63                 SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
64                 this, SLOT(availableChanged(const QModelIndex &, const QModelIndex &)));
65         connect(selectedLV->selectionModel(),
66                 SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
67                 this, SLOT(selectedChanged(const QModelIndex &, const QModelIndex &)));
68         connect(this, SIGNAL(rejected()), this, SLOT(cleanUp()));
69         availableLV->installEventFilter(this);
70         selectedLV->installEventFilter(this);
71 }
72
73
74 QCitationDialog::~QCitationDialog()
75 {
76 }
77
78
79 bool QCitationDialog::eventFilter(QObject * obj, QEvent * event) 
80 {
81         if (obj == availableLV) {
82                 if (event->type() != QEvent::KeyPress)
83                         return QObject::eventFilter(obj, event);
84                 QKeyEvent * keyEvent = static_cast<QKeyEvent *>(event);
85                 int const keyPressed = keyEvent->key();
86                 Qt::KeyboardModifiers const keyModifiers = keyEvent->modifiers();
87                 //Enter key without modifier will add current item.
88                 //Ctrl-Enter will add it and close the dialog.
89                 //This is designed to work both with the main enter key
90                 //and the one on the numeric keypad.
91                 if ((keyPressed == Qt::Key_Enter || keyPressed == Qt::Key_Return) &&
92                                 //We want one or both of Control and Keypad, and nothing else
93                                 //(KeypadModifier is what you get if you use the Enter key on the
94                                 //numeric keypad.)
95                                 (!keyModifiers || 
96                                  (keyModifiers == Qt::ControlModifier) ||
97                                  (keyModifiers == Qt::KeypadModifier)  ||
98                                  (keyModifiers == (Qt::ControlModifier | Qt::KeypadModifier))
99                                 )
100                         ) {
101                                 if (addPB->isEnabled())
102                                         on_addPB_clicked();
103                                 if (keyModifiers & Qt::ControlModifier)
104                                         on_okPB_clicked();
105                                 event->accept();
106                                 return true;
107                 } 
108         } else if (obj == selectedLV) {
109                 //Delete or backspace key will delete current item
110                 //...with control modifier will clear the list
111                 if (event->type() != QEvent::KeyPress)
112                         return QObject::eventFilter(obj, event);
113                 QKeyEvent * keyEvent = static_cast<QKeyEvent *>(event);
114                 int const keyPressed = keyEvent->key();
115                 Qt::KeyboardModifiers const keyModifiers = keyEvent->modifiers();
116                 if (keyPressed == Qt::Key_Delete || keyPressed == Qt::Key_Backspace) {
117                         if (keyModifiers == Qt::NoModifier && deletePB->isEnabled())
118                                 on_deletePB_clicked();
119                         else if (keyModifiers == Qt::ControlModifier) {
120                                 form_->clearSelection();
121                                 update();
122                         } else
123                                 //ignore it otherwise
124                                 return QObject::eventFilter(obj, event);
125                         event->accept();
126                         return true;
127                 }
128         }
129         return QObject::eventFilter(obj, event);
130 }
131
132
133 void QCitationDialog::cleanUp() 
134 {
135         form_->clearSelection();
136         form_->clearParams();
137         close();
138 }
139
140
141 void QCitationDialog::closeEvent(QCloseEvent * e)
142 {
143         form_->clearSelection();
144         form_->clearParams();
145         e->accept();
146 }
147
148
149 void QCitationDialog::apply()
150 {
151         int  const choice = std::max(0, citationStyleCO->currentIndex());
152         style_ = choice;
153         bool const full  = fulllistCB->isChecked();
154         bool const force = forceuppercaseCB->isChecked();
155
156         QString const before = textBeforeED->text();
157         QString const after = textAfterED->text();
158
159         form_->apply(choice, full, force, before, after);
160 }
161
162
163 void QCitationDialog::hide()
164 {
165         form_->clearParams();
166         accept();
167 }
168
169
170 void QCitationDialog::show()
171 {
172         findLE->clear();
173         availableLV->setFocus();
174         QDialog::show();
175         raise();
176         activateWindow();
177 }
178
179
180 bool QCitationDialog::isVisible() const
181 {
182         return QDialog::isVisible();
183 }
184
185
186 void QCitationDialog::on_okPB_clicked()
187 {
188         apply();
189         form_->clearSelection();
190         hide();
191 }
192
193
194 void QCitationDialog::on_cancelPB_clicked()
195 {
196         form_->clearSelection();
197         hide();
198 }
199
200
201 void QCitationDialog::on_applyPB_clicked()
202 {
203         apply();
204 }
205
206
207 void QCitationDialog::on_restorePB_clicked()
208 {
209         form_->init();
210         update();
211 }
212
213
214 void QCitationDialog::update()
215 {
216         if (selectedLV->selectionModel()->selectedIndexes().isEmpty()) {
217                 if (availableLV->selectionModel()->selectedIndexes().isEmpty()
218                         && availableLV->model()->rowCount() > 0)
219                                 availableLV->setCurrentIndex(availableLV->model()->index(0,0));
220                 updateInfo(availableLV->currentIndex());
221         } else
222                 updateInfo(selectedLV->currentIndex());
223
224         setButtons();
225
226         textBeforeED->setText(form_->textBefore());
227         textAfterED->setText(form_->textAfter());
228
229         fillStyles();
230         updateStyle();
231 }
232
233
234 void QCitationDialog::updateStyle()
235 {
236         biblio::CiteEngine const engine = form_->getEngine();
237         bool const natbib_engine =
238                 engine == biblio::ENGINE_NATBIB_AUTHORYEAR ||
239                 engine == biblio::ENGINE_NATBIB_NUMERICAL;
240         bool const basic_engine = engine == biblio::ENGINE_BASIC;
241
242         fulllistCB->setEnabled(natbib_engine);
243         forceuppercaseCB->setEnabled(natbib_engine);
244         textBeforeED->setEnabled(!basic_engine);
245         textBeforeLA->setEnabled(!basic_engine);
246
247         string const & command = form_->params().getCmdName();
248
249         // Find the style of the citekeys
250         vector<biblio::CiteStyle> const & styles =
251                 ControlCitation::getCiteStyles();
252         biblio::CitationStyle const cs(command);
253
254         vector<biblio::CiteStyle>::const_iterator cit =
255                 std::find(styles.begin(), styles.end(), cs.style);
256
257         // restore the latest natbib style
258         if (style_ >= 0 && style_ < citationStyleCO->count())
259                 citationStyleCO->setCurrentIndex(style_);
260         else
261                 citationStyleCO->setCurrentIndex(0);
262
263         fulllistCB->setChecked(false);
264         forceuppercaseCB->setChecked(false);
265
266         if (cit != styles.end()) {
267                 int const i = int(cit - styles.begin());
268                 citationStyleCO->setCurrentIndex(i);
269                 fulllistCB->setChecked(cs.full);
270                 forceuppercaseCB->setChecked(cs.forceUCase);
271         }
272 }
273
274
275 void QCitationDialog::fillStyles()
276 {
277         int const orig = citationStyleCO->currentIndex();
278
279         citationStyleCO->clear();
280
281         QStringList selected_keys = form_->selected()->stringList();
282         if (selected_keys.empty()) {
283                 citationStyleCO->setEnabled(false);
284                 citationStyleLA->setEnabled(false);
285                 return;
286         }
287
288         int curr = selectedLV->model()->rowCount() - 1;
289         if (curr < 0)
290                 return;
291
292         if (!selectedLV->selectionModel()->selectedIndexes().empty())
293                 curr = selectedLV->selectionModel()->selectedIndexes()[0].row();
294
295         QStringList sty = form_->citationStyles(curr);
296
297         bool const basic_engine =
298                 (form_->getEngine() == biblio::ENGINE_BASIC);
299
300         citationStyleCO->setEnabled(!sty.isEmpty() && !basic_engine);
301         citationStyleLA->setEnabled(!sty.isEmpty() && !basic_engine);
302
303         if (sty.isEmpty() || basic_engine)
304                 return;
305
306         citationStyleCO->insertItems(0, sty);
307
308         if (orig != -1 && orig < citationStyleCO->count())
309                 citationStyleCO->setCurrentIndex(orig);
310 }
311
312
313 bool QCitationDialog::isSelected(const QModelIndex & idx)
314 {
315         QString const str = idx.data().toString();
316         return form_->selected()->stringList().contains(str);
317 }
318
319
320 void QCitationDialog::setButtons()
321 {
322         int const arows = availableLV->model()->rowCount();
323         QModelIndexList const availSels = 
324                         availableLV->selectionModel()->selectedIndexes();
325         addPB->setEnabled(arows > 0 &&
326                         !availSels.isEmpty() &&
327                         !isSelected(availSels.first()));
328
329         int const srows = selectedLV->model()->rowCount();
330         QModelIndexList const selSels = 
331                         selectedLV->selectionModel()->selectedIndexes();
332         int const sel_nr =      selSels.empty() ? -1 : selSels.first().row();
333         deletePB->setEnabled(sel_nr >= 0);
334         upPB->setEnabled(sel_nr > 0);
335         downPB->setEnabled(sel_nr >= 0 && sel_nr < srows - 1);
336 }
337
338
339 void QCitationDialog::updateInfo(const QModelIndex & idx)
340 {
341         if (idx.isValid()) {
342                 QString const keytxt = form_->getKeyInfo(idx.data().toString());
343                 infoML->document()->setPlainText(keytxt);
344         } else
345                 infoML->document()->clear();
346 }
347
348
349 void QCitationDialog::on_selectedLV_clicked(const QModelIndex &)
350 {
351         update();
352 }
353
354
355 void QCitationDialog::selectedChanged(const QModelIndex & idx, const QModelIndex &)
356 {
357         if (!idx.isValid())
358                 return;
359         update();
360 }
361
362
363 void QCitationDialog::on_availableLV_clicked(const QModelIndex &)
364 {
365         update();
366 }
367
368
369 void QCitationDialog::availableChanged(const QModelIndex & idx, const QModelIndex &)
370 {
371         if (!idx.isValid())
372                 return;
373         update();
374 }
375
376
377 void QCitationDialog::on_availableLV_doubleClicked(const QModelIndex & idx)
378 {
379         if (isSelected(idx))
380                 return;
381         on_addPB_clicked();
382 }
383
384
385 namespace {
386 //helper function for next two
387 QModelIndex getSelectedIndex(QListView * lv) {
388         //Encourage compiler to use NRVO
389         QModelIndex retval = QModelIndex();
390         QModelIndexList selIdx = 
391                 lv->selectionModel()->selectedIndexes();
392         if (!selIdx.empty())
393                 retval = selIdx.first();
394         return retval;
395 }
396 }//anonymous namespace
397
398
399 void QCitationDialog::on_addPB_clicked()
400 {
401         QModelIndex const idxToAdd = getSelectedIndex(availableLV);
402         if (!idxToAdd.isValid())
403                 return;
404         QModelIndex idx = selectedLV->currentIndex();
405         form_->addKey(idxToAdd);
406         if (idx.isValid())
407                 selectedLV->setCurrentIndex(idx);
408         update();
409 }
410
411
412 void QCitationDialog::on_deletePB_clicked()
413 {
414         QModelIndex idx = getSelectedIndex(selectedLV);
415         if (!idx.isValid())
416                 return;
417         int nrows = selectedLV->model()->rowCount();
418
419         form_->deleteKey(idx);
420
421         if (idx.row() == nrows - 1)
422                 idx = idx.sibling(idx.row() - 1, idx.column());
423
424         if (nrows>1)
425                 selectedLV->setCurrentIndex(idx);
426
427         update();
428 }
429
430
431 void QCitationDialog::on_upPB_clicked()
432 {
433         QModelIndex idx = selectedLV->currentIndex();
434         form_->upKey(idx);
435         selectedLV->setCurrentIndex(idx.sibling(idx.row() - 1, idx.column()));
436         availableLV->selectionModel()->reset();
437         update();
438 }
439
440
441 void QCitationDialog::on_downPB_clicked()
442 {
443         QModelIndex idx = selectedLV->currentIndex();
444         form_->downKey(idx);
445         selectedLV->setCurrentIndex(idx.sibling(idx.row() + 1, idx.column()));
446         availableLV->selectionModel()->reset();
447         update();
448 }
449
450
451 void QCitationDialog::findText(QString const & text)
452 {
453         bool const case_sentitive = caseCB->checkState();
454         bool const reg_exp = regexCB->checkState();
455         form_->findKey(text, false, case_sentitive, reg_exp);
456         selectedLV->selectionModel()->reset();
457         update();
458 }
459
460
461 void QCitationDialog::on_findLE_textChanged(const QString & text)
462 {
463         clearPB->setDisabled(text.isEmpty());
464         if (text.isEmpty())
465                 findLE->setFocus();
466         findText(text);
467 }
468
469
470 void QCitationDialog::on_caseCB_stateChanged(int)
471 {
472         findText(findLE->text());
473 }
474
475
476 void QCitationDialog::on_regexCB_stateChanged(int)
477 {
478         findText(findLE->text());
479 }
480
481
482 void QCitationDialog::changed()
483 {
484         fillStyles();
485         setButtons();
486 }
487
488
489 } // namespace frontend
490 } // namespace lyx
491
492 #include "QCitationDialog_moc.cpp"