]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiRef.cpp
Connect filter reset button and remove unnecessary directives.
[lyx.git] / src / frontends / qt4 / GuiRef.cpp
1 /**
2  * \file GuiRef.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author John Levon
7  * \author Jürgen Spitzmüller
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "GuiRef.h"
15
16 #include "GuiApplication.h"
17
18 #include "Buffer.h"
19 #include "BufferList.h"
20 #include "FuncRequest.h"
21
22 #include "qt_helpers.h"
23
24 #include "insets/InsetRef.h"
25
26 #include "support/FileName.h"
27 #include "support/FileNameList.h"
28 #include "support/filetools.h" // makeAbsPath, makeDisplayPath
29
30 #include <QLineEdit>
31 #include <QCheckBox>
32 #include <QTreeWidget>
33 #include <QTreeWidgetItem>
34 #include <QPushButton>
35 #include <QToolTip>
36 #include <QCloseEvent>
37 #include <QHeaderView>
38
39 using namespace std;
40 using namespace lyx::support;
41
42 namespace lyx {
43 namespace frontend {
44
45 GuiRef::GuiRef(GuiView & lv)
46         : GuiDialog(lv, "ref", qt_("Cross-reference")),
47           params_(insetCode("ref"))
48 {
49         setupUi(this);
50
51         at_ref_ = false;
52
53         // The filter bar
54         filter_ = new FancyLineEdit(this);
55         filter_->setButtonPixmap(FancyLineEdit::Right, getPixmap("images/", "editclear", "svgz,png"));
56         filter_->setButtonVisible(FancyLineEdit::Right, true);
57         filter_->setButtonToolTip(FancyLineEdit::Right, qt_("Clear text"));
58         filter_->setAutoHideButton(FancyLineEdit::Right, true);
59         filter_->setPlaceholderText(qt_("All available labels"));
60         filter_->setToolTip(qt_("Enter string to filter the list of available labels"));
61
62         filterBarL->addWidget(filter_, 0);
63         findKeysLA->setBuddy(filter_);
64
65         sortingCO->addItem(qt_("By Occurrence"), "unsorted");
66         sortingCO->addItem(qt_("Alphabetically (Case-Insensitive)"), "nocase");
67         sortingCO->addItem(qt_("Alphabetically (Case-Sensitive)"), "case");
68
69         refsTW->setColumnCount(1);
70         refsTW->header()->setVisible(false);
71
72         connect(okPB, SIGNAL(clicked()), this, SLOT(slotOK()));
73         connect(applyPB, SIGNAL(clicked()), this, SLOT(slotApply()));
74         connect(closePB, SIGNAL(clicked()), this, SLOT(slotClose()));
75         connect(closePB, SIGNAL(clicked()), this, SLOT(resetDialog()));
76         connect(this, SIGNAL(rejected()), this, SLOT(dialogRejected()));
77
78         connect(typeCO, SIGNAL(activated(int)),
79                 this, SLOT(changed_adaptor()));
80         connect(referenceED, SIGNAL(textChanged(QString)),
81                 this, SLOT(refTextChanged(QString)));
82         connect(referenceED, SIGNAL(textChanged(QString)),
83                 this, SLOT(changed_adaptor()));
84         connect(filter_, SIGNAL(textEdited(QString)),
85                 this, SLOT(filterLabels()));
86         connect(filter_, SIGNAL(rightButtonClicked()),
87                 this, SLOT(resetFilter()));
88         connect(csFindCB, SIGNAL(clicked()),
89                 this, SLOT(filterLabels()));
90         connect(nameED, SIGNAL(textChanged(QString)),
91                 this, SLOT(changed_adaptor()));
92         connect(refsTW, SIGNAL(itemClicked(QTreeWidgetItem *, int)),
93                 this, SLOT(refHighlighted(QTreeWidgetItem *)));
94         connect(refsTW, SIGNAL(itemSelectionChanged()),
95                 this, SLOT(selectionChanged()));
96         connect(refsTW, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)),
97                 this, SLOT(refSelected(QTreeWidgetItem *)));
98         connect(sortingCO, SIGNAL(activated(int)),
99                 this, SLOT(sortToggled()));
100         connect(groupCB, SIGNAL(clicked()),
101                 this, SLOT(groupToggled()));
102         connect(gotoPB, SIGNAL(clicked()),
103                 this, SLOT(gotoClicked()));
104         connect(updatePB, SIGNAL(clicked()),
105                 this, SLOT(updateClicked()));
106         connect(bufferCO, SIGNAL(activated(int)),
107                 this, SLOT(updateClicked()));
108
109         bc().setPolicy(ButtonPolicy::NoRepeatedApplyReadOnlyPolicy);
110         bc().setOK(okPB);
111         bc().setApply(applyPB);
112         bc().setCancel(closePB);
113         bc().addReadOnly(typeCO);
114
115         restored_buffer_ = -1;
116         active_buffer_ = -1;
117 }
118
119
120 void GuiRef::enableView(bool enable)
121 {
122         if (!enable)
123                 // In the opposite case, updateContents() will be called anyway.
124                 updateContents();
125         GuiDialog::enableView(enable);
126 }
127
128
129 void GuiRef::changed_adaptor()
130 {
131         changed();
132 }
133
134
135 void GuiRef::gotoClicked()
136 {
137         // By setting last_reference_, we ensure that the reference
138         // to which we are going (or from which we are returning) is
139         // restored in the dialog. It's a bit of a hack, but it works,
140         // and no-one seems to have any better idea.
141         bool const toggled = 
142                 last_reference_.isEmpty() || last_reference_.isNull();
143         if (toggled)
144                 last_reference_ = referenceED->text();
145         gotoRef();
146         if (toggled)
147                 last_reference_.clear();
148 }
149
150
151 void GuiRef::selectionChanged()
152 {
153         if (isBufferReadonly())
154                 return;
155
156         QList<QTreeWidgetItem *> selections = refsTW->selectedItems();
157         if (selections.isEmpty())
158                 return;
159         QTreeWidgetItem * sel = selections.first();
160         refHighlighted(sel);
161         return;
162 }
163
164
165 void GuiRef::refHighlighted(QTreeWidgetItem * sel)
166 {
167         if (sel->childCount() > 0) {
168                 sel->setExpanded(true);
169                 return;
170         }
171
172 /*      int const cur_item = refsTW->currentRow();
173         bool const cur_item_selected = cur_item >= 0 ?
174                 refsLB->isSelected(cur_item) : false;*/
175         bool const cur_item_selected = refsTW->isItemSelected(sel);
176
177         if (cur_item_selected)
178                 referenceED->setText(sel->text(0));
179
180         if (at_ref_)
181                 gotoRef();
182         gotoPB->setEnabled(true);
183         if (typeAllowed() && !isBufferReadonly())
184                 typeCO->setEnabled(true);
185         nameED->setHidden(!nameAllowed());
186         nameL->setHidden(!nameAllowed());
187 }
188
189
190 void GuiRef::refTextChanged(QString const & str)
191 {
192         gotoPB->setEnabled(!str.isEmpty());
193         typeCO->setEnabled(!str.isEmpty());
194         typeLA->setEnabled(!str.isEmpty());
195 }
196
197
198 void GuiRef::refSelected(QTreeWidgetItem * sel)
199 {
200         if (isBufferReadonly())
201                 return;
202
203         if (sel->childCount()) {
204                 sel->setExpanded(false);
205                 return;
206         }
207
208 /*      int const cur_item = refsTW->currentRow();
209         bool const cur_item_selected = cur_item >= 0 ?
210                 refsLB->isSelected(cur_item) : false;*/
211         bool const cur_item_selected = refsTW->isItemSelected(sel);
212
213         if (cur_item_selected)
214                 referenceED->setText(sel->text(0));
215         // <enter> or double click, inserts ref and closes dialog
216         slotOK();
217 }
218
219
220 void GuiRef::sortToggled()
221 {
222         redoRefs();
223 }
224
225
226 void GuiRef::groupToggled()
227 {
228         redoRefs();
229 }
230
231
232 void GuiRef::updateClicked()
233 {
234         updateRefs();
235 }
236
237
238 void GuiRef::dialogRejected()
239 {
240         resetDialog();
241         // We have to do this manually, instead of calling slotClose(), because
242         // the dialog has already been made invisible before rejected() triggers.
243         Dialog::disconnect();
244 }
245
246
247 void GuiRef::resetDialog()
248 {
249         at_ref_ = false;
250         setGotoRef();
251 }
252
253
254 void GuiRef::closeEvent(QCloseEvent * e)
255 {
256         slotClose();
257         resetDialog();
258         e->accept();
259 }
260
261
262 void GuiRef::updateContents()
263 {
264         int orig_type = typeCO->currentIndex();
265
266         referenceED->clear();
267         nameED->clear();
268
269         referenceED->setText(toqstr(params_["reference"]));
270         nameED->setText(toqstr(params_["name"]));
271         nameED->setHidden(!nameAllowed());
272         nameL->setHidden(!nameAllowed());
273
274         // restore type settings for new insets
275         bool const new_inset = params_["reference"].empty();
276         if (new_inset)
277                 typeCO->setCurrentIndex(orig_type);
278         else
279                 typeCO->setCurrentIndex(InsetRef::getType(params_.getCmdName()));
280         typeCO->setEnabled(typeAllowed() && !isBufferReadonly());
281         if (!typeAllowed())
282                 typeCO->setCurrentIndex(0);
283
284         // insert buffer list
285         bufferCO->clear();
286         FileNameList const buffers(theBufferList().fileNames());
287         for (FileNameList::const_iterator it = buffers.begin();
288              it != buffers.end(); ++it) {
289                 bufferCO->addItem(toqstr(makeDisplayPath(it->absFileName())));
290         }
291
292         int const thebuffer = theBufferList().bufferNum(buffer().fileName());
293         // restore the buffer combo setting for new insets
294         if (new_inset && restored_buffer_ != -1
295             && restored_buffer_ < bufferCO->count() && thebuffer == active_buffer_)
296                 bufferCO->setCurrentIndex(restored_buffer_);
297         else {
298                 int const num = theBufferList().bufferNum(buffer().fileName());
299                 bufferCO->setCurrentIndex(num);
300                 if (thebuffer != active_buffer_)
301                         restored_buffer_ = num;
302         }
303         active_buffer_ = thebuffer;
304
305         updateRefs();
306         // Activate OK/Apply buttons if the users inserts a new ref
307         // and we have a valid pre-setting.
308         bc().setValid(isValid() && new_inset);
309 }
310
311
312 void GuiRef::applyView()
313 {
314         last_reference_ = referenceED->text();
315
316         params_.setCmdName(InsetRef::getName(typeCO->currentIndex()));
317         params_["reference"] = qstring_to_ucs4(last_reference_);
318         params_["name"] = qstring_to_ucs4(nameED->text());
319
320         restored_buffer_ = bufferCO->currentIndex();
321 }
322
323
324 bool GuiRef::nameAllowed()
325 {
326         KernelDocType const doc_type = docType();
327         return doc_type != LATEX && doc_type != LITERATE;
328 }
329
330
331 bool GuiRef::typeAllowed()
332 {
333         return docType() != DOCBOOK;
334 }
335
336
337 void GuiRef::setGoBack()
338 {
339         gotoPB->setText(qt_("&Go Back"));
340         gotoPB->setToolTip(qt_("Jump back to the original cursor location"));
341 }
342
343
344 void GuiRef::setGotoRef()
345 {
346         gotoPB->setText(qt_("&Go to Label"));
347         gotoPB->setToolTip(qt_("Jump to the selected label"));
348 }
349
350
351 void GuiRef::gotoRef()
352 {
353         string ref = fromqstr(referenceED->text());
354
355         if (at_ref_) {
356                 // go back
357                 setGotoRef();
358                 gotoBookmark();
359         } else {
360                 // go to the ref
361                 setGoBack();
362                 gotoRef(ref);
363         }
364         at_ref_ = !at_ref_;
365 }
366
367 inline bool caseInsensitiveLessThan(QString const & s1, QString const & s2)
368 {
369         return s1.toLower() < s2.toLower();
370 }
371
372
373 void GuiRef::redoRefs()
374 {
375         // Prevent these widgets from emitting any signals whilst
376         // we modify their state.
377         refsTW->blockSignals(true);
378         referenceED->blockSignals(true);
379         refsTW->setUpdatesEnabled(false);
380
381         refsTW->clear();
382
383         // need this because Qt will send a highlight() here for
384         // the first item inserted
385         QString const oldSelection(referenceED->text());
386
387         QStringList refsStrings;
388         QStringList refsCategories;
389         vector<docstring>::const_iterator iter;
390         bool noprefix = false;
391         for (iter = refs_.begin(); iter != refs_.end(); ++iter) {
392                 QString const lab = toqstr(*iter);
393                 refsStrings.append(lab);
394                 if (groupCB->isChecked()) {
395                         if (lab.contains(":")) {
396                                 QString const pref = lab.split(':')[0];
397                                 if (!refsCategories.contains(pref)) {
398                                         if (!pref.isEmpty())
399                                                 refsCategories.append(pref);
400                                         else
401                                                 noprefix = true;
402                                 }
403                         }
404                         else
405                                 noprefix = true;
406                 }
407         }
408         // sort categories case-intensively
409         qSort(refsCategories.begin(), refsCategories.end(),
410               caseInsensitiveLessThan /*defined above*/);
411         if (noprefix)
412                 refsCategories.insert(0, qt_("<No prefix>"));
413
414         QString const sort = sortingCO->isEnabled() ?
415                                 sortingCO->itemData(sortingCO->currentIndex()).toString()
416                                 : QString();
417         if (sort == "nocase")
418                 qSort(refsStrings.begin(), refsStrings.end(),
419                       caseInsensitiveLessThan /*defined above*/);
420         else if (sort == "case")
421                 qSort(refsStrings.begin(), refsStrings.end());
422
423         if (groupCB->isChecked()) {
424                 QList<QTreeWidgetItem *> refsCats;
425                 for (int i = 0; i < refsCategories.size(); ++i) {
426                         QString const cat = refsCategories.at(i);
427                         QTreeWidgetItem * item = new QTreeWidgetItem(refsTW);
428                         item->setText(0, cat);
429                         for (int i = 0; i < refsStrings.size(); ++i) {
430                                 QString const ref = refsStrings.at(i);
431                                 if ((ref.startsWith(cat + QString(":")))
432                                     || (cat == qt_("<No prefix>")
433                                        && (!ref.mid(1).contains(":") || ref.left(1).contains(":")))) {
434                                                 QTreeWidgetItem * child =
435                                                         new QTreeWidgetItem(item);
436                                                 child->setText(0, ref);
437                                                 item->addChild(child);
438                                 }
439                         }
440                         refsCats.append(item);
441                 }
442                 refsTW->addTopLevelItems(refsCats);
443         } else {
444                 QList<QTreeWidgetItem *> refsItems;
445                 for (int i = 0; i < refsStrings.size(); ++i) {
446                         QTreeWidgetItem * item = new QTreeWidgetItem(refsTW);
447                         item->setText(0, refsStrings.at(i));
448                         refsItems.append(item);
449                 }
450                 refsTW->addTopLevelItems(refsItems);
451         }
452
453         // restore the last selection or, for new insets, highlight
454         // the previous selection
455         if (!oldSelection.isEmpty() || !last_reference_.isEmpty()) {
456                 bool const newInset = oldSelection.isEmpty();
457                 QString textToFind = newInset ? last_reference_ : oldSelection;
458                 referenceED->setText(textToFind);
459                 last_reference_.clear();
460                 QTreeWidgetItemIterator it(refsTW);
461                 while (*it) {
462                         if ((*it)->text(0) == textToFind) {
463                                 refsTW->setCurrentItem(*it);
464                                 refsTW->setItemSelected(*it, true);
465                                 //Make sure selected item is visible
466                                 refsTW->scrollToItem(*it);
467                                 last_reference_ = textToFind;
468                                 break;
469                         }
470                         ++it;
471                 }
472         }
473         refsTW->setUpdatesEnabled(true);
474         refsTW->update();
475
476         // redo filter
477         filterLabels();
478
479         // Re-activate the emission of signals by these widgets.
480         refsTW->blockSignals(false);
481         referenceED->blockSignals(false);
482
483         gotoPB->setEnabled(!referenceED->text().isEmpty());
484         typeCO->setEnabled(!referenceED->text().isEmpty());
485         typeLA->setEnabled(!referenceED->text().isEmpty());
486 }
487
488
489 void GuiRef::updateRefs()
490 {
491         refs_.clear();
492         int const the_buffer = bufferCO->currentIndex();
493         if (the_buffer != -1) {
494                 FileNameList const names(theBufferList().fileNames());
495                 FileName const & name = names[the_buffer];
496                 Buffer const * buf = theBufferList().getBuffer(name);
497                 buf->getLabelList(refs_);
498         }
499         sortingCO->setEnabled(!refs_.empty());
500         refsTW->setEnabled(!refs_.empty());
501         groupCB->setEnabled(!refs_.empty());
502         // refsTW should only be the focus proxy when it is enabled
503         setFocusProxy(refs_.empty() ? 0 : refsTW);
504         redoRefs();
505 }
506
507
508 bool GuiRef::isValid()
509 {
510         return !referenceED->text().isEmpty();
511 }
512
513
514 void GuiRef::gotoRef(string const & ref)
515 {
516         dispatch(FuncRequest(LFUN_BOOKMARK_SAVE, "0"));
517         dispatch(FuncRequest(LFUN_LABEL_GOTO, ref));
518 }
519
520
521 void GuiRef::gotoBookmark()
522 {
523         dispatch(FuncRequest(LFUN_BOOKMARK_GOTO, "0"));
524 }
525
526
527 void GuiRef::filterLabels()
528 {
529         Qt::CaseSensitivity cs = csFindCB->isChecked() ?
530                 Qt::CaseSensitive : Qt::CaseInsensitive;
531         QTreeWidgetItemIterator it(refsTW);
532         while (*it) {
533                 (*it)->setHidden(
534                         (*it)->childCount() == 0
535                         && !(*it)->text(0).contains(filter_->text(), cs)
536                 );
537                 ++it;
538         }
539 }
540
541
542 void GuiRef::resetFilter()
543 {
544         filter_->setText(QString());
545         filterLabels();
546 }
547
548
549 bool GuiRef::initialiseParams(std::string const & data)
550 {
551         InsetCommand::string2params(data, params_);
552         return true;
553 }
554
555
556 void GuiRef::dispatchParams()
557 {
558         std::string const lfun = InsetCommand::params2string(params_);
559         dispatch(FuncRequest(getLfun(), lfun));
560 }
561
562
563
564 Dialog * createGuiRef(GuiView & lv) { return new GuiRef(lv); }
565
566
567 } // namespace frontend
568 } // namespace lyx
569
570 #include "moc_GuiRef.cpp"