3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
7 * \author Abdelrazak Younes
9 * Full author contact details are available in file CREDITS.
14 #include "TocWidget.h"
16 #include "GuiApplication.h"
18 #include "qt_helpers.h"
22 #include "BufferView.h"
23 #include "CutAndPaste.h"
24 #include "FuncRequest.h"
25 #include "FuncStatus.h"
28 #include "TocBackend.h"
30 #include "insets/InsetCommand.h"
31 #include "insets/InsetRef.h"
33 #include "support/debug.h"
34 #include "support/lassert.h"
36 #include <QHeaderView>
46 TocWidget::TocWidget(GuiView & gui_view, QWidget * parent)
47 : QWidget(parent), depth_(0), persistent_(false), gui_view_(gui_view),
48 timer_(new QTimer(this))
52 moveOutTB->setIcon(QIcon(getPixmap("images/", "outline-out", "svgz,png")));
53 moveInTB->setIcon(QIcon(getPixmap("images/", "outline-in", "svgz,png")));
54 moveUpTB->setIcon(QIcon(getPixmap("images/", "outline-up", "svgz,png")));
55 moveDownTB->setIcon(QIcon(getPixmap("images/", "outline-down", "svgz,png")));
56 updateTB->setIcon(QIcon(getPixmap("images/", "reload", "svgz,png")));
58 QSize icon_size = gui_view.iconSize();
59 moveOutTB->setIconSize(icon_size);
60 moveInTB->setIconSize(icon_size);
61 moveUpTB->setIconSize(icon_size);
62 moveDownTB->setIconSize(icon_size);
63 updateTB->setIconSize(icon_size);
66 tocTV->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
70 // hide the pointless QHeader for now
71 // in the future, new columns may appear
72 // like labels, bookmarks, etc...
73 // tocTV->header()->hide();
74 tocTV->header()->setVisible(false);
76 // Only one item selected at a time.
77 tocTV->setSelectionMode(QAbstractItemView::SingleSelection);
80 // The toc types combo won't change its model.
81 typeCO->setModel(gui_view_.tocModels().nameModel());
83 // Make sure the buttons are disabled when first shown without a loaded
85 enableControls(false);
87 // make us responsible for the context menu of the tabbar
88 setContextMenuPolicy(Qt::CustomContextMenu);
89 connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
90 this, SLOT(showContextMenu(const QPoint &)));
91 connect(tocTV, SIGNAL(customContextMenuRequested(const QPoint &)),
92 this, SLOT(showContextMenu(const QPoint &)));
93 connect(filterLE, SIGNAL(textEdited(QString)),
94 this, SLOT(filterContents()));
95 connect(activeFilterCO, SIGNAL(activated(int)),
96 this, SLOT(filterContents()));
98 // setting the update timer
99 timer_->setSingleShot(true);
100 connect(timer_, SIGNAL(timeout()), this, SLOT(finishUpdateView()));
106 void TocWidget::showContextMenu(const QPoint & pos)
108 std::string name = "context-toc-" + fromqstr(current_type_);
109 QMenu * menu = guiApp->menus().menu(toqstr(name), gui_view_);
112 menu->exec(mapToGlobal(pos));
116 Inset * TocWidget::itemInset() const
118 QModelIndex const & index = tocTV->currentIndex();
119 TocItem const & item =
120 gui_view_.tocModels().currentItem(current_type_, index);
121 DocIterator const & dit = item.dit();
123 Inset * inset = nullptr;
124 if (current_type_ == "label"
125 || current_type_ == "graphics"
126 || current_type_ == "citation"
127 || current_type_ == "child")
128 inset = dit.nextInset();
130 else if (current_type_ == "branch"
131 || current_type_ == "index"
132 || current_type_ == "change"
133 || current_type_ == "table"
134 || current_type_ == "listing"
135 || current_type_ == "figure")
136 inset = &dit.inset();
142 bool TocWidget::getStatus(Cursor & cur, FuncRequest const & cmd,
143 FuncStatus & status) const
145 Inset * inset = itemInset();
146 FuncRequest tmpcmd(cmd);
148 QModelIndex const & index = tocTV->currentIndex();
149 TocItem const & item =
150 gui_view_.tocModels().currentItem(current_type_, index);
152 switch (cmd.action())
154 case LFUN_CHANGE_ACCEPT:
155 case LFUN_CHANGE_REJECT:
156 case LFUN_OUTLINE_UP:
157 case LFUN_OUTLINE_DOWN:
158 case LFUN_OUTLINE_IN:
159 case LFUN_OUTLINE_OUT:
160 case LFUN_SECTION_SELECT:
161 status.setEnabled((bool)item.dit());
164 case LFUN_LABEL_COPY_AS_REFERENCE: {
165 // For labels in math, we need to supply the label as a string
166 FuncRequest label_copy(LFUN_LABEL_COPY_AS_REFERENCE, item.str());
168 return inset->getStatus(cur, label_copy, status);
174 return inset->getStatus(cur, tmpcmd, status);
181 void TocWidget::doDispatch(Cursor & cur, FuncRequest const & cmd,
185 Inset * inset = itemInset();
187 QModelIndex const & index = tocTV->currentIndex();
188 TocItem const & item =
189 gui_view_.tocModels().currentItem(current_type_, index);
191 // Start an undo group.
192 cur.beginUndoGroup();
194 switch (cmd.action())
196 case LFUN_CHANGE_ACCEPT:
197 case LFUN_CHANGE_REJECT: {
198 // The action is almost always LYX_UNKNOWN_ACTION, which will
199 // have the effect of moving the cursor to the location of
200 // the change. (See TocItem::action.)
201 dispatch(item.action());
202 // If we do not reset the origin, then the request will be sent back
203 // here, and we are in an infinite loop. But we need the dispatch
204 // machinery to clean up for us, if the cursor is in an inset that
205 // will be deleted. See bug #10316.
206 FuncRequest tmpcmd(cmd);
207 tmpcmd.setOrigin(FuncRequest::INTERNAL);
209 dr.forceBufferUpdate();
213 case LFUN_SECTION_SELECT:
214 dispatch(item.action());
216 // necessary to get the selection drawn.
217 cur.buffer()->changed(true);
218 gui_view_.setFocus();
221 case LFUN_LABEL_COPY_AS_REFERENCE: {
222 // For labels in math, we need to supply the label as a string
223 FuncRequest label_copy(LFUN_LABEL_COPY_AS_REFERENCE, item.str());
225 inset->dispatch(cur, label_copy);
229 case LFUN_OUTLINE_UP:
230 case LFUN_OUTLINE_DOWN:
231 case LFUN_OUTLINE_IN:
232 case LFUN_OUTLINE_OUT:
233 outline(cmd.action());
237 FuncRequest tmpcmd(cmd);
239 inset->dispatch(cur, tmpcmd);
246 void TocWidget::on_tocTV_activated(QModelIndex const & index)
252 void TocWidget::on_tocTV_pressed(QModelIndex const & index)
255 Qt::MouseButtons const button = QApplication::mouseButtons();
256 if (button & Qt::LeftButton) {
258 gui_view_.setFocus();
259 gui_view_.activateWindow();
264 void TocWidget::goTo(QModelIndex const & index)
266 LYXERR(Debug::GUI, "goto " << index.row()
267 << ", " << index.column());
269 sendDispatch(gui_view_.tocModels().goTo(current_type_, index));
273 void TocWidget::on_updateTB_clicked()
275 // The backend update can take some time so we disable
276 // the controls while waiting.
277 enableControls(false);
278 gui_view_.currentBufferView()->buffer().updateBuffer();
282 void TocWidget::on_sortCB_stateChanged(int state)
284 gui_view_.tocModels().sort(current_type_, state == Qt::Checked);
289 void TocWidget::on_persistentCB_stateChanged(int state)
291 persistent_ = state == Qt::Checked;
296 /* FIXME (Ugras 17/11/06):
297 I have implemented a indexDepth function to get the model indices. In my
298 opinion, somebody should derive a new qvariant class for tocModelItem
299 which saves the string data and depth information. That will save the
300 depth calculation. */
302 static int indexDepth(QModelIndex const & index, int depth = -1)
305 return index.parent() == QModelIndex()
306 ? depth : indexDepth(index.parent(), depth);
310 void TocWidget::on_depthSL_valueChanged(int depth)
315 gui_view_.setFocus();
319 void TocWidget::setTreeDepth(int depth)
326 tocTV->collapseAll();
328 tocTV->expandToDepth(depth - 1);
332 void TocWidget::on_typeCO_currentIndexChanged(int index)
337 current_type_ = typeCO->itemData(index).toString();
339 if (typeCO->hasFocus())
340 gui_view_.setFocus();
344 void TocWidget::outline(FuncCode func_code)
346 QModelIndexList const & list = tocTV->selectionModel()->selectedIndexes();
350 //if another window is active, this attempt will fail,
351 //but it will work at least for the second attempt
352 gui_view_.activateWindow();
354 enableControls(false);
356 sendDispatch(FuncRequest(func_code));
357 enableControls(true);
358 gui_view_.setFocus();
362 void TocWidget::sendDispatch(FuncRequest fr)
365 fr.setViewOrigin(&gui_view_);
366 DispatchResult dr=dispatch(fr);
368 gui_view_.message(dr.message());
372 void TocWidget::on_moveUpTB_clicked()
374 outline(LFUN_OUTLINE_UP);
378 void TocWidget::on_moveDownTB_clicked()
380 outline(LFUN_OUTLINE_DOWN);
384 void TocWidget::on_moveInTB_clicked()
386 outline(LFUN_OUTLINE_IN);
390 void TocWidget::on_moveOutTB_clicked()
392 outline(LFUN_OUTLINE_OUT);
396 void TocWidget::select(QModelIndex const & index)
398 if (!index.isValid()) {
399 LYXERR(Debug::GUI, "TocWidget::select(): QModelIndex is invalid!");
403 tocTV->scrollTo(index);
404 tocTV->clearSelection();
405 tocTV->setCurrentIndex(index);
409 void TocWidget::enableControls(bool enable)
411 updateTB->setEnabled(enable);
416 moveUpTB->setEnabled(enable);
417 moveDownTB->setEnabled(enable);
418 moveInTB->setEnabled(enable);
419 moveOutTB->setEnabled(enable);
423 void TocWidget::updateView()
425 if (!gui_view_.documentBufferView()) {
426 tocTV->setModel(nullptr);
427 depthSL->setMaximum(0);
428 depthSL->setValue(0);
433 bool const is_sortable = isSortable();
434 sortCB->setEnabled(is_sortable);
435 bool focus = tocTV->hasFocus();
436 tocTV->setEnabled(false);
437 tocTV->setUpdatesEnabled(false);
439 QAbstractItemModel * toc_model =
440 gui_view_.tocModels().model(current_type_);
441 if (tocTV->model() != toc_model) {
442 tocTV->setModel(toc_model);
443 tocTV->setEditTriggers(QAbstractItemView::NoEditTriggers);
445 setTreeDepth(depth_);
448 sortCB->blockSignals(true);
449 sortCB->setChecked(is_sortable
450 && gui_view_.tocModels().isSorted(current_type_));
451 sortCB->blockSignals(false);
453 persistentCB->setEnabled(canNavigate());
455 bool controls_enabled = toc_model && toc_model->rowCount() > 0
456 && !gui_view_.documentBufferView()->buffer().isReadonly();
457 enableControls(controls_enabled);
459 depthSL->setMaximum(gui_view_.tocModels().depth(current_type_));
460 depthSL->setValue(depth_);
461 tocTV->setEnabled(true);
462 tocTV->setUpdatesEnabled(true);
466 // Expensive operations are on a timer. We finish the update immediately
467 // for sparse edition actions, i.e. there was no edition/cursor movement
468 // recently, then every 300ms.
469 if (!timer_->isActive()) {
476 void TocWidget::updateViewNow()
483 void TocWidget::finishUpdateView()
485 // Profiling shows that this is the expensive stuff in the context of typing
486 // text and moving with arrows. For bigger operations, this is negligible,
487 // and outweighted by TocModels::reset() anyway.
490 setTreeDepth(depth_);
491 persistentCB->setChecked(persistent_);
492 // select the item at current cursor location
493 if (gui_view_.documentBufferView()) {
494 DocIterator const & dit = gui_view_.documentBufferView()->cursor();
495 select(gui_view_.tocModels().currentIndex(current_type_, dit));
502 void TocWidget::filterContents()
507 QModelIndexList indices = tocTV->model()->match(
508 tocTV->model()->index(0, 0),
509 Qt::DisplayRole, "*", -1,
510 Qt::MatchFlags(Qt::MatchWildcard|Qt::MatchRecursive));
512 bool const show_active =
513 activeFilterCO->currentIndex() != 2;
514 bool const show_inactive =
515 activeFilterCO->currentIndex() != 1;
517 int size = indices.size();
518 for (int i = 0; i < size; i++) {
519 QModelIndex index = indices[i];
521 index.data().toString().contains(
522 filterLE->text(), Qt::CaseInsensitive);
523 TocItem const & item =
524 gui_view_.tocModels().currentItem(current_type_, index);
525 matches &= (show_active && item.isOutput()) || (show_inactive && !item.isOutput());
526 tocTV->setRowHidden(index.row(), index.parent(), !matches);
528 // recursively unhide parents of unhidden children
529 for (int i = size - 1; i >= 0; i--) {
530 QModelIndex index = indices[i];
531 if (!tocTV->isRowHidden(index.row(), index.parent())
532 && index.parent() != QModelIndex())
533 tocTV->setRowHidden(index.parent().row(),
534 index.parent().parent(), false);
539 static QString decodeType(QString const & str)
542 if (type.contains("tableofcontents"))
543 type = "tableofcontents";
544 else if (type.contains("lstlistoflistings"))
546 else if (type.contains("floatlist")) {
547 if (type.contains("\"figure"))
549 else if (type.contains("\"table"))
551 else if (type.contains("\"algorithm"))
558 void TocWidget::init(QString const & str)
562 new_index = typeCO->findData(current_type_);
564 new_index = typeCO->findData(decodeType(str));
566 // If everything else fails, settle on the table of contents which is
567 // guaranteed to exist.
568 if (new_index == -1) {
569 current_type_ = "tableofcontents";
570 new_index = typeCO->findData(current_type_);
572 current_type_ = typeCO->itemData(new_index).toString();
575 typeCO->blockSignals(true);
576 typeCO->setCurrentIndex(new_index);
577 typeCO->blockSignals(false);
581 } // namespace frontend
584 #include "moc_TocWidget.cpp"