X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Ffrontends%2Fqt4%2FTocWidget.cpp;h=80c9db97d952a6ab773ca96eca3f0675fbc5f7a8;hb=59e0cb8f85f0d2f985b31532dd3308315659c662;hp=ec1fa42eecb0f6f8fe1b59e1e42b80f98a748691;hpb=e1ce2f92db49d50c688414a32944be8e718faa2e;p=lyx.git diff --git a/src/frontends/qt4/TocWidget.cpp b/src/frontends/qt4/TocWidget.cpp index ec1fa42eec..80c9db97d9 100644 --- a/src/frontends/qt4/TocWidget.cpp +++ b/src/frontends/qt4/TocWidget.cpp @@ -13,37 +13,50 @@ #include "TocWidget.h" -#include "GuiToc.h" +#include "GuiApplication.h" +#include "GuiView.h" #include "qt_helpers.h" +#include "TocModel.h" -#include "debug.h" +#include "Buffer.h" +#include "BufferView.h" +#include "CutAndPaste.h" +#include "FuncRequest.h" +#include "FuncStatus.h" +#include "LyX.h" +#include "Menus.h" +#include "TocBackend.h" + +#include "insets/InsetCommand.h" +#include "insets/InsetRef.h" + +#include "support/debug.h" +#include "support/lassert.h" #include -#include -#include +#include +#include #include -using std::endl; -using std::vector; +#define DELAY_UPDATE_VIEW +using namespace std; namespace lyx { namespace frontend { -TocWidget::TocWidget(GuiToc & form, QWidget * parent) - : QWidget(parent), depth_(0), form_(form) +TocWidget::TocWidget(GuiView & gui_view, QWidget * parent) + : QWidget(parent), depth_(0), persistent_(false), gui_view_(gui_view), update_delay_(0) + { setupUi(this); - setWindowTitle(qt_("Outline")); - - connect(&form_, SIGNAL(modelReset()), SLOT(updateGui())); - moveOutTB->setIcon(QIcon(":/images/promote.png")); - moveInTB->setIcon(QIcon(":/images/demote.png")); - moveUpTB->setIcon(QIcon(":/images/up.png")); - moveDownTB->setIcon(QIcon(":/images/down.png")); - updateTB->setIcon(QIcon(":/images/reload.png")); + moveOutTB->setIcon(QIcon(getPixmap("images/", "promote", "png"))); + moveInTB->setIcon(QIcon(getPixmap("images/", "demote", "png"))); + moveUpTB->setIcon(QIcon(getPixmap("images/", "up", "png"))); + moveDownTB->setIcon(QIcon(getPixmap("images/", "down", "png"))); + updateTB->setIcon(QIcon(getPixmap("images/", "reload", "png"))); // avoid flickering tocTV->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); @@ -58,16 +71,172 @@ TocWidget::TocWidget(GuiToc & form, QWidget * parent) // Only one item selected at a time. tocTV->setSelectionMode(QAbstractItemView::SingleSelection); + setFocusProxy(tocTV); + + // The toc types combo won't change its model. + typeCO->setModel(gui_view_.tocModels().nameModel()); + + // Make sure the buttons are disabled when first shown without a loaded + // Buffer. + enableControls(false); + + // make us responsible for the context menu of the tabbar + setContextMenuPolicy(Qt::CustomContextMenu); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(showContextMenu(const QPoint &))); + connect(tocTV, SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(showContextMenu(const QPoint &))); + connect(filterLE, SIGNAL(textEdited(QString)), + this, SLOT(filterContents())); + + init(QString()); +} + + +void TocWidget::showContextMenu(const QPoint & pos) +{ + std::string name = "context-toc-" + fromqstr(current_type_); + QMenu * menu = guiApp->menus().menu(toqstr(name), gui_view_); + if (!menu) + return; + menu->exec(mapToGlobal(pos)); +} + + +Inset * TocWidget::itemInset() const +{ + QModelIndex const & index = tocTV->currentIndex(); + TocItem const & item = + gui_view_.tocModels().currentItem(current_type_, index); + DocIterator const & dit = item.dit(); + + Inset * inset = 0; + if (current_type_ == "label" + || current_type_ == "graphics" + || current_type_ == "citation" + || current_type_ == "child") + inset = dit.nextInset(); + + else if (current_type_ == "branch" + || current_type_ == "index" + || current_type_ == "change") + inset = &dit.inset(); + + else if (current_type_ == "table" + || current_type_ == "listing" + || current_type_ == "figure") { + DocIterator tmp_dit(dit); + tmp_dit.pop_back(); + inset = &tmp_dit.inset(); + } + return inset; +} + + +bool TocWidget::getStatus(Cursor & cur, FuncRequest const & cmd, + FuncStatus & status) const +{ + Inset * inset = itemInset(); + FuncRequest tmpcmd(cmd); + + QModelIndex const & index = tocTV->currentIndex(); + TocItem const & item = + gui_view_.tocModels().currentItem(current_type_, index); + + switch (cmd.action()) + { + case LFUN_CHANGE_ACCEPT: + case LFUN_CHANGE_REJECT: + case LFUN_OUTLINE_UP: + case LFUN_OUTLINE_DOWN: + case LFUN_OUTLINE_IN: + case LFUN_OUTLINE_OUT: + case LFUN_SECTION_SELECT: + status.setEnabled(true); + return true; + + case LFUN_LABEL_COPY_AS_REF: { + // For labels in math, we need to supply the label as a string + FuncRequest label_copy(LFUN_LABEL_COPY_AS_REF, item.asString()); + if (inset) + return inset->getStatus(cur, label_copy, status); + } + + default: + if (inset) + return inset->getStatus(cur, tmpcmd, status); + } + + return false; +} + + +void TocWidget::doDispatch(Cursor & cur, FuncRequest const & cmd) +{ + Inset * inset = itemInset(); + FuncRequest tmpcmd(cmd); + + QModelIndex const & index = tocTV->currentIndex(); + TocItem const & item = + gui_view_.tocModels().currentItem(current_type_, index); + + // Start an undo group. + cur.beginUndoGroup(); + + switch (cmd.action()) + { + case LFUN_CHANGE_ACCEPT: + case LFUN_CHANGE_REJECT: + case LFUN_SECTION_SELECT: + dispatch(item.action()); + cur.dispatch(tmpcmd); + break; + + case LFUN_LABEL_COPY_AS_REF: { + // For labels in math, we need to supply the label as a string + FuncRequest label_copy(LFUN_LABEL_COPY_AS_REF, item.asString()); + if (inset) + inset->dispatch(cur, label_copy); + break; + } + + case LFUN_OUTLINE_UP: + case LFUN_OUTLINE_DOWN: + case LFUN_OUTLINE_IN: + case LFUN_OUTLINE_OUT: + outline(cmd.action()); + break; + + default: + if (inset) + inset->dispatch(cur, tmpcmd); + } + cur.endUndoGroup(); +} + + +void TocWidget::on_tocTV_activated(QModelIndex const & index) +{ + goTo(index); } -void TocWidget::selectionChanged(const QModelIndex & current, - const QModelIndex & /*previous*/) +void TocWidget::on_tocTV_pressed(QModelIndex const & index) { - LYXERR(Debug::GUI, "selectionChanged index " << current.row() - << ", " << current.column()); + Qt::MouseButtons const button = QApplication::mouseButtons(); + if (button & Qt::LeftButton) { + goTo(index); + gui_view_.setFocus(); + } +} + - form_.goTo(typeCO->currentIndex(), current); +void TocWidget::goTo(QModelIndex const & index) +{ + LYXERR(Debug::GUI, "goto " << index.row() + << ", " << index.column()); + + gui_view_.tocModels().goTo(current_type_, index); } @@ -76,20 +245,34 @@ void TocWidget::on_updateTB_clicked() // The backend update can take some time so we disable // the controls while waiting. enableControls(false); - form_.updateBackend(); + gui_view_.currentBufferView()->buffer().updateBuffer(); +} + + +void TocWidget::on_sortCB_stateChanged(int state) +{ + gui_view_.tocModels().sort(current_type_, state == Qt::Checked); + updateViewForce(); +} + + +void TocWidget::on_persistentCB_stateChanged(int state) +{ + persistent_ = state == Qt::Checked; } + /* FIXME (Ugras 17/11/06): -I have implemented a getIndexDepth function to get the model indices. In my +I have implemented a indexDepth function to get the model indices. In my opinion, somebody should derive a new qvariant class for tocModelItem -which saves the string data and depth information. that will save the -depth calculation. -*/ -int TocWidget::getIndexDepth(QModelIndex const & index, int depth) +which saves the string data and depth information. That will save the +depth calculation. */ + +static int indexDepth(QModelIndex const & index, int depth = -1) { ++depth; - return (index.parent() == QModelIndex()) - ? depth : getIndexDepth(index.parent(),depth); + return index.parent() == QModelIndex() + ? depth : indexDepth(index.parent(), depth); } @@ -98,85 +281,85 @@ void TocWidget::on_depthSL_valueChanged(int depth) if (depth == depth_) return; setTreeDepth(depth); + gui_view_.setFocus(); } void TocWidget::setTreeDepth(int depth) { depth_ = depth; + if (!tocTV->model()) + return; +#if QT_VERSION >= 0x040300 + // this should be faster than our own code below + if (depth == 0) + tocTV->collapseAll(); + else + tocTV->expandToDepth(depth - 1); +#else // expanding and then collapsing is probably better, // but my qt 4.1.2 doesn't have expandAll().. //tocTV->expandAll(); QModelIndexList indices = tocTV->model()->match( - tocTV->model()->index(0,0), + tocTV->model()->index(0, 0), Qt::DisplayRole, "*", -1, Qt::MatchFlags(Qt::MatchWildcard|Qt::MatchRecursive)); int size = indices.size(); for (int i = 0; i < size; i++) { QModelIndex index = indices[i]; - if (getIndexDepth(index) < depth_) - tocTV->expand(index); - else - tocTV->collapse(index); + tocTV->setExpanded(index, indexDepth(index) < depth_); } +#endif } -void TocWidget::on_typeCO_currentIndexChanged(int value) + +void TocWidget::on_typeCO_currentIndexChanged(int index) { - setTocModel(value); + if (index == -1) + return; + current_type_ = typeCO->itemData(index).toString(); + updateViewForce(); + if (typeCO->hasFocus()) + gui_view_.setFocus(); } -void TocWidget::on_moveUpTB_clicked() +void TocWidget::outline(FuncCode func_code) { - enableControls(false); QModelIndexList const & list = tocTV->selectionModel()->selectedIndexes(); - if (!list.isEmpty()) { - enableControls(false); - form_.goTo(typeCO->currentIndex(), list[0]); - form_.outlineUp(); - enableControls(true); - } + if (list.isEmpty()) + return; + enableControls(false); + goTo(list[0]); + dispatch(FuncRequest(func_code)); + enableControls(true); + gui_view_.setFocus(); +} + + +void TocWidget::on_moveUpTB_clicked() +{ + outline(LFUN_OUTLINE_UP); } void TocWidget::on_moveDownTB_clicked() { - enableControls(false); - QModelIndexList const & list = tocTV->selectionModel()->selectedIndexes(); - if (!list.isEmpty()) { - enableControls(false); - form_.goTo(typeCO->currentIndex(), list[0]); - form_.outlineDown(); - enableControls(true); - } + outline(LFUN_OUTLINE_DOWN); } void TocWidget::on_moveInTB_clicked() { - enableControls(false); - QModelIndexList const & list = tocTV->selectionModel()->selectedIndexes(); - if (!list.isEmpty()) { - enableControls(false); - form_.goTo(typeCO->currentIndex(), list[0]); - form_.outlineIn(); - enableControls(true); - } + outline(LFUN_OUTLINE_IN); } void TocWidget::on_moveOutTB_clicked() { - QModelIndexList const & list = tocTV->selectionModel()->selectedIndexes(); - if (!list.isEmpty()) { - enableControls(false); - form_.goTo(typeCO->currentIndex(), list[0]); - form_.outlineOut(); - enableControls(true); - } + outline(LFUN_OUTLINE_OUT); } @@ -187,10 +370,9 @@ void TocWidget::select(QModelIndex const & index) return; } - disconnectSelectionModel(); - tocTV->setCurrentIndex(index); tocTV->scrollTo(index); - reconnectSelectionModel(); + tocTV->clearSelection(); + tocTV->setCurrentIndex(index); } @@ -198,107 +380,162 @@ void TocWidget::enableControls(bool enable) { updateTB->setEnabled(enable); - if (!form_.canOutline(typeCO->currentIndex())) + if (!canOutline()) enable = false; moveUpTB->setEnabled(enable); moveDownTB->setEnabled(enable); moveInTB->setEnabled(enable); moveOutTB->setEnabled(enable); - - depthSL->setEnabled(enable); } void TocWidget::updateView() { - LYXERR(Debug::GUI, "In TocWidget::updateView()"); - select(form_.currentIndex(typeCO->currentIndex())); +// Enable if you dont want the delaying business, cf #7138. +#ifndef DELAY_UPDATE_VIEW + updateViewForce(); + return; +#endif + // already scheduled? + if (update_delay_ == -1) + return; + QTimer::singleShot(update_delay_, this, SLOT(updateViewForce())); + // Subtler optimization for having the delay more UI invisible. + // We trigger update immediately for sparse editation actions, + // i.e. there was no editation/cursor movement in last 2 sec. + // At worst there will be +1 redraw after 2s in a such "calm" mode. + if (update_delay_ != 0) + updateViewForce(); + update_delay_ = -1; } - -void TocWidget::updateGui() +void TocWidget::updateViewForce() { - vector const & type_names = form_.typeNames(); - if (type_names.empty()) { - enableControls(false); - typeCO->clear(); - tocTV->setModel(new QStandardItemModel); - tocTV->setEditTriggers(QAbstractItemView::NoEditTriggers); + update_delay_ = 2000; + if (!gui_view_.documentBufferView()) { + tocTV->setModel(0); + depthSL->setMaximum(0); + depthSL->setValue(0); + setEnabled(false); return; } - - QString current_text = typeCO->currentText(); - //lyxerr << "current_text " << fromqstr(current_text) << endl; - typeCO->blockSignals(true); - typeCO->clear(); - int current_type = -1; - for (size_t i = 0; i != type_names.size(); ++i) { - QString item = toqstr(type_names[i]); - typeCO->addItem(item); - if (item == current_text) - current_type = i; - } - if (current_type != -1) - typeCO->setCurrentIndex(current_type); - else - typeCO->setCurrentIndex(form_.selectedType()); - typeCO->blockSignals(false); - - setTocModel(typeCO->currentIndex()); -} - - -void TocWidget::setTocModel(size_t type) -{ - bool controls_enabled = false; - QStandardItemModel * toc_model = form_.tocModel(type); - if (toc_model) { - controls_enabled = toc_model->rowCount() > 0; + setEnabled(true); + bool const is_sortable = isSortable(); + sortCB->setEnabled(is_sortable); + bool focus_ = tocTV->hasFocus(); + tocTV->setEnabled(false); + tocTV->setUpdatesEnabled(false); + + QAbstractItemModel * toc_model = + gui_view_.tocModels().model(current_type_); + if (tocTV->model() != toc_model) { tocTV->setModel(toc_model); tocTV->setEditTriggers(QAbstractItemView::NoEditTriggers); + if (persistent_) + setTreeDepth(depth_); } - enableControls(controls_enabled); + sortCB->blockSignals(true); + sortCB->setChecked(is_sortable + && gui_view_.tocModels().isSorted(current_type_)); + sortCB->blockSignals(false); + + bool const can_navigate_ = canNavigate(); + persistentCB->setEnabled(can_navigate_); - reconnectSelectionModel(); + bool controls_enabled = toc_model && toc_model->rowCount() > 0 + && !gui_view_.documentBufferView()->buffer().isReadonly(); + enableControls(controls_enabled); - if (controls_enabled) { - depthSL->setMaximum(form_.getTocDepth(type)); - depthSL->setValue(depth_); + depthSL->setMaximum(gui_view_.tocModels().depth(current_type_)); + depthSL->setValue(depth_); + if (!persistent_ && can_navigate_) + setTreeDepth(depth_); + if (can_navigate_) { + persistentCB->setChecked(persistent_); + select(gui_view_.tocModels().currentIndex(current_type_)); } + filterContents(); + tocTV->setEnabled(true); + tocTV->setUpdatesEnabled(true); + if (focus_) + tocTV->setFocus(); +} - LYXERR(Debug::GUI, "In TocWidget::updateGui()"); - select(form_.currentIndex(typeCO->currentIndex())); +void TocWidget::filterContents() +{ + if (!tocTV->model()) + return; + + QModelIndexList indices = tocTV->model()->match( + tocTV->model()->index(0, 0), + Qt::DisplayRole, "*", -1, + Qt::MatchFlags(Qt::MatchWildcard|Qt::MatchRecursive)); - if (toc_model) { - LYXERR(Debug::GUI, "tocModel()->rowCount " - << toc_model->rowCount() - << "\nform_->tocModel()->columnCount " - << toc_model->columnCount()); + int size = indices.size(); + for (int i = 0; i < size; i++) { + QModelIndex index = indices[i]; + bool const matches = + index.data().toString().contains( + filterLE->text(), Qt::CaseInsensitive); + tocTV->setRowHidden(index.row(), index.parent(), !matches); + } + // recursively unhide parents of unhidden children + for (int i = size - 1; i >= 0; i--) { + QModelIndex index = indices[i]; + if (!tocTV->isRowHidden(index.row(), index.parent()) + && index.parent() != QModelIndex()) + tocTV->setRowHidden(index.parent().row(), + index.parent().parent(), false); } } -void TocWidget::reconnectSelectionModel() +static QString decodeType(QString const & str) { - connect(tocTV->selectionModel(), - SIGNAL(currentChanged(const QModelIndex &, - const QModelIndex &)), - this, - SLOT(selectionChanged(const QModelIndex &, - const QModelIndex &))); + QString type = str; + if (type.contains("tableofcontents")) { + type = "tableofcontents"; + } else if (type.contains("floatlist")) { + if (type.contains("\"figure")) + type = "figure"; + else if (type.contains("\"table")) + type = "table"; + else if (type.contains("\"algorithm")) + type = "algorithm"; + } + return type; } -void TocWidget::disconnectSelectionModel() + +void TocWidget::init(QString const & str) { - disconnect(tocTV->selectionModel(), - SIGNAL(currentChanged(QModelIndex, QModelIndex)), - this, SLOT(selectionChanged(QModelIndex, QModelIndex))); + int new_index; + if (str.isEmpty()) + new_index = typeCO->findData(current_type_); + else + new_index = typeCO->findData(decodeType(str)); + + // If everything else fails, settle on the table of contents which is + // guaranteed to exist. + if (new_index == -1) { + current_type_ = "tableofcontents"; + new_index = typeCO->findData(current_type_); + } else { + current_type_ = typeCO->itemData(new_index).toString(); + } + + typeCO->blockSignals(true); + typeCO->setCurrentIndex(new_index); + typeCO->blockSignals(false); + + // no delay when the whole outliner is reseted. + update_delay_ = 0; } } // namespace frontend } // namespace lyx -#include "TocWidget_moc.cpp" +#include "moc_TocWidget.cpp"