Editors: Support dragging from outline views

Fill the line and column information in the location returned by
QmlOutlineModel::sourceLocation for that.
The drag & drop code also needed a way to override the executed drop
action for file drops, because the QML outline supports move-drags, which
would lead to the items being removed from the outline when dragged onto
a split...

Change-Id: I2478abc7d5aa2f3aa676cdd609ecb69a50adce8c
Reviewed-by: Daniel Teske <daniel.teske@digia.com>
Reviewed-by: Fawzi Mohamed <fawzi.mohamed@digia.com>
This commit is contained in:
Eike Ziller
2014-09-29 14:41:42 +02:00
parent 92c4f26e99
commit 81cb471997
9 changed files with 119 additions and 14 deletions

View File

@@ -34,6 +34,7 @@
#include <cplusplus/Scope.h>
#include <cplusplus/Literals.h>
#include <cplusplus/Symbols.h>
#include <utils/fileutils.h>
using namespace CPlusPlus;
@@ -242,3 +243,35 @@ void OverviewModel::rebuild(Document::Ptr doc)
_cppDocument = doc;
endResetModel();
}
Qt::ItemFlags OverviewModel::flags(const QModelIndex &index) const
{
Q_UNUSED(index)
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
}
Qt::DropActions OverviewModel::supportedDragActions() const
{
return Qt::MoveAction;
}
QStringList OverviewModel::mimeTypes() const
{
return Utils::FileDropSupport::mimeTypesForFilePaths();
}
QMimeData *OverviewModel::mimeData(const QModelIndexList &indexes) const
{
auto mimeData = new Utils::FileDropMimeData;
foreach (const QModelIndex &index, indexes) {
const QVariant fileName = data(index, FileNameRole);
if (!fileName.canConvert<QString>())
continue;
const QVariant lineNumber = data(index, LineNumberRole);
if (!fileName.canConvert<unsigned>())
continue;
mimeData->addFile(fileName.toString(), lineNumber.value<unsigned>());
}
return mimeData;
}

View File

@@ -61,6 +61,11 @@ public:
Document::Ptr document() const;
Symbol *symbolFromIndex(const QModelIndex &index) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
Qt::DropActions supportedDragActions() const;
QStringList mimeTypes() const;
QMimeData *mimeData(const QModelIndexList &indexes) const;
public Q_SLOTS:
void rebuild(CPlusPlus::Document::Ptr doc);

View File

@@ -778,12 +778,22 @@ bool FileDropSupport::eventFilter(QObject *obj, QEvent *event)
QList<FileSpec> tempFiles;
if (isFileDrop(de->mimeData(), &tempFiles)
&& (!m_filterFunction || m_filterFunction(de))) {
const FileDropMimeData *fileDropMimeData = qobject_cast<const FileDropMimeData *>(de->mimeData());
event->accept();
de->acceptProposedAction();
if (fileDropMimeData && fileDropMimeData->isOverridingFileDropAction())
de->setDropAction(fileDropMimeData->overrideFileDropAction());
else
de->acceptProposedAction();
bool needToScheduleEmit = m_files.isEmpty();
m_files.append(tempFiles);
if (needToScheduleEmit) // otherwise we already have a timer pending
QTimer::singleShot(0, this, SLOT(emitFilesDropped()));
if (needToScheduleEmit) { // otherwise we already have a timer pending
// Delay the actual drop, to avoid conflict between
// actions that happen when opening files, and actions that the item views do
// after the drag operation.
// If we do not do this, e.g. dragging from Outline view crashes if the editor and
// the selected item changes
QTimer::singleShot(100, this, SLOT(emitFilesDropped()));
}
} else {
event->ignore();
}
@@ -799,6 +809,34 @@ void FileDropSupport::emitFilesDropped()
m_files.clear();
}
/*!
Sets the drop action to effectively use, instead of the "proposed" drop action from the
drop event. This can be useful when supporting move drags within an item view, but not
"moving" an item from the item view into a split.
*/
FileDropMimeData::FileDropMimeData()
: m_overrideDropAction(Qt::IgnoreAction),
m_isOverridingDropAction(false)
{
}
void FileDropMimeData::setOverrideFileDropAction(Qt::DropAction action)
{
m_isOverridingDropAction = true;
m_overrideDropAction = action;
}
Qt::DropAction FileDropMimeData::overrideFileDropAction() const
{
return m_overrideDropAction;
}
bool FileDropMimeData::isOverridingFileDropAction() const
{
return m_isOverridingDropAction;
}
void FileDropMimeData::addFile(const QString &filePath, int line, int column)
{
// standard mime data

View File

@@ -236,11 +236,19 @@ class QTCREATOR_UTILS_EXPORT FileDropMimeData : public QMimeData
{
Q_OBJECT
public:
FileDropMimeData();
void setOverrideFileDropAction(Qt::DropAction action);
Qt::DropAction overrideFileDropAction() const;
bool isOverridingFileDropAction() const;
void addFile(const QString &filePath, int line = -1, int column = -1);
QList<FileDropSupport::FileSpec> files() const;
private:
QList<FileDropSupport::FileSpec> m_files;
Qt::DropAction m_overrideDropAction;
bool m_isOverridingDropAction;
};
} // namespace Utils

View File

@@ -51,6 +51,8 @@ CppOutlineTreeView::CppOutlineTreeView(QWidget *parent) :
Utils::NavigationTreeView(parent)
{
setExpandsOnDoubleClick(false);
setDragEnabled(true);
setDragDropMode(QAbstractItemView::DragOnly);
}
void CppOutlineTreeView::contextMenuEvent(QContextMenuEvent *event)
@@ -90,6 +92,11 @@ bool CppOutlineFilterModel::filterAcceptsRow(int sourceRow,
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
}
Qt::DropActions CppOutlineFilterModel::supportedDragActions() const
{
return sourceModel()->supportedDragActions();
}
CppOutlineWidget::CppOutlineWidget(CppEditorWidget *editor) :
TextEditor::IOutlineWidget(),

View File

@@ -60,6 +60,7 @@ public:
// QSortFilterProxyModel
bool filterAcceptsRow(int sourceRow,
const QModelIndex &sourceParent) const;
Qt::DropActions supportedDragActions() const;
private:
CPlusPlus::OverviewModel *m_sourceModel;
};

View File

@@ -82,6 +82,11 @@ QVariant QmlJSOutlineFilterModel::data(const QModelIndex &index, int role) const
return QSortFilterProxyModel::data(index, role);
}
Qt::DropActions QmlJSOutlineFilterModel::supportedDragActions() const
{
return sourceModel()->supportedDragActions();
}
bool QmlJSOutlineFilterModel::filterBindings() const
{
return m_filterBindings;

View File

@@ -54,6 +54,7 @@ public:
bool filterAcceptsRow(int sourceRow,
const QModelIndex &sourceParent) const;
QVariant data(const QModelIndex &index, int role) const;
Qt::DropActions supportedDragActions() const;
bool filterBindings() const;
void setFilterBindings(bool filterBindings);

View File

@@ -37,6 +37,7 @@
#include <qmljs/qmljsrewriter.h>
#include <qmljstools/qmljsrefactoringchanges.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
#include <coreplugin/icore.h>
@@ -53,6 +54,8 @@ enum {
debug = false
};
static const char INTERNAL_MIMETYPE[] = "application/x-qtcreator-qmloutlinemodel";
namespace QmlJSEditor {
namespace Internal {
@@ -312,7 +315,8 @@ QmlOutlineModel::QmlOutlineModel(QmlJSEditorDocument *document) :
QStringList QmlOutlineModel::mimeTypes() const
{
QStringList types;
types << QLatin1String("application/x-qtcreator-qmloutlinemodel");
types << QLatin1String(INTERNAL_MIMETYPE);
types << Utils::FileDropSupport::mimeTypesForFilePaths();
return types;
}
@@ -321,9 +325,8 @@ QMimeData *QmlOutlineModel::mimeData(const QModelIndexList &indexes) const
{
if (indexes.count() <= 0)
return 0;
QStringList types = mimeTypes();
QMimeData *data = new QMimeData();
QString format = types.at(0);
auto data = new Utils::FileDropMimeData;
data->setOverrideFileDropAction(Qt::CopyAction);
QByteArray encoded;
QDataStream stream(&encoded, QIODevice::WriteOnly);
stream << indexes.size();
@@ -331,6 +334,10 @@ QMimeData *QmlOutlineModel::mimeData(const QModelIndexList &indexes) const
for (int i = 0; i < indexes.size(); ++i) {
QModelIndex index = indexes.at(i);
AST::SourceLocation location = sourceLocation(index);
data->addFile(m_editorDocument->filePath(), location.startLine,
location.startColumn - 1 /*editors have 0-based column*/);
QList<int> rowPath;
for (QModelIndex i = index; i.isValid(); i = i.parent()) {
rowPath.prepend(i.row());
@@ -338,7 +345,7 @@ QMimeData *QmlOutlineModel::mimeData(const QModelIndexList &indexes) const
stream << rowPath;
}
data->setData(format, encoded);
data->setData(QLatin1String(INTERNAL_MIMETYPE), encoded);
return data;
}
@@ -410,8 +417,8 @@ Qt::ItemFlags QmlOutlineModel::flags(const QModelIndex &index) const
Qt::DropActions QmlOutlineModel::supportedDragActions() const
{
// TODO: Maybe add a Copy Action?
return Qt::MoveAction;
// copy action used for dragging onto editor splits
return Qt::MoveAction | Qt::CopyAction;
}
@@ -915,7 +922,7 @@ QString QmlOutlineModel::asString(AST::UiQualifiedId *id)
AST::SourceLocation QmlOutlineModel::getLocation(AST::UiObjectMember *objMember) {
AST::SourceLocation location;
location.offset = objMember->firstSourceLocation().offset;
location = objMember->firstSourceLocation();
location.length = objMember->lastSourceLocation().offset
- objMember->firstSourceLocation().offset
+ objMember->lastSourceLocation().length;
@@ -924,7 +931,7 @@ AST::SourceLocation QmlOutlineModel::getLocation(AST::UiObjectMember *objMember)
AST::SourceLocation QmlOutlineModel::getLocation(AST::ExpressionNode *exprNode) {
AST::SourceLocation location;
location.offset = exprNode->firstSourceLocation().offset;
location = exprNode->firstSourceLocation();
location.length = exprNode->lastSourceLocation().offset
- exprNode->firstSourceLocation().offset
+ exprNode->lastSourceLocation().length;
@@ -941,7 +948,7 @@ AST::SourceLocation QmlOutlineModel::getLocation(AST::PropertyAssignmentList *pr
AST::SourceLocation QmlOutlineModel::getLocation(AST::PropertyNameAndValue *propertyNode) {
AST::SourceLocation location;
location.offset = propertyNode->name->propertyNameToken.offset;
location = propertyNode->name->propertyNameToken;
location.length = propertyNode->value->lastSourceLocation().end() - location.offset;
return location;
@@ -949,7 +956,7 @@ AST::SourceLocation QmlOutlineModel::getLocation(AST::PropertyNameAndValue *prop
AST::SourceLocation QmlOutlineModel::getLocation(AST::PropertyGetterSetter *propertyNode) {
AST::SourceLocation location;
location.offset = propertyNode->name->propertyNameToken.offset;
location = propertyNode->name->propertyNameToken;
location.length = propertyNode->rbraceToken.end() - location.offset;
return location;