Commit 81cb4719 authored by Eike Ziller's avatar Eike Ziller

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: default avatarDaniel Teske <daniel.teske@digia.com>
Reviewed-by: default avatarFawzi Mohamed <fawzi.mohamed@digia.com>
parent 92c4f26e
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include <cplusplus/Scope.h> #include <cplusplus/Scope.h>
#include <cplusplus/Literals.h> #include <cplusplus/Literals.h>
#include <cplusplus/Symbols.h> #include <cplusplus/Symbols.h>
#include <utils/fileutils.h>
using namespace CPlusPlus; using namespace CPlusPlus;
...@@ -242,3 +243,35 @@ void OverviewModel::rebuild(Document::Ptr doc) ...@@ -242,3 +243,35 @@ void OverviewModel::rebuild(Document::Ptr doc)
_cppDocument = doc; _cppDocument = doc;
endResetModel(); 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;
}
...@@ -61,6 +61,11 @@ public: ...@@ -61,6 +61,11 @@ public:
Document::Ptr document() const; Document::Ptr document() const;
Symbol *symbolFromIndex(const QModelIndex &index) 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: public Q_SLOTS:
void rebuild(CPlusPlus::Document::Ptr doc); void rebuild(CPlusPlus::Document::Ptr doc);
......
...@@ -778,12 +778,22 @@ bool FileDropSupport::eventFilter(QObject *obj, QEvent *event) ...@@ -778,12 +778,22 @@ bool FileDropSupport::eventFilter(QObject *obj, QEvent *event)
QList<FileSpec> tempFiles; QList<FileSpec> tempFiles;
if (isFileDrop(de->mimeData(), &tempFiles) if (isFileDrop(de->mimeData(), &tempFiles)
&& (!m_filterFunction || m_filterFunction(de))) { && (!m_filterFunction || m_filterFunction(de))) {
const FileDropMimeData *fileDropMimeData = qobject_cast<const FileDropMimeData *>(de->mimeData());
event->accept(); event->accept();
de->acceptProposedAction(); if (fileDropMimeData && fileDropMimeData->isOverridingFileDropAction())
de->setDropAction(fileDropMimeData->overrideFileDropAction());
else
de->acceptProposedAction();
bool needToScheduleEmit = m_files.isEmpty(); bool needToScheduleEmit = m_files.isEmpty();
m_files.append(tempFiles); m_files.append(tempFiles);
if (needToScheduleEmit) // otherwise we already have a timer pending if (needToScheduleEmit) { // otherwise we already have a timer pending
QTimer::singleShot(0, this, SLOT(emitFilesDropped())); // 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 { } else {
event->ignore(); event->ignore();
} }
...@@ -799,6 +809,34 @@ void FileDropSupport::emitFilesDropped() ...@@ -799,6 +809,34 @@ void FileDropSupport::emitFilesDropped()
m_files.clear(); 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) void FileDropMimeData::addFile(const QString &filePath, int line, int column)
{ {
// standard mime data // standard mime data
......
...@@ -236,11 +236,19 @@ class QTCREATOR_UTILS_EXPORT FileDropMimeData : public QMimeData ...@@ -236,11 +236,19 @@ class QTCREATOR_UTILS_EXPORT FileDropMimeData : public QMimeData
{ {
Q_OBJECT Q_OBJECT
public: 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); void addFile(const QString &filePath, int line = -1, int column = -1);
QList<FileDropSupport::FileSpec> files() const; QList<FileDropSupport::FileSpec> files() const;
private: private:
QList<FileDropSupport::FileSpec> m_files; QList<FileDropSupport::FileSpec> m_files;
Qt::DropAction m_overrideDropAction;
bool m_isOverridingDropAction;
}; };
} // namespace Utils } // namespace Utils
......
...@@ -51,6 +51,8 @@ CppOutlineTreeView::CppOutlineTreeView(QWidget *parent) : ...@@ -51,6 +51,8 @@ CppOutlineTreeView::CppOutlineTreeView(QWidget *parent) :
Utils::NavigationTreeView(parent) Utils::NavigationTreeView(parent)
{ {
setExpandsOnDoubleClick(false); setExpandsOnDoubleClick(false);
setDragEnabled(true);
setDragDropMode(QAbstractItemView::DragOnly);
} }
void CppOutlineTreeView::contextMenuEvent(QContextMenuEvent *event) void CppOutlineTreeView::contextMenuEvent(QContextMenuEvent *event)
...@@ -90,6 +92,11 @@ bool CppOutlineFilterModel::filterAcceptsRow(int sourceRow, ...@@ -90,6 +92,11 @@ bool CppOutlineFilterModel::filterAcceptsRow(int sourceRow,
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent); return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
} }
Qt::DropActions CppOutlineFilterModel::supportedDragActions() const
{
return sourceModel()->supportedDragActions();
}
CppOutlineWidget::CppOutlineWidget(CppEditorWidget *editor) : CppOutlineWidget::CppOutlineWidget(CppEditorWidget *editor) :
TextEditor::IOutlineWidget(), TextEditor::IOutlineWidget(),
......
...@@ -60,6 +60,7 @@ public: ...@@ -60,6 +60,7 @@ public:
// QSortFilterProxyModel // QSortFilterProxyModel
bool filterAcceptsRow(int sourceRow, bool filterAcceptsRow(int sourceRow,
const QModelIndex &sourceParent) const; const QModelIndex &sourceParent) const;
Qt::DropActions supportedDragActions() const;
private: private:
CPlusPlus::OverviewModel *m_sourceModel; CPlusPlus::OverviewModel *m_sourceModel;
}; };
......
...@@ -82,6 +82,11 @@ QVariant QmlJSOutlineFilterModel::data(const QModelIndex &index, int role) const ...@@ -82,6 +82,11 @@ QVariant QmlJSOutlineFilterModel::data(const QModelIndex &index, int role) const
return QSortFilterProxyModel::data(index, role); return QSortFilterProxyModel::data(index, role);
} }
Qt::DropActions QmlJSOutlineFilterModel::supportedDragActions() const
{
return sourceModel()->supportedDragActions();
}
bool QmlJSOutlineFilterModel::filterBindings() const bool QmlJSOutlineFilterModel::filterBindings() const
{ {
return m_filterBindings; return m_filterBindings;
......
...@@ -54,6 +54,7 @@ public: ...@@ -54,6 +54,7 @@ public:
bool filterAcceptsRow(int sourceRow, bool filterAcceptsRow(int sourceRow,
const QModelIndex &sourceParent) const; const QModelIndex &sourceParent) const;
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const;
Qt::DropActions supportedDragActions() const;
bool filterBindings() const; bool filterBindings() const;
void setFilterBindings(bool filterBindings); void setFilterBindings(bool filterBindings);
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include <qmljs/qmljsrewriter.h> #include <qmljs/qmljsrewriter.h>
#include <qmljstools/qmljsrefactoringchanges.h> #include <qmljstools/qmljsrefactoringchanges.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
...@@ -53,6 +54,8 @@ enum { ...@@ -53,6 +54,8 @@ enum {
debug = false debug = false
}; };
static const char INTERNAL_MIMETYPE[] = "application/x-qtcreator-qmloutlinemodel";
namespace QmlJSEditor { namespace QmlJSEditor {
namespace Internal { namespace Internal {
...@@ -312,7 +315,8 @@ QmlOutlineModel::QmlOutlineModel(QmlJSEditorDocument *document) : ...@@ -312,7 +315,8 @@ QmlOutlineModel::QmlOutlineModel(QmlJSEditorDocument *document) :
QStringList QmlOutlineModel::mimeTypes() const QStringList QmlOutlineModel::mimeTypes() const
{ {
QStringList types; QStringList types;
types << QLatin1String("application/x-qtcreator-qmloutlinemodel"); types << QLatin1String(INTERNAL_MIMETYPE);
types << Utils::FileDropSupport::mimeTypesForFilePaths();
return types; return types;
} }
...@@ -321,9 +325,8 @@ QMimeData *QmlOutlineModel::mimeData(const QModelIndexList &indexes) const ...@@ -321,9 +325,8 @@ QMimeData *QmlOutlineModel::mimeData(const QModelIndexList &indexes) const
{ {
if (indexes.count() <= 0) if (indexes.count() <= 0)
return 0; return 0;
QStringList types = mimeTypes(); auto data = new Utils::FileDropMimeData;
QMimeData *data = new QMimeData(); data->setOverrideFileDropAction(Qt::CopyAction);
QString format = types.at(0);
QByteArray encoded; QByteArray encoded;
QDataStream stream(&encoded, QIODevice::WriteOnly); QDataStream stream(&encoded, QIODevice::WriteOnly);
stream << indexes.size(); stream << indexes.size();
...@@ -331,6 +334,10 @@ QMimeData *QmlOutlineModel::mimeData(const QModelIndexList &indexes) const ...@@ -331,6 +334,10 @@ QMimeData *QmlOutlineModel::mimeData(const QModelIndexList &indexes) const
for (int i = 0; i < indexes.size(); ++i) { for (int i = 0; i < indexes.size(); ++i) {
QModelIndex index = indexes.at(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; QList<int> rowPath;
for (QModelIndex i = index; i.isValid(); i = i.parent()) { for (QModelIndex i = index; i.isValid(); i = i.parent()) {
rowPath.prepend(i.row()); rowPath.prepend(i.row());
...@@ -338,7 +345,7 @@ QMimeData *QmlOutlineModel::mimeData(const QModelIndexList &indexes) const ...@@ -338,7 +345,7 @@ QMimeData *QmlOutlineModel::mimeData(const QModelIndexList &indexes) const
stream << rowPath; stream << rowPath;
} }
data->setData(format, encoded); data->setData(QLatin1String(INTERNAL_MIMETYPE), encoded);
return data; return data;
} }
...@@ -410,8 +417,8 @@ Qt::ItemFlags QmlOutlineModel::flags(const QModelIndex &index) const ...@@ -410,8 +417,8 @@ Qt::ItemFlags QmlOutlineModel::flags(const QModelIndex &index) const
Qt::DropActions QmlOutlineModel::supportedDragActions() const Qt::DropActions QmlOutlineModel::supportedDragActions() const
{ {
// TODO: Maybe add a Copy Action? // copy action used for dragging onto editor splits
return Qt::MoveAction; return Qt::MoveAction | Qt::CopyAction;
} }
...@@ -915,7 +922,7 @@ QString QmlOutlineModel::asString(AST::UiQualifiedId *id) ...@@ -915,7 +922,7 @@ QString QmlOutlineModel::asString(AST::UiQualifiedId *id)
AST::SourceLocation QmlOutlineModel::getLocation(AST::UiObjectMember *objMember) { AST::SourceLocation QmlOutlineModel::getLocation(AST::UiObjectMember *objMember) {
AST::SourceLocation location; AST::SourceLocation location;
location.offset = objMember->firstSourceLocation().offset; location = objMember->firstSourceLocation();
location.length = objMember->lastSourceLocation().offset location.length = objMember->lastSourceLocation().offset
- objMember->firstSourceLocation().offset - objMember->firstSourceLocation().offset
+ objMember->lastSourceLocation().length; + objMember->lastSourceLocation().length;
...@@ -924,7 +931,7 @@ AST::SourceLocation QmlOutlineModel::getLocation(AST::UiObjectMember *objMember) ...@@ -924,7 +931,7 @@ AST::SourceLocation QmlOutlineModel::getLocation(AST::UiObjectMember *objMember)
AST::SourceLocation QmlOutlineModel::getLocation(AST::ExpressionNode *exprNode) { AST::SourceLocation QmlOutlineModel::getLocation(AST::ExpressionNode *exprNode) {
AST::SourceLocation location; AST::SourceLocation location;
location.offset = exprNode->firstSourceLocation().offset; location = exprNode->firstSourceLocation();
location.length = exprNode->lastSourceLocation().offset location.length = exprNode->lastSourceLocation().offset
- exprNode->firstSourceLocation().offset - exprNode->firstSourceLocation().offset
+ exprNode->lastSourceLocation().length; + exprNode->lastSourceLocation().length;
...@@ -941,7 +948,7 @@ AST::SourceLocation QmlOutlineModel::getLocation(AST::PropertyAssignmentList *pr ...@@ -941,7 +948,7 @@ AST::SourceLocation QmlOutlineModel::getLocation(AST::PropertyAssignmentList *pr
AST::SourceLocation QmlOutlineModel::getLocation(AST::PropertyNameAndValue *propertyNode) { AST::SourceLocation QmlOutlineModel::getLocation(AST::PropertyNameAndValue *propertyNode) {
AST::SourceLocation location; AST::SourceLocation location;
location.offset = propertyNode->name->propertyNameToken.offset; location = propertyNode->name->propertyNameToken;
location.length = propertyNode->value->lastSourceLocation().end() - location.offset; location.length = propertyNode->value->lastSourceLocation().end() - location.offset;
return location; return location;
...@@ -949,7 +956,7 @@ AST::SourceLocation QmlOutlineModel::getLocation(AST::PropertyNameAndValue *prop ...@@ -949,7 +956,7 @@ AST::SourceLocation QmlOutlineModel::getLocation(AST::PropertyNameAndValue *prop
AST::SourceLocation QmlOutlineModel::getLocation(AST::PropertyGetterSetter *propertyNode) { AST::SourceLocation QmlOutlineModel::getLocation(AST::PropertyGetterSetter *propertyNode) {
AST::SourceLocation location; AST::SourceLocation location;
location.offset = propertyNode->name->propertyNameToken.offset; location = propertyNode->name->propertyNameToken;
location.length = propertyNode->rbraceToken.end() - location.offset; location.length = propertyNode->rbraceToken.end() - location.offset;
return location; return location;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment