Здравствуйте,
Не могу сбросить узлы дерева никуда, кроме верхнего уровня иерархии. Подскажите, в каком направлении копать, чтобы разрешить бросать узлы в любого потомка? Вкратце опишу ситуацию:
1. Переопределен класс QTreeView (CustomTreeView)
2. Реализована своя модель TreeModel
Через методы mimeData()/dropMimeData() реализовал перетаскивание узлов, из любого уровня иерархии, но view позволяет сбрасывать только в первый уровеннь иерархии. Может это связано с тем, что к View приставлена SortFilterModel?..
Ниже привожу код классов, при желании все вживую скачать и запустить можно с SVN:
svn co https://dbassistant.svn.sourceforge.net/svnroot/dbassistant dbassistant
/* customtreeview.h */
class CustomTreeView : public QTreeView
{
Q_OBJECT
TreeSortFilterModel filter_model;
SearchStringWidget search_widget;
public:
// VARIABLES :
bool highlight_enabled;
explicit CustomTreeView(QWidget *parent = 0);
void setModel(QAbstractItemModel *model);
TreeSortFilterModel* filterModel();
protected:
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void keyPressEvent(QKeyEvent *);
private:
signals:
void clickedWithModifiers(const QModelIndex &index, QMouseEvent* event);
public slots:
};
/* customtreeview.cpp */
#include "customtreeview.h"
#include <QMessageBox>
CustomTreeView::CustomTreeView(QWidget *parent) :
QTreeView(parent)
{
highlight_enabled = false;
search_widget.setParent(this);
search_widget.hide();
connect(search_widget.edt_search_field, SIGNAL(textChanged(QString)), &filter_model, SLOT(updateFilterPattern(QString)));
connect(&search_widget, SIGNAL(hidden()), &filter_model, SLOT(resetSearch()));
// setModel(&filter_model);
}
///////////////////////////////////////////////////////////////////////
void CustomTreeView::mouseMoveEvent(QMouseEvent *event)
{
if(highlight_enabled)
{ selectionModel()->setCurrentIndex(indexAt(event->pos()), QItemSelectionModel::SelectCurrent); }
QTreeView::mouseMoveEvent(event);
}
///////////////////////////////////////////////////////////////////////
void CustomTreeView::mousePressEvent(QMouseEvent *event)
{
// QModelIndex proxy_index = indexAt(event->pos());
// QModelIndex real_index = filter_model.mapToSource(proxy_index);
emit clickedWithModifiers(indexAt(event->pos()), event);
// emit clicked(real_index);
// setExpanded(proxy_index, !isExpanded(proxy_index));
QTreeView::mousePressEvent(event);
}
///////////////////////////////////////////////////////////////////////
void CustomTreeView::keyPressEvent(QKeyEvent *evt)
{
QString str = evt->text();
if(str.isEmpty())
{ return; }
// if(evt->key() == Key_Escape)
// { search_widget.edt_search_field->clear(); search_widget.hide(); }
QString search_text = search_widget.edt_search_field->text();
search_widget.edt_search_field->setText(search_text + str);
if(!search_widget.isVisible())
{
QPoint bottom_right = rect().bottomRight();
search_widget.move(bottom_right.x()-search_widget.width(), bottom_right.y()-search_widget.height());
search_widget.show();
search_widget.edt_search_field->setFocus();
}
}
///////////////////////////////////////////////////////////////////////
void CustomTreeView::setModel(QAbstractItemModel *model)
{
filter_model.setSourceModel(model);
QTreeView::setModel(&filter_model);
}
///////////////////////////////////////////////////////////////////////
TreeSortFilterModel* CustomTreeView::filterModel()
{
return &filter_model;
}
///////////////////////////////////////////////////////////////////////
/* treemodel.h */
class TreeModel : public QAbstractItemModel
{
Q_OBJECT
protected:
TreeNode* root_item;
public:
explicit TreeModel(QObject *parent = 0);
~TreeModel();
QVariant headerData(int section, const Qt::Orientation & orientation, int role) const;
QModelIndex index(int row, int column, const QModelIndex & parent) const;
QModelIndex parent(const QModelIndex & child) const;
Qt::ItemFlags flags(const QModelIndex & index) const;
int rowCount(const QModelIndex & parent) const;
int columnCount(const QModelIndex & parent) const;
void addNode(TreeNode* item, TreeNode* parent = 0);
void moveNode(TreeNode* item, int to);
void removeNode(TreeNode* item, bool kill = true);
void removeAll();
virtual QStringList mimeTypes() const;
virtual QMimeData* mimeData(const QModelIndexList &indexes) const;
virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
virtual Qt::DropActions supportedDropActions() const;
signals:
public slots:
};
/* treemodel.cpp */
#include "treemodel.h"
TreeModel::TreeModel(QObject *parent) :
QAbstractItemModel(parent)
{
root_item = new TreeNode();
}
////////////////////////////////////////////////////////////////////////////////
TreeModel::~TreeModel()
{
delete root_item;
}
////////////////////////////////////////////////////////////////////////////////
QVariant TreeModel::headerData(int section, const Qt::Orientation &orientation, int role) const
{
Q_UNUSED(section);
if(orientation == Qt::Horizontal && role == Qt::DisplayRole)
{ return "Object"; }
return QVariant();
}
////////////////////////////////////////////////////////////////////////////////
QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
{
if(!hasIndex(row, column, parent))
{ return QModelIndex(); }
TreeNode* parent_node;
if(!parent.isValid())
{ parent_node = root_item; }
else { parent_node = static_cast<TreeNode*>(parent.internalPointer()); }
TreeNode* child_node = static_cast<TreeNode*>(parent_node->children().at(row));
if(child_node)
{ return createIndex(row, column, child_node); }
else { return QModelIndex(); }
}
////////////////////////////////////////////////////////////////////////////////
QModelIndex TreeModel::parent(const QModelIndex &child) const
{
if(!child.isValid())
{ return QModelIndex(); }
TreeNode* child_node = static_cast<TreeNode*>(child.internalPointer());
TreeNode* parent_node = static_cast<TreeNode*>(child_node->parent());
if(parent_node == root_item)
{ return QModelIndex(); }
return createIndex(parent_node->row(), 0, parent_node);
}
////////////////////////////////////////////////////////////////////////////////
Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
if(index.isValid())
{ return Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsSelectable; }
return Qt::ItemIsDropEnabled;
}
////////////////////////////////////////////////////////////////////////////////
int TreeModel::rowCount(const QModelIndex &parent) const
{
TreeNode* parent_node;
if(!parent.isValid())
{ parent_node = root_item; }
else { parent_node = static_cast<TreeNode*>(parent.internalPointer()); }
return parent_node->children().count();
}
////////////////////////////////////////////////////////////////////////////////
int TreeModel::columnCount(const QModelIndex &parent) const
{
return 1;
}
////////////////////////////////////////////////////////////////////////////////
void TreeModel::addNode(TreeNode* item, TreeNode* parent)
{
QModelIndex parent_index;
if(!parent || parent == root_item)
{
parent = root_item;
parent_index = QModelIndex();
}
else { parent_index = createIndex(parent->row(), 0, parent); }
int idx = parent->children().count();
beginInsertRows(parent_index, idx, idx);
item->setParent(parent);
endInsertRows();
}
////////////////////////////////////////////////////////////////////////////////
void TreeModel::removeAll()
{
foreach(QObject* child, root_item->children())
{ delete child; }
reset();
}
////////////////////////////////////////////////////////////////////////////////
void TreeModel::removeNode(TreeNode* item, bool kill)
{
TreeNode* parent_node;
QModelIndex parent_index;
if(item->parent() == root_item)
{ parent_index = QModelIndex(); }
else {
parent_node = static_cast<TreeNode*>(item->parent());
parent_index = createIndex(parent_node->row(), 0, parent_node);
}
int idx = item->row();
beginRemoveRows(parent_index, idx, idx);
if(kill)
{ delete item; }
else { item->setParent(NULL); }
endRemoveRows();
}
////////////////////////////////////////////////////////////////////////////////
QStringList TreeModel::mimeTypes() const
{
QStringList list;
list << "text/plain";
return list;
}
////////////////////////////////////////////////////////////////////////////////
Qt::DropActions TreeModel::supportedDropActions() const
{
return Qt::CopyAction | Qt::MoveAction;
}
////////////////////////////////////////////////////////////////////////////////
QMimeData* TreeModel::mimeData(const QModelIndexList &indexes) const
{
QMimeData* data = new QMimeData();
QStringList all_index_paths;
foreach(QModelIndex index, indexes)
{
TreeNode* node = static_cast<TreeNode*>(index.internalPointer());
QObject *child = node;
QStringList pos_list;
// pos_list.prepend(QString("%1").arg(child->parent()->children().indexOf(node)));
while(child != root_item)
{
pos_list.prepend(QString("%1").arg(child->parent()->children().indexOf(child)));
child = child->parent();
}
QString index_path = pos_list.join("<<");
all_index_paths.append(index_path);
}
data->setText(all_index_paths.join("|"));
return data;
}
////////////////////////////////////////////////////////////////////////////////
bool TreeModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
qDebug("root_item = 0x%x", root_item);
// qDebug("%s", data->text().toAscii().data());
QStringList index_paths = data->text().split("|");
foreach(QString index_path, index_paths)
{
QStringList pos_list = index_path.split("<<");
TreeNode* needle = root_item;
for(int i = 0; i < pos_list.count(); i++)
{ needle = static_cast<TreeNode*>(needle->children().at(pos_list.at(i).toInt())); }
removeNode(needle, false);
TreeNode* target = static_cast<TreeNode*>(parent.internalPointer());
qDebug("target = 0x%x", target);
if(!target) { target = root_item; }
addNode(needle, target);
qDebug("needle = 0x%x", needle);
qDebug("needle new parent: 0x%x", needle->parent());
moveNode(needle, row);
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
void TreeModel::moveNode(TreeNode *item, int to)
{
TreeNode* parent = static_cast<TreeNode*>(item->parent());
if(parent == root_item) { return; } // NOT POSSIBLE TO MOVE TOPLEVEL ITEMS YET :-(
if(parent)
{
int parent_row;
QModelIndex parent_index = QModelIndex();
parent_row = parent->parent()->children().indexOf(parent),
parent_index = createIndex(parent_row, 0, parent);
int child_row = parent->children().indexOf(item);
if(child_row != to)
{
beginMoveRows(parent_index, child_row, child_row, parent_index, to);
parent->moveChild(item, to);
endMoveRows();
}
}
}
////////////////////////////////////////////////////////////////////////////////
/* treesortfiltermodel.h */
class TreeSortFilterModel : public QSortFilterProxyModel
{
Q_OBJECT
QRegExp* re_filter;
bool isNodeMatchesFilter(TreeNode* node) const;
public:
explicit TreeSortFilterModel(QObject *parent = 0);
QModelIndex mapToSource(const QModelIndex &proxyIndex) const;
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const;
protected:
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
signals:
public slots:
void updateFilterPattern(QString);
void resetSearch();
};
/* treesortfiltermodel.cpp */
#include "treesortfiltermodel.h"
TreeSortFilterModel::TreeSortFilterModel(QObject *parent) :
QSortFilterProxyModel(parent)
{
re_filter = new QRegExp(".*", Qt::CaseInsensitive);
}
///////////////////////////////////////////////////////////////////////////
void TreeSortFilterModel::updateFilterPattern(QString patt)
{
if(patt.isEmpty())
{ resetSearch(); }
else { re_filter->setPattern(patt); invalidateFilter(); }
}
///////////////////////////////////////////////////////////////////////////
void TreeSortFilterModel::resetSearch()
{
re_filter->setPattern(".*");
invalidateFilter();
}
///////////////////////////////////////////////////////////////////////////
bool TreeSortFilterModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
bool child_matches_filter = false;
/*
if(!source_parent.isValid())
{ return true; }
*/
// TreeNode* parent_node;
QModelIndex asked_index = sourceModel()->index(source_row, 0, source_parent);
TreeNode* asked_node = static_cast<TreeNode*>(asked_index.internalPointer());
foreach(QObject* child, asked_node->children())
{
TreeNode* child_node = static_cast<TreeNode*>(child);
if(child_matches_filter = isNodeMatchesFilter(child_node))
{ break; }
}
if(child_matches_filter) // if item has at least one appropriate child, show it
{ return true; }
else { // or check if it matches filter
return isNodeMatchesFilter(asked_node);
}
}
///////////////////////////////////////////////////////////////////////////
bool TreeSortFilterModel::isNodeMatchesFilter(TreeNode *node) const
{
bool result;
SqlDatabase* db;
SqlTable* table;
SqlAttribute* attrib;
WebPage* page;
SqlForm* form;
SqlFormField* ff;
SqlFormFieldGroup* ffg;
SqlDisplay* display;
SqlQuery* query;
SqlQueryField* qf;
DataSource* data_source;
switch(node->type())
{
case T_SQLDB:
db = static_cast<SqlDatabase*>(node);
result = re_filter->indexIn(db->alias()) != -1;
break;
case T_SQLTABLE:
table = static_cast<SqlTable*>(node);
result = re_filter->indexIn(table->alias()) != -1;
break;
case T_SQLATTRIB:
attrib = static_cast<SqlAttribute*>(node);
result = re_filter->indexIn(attrib->alias()) != -1;
break;
case T_WEBPAGE:
page = static_cast<WebPage*>(node);
result = re_filter->indexIn(page->name()) != -1;
break;
case T_SQLFORM:
form = static_cast<SqlForm*>(node);
result = re_filter->indexIn(form->name()) != -1;
break;
case T_SQLFORMFIELD:
ff = static_cast<SqlFormField*>(node);
result = re_filter->indexIn(ff->name()) != -1;
break;
case T_SQLFORMFIELDGROUP:
ffg = static_cast<SqlFormFieldGroup*>(node);
result = re_filter->indexIn(ffg->name()) != -1;
break;
case T_SQLDISPLAY:
display = static_cast<SqlDisplay*>(node);
result = re_filter->indexIn(display->name()) != -1;
break;
case T_SQLQUERY:
query = static_cast<SqlQuery*>(node);
result = re_filter->indexIn(query->name()) != -1;
break;
case T_SQLQUERYFIELD:
qf = static_cast<SqlQueryField*>(node);
result = re_filter->indexIn(qf->alias()) != -1;
break;
case T_DATASOURCE:
data_source = static_cast<DataSource*>(node);
result = re_filter->indexIn(data_source->name()) != -1;
break;
}
return result;
}
///////////////////////////////////////////////////////////////////////////
QModelIndex TreeSortFilterModel::mapToSource(const QModelIndex &proxyIndex) const
{
return QSortFilterProxyModel::mapToSource(proxyIndex);
}
///////////////////////////////////////////////////////////////////////////
QModelIndex TreeSortFilterModel::mapFromSource(const QModelIndex &sourceIndex) const
{
return QSortFilterProxyModel::mapFromSource(sourceIndex);
}
///////////////////////////////////////////////////////////////////////////