From 6ad1b1cd0968021c7aba0b3358a3e0e860c61ed7 Mon Sep 17 00:00:00 2001 From: Abdelrazak Younes Date: Tue, 17 Jun 2008 15:10:03 +0000 Subject: [PATCH] General cleanup of the Outline/Navigator: - Make use the Qt Model/View separation concept, TocWidget is now really just a view of the current toc model. - the toc type combo now use a model defined in TocModels. - the models are not deleted at each reset, they are now just cleared out. git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@25289 a592a061-630c-0410-9148-cb99ea01b6c8 --- src/frontends/qt4/GuiToc.cpp | 4 +- src/frontends/qt4/TocModel.cpp | 181 ++++++++++++++++++-------------- src/frontends/qt4/TocModel.h | 49 +++++---- src/frontends/qt4/TocWidget.cpp | 81 +++++++++----- src/frontends/qt4/TocWidget.h | 2 + 5 files changed, 187 insertions(+), 130 deletions(-) diff --git a/src/frontends/qt4/GuiToc.cpp b/src/frontends/qt4/GuiToc.cpp index f130f878c5..3ab8cee212 100644 --- a/src/frontends/qt4/GuiToc.cpp +++ b/src/frontends/qt4/GuiToc.cpp @@ -83,8 +83,8 @@ void GuiToc::dispatchParams() void GuiToc::enableView(bool enable) { - widget_->init(QString()); - widget_->setEnabled(enable); + if (!enable) + widget_->init(QString()); } diff --git a/src/frontends/qt4/TocModel.cpp b/src/frontends/qt4/TocModel.cpp index cd6cd7058e..6b115c8390 100644 --- a/src/frontends/qt4/TocModel.cpp +++ b/src/frontends/qt4/TocModel.cpp @@ -32,19 +32,29 @@ using namespace std; namespace lyx { namespace frontend { +TocTypeModel::TocTypeModel(QObject * parent): QStandardItemModel(parent) +{ +} + + +void TocTypeModel::reset() +{ + QStandardItemModel::reset(); +} + TocItem const & TocModel::tocItem(QModelIndex const & index) const { - return toc_[data(index, Qt::UserRole).toUInt()]; + return (*toc_)[data(index, Qt::UserRole).toUInt()]; } QModelIndex TocModel::modelIndex(DocIterator const & dit) const { - if (toc_.empty()) + if (toc_->empty()) return QModelIndex(); - unsigned int const toc_index = toc_.item(dit) - toc_.begin(); + unsigned int const toc_index = toc_->item(dit) - toc_->begin(); QModelIndexList list = match(index(0, 0), Qt::UserRole, QVariant(toc_index), 1, @@ -55,19 +65,35 @@ QModelIndex TocModel::modelIndex(DocIterator const & dit) const } -TocModel::TocModel(Toc const & toc): toc_(toc) +TocModel::TocModel(QObject * parent): QStandardItemModel(parent) +{ +} + + +void TocModel::reset() +{ + QStandardItemModel::reset(); +} + + +void TocModel::reset(Toc const & toc) { - if (toc_.empty()) + toc_ = &toc; + if (toc_->empty()) { + reset(); return; + } + + blockSignals(true); int current_row; QModelIndex top_level_item; insertColumns(0, 1); maxdepth_ = 0; mindepth_ = INT_MAX; - size_t end = toc.size(); + size_t end = toc_->size(); for (unsigned int index = 0; index != end; ++index) { - TocItem const & item = toc_[index]; + TocItem const & item = (*toc_)[index]; maxdepth_ = max(maxdepth_, item.depth()); mindepth_ = min(mindepth_, item.depth()); current_row = rowCount(); @@ -85,22 +111,24 @@ TocModel::TocModel(Toc const & toc): toc_(toc) } setHeaderData(0, Qt::Horizontal, QVariant("title"), Qt::DisplayRole); + blockSignals(false); + reset(); // emit headerDataChanged(); } void TocModel::populate(unsigned int & index, QModelIndex const & parent) { - int curdepth = toc_[index].depth() + 1; + int curdepth = (*toc_)[index].depth() + 1; int current_row; QModelIndex child_item; insertColumns(0, 1, parent); - size_t end = toc_.size(); + size_t end = toc_->size(); ++index; for (; index != end; ++index) { - TocItem const & item = toc_[index]; + TocItem const & item = (*toc_)[index]; if (item.depth() < curdepth) { --index; return; @@ -128,62 +156,66 @@ int TocModel::modelDepth() const /////////////////////////////////////////////////////////////////////////////// // TocModels implementation. /////////////////////////////////////////////////////////////////////////////// + +TocModels::TocModels(): bv_(0) +{ + names_ = new TocTypeModel(this); +} + + void TocModels::clear() { - types_.clear(); - type_names_.clear(); - const unsigned int size = models_.size(); - for (unsigned int i = 0; i < size; ++i) { - delete models_[i]; + names_->blockSignals(true); + names_->clear(); + names_->blockSignals(false); + iterator end = models_.end(); + for (iterator it = models_.begin(); it != end; ++it) { + it.value()->blockSignals(true); + it.value()->clear(); + it.value()->blockSignals(false); } - models_.clear(); } -int TocModels::depth(int type) +int TocModels::depth(QString const & type) { - if (type < 0) + const_iterator it = models_.find(type); + if (!bv_ || it == models_.end()) return 0; - return models_[type]->modelDepth(); + return it.value()->modelDepth(); } -QStandardItemModel * TocModels::model(int type) +QStandardItemModel * TocModels::model(QString const & type) { - if (type < 0) - return 0; - - if (models_.empty()) { - LYXERR(Debug::GUI, "TocModels::tocModel(): no types available "); + if (!bv_) return 0; - } - - LYXERR(Debug::GUI, "TocModels: type " << type - << " models_.size() " << models_.size()); - - LASSERT(type >= 0 && type < int(models_.size()), /**/); - return models_[type]; + iterator it = models_.find(type); + if (it != models_.end()) + return it.value(); + LYXERR0("type not found: " << type); + return 0; } -QModelIndex TocModels::currentIndex(int type) const +QModelIndex TocModels::currentIndex(QString const & type) const { - if (type < 0 || !bv_) + const_iterator it = models_.find(type); + if (!bv_ || it == models_.end()) return QModelIndex(); - return models_[type]->modelIndex(bv_->cursor()); + return it.value()->modelIndex(bv_->cursor()); } -void TocModels::goTo(int type, QModelIndex const & index) const +void TocModels::goTo(QString const & type, QModelIndex const & index) const { - if (type < 0 || !index.isValid() - || index.model() != models_[type]) { + const_iterator it = models_.find(type); + if (it == models_.end() || !index.isValid()) { LYXERR(Debug::GUI, "TocModels::goTo(): QModelIndex is invalid!"); return; } - - LASSERT(type >= 0 && type < int(models_.size()), /**/); - TocItem const item = models_[type]->tocItem(index); + LASSERT(index.model() == it.value(), return); + TocItem const item = it.value()->tocItem(index); LYXERR(Debug::GUI, "TocModels::goTo " << item.str()); dispatch(item.action()); } @@ -200,54 +232,41 @@ void TocModels::reset(BufferView const * bv) { bv_ = bv; clear(); - if (!bv_) + if (!bv_) { + iterator end = models_.end(); + for (iterator it = models_.begin(); it != end; ++it) + it.value()->reset(); + names_->reset(); return; + } + names_->blockSignals(true); + names_->insertColumns(0, 1); TocList const & tocs = bv_->buffer().masterBuffer()->tocBackend().tocs(); TocList::const_iterator it = tocs.begin(); - TocList::const_iterator end = tocs.end(); - for (; it != end; ++it) { - types_.push_back(toqstr(it->first)); - type_names_.push_back(guiName(it->first, bv->buffer().params())); - models_.push_back(new TocModel(it->second)); + TocList::const_iterator toc_end = tocs.end(); + for (; it != toc_end; ++it) { + QString const type = toqstr(it->first); + + // First, fill in the toc models. + iterator mod_it = models_.find(type); + if (mod_it == models_.end()) + mod_it = models_.insert(type, new TocModel(this)); + mod_it.value()->reset(it->second); + + // Fill in the names_ model. + QString const gui_name = guiName(it->first, bv->buffer().params()); + int const current_row = names_->rowCount(); + names_->insertRows(current_row, 1); + QModelIndex const index = names_->index(current_row, 0); + names_->setData(index, gui_name, Qt::DisplayRole); + names_->setData(index, type, Qt::UserRole); } + names_->blockSignals(false); + names_->reset(); } -bool TocModels::canOutline(int type) const -{ - if (type < 0 || type >= types_.size()) - return false; - return types_[type] == "tableofcontents"; -} - - -int TocModels::decodeType(QString const & str) const -{ - QString new_type; - if (str.contains("tableofcontents")) { - new_type = "tableofcontents"; - } else if (str.contains("floatlist")) { - if (str.contains("\"figure")) - new_type = "figure"; - else if (str.contains("\"table")) - new_type = "table"; - else if (str.contains("\"algorithm")) - new_type = "algorithm"; - } else if (!str.isEmpty()) { - new_type = str; - } else { - // Default to Outliner. - new_type = "tableofcontents"; - } - int const type = types_.indexOf(new_type); - if (type != -1) - return type; - // If everything else fails, settle on the table of contents which is - // guaranted to exist. - return types_.indexOf("tableofcontents"); -} - } // namespace frontend } // namespace lyx diff --git a/src/frontends/qt4/TocModel.h b/src/frontends/qt4/TocModel.h index efa4e56e10..59e83a999c 100644 --- a/src/frontends/qt4/TocModel.h +++ b/src/frontends/qt4/TocModel.h @@ -28,11 +28,25 @@ class TocItem; namespace frontend { +class TocTypeModel : public QStandardItemModel +{ +public: + /// + TocTypeModel(QObject * parent = 0); + /// + void reset(); +}; + + class TocModel : public QStandardItemModel { public: /// - TocModel(Toc const & toc); + TocModel(QObject * parent = 0); + /// + void reset(Toc const & toc); + /// + void reset(); /// TocItem const & tocItem(QModelIndex const & index) const; /// @@ -46,7 +60,7 @@ private: /// QList toc_indexes_; /// - Toc const & toc_; + Toc const * toc_; /// int maxdepth_; int mindepth_; @@ -58,45 +72,44 @@ class TocModels: public QObject Q_OBJECT public: /// - TocModels(): bv_(0) {} + TocModels(); /// - ~TocModels() { clear(); } + typedef QHash::const_iterator const_iterator; + const_iterator begin() const { return models_.begin(); } + const_iterator end() const { return models_.end(); } /// void reset(BufferView const * bv); /// - int depth(int type); + int depth(QString const & type); + /// + QStandardItemModel * model(QString const & type); /// - QStandardItemModel * model(int type); + QStandardItemModel * nameModel() { return names_; } /// - QModelIndex currentIndex(int type) const; + QModelIndex currentIndex(QString const & type) const; /// - void goTo(int type, QModelIndex const & index) const; + void goTo(QString const & type, QModelIndex const & index) const; /// void init(Buffer const & buffer); - /// Test if outlining operation is possible - bool canOutline(int type) const; - /// Return the list of types available - QStringList const & typeNames() const { return type_names_; } /// void updateBackend() const; - /// - int decodeType(QString const & str) const; Q_SIGNALS: /// Signal that the internal toc_models_ has been reset. void modelReset(); private: + typedef QHash::iterator iterator; /// void clear(); /// - BufferView const * bv_; + void deleteAll(); /// - QList models_; + BufferView const * bv_; /// - QStringList types_; + QHash models_; /// - QStringList type_names_; + TocTypeModel * names_; }; } // namespace frontend diff --git a/src/frontends/qt4/TocWidget.cpp b/src/frontends/qt4/TocWidget.cpp index eb3fc49331..3be6aeb220 100644 --- a/src/frontends/qt4/TocWidget.cpp +++ b/src/frontends/qt4/TocWidget.cpp @@ -57,6 +57,9 @@ TocWidget::TocWidget(GuiView & gui_view, QWidget * parent) // Only one item selected at a time. tocTV->setSelectionMode(QAbstractItemView::SingleSelection); + + // The toc types combo won't change its model. + typeCO->setModel(gui_view_.tocModels().nameModel()); } @@ -78,7 +81,7 @@ void TocWidget::goTo(QModelIndex const & index) LYXERR(Debug::GUI, "goto " << index.row() << ", " << index.column()); - gui_view_.tocModels().goTo(typeCO->currentIndex(), index); + gui_view_.tocModels().goTo(current_type_, index); } @@ -117,6 +120,8 @@ void TocWidget::on_depthSL_valueChanged(int depth) void TocWidget::setTreeDepth(int depth) { depth_ = depth; + if (!tocTV->model()) + return; // expanding and then collapsing is probably better, // but my qt 4.1.2 doesn't have expandAll().. @@ -134,8 +139,9 @@ void TocWidget::setTreeDepth(int depth) } -void TocWidget::on_typeCO_currentIndexChanged(int) +void TocWidget::on_typeCO_currentIndexChanged(int index) { + current_type_ = typeCO->itemData(index).toString(); updateView(); gui_view_.setFocus(); } @@ -192,11 +198,18 @@ void TocWidget::select(QModelIndex const & index) } +/// Test if outlining operation is possible +static bool canOutline(QString const & type) +{ + return type == "tableofcontents"; +} + + void TocWidget::enableControls(bool enable) { updateTB->setEnabled(enable); - if (!gui_view_.tocModels().canOutline(typeCO->currentIndex())) + if (!canOutline(current_type_)) enable = false; moveUpTB->setEnabled(enable); @@ -213,64 +226,74 @@ void TocWidget::updateView() LYXERR(Debug::GUI, "In TocWidget::updateView()"); setTocModel(); setTreeDepth(depth_); - select(gui_view_.tocModels().currentIndex(typeCO->currentIndex())); + select(gui_view_.tocModels().currentIndex(current_type_)); +} + + +static QString decodeType(QString const & str) +{ + 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::init(QString const & str) { - QStringList const & type_names = gui_view_.tocModels().typeNames(); - if (type_names.isEmpty()) { + if (!gui_view_.view()) { enableControls(false); typeCO->setEnabled(false); - tocTV->setModel(new QStandardItemModel); + tocTV->setModel(0); tocTV->setEnabled(false); return; } typeCO->setEnabled(true); tocTV->setEnabled(true); - int selected_type = gui_view_.tocModels().decodeType(str); - QString const current_text = typeCO->currentText(); typeCO->blockSignals(true); - typeCO->clear(); - for (int i = 0; i != type_names.size(); ++i) - typeCO->addItem(type_names[i]); - if (!str.isEmpty()) - typeCO->setCurrentIndex(selected_type); - else { - int const new_index = typeCO->findText(current_text); - if (new_index != -1) - typeCO->setCurrentIndex(new_index); - else - typeCO->setCurrentIndex(selected_type); + + 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 + // guaranted to exist. + if (new_index == -1) { + current_type_ = "tableofcontents"; + new_index = typeCO->findData(current_type_); } + typeCO->setCurrentIndex(new_index); typeCO->blockSignals(false); - - // setTocModel produce QTreeView reset and setting depth again - // is needed. That must be done after all Qt updates are processed. - QTimer::singleShot(0, this, SLOT(updateView())); } void TocWidget::setTocModel() { - int const toc_type = typeCO->currentIndex(); - QStandardItemModel * toc_model = gui_view_.tocModels().model(toc_type); - LASSERT(toc_model, return); + QStandardItemModel * toc_model = gui_view_.tocModels().model(current_type_); if (tocTV->model() != toc_model) { tocTV->setModel(toc_model); tocTV->setEditTriggers(QAbstractItemView::NoEditTriggers); } - bool controls_enabled = toc_model->rowCount() > 0;; + bool controls_enabled = toc_model && toc_model->rowCount() > 0;; enableControls(controls_enabled); if (controls_enabled) { - depthSL->setMaximum(gui_view_.tocModels().depth(toc_type)); + depthSL->setMaximum(gui_view_.tocModels().depth(current_type_)); depthSL->setValue(depth_); } } diff --git a/src/frontends/qt4/TocWidget.h b/src/frontends/qt4/TocWidget.h index ea145539e9..a9dc03f572 100644 --- a/src/frontends/qt4/TocWidget.h +++ b/src/frontends/qt4/TocWidget.h @@ -64,6 +64,8 @@ private: void setTreeDepth(int depth); /// void outline(int func_code); + /// + QString current_type_; /// depth of list shown int depth_; -- 2.39.2