Skip to content

Commit

Permalink
Support for default actions in layer tree view
Browse files Browse the repository at this point in the history
  • Loading branch information
wonder-sk committed May 21, 2014
1 parent c3315c4 commit 6a5b752
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 67 deletions.
2 changes: 2 additions & 0 deletions src/gui/CMakeLists.txt
Expand Up @@ -62,6 +62,7 @@ layertree/qgscustomlayerorderwidget.cpp
layertree/qgslayertreemapcanvasbridge.cpp
layertree/qgslayertreemodel.cpp
layertree/qgslayertreeview.cpp
layertree/qgslayertreeviewdefaultactions.cpp

qgisgui.cpp
qgisinterface.cpp
Expand Down Expand Up @@ -223,6 +224,7 @@ layertree/qgscustomlayerorderwidget.h
layertree/qgslayertreemapcanvasbridge.h
layertree/qgslayertreemodel.h
layertree/qgslayertreeview.h
layertree/qgslayertreeviewdefaultactions.h

qgisinterface.h
qgsattributedialog.h
Expand Down
98 changes: 39 additions & 59 deletions src/gui/layertree/qgslayertreeview.cpp
Expand Up @@ -2,13 +2,15 @@

#include "qgslayertreemodel.h"
#include "qgslayertreenode.h"
#include "qgslayertreeviewdefaultactions.h"

#include <QMenu>
#include <QContextMenuEvent>

QgsLayerTreeView::QgsLayerTreeView(QWidget *parent)
: QTreeView(parent)
, mCurrentLayer(0)
, mDefaultActions(0)
{
setHeaderHidden(true);

Expand Down Expand Up @@ -36,11 +38,18 @@ void QgsLayerTreeView::setModel(QAbstractItemModel* model)
updateExpandedStateFromNode(layerTreeModel()->rootGroup());
}

QgsLayerTreeModel *QgsLayerTreeView::layerTreeModel()
QgsLayerTreeModel *QgsLayerTreeView::layerTreeModel() const
{
return qobject_cast<QgsLayerTreeModel*>(model());
}

QgsLayerTreeViewDefaultActions* QgsLayerTreeView::defaultActions()
{
if (!mDefaultActions)
mDefaultActions = new QgsLayerTreeViewDefaultActions(this);
return mDefaultActions;
}

QgsMapLayer* QgsLayerTreeView::currentLayer() const
{
return mCurrentLayer;
Expand All @@ -63,7 +72,9 @@ void QgsLayerTreeView::contextMenuEvent(QContextMenuEvent *event)
QModelIndex idx = indexAt(event->pos());
if (!idx.isValid())
{
menu.addAction(create_addGroup(&menu));
setCurrentIndex(QModelIndex());

menu.addAction( defaultActions()->actionAddGroup(&menu) );
}
else
{
Expand All @@ -72,70 +83,20 @@ void QgsLayerTreeView::contextMenuEvent(QContextMenuEvent *event)
return; // probably a symbology item

if (node->nodeType() == QgsLayerTreeNode::NodeGroup)
menu.addAction(create_addGroup(&menu, node));
menu.addAction( defaultActions()->actionAddGroup(&menu) );
else if (node->nodeType() == QgsLayerTreeNode::NodeLayer)
menu.addAction(create_showInOverview(&menu, node)); // TODO: should be custom action
{
// TODO menu.addAction( defaultActions()->actionZoomToLayer(canvas, &menu) );
menu.addAction( defaultActions()->actionShowInOverview(&menu) ); // TODO: should be custom action
}

menu.addAction(create_removeGroupOrLayer(&menu, node));
menu.addAction( defaultActions()->actionRemoveGroupOrLayer(&menu) );
menu.addAction( defaultActions()->actionRenameGroupOrLayer(&menu) );
}

menu.exec(mapToGlobal(event->pos()));
}

QAction *QgsLayerTreeView::create_addGroup(QObject* parent, QgsLayerTreeNode* parentNode)
{
QAction* a = new QAction(tr("Add Group"), parent);
connect(a, SIGNAL(triggered()), this, SLOT(addGroup()));
a->setData(QVariant::fromValue((QObject*)parentNode));
return a;
}

QAction *QgsLayerTreeView::create_removeGroupOrLayer(QObject *parent, QgsLayerTreeNode *parentNode)
{
QAction* a = new QAction(tr("Remove"), parent);
connect(a, SIGNAL(triggered()), this, SLOT(removeGroupOrLayer()));
a->setData(QVariant::fromValue((QObject*)parentNode));
return a;
}

QAction* QgsLayerTreeView::create_showInOverview(QObject* parent, QgsLayerTreeNode* parentNode)
{
QAction* a = new QAction(tr("Show in overview"), parent);
connect(a, SIGNAL(triggered()), this, SLOT(showInOverview()));
a->setData(QVariant::fromValue((QObject*)parentNode));
a->setCheckable(true);
a->setChecked(parentNode->customProperty("overview", 0).toInt());
return a;
}

void QgsLayerTreeView::addGroup()
{
QVariant v = qobject_cast<QAction*>(sender())->data();
QgsLayerTreeGroup* group = qobject_cast<QgsLayerTreeGroup*>(v.value<QObject*>());
if (!group)
group = layerTreeModel()->rootGroup();

group->addGroup("group");
}

void QgsLayerTreeView::removeGroupOrLayer()
{
QList<QgsLayerTreeNode*> nodes = layerTreeModel()->indexes2nodes(selectionModel()->selectedIndexes(), true);
foreach (QgsLayerTreeNode* node, nodes)
{
// could be more efficient if working directly with ranges instead of individual nodes
qobject_cast<QgsLayerTreeGroup*>(node->parent())->removeChildNode(node);
}
}

void QgsLayerTreeView::showInOverview()
{
QVariant v = qobject_cast<QAction*>(sender())->data();
QgsLayerTreeNode* node = qobject_cast<QgsLayerTreeNode*>(v.value<QObject*>());
Q_ASSERT(node);

node->setCustomProperty("overview", node->customProperty("overview", 0).toInt() ? 0 : 1);
}

void QgsLayerTreeView::modelRowsInserted(QModelIndex index, int start, int end)
{
Expand Down Expand Up @@ -182,3 +143,22 @@ void QgsLayerTreeView::updateExpandedStateFromNode(QgsLayerTreeNode* node)
foreach (QgsLayerTreeNode* child, node->children())
updateExpandedStateFromNode(child);
}

QgsLayerTreeNode* QgsLayerTreeView::currentNode() const
{
return layerTreeModel()->index2node(selectionModel()->currentIndex());
}

QgsLayerTreeGroup* QgsLayerTreeView::currentGroupNode() const
{
// TODO: also handle if a layer / symbology is selected within a group?
QgsLayerTreeNode* node = currentNode();
if (node && node->nodeType() == QgsLayerTreeNode::NodeGroup)
return static_cast<QgsLayerTreeGroup*>(node);
return 0;
}

QList<QgsLayerTreeNode*> QgsLayerTreeView::selectedNodes(bool skipInternal) const
{
return layerTreeModel()->indexes2nodes(selectionModel()->selectedIndexes(), skipInternal);
}
21 changes: 13 additions & 8 deletions src/gui/layertree/qgslayertreeview.h
Expand Up @@ -3,8 +3,10 @@

#include <QTreeView>

class QgsLayerTreeGroup;
class QgsLayerTreeModel;
class QgsLayerTreeNode;
class QgsLayerTreeViewDefaultActions;
class QgsMapLayer;

class GUI_EXPORT QgsLayerTreeView : public QTreeView
Expand All @@ -15,18 +17,21 @@ class GUI_EXPORT QgsLayerTreeView : public QTreeView

virtual void setModel(QAbstractItemModel* model);

QgsLayerTreeModel* layerTreeModel();
QgsLayerTreeModel* layerTreeModel() const;

QgsLayerTreeViewDefaultActions* defaultActions();

QgsMapLayer* currentLayer() const;
void setCurrentLayer(QgsMapLayer* layer);

QgsLayerTreeNode* currentNode() const;
QgsLayerTreeGroup* currentGroupNode() const;

QList<QgsLayerTreeNode*> selectedNodes(bool skipInternal = false) const;

protected:
void contextMenuEvent(QContextMenuEvent* event);

QAction* create_addGroup(QObject* parent, QgsLayerTreeNode* parentNode = 0);
QAction* create_removeGroupOrLayer(QObject* parent, QgsLayerTreeNode* parentNode);
QAction* create_showInOverview(QObject* parent, QgsLayerTreeNode* parentNode);

void updateExpandedStateFromNode(QgsLayerTreeNode* node);

signals:
Expand All @@ -35,9 +40,6 @@ class GUI_EXPORT QgsLayerTreeView : public QTreeView
public slots:

protected slots:
void addGroup();
void removeGroupOrLayer();
void showInOverview();

void modelRowsInserted(QModelIndex index, int start, int end);

Expand All @@ -47,6 +49,9 @@ protected slots:

protected:
QgsMapLayer* mCurrentLayer;

QgsLayerTreeViewDefaultActions* mDefaultActions;
};


#endif // QGSLAYERTREEVIEW_H
151 changes: 151 additions & 0 deletions src/gui/layertree/qgslayertreeviewdefaultactions.cpp
@@ -0,0 +1,151 @@
#include "qgslayertreeviewdefaultactions.h"

#include "qgsapplication.h"
#include "qgslayertreemodel.h"
#include "qgslayertreenode.h"
#include "qgslayertreeview.h"
#include "qgsmapcanvas.h"
#include "qgsvectorlayer.h"

#include <QAction>

QgsLayerTreeViewDefaultActions::QgsLayerTreeViewDefaultActions(QgsLayerTreeView* view)
: QObject(view)
, mView(view)
{
}

QAction* QgsLayerTreeViewDefaultActions::actionAddGroup(QObject* parent)
{
QAction* a = new QAction(tr("&Add Group"), parent);
connect(a, SIGNAL(triggered()), this, SLOT(addGroup()));
return a;
}

QAction* QgsLayerTreeViewDefaultActions::actionRemoveGroupOrLayer(QObject* parent)
{
QAction* a = new QAction(tr("Remove"), parent);
connect(a, SIGNAL(triggered()), this, SLOT(removeGroupOrLayer()));
return a;
}

QAction* QgsLayerTreeViewDefaultActions::actionShowInOverview(QObject* parent)
{
QgsLayerTreeNode* node = mView->currentNode();
if (!node)
return 0;

QAction* a = new QAction(tr("Show in overview"), parent);
connect(a, SIGNAL(triggered()), this, SLOT(showInOverview()));
a->setCheckable(true);
a->setChecked(node->customProperty("overview", 0).toInt());
return a;
}

QAction* QgsLayerTreeViewDefaultActions::actionRenameGroupOrLayer(QObject* parent)
{
QAction* a = new QAction(tr("Re&name"), parent);
connect(a, SIGNAL(triggered()), this, SLOT(renameGroupOrLayer()));
return a;
}

QAction* QgsLayerTreeViewDefaultActions::actionZoomToLayer(QgsMapCanvas* canvas, QObject* parent)
{
QAction* a = new QAction(QgsApplication::getThemeIcon( "/mActionZoomToLayer.svg" ),
tr("&Zoom to Layer Extent"), parent);
a->setData(QVariant::fromValue(reinterpret_cast<void*>(canvas)));
connect(a, SIGNAL(triggered()), this, SLOT(zoomToLayer()));
return a;
}

void QgsLayerTreeViewDefaultActions::addGroup()
{
QgsLayerTreeGroup* group = mView->currentGroupNode();
QString prefix = group == mView->layerTreeModel()->rootGroup() ? "group" : "sub-group";

QString newName = prefix + "1";
for ( int i = 2; group->findGroup(newName); ++i)
newName = prefix + QString::number(i);

QgsLayerTreeGroup* newGroup = group->addGroup(newName);
mView->edit( mView->layerTreeModel()->node2index(newGroup) );
}

void QgsLayerTreeViewDefaultActions::removeGroupOrLayer()
{
foreach (QgsLayerTreeNode* node, mView->selectedNodes(true))
{
// could be more efficient if working directly with ranges instead of individual nodes
qobject_cast<QgsLayerTreeGroup*>(node->parent())->removeChildNode(node);
}
}

void QgsLayerTreeViewDefaultActions::renameGroupOrLayer()
{
/*QgsLayerTreeNode* node = mView->currentNode();
if (!node)
return;*/

mView->edit(mView->currentIndex());
}

void QgsLayerTreeViewDefaultActions::showInOverview()
{
QgsLayerTreeNode* node = mView->currentNode();
if (!node)
return;

node->setCustomProperty("overview", node->customProperty("overview", 0).toInt() ? 0 : 1);
}

void QgsLayerTreeViewDefaultActions::zoomToLayer()
{
QAction* s = qobject_cast<QAction*>(sender());
QgsMapCanvas* canvas = reinterpret_cast<QgsMapCanvas*>(s->data().value<void*>());

QgsMapLayer* layer = mView->currentLayer();
if (!layer)
return;

QList<QgsMapLayer*> layers;
layers << layer;
zoomToLayers(canvas, layers);
}


void QgsLayerTreeViewDefaultActions::zoomToLayers(QgsMapCanvas* canvas, const QList<QgsMapLayer*>& layers)
{
QgsRectangle extent;

for ( int i = 0; i < layers.size(); ++i )
{
QgsMapLayer* layer = layers.at( i );
QgsRectangle layerExtent = layer->extent();

QgsVectorLayer* vLayer = qobject_cast<QgsVectorLayer*>( layer );

if ( layerExtent.isEmpty() && layer->type() == QgsMapLayer::VectorLayer )
{
qobject_cast<QgsVectorLayer*>( layer )->updateExtents();
layerExtent = vLayer->extent();
}

//transform extent if otf-projection is on
if ( canvas->hasCrsTransformEnabled() )
layerExtent = canvas->mapSettings().layerExtentToOutputExtent( layer, layerExtent );

if ( i == 0 )
extent = layerExtent;
else
extent.combineExtentWith( &layerExtent );
}

if ( extent.isEmpty() )
return;

// Increase bounding box with 5%, so that layer is a bit inside the borders
extent.scale( 1.05 );

//zoom to bounding box
canvas->setExtent( extent );
}

0 comments on commit 6a5b752

Please sign in to comment.