]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/QCitationDialog.cpp
delete unneeded Menubar virtual interface.
[lyx.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 "support/docstring.h"
24
25 #include "debug.h"
26 #include "gettext.h"
27
28 #include <algorithm>
29 #include <vector>
30 #include <string>
31
32 #include <QCloseEvent>
33 #include <QKeyEvent>
34
35 #undef KeyPress
36
37 using std::vector;
38 using std::string;
39
40 namespace lyx {
41 namespace frontend {
42
43
44 QCitationDialog::QCitationDialog(Dialog & dialog, QCitation * form)
45         : Dialog::View(dialog, _("Citation")), form_(form)
46 {
47         setupUi(this);
48
49         setWindowTitle(toqstr("LyX: " + getTitle()));
50
51         selectedLV->setModel(form_->selected());
52         availableLV->setModel(form_->available());
53
54         connect(citationStyleCO, SIGNAL(activated(int)),
55                 this, SLOT(changed()));
56         connect(fulllistCB, SIGNAL(clicked()),
57                 this, SLOT(changed()));
58         connect(forceuppercaseCB, SIGNAL(clicked()),
59                 this, SLOT(changed()));
60         connect(textBeforeED, SIGNAL(textChanged(const QString&)),
61                 this, SLOT(changed()));
62         connect(textAfterED, SIGNAL(textChanged(const QString&)),
63                 this, SLOT(changed()));
64         connect(clearPB, SIGNAL(clicked()),
65                 findLE, SLOT(clear()));
66         connect(availableLV->selectionModel(),
67                 SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
68                 this, SLOT(availableChanged(const QModelIndex &, const QModelIndex &)));
69         connect(selectedLV->selectionModel(),
70                 SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
71                 this, SLOT(selectedChanged(const QModelIndex &, const QModelIndex &)));
72         connect(this, SIGNAL(rejected()), this, SLOT(cleanUp()));
73         availableLV->installEventFilter(this);
74         selectedLV->installEventFilter(this);
75         availableFocused_ = true;
76 }
77
78
79 QCitationDialog::~QCitationDialog()
80 {
81 }
82
83
84 bool QCitationDialog::eventFilter(QObject * obj, QEvent * event) 
85 {
86         if (obj == availableLV) {
87                 if (event->type() != QEvent::KeyPress)
88                         return QObject::eventFilter(obj, event);
89                 QKeyEvent * keyEvent = static_cast<QKeyEvent *>(event);
90                 int const keyPressed = keyEvent->key();
91                 Qt::KeyboardModifiers const keyModifiers = keyEvent->modifiers();
92                 //Enter key without modifier will add current item.
93                 //Ctrl-Enter will add it and close the dialog.
94                 //This is designed to work both with the main enter key
95                 //and the one on the numeric keypad.
96                 if ((keyPressed == Qt::Key_Enter || keyPressed == Qt::Key_Return) &&
97                                 //We want one or both of Control and Keypad, and nothing else
98                                 //(KeypadModifier is what you get if you use the Enter key on the
99                                 //numeric keypad.)
100                                 (!keyModifiers || 
101                                  (keyModifiers == Qt::ControlModifier) ||
102                                  (keyModifiers == Qt::KeypadModifier)  ||
103                                  (keyModifiers == (Qt::ControlModifier | Qt::KeypadModifier))
104                                 )
105                         ) {
106                                 if (addPB->isEnabled())
107                                         on_addPB_clicked();
108                                 if (keyModifiers & Qt::ControlModifier)
109                                         on_okPB_clicked();
110                                 event->accept();
111                                 return true;
112                 } 
113         } else if (obj == selectedLV) {
114                 //Delete or backspace key will delete current item
115                 //...with control modifier will clear the list
116                 if (event->type() != QEvent::KeyPress)
117                         return QObject::eventFilter(obj, event);
118                 QKeyEvent * keyEvent = static_cast<QKeyEvent *>(event);
119                 int const keyPressed = keyEvent->key();
120                 Qt::KeyboardModifiers const keyModifiers = keyEvent->modifiers();
121                 if (keyPressed == Qt::Key_Delete || keyPressed == Qt::Key_Backspace) {
122                         if (keyModifiers == Qt::NoModifier && deletePB->isEnabled())
123                                 on_deletePB_clicked();
124                         else if (keyModifiers == Qt::ControlModifier) {
125                                 form_->clearSelection();
126                                 updateDialog();
127                         } else
128                                 //ignore it otherwise
129                                 return QObject::eventFilter(obj, event);
130                         event->accept();
131                         return true;
132                 }
133         }
134         return QObject::eventFilter(obj, event);
135 }
136
137
138 void QCitationDialog::cleanUp() 
139 {
140         form_->clearSelection();
141         form_->clearParams();
142         close();
143 }
144
145
146 void QCitationDialog::closeEvent(QCloseEvent * e)
147 {
148         form_->clearSelection();
149         form_->clearParams();
150         e->accept();
151 }
152
153
154 void QCitationDialog::apply()
155 {
156         int  const choice = std::max(0, citationStyleCO->currentIndex());
157         style_ = choice;
158         bool const full  = fulllistCB->isChecked();
159         bool const force = forceuppercaseCB->isChecked();
160
161         QString const before = textBeforeED->text();
162         QString const after = textAfterED->text();
163
164         form_->apply(choice, full, force, before, after);
165 }
166
167
168 void QCitationDialog::hide()
169 {
170         form_->clearParams();
171         accept();
172 }
173
174
175 void QCitationDialog::show()
176 {
177         findLE->clear();
178         availableLV->setFocus();
179         QDialog::show();
180         raise();
181         activateWindow();
182 }
183
184
185 bool QCitationDialog::isVisible() const
186 {
187         return QDialog::isVisible();
188 }
189
190
191 void QCitationDialog::on_okPB_clicked()
192 {
193         apply();
194         form_->clearSelection();
195         hide();
196 }
197
198
199 void QCitationDialog::on_cancelPB_clicked()
200 {
201         form_->clearSelection();
202         hide();
203 }
204
205
206 void QCitationDialog::on_applyPB_clicked()
207 {
208         apply();
209 }
210
211
212 void QCitationDialog::on_restorePB_clicked()
213 {
214         form_->init();
215         update();
216 }
217
218
219 void QCitationDialog::update()
220 {
221         fillFields();
222         fillEntries();
223         updateDialog();
224 }
225
226
227 //The main point of separating this out is that the fill*() methods
228 //called in update() do not need to be called for INTERNAL updates,
229 //such as when addPB is pressed, as the list of fields, entries, etc,
230 //will not have changed. At the moment, however, the division between
231 //fillStyles() and updateStyles() doesn't lend itself to dividing the
232 //two methods, though they should be divisible.
233 void QCitationDialog::updateDialog()
234 {
235         if (availableFocused_ || 
236             selectedLV->selectionModel()->selectedIndexes().isEmpty()) {
237                 if (availableLV->selectionModel()->selectedIndexes().isEmpty()
238                                         && availableLV->model()->rowCount() > 0)
239                         availableLV->setCurrentIndex(availableLV->model()->index(0,0));
240                 updateInfo(availableLV);
241         } else
242                 updateInfo(selectedLV);
243
244         setButtons();
245
246         textBeforeED->setText(form_->textBefore());
247         textAfterED->setText(form_->textAfter());
248         fillStyles();
249         updateStyle();
250 }
251
252
253 void QCitationDialog::updateStyle()
254 {
255         biblio::CiteEngine const engine = form_->getEngine();
256         bool const natbib_engine =
257                 engine == biblio::ENGINE_NATBIB_AUTHORYEAR ||
258                 engine == biblio::ENGINE_NATBIB_NUMERICAL;
259         bool const basic_engine = engine == biblio::ENGINE_BASIC;
260
261         bool const haveSelection = 
262                 selectedLV->model()->rowCount() > 0;
263         fulllistCB->setEnabled(natbib_engine && haveSelection);
264         forceuppercaseCB->setEnabled(natbib_engine && haveSelection);
265         textBeforeED->setEnabled(!basic_engine && haveSelection);
266         textBeforeLA->setEnabled(!basic_engine && haveSelection);
267         textAfterED->setEnabled(haveSelection);
268         textAfterLA->setEnabled(haveSelection);
269         citationStyleCO->setEnabled(!basic_engine && haveSelection);
270         citationStyleLA->setEnabled(!basic_engine && haveSelection);
271
272         string const & command = form_->params().getCmdName();
273
274         // Find the style of the citekeys
275         vector<biblio::CiteStyle> const & styles =
276                 ControlCitation::getCiteStyles();
277         biblio::CitationStyle const cs(command);
278
279         vector<biblio::CiteStyle>::const_iterator cit =
280                 std::find(styles.begin(), styles.end(), cs.style);
281
282         // restore the latest natbib style
283         if (style_ >= 0 && style_ < citationStyleCO->count())
284                 citationStyleCO->setCurrentIndex(style_);
285         else
286                 citationStyleCO->setCurrentIndex(0);
287
288         if (cit != styles.end()) {
289                 int const i = int(cit - styles.begin());
290                 citationStyleCO->setCurrentIndex(i);
291                 fulllistCB->setChecked(cs.full);
292                 forceuppercaseCB->setChecked(cs.forceUCase);
293         } else {
294                 fulllistCB->setChecked(false);
295                 forceuppercaseCB->setChecked(false);
296         }
297 }
298
299
300 //This one needs to be called whenever citationStyleCO needs
301 //to be updated---and this would be on anything that changes the
302 //selection in selectedLV, or on a general update.
303 void QCitationDialog::fillStyles()
304 {
305         int const oldIndex = citationStyleCO->currentIndex();
306
307         citationStyleCO->clear();
308
309         QStringList selected_keys = form_->selected()->stringList();
310         if (selected_keys.empty()) {
311                 citationStyleCO->setEnabled(false);
312                 citationStyleLA->setEnabled(false);
313                 return;
314         }
315
316         int curr = selectedLV->model()->rowCount() - 1;
317         if (curr < 0)
318                 return;
319
320         if (!selectedLV->selectionModel()->selectedIndexes().empty())
321                 curr = selectedLV->selectionModel()->selectedIndexes()[0].row();
322
323         QStringList sty = form_->citationStyles(curr);
324
325         bool const basic_engine =
326                         (form_->getEngine() == biblio::ENGINE_BASIC);
327
328         citationStyleCO->setEnabled(!sty.isEmpty() && !basic_engine);
329         citationStyleLA->setEnabled(!sty.isEmpty() && !basic_engine);
330
331         if (sty.isEmpty() || basic_engine)
332                 return;
333
334         citationStyleCO->insertItems(0, sty);
335
336         if (oldIndex != -1 && oldIndex < citationStyleCO->count())
337                 citationStyleCO->setCurrentIndex(oldIndex);
338 }
339
340
341 void QCitationDialog::fillFields()
342 {
343         fieldsCO->blockSignals(true);
344         int const oldIndex = fieldsCO->currentIndex();
345         fieldsCO->clear();
346         QStringList const & fields = form_->getFieldsAsQStringList();
347         fieldsCO->insertItem(0, qt_("All Fields"));
348         fieldsCO->insertItem(1, qt_("Keys"));
349         fieldsCO->insertItems(2, fields);
350         if (oldIndex != -1 && oldIndex < fieldsCO->count())
351                 fieldsCO->setCurrentIndex(oldIndex);
352         fieldsCO->blockSignals(false);
353 }
354
355
356 void QCitationDialog::fillEntries()
357 {
358         entriesCO->blockSignals(true);
359         int const oldIndex = entriesCO->currentIndex();
360         entriesCO->clear();
361         QStringList const & entries = form_->getEntriesAsQStringList();
362         entriesCO->insertItem(0, qt_("All Entry Types"));
363         entriesCO->insertItems(1, entries);
364         if (oldIndex != -1 && oldIndex < entriesCO->count())
365                 entriesCO->setCurrentIndex(oldIndex);
366         entriesCO->blockSignals(false);
367 }
368
369
370 bool QCitationDialog::isSelected(const QModelIndex & idx)
371 {
372         QString const str = idx.data().toString();
373         return form_->selected()->stringList().contains(str);
374 }
375
376
377 void QCitationDialog::setButtons()
378 {
379         int const arows = availableLV->model()->rowCount();
380         QModelIndexList const availSels = 
381                         availableLV->selectionModel()->selectedIndexes();
382         addPB->setEnabled(arows > 0 &&
383                         !availSels.isEmpty() &&
384                         !isSelected(availSels.first()));
385
386         int const srows = selectedLV->model()->rowCount();
387         QModelIndexList const selSels = 
388                         selectedLV->selectionModel()->selectedIndexes();
389         int const sel_nr =      selSels.empty() ? -1 : selSels.first().row();
390         deletePB->setEnabled(sel_nr >= 0);
391         upPB->setEnabled(sel_nr > 0);
392         downPB->setEnabled(sel_nr >= 0 && sel_nr < srows - 1);
393 }
394
395
396 void QCitationDialog::updateInfo(QListView const * const qlv)
397 {
398         QModelIndex idx = qlv->currentIndex();
399         if (idx.isValid()) {
400                 QString const keytxt = form_->getKeyInfo(idx.data().toString());
401                 infoML->document()->setPlainText(keytxt);
402         } else
403                 infoML->document()->clear();
404 }
405
406
407 void QCitationDialog::on_selectedLV_clicked(const QModelIndex &)
408 {
409         availableFocused_ = false;
410         updateDialog();
411 }
412
413
414 void QCitationDialog::selectedChanged(const QModelIndex & idx, const QModelIndex &)
415 {
416         if (!idx.isValid())
417                 return;
418         updateDialog();
419 }
420
421
422 void QCitationDialog::on_availableLV_clicked(const QModelIndex &)
423 {
424         availableFocused_ = true;
425         updateDialog();
426 }
427
428
429 void QCitationDialog::availableChanged(const QModelIndex & idx, const QModelIndex &)
430 {
431         if (!idx.isValid())
432                 return;
433         availableFocused_ = true;
434         updateDialog();
435 }
436
437
438 void QCitationDialog::on_availableLV_doubleClicked(const QModelIndex & idx)
439 {
440         if (isSelected(idx))
441                 return;
442         availableFocused_ = true;
443         on_addPB_clicked();
444 }
445
446
447 namespace {
448 //helper function for next two
449 QModelIndex getSelectedIndex(QListView * lv) {
450         //Encourage compiler to use NRVO
451         QModelIndex retval = QModelIndex();
452         QModelIndexList selIdx = 
453                 lv->selectionModel()->selectedIndexes();
454         if (!selIdx.empty())
455                 retval = selIdx.first();
456         return retval;
457 }
458 }//anonymous namespace
459
460
461 void QCitationDialog::on_addPB_clicked()
462 {
463         QModelIndex const idxToAdd = getSelectedIndex(availableLV);
464         if (!idxToAdd.isValid())
465                 return;
466         QModelIndex idx = selectedLV->currentIndex();
467         form_->addKey(idxToAdd);
468         if (idx.isValid())
469                 selectedLV->setCurrentIndex(idx);
470         availableFocused_ = true;
471         updateDialog();
472 }
473
474
475 void QCitationDialog::on_deletePB_clicked()
476 {
477         QModelIndex idx = getSelectedIndex(selectedLV);
478         if (!idx.isValid())
479                 return;
480         int nrows = selectedLV->model()->rowCount();
481
482         form_->deleteKey(idx);
483
484         if (idx.row() == nrows - 1)
485                 idx = idx.sibling(idx.row() - 1, idx.column());
486
487         if (nrows>1)
488                 selectedLV->setCurrentIndex(idx);
489         availableFocused_ = true;
490         updateDialog();
491 }
492
493
494 void QCitationDialog::on_upPB_clicked()
495 {
496         QModelIndex idx = selectedLV->currentIndex();
497         form_->upKey(idx);
498         selectedLV->setCurrentIndex(idx.sibling(idx.row() - 1, idx.column()));
499         availableLV->selectionModel()->reset();
500         availableFocused_ = false;
501         updateDialog();
502 }
503
504
505 void QCitationDialog::on_downPB_clicked()
506 {
507         QModelIndex idx = selectedLV->currentIndex();
508         form_->downKey(idx);
509         selectedLV->setCurrentIndex(idx.sibling(idx.row() + 1, idx.column()));
510         availableLV->selectionModel()->reset();
511         availableFocused_ = false;
512         updateDialog();
513 }
514
515
516 void QCitationDialog::findText(QString const & text, bool reset)
517 {
518         //"All Fields" and "Keys" are the first two
519         int index = fieldsCO->currentIndex() - 2; 
520         vector<docstring> const & fields = form_->availableFields();
521         docstring field;
522         
523         if (index <= -1 || index >= int(fields.size()))
524                 //either "All Fields" or "Keys" or an invalid value
525                 field = from_ascii("");
526         else
527                 field = fields[index];
528         
529         //Was it "Keys"?
530         bool const onlyKeys = index == -1;
531         
532         //"All Entry Types" is first.
533         index = entriesCO->currentIndex() - 1; 
534         vector<docstring> const & entries = form_->availableEntries();
535         docstring entryType;
536         if (index < 0 || index >= int(entries.size()))
537                 entryType = from_ascii("");
538         else 
539                 entryType = entries[index];
540         
541         bool const case_sentitive = caseCB->checkState();
542         bool const reg_exp = regexCB->checkState();
543         form_->findKey(text, onlyKeys, field, entryType, 
544                        case_sentitive, reg_exp, reset);
545         //FIXME
546         //It'd be nice to save and restore the current selection in 
547         //availableLV. Currently, we get an automatic reset, since the
548         //model is reset.
549         
550         updateDialog();
551 }
552
553
554 void QCitationDialog::on_fieldsCO_currentIndexChanged(int /*index*/)
555 {
556         findText(findLE->text(), true);
557 }
558
559
560 void QCitationDialog::on_entriesCO_currentIndexChanged(int /*index*/)
561 {
562         findText(findLE->text(), true);
563 }
564
565
566 void QCitationDialog::on_findLE_textChanged(const QString & text)
567 {
568         clearPB->setDisabled(text.isEmpty());
569         if (text.isEmpty())
570                 findLE->setFocus();
571         findText(text);
572 }
573
574
575 void QCitationDialog::on_caseCB_stateChanged(int)
576 {
577         findText(findLE->text());
578 }
579
580
581 void QCitationDialog::on_regexCB_stateChanged(int)
582 {
583         findText(findLE->text());
584 }
585
586
587 void QCitationDialog::changed()
588 {
589         fillStyles();
590         setButtons();
591 }
592
593
594 } // namespace frontend
595 } // namespace lyx
596
597 #include "QCitationDialog_moc.cpp"