]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt4/TocWidget.cpp
Remove the .aux and .bbl files and update the citation labels
[lyx.git] / src / frontends / qt4 / TocWidget.cpp
index ec1fa42eecb0f6f8fe1b59e1e42b80f98a748691..80c9db97d952a6ab773ca96eca3f0675fbc5f7a8 100644 (file)
 
 #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 <QHeaderView>
-#include <QPushButton>
-#include <QTreeWidgetItem>
+#include <QMenu>
+#include <QTimer>
 
 #include <vector>
 
-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<docstring> 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"