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
This commit is contained in:
Abdelrazak Younes 2008-06-17 15:10:03 +00:00
parent b5252b737f
commit 6ad1b1cd09
5 changed files with 187 additions and 130 deletions

View File

@ -83,8 +83,8 @@ void GuiToc::dispatchParams()
void GuiToc::enableView(bool enable)
{
widget_->init(QString());
widget_->setEnabled(enable);
if (!enable)
widget_->init(QString());
}

View File

@ -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)
{
if (toc_.empty())
}
void TocModel::reset()
{
QStandardItemModel::reset();
}
void TocModel::reset(Toc const & toc)
{
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)
if (!bv_)
return 0;
if (models_.empty()) {
LYXERR(Debug::GUI, "TocModels::tocModel(): no types available ");
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

View File

@ -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<QModelIndex> 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<QString, TocModel *>::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(int type);
QStandardItemModel * model(QString const & type);
///
QModelIndex currentIndex(int type) const;
QStandardItemModel * nameModel() { return names_; }
///
void goTo(int type, QModelIndex const & index) const;
QModelIndex currentIndex(QString const & type) 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<QString, TocModel *>::iterator iterator;
///
void clear();
///
void deleteAll();
///
BufferView const * bv_;
///
QList<TocModel *> models_;
QHash<QString, TocModel *> models_;
///
QStringList types_;
///
QStringList type_names_;
TocTypeModel * names_;
};
} // namespace frontend

View File

@ -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_);
}
}

View File

@ -64,6 +64,8 @@ private:
void setTreeDepth(int depth);
///
void outline(int func_code);
///
QString current_type_;
/// depth of list shown
int depth_;