QGIS/src/app/legend/qgslegend.cpp

2972 lines
77 KiB
C++

/***************************************************************************
qgslegend.cpp - description
-------------------
begin : Sun Jul 28 2002
copyright : (C) 2002 by Gary E.Sherman
email : sherman at mrcc dot com
Romans 3:23=>Romans 6:23=>Romans 10:9,10=>Romans 12
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsapplication.h"
#include "qgisapp.h"
#include "qgslogger.h"
#include "qgslegend.h"
#include "qgslegendgroup.h"
#include "qgslegendlayer.h"
#include "qgslegendpropertygroup.h"
#include "qgslegendsymbologyitem.h"
#include "qgsmapcanvas.h"
#include "qgsmapcanvasmap.h"
#include "qgsmaplayer.h"
#include "qgsmaplayerregistry.h"
#include "qgsmaprenderer.h"
#include "qgsproject.h"
#include "qgsrasterlayer.h"
#include "qgsvectorlayer.h"
#include "qgsgenericprojectionselector.h"
#include "qgsclipboard.h"
#include <QFont>
#include <QDomDocument>
#include <QHeaderView>
#include <QMenu>
#include <QMessageBox>
#include <QMouseEvent>
#include <QPixmap>
#include <QTreeWidgetItem>
#include <QClipboard>
const int AUTOSCROLL_MARGIN = 16;
// This function finds a unique group name [prefix1, prefix2, ...] by adding an
// incremental integer to prefix. It is necessary because group names are the
// only way of identifying groups in QgsLegendInterface.
// Could add a "parent" argument and use that instead, or pass it as prefix
static QString getUniqueGroupName( QString prefix, QStringList groups )
{
QString suffix;
if ( groups.size() == 0 )
{
suffix = "1";
}
else
{
// get a list of strings that match prefix, and keep the suffix
QStringList match = groups.filter( QRegExp( QString( "^" + prefix ) ) );
match.replaceInStrings( prefix, QString( "" ) );
// find the maximum
int max = 0;
foreach ( QString m, match )
{
if ( m.toInt() > max )
max = m.toInt();
}
suffix = QString( "%1" ).arg( max + 1 );
}
return prefix + suffix;
}
QgsLegend::QgsLegend( QgsMapCanvas *canvas, QWidget * parent, const char *name )
: QTreeWidget( parent )
, mMousePressedFlag( false )
, mMapCanvas( canvas )
, mMinimumIconSize( 20, 20 )
, mChanging( false )
, mUpdateDrawingOrder( true )
{
setObjectName( name );
connect( this, SIGNAL( itemChanged( QTreeWidgetItem*, int ) ),
this, SLOT( handleItemChange( QTreeWidgetItem*, int ) ) );
connect( itemDelegate(), SIGNAL( closeEditor( QWidget *, QAbstractItemDelegate::EndEditHint ) ),
this, SLOT( handleCloseEditor( QWidget *, QAbstractItemDelegate::EndEditHint ) ) );
connect( this, SIGNAL( currentItemChanged( QTreeWidgetItem*, QTreeWidgetItem* ) ),
this, SLOT( handleCurrentItemChanged( QTreeWidgetItem*, QTreeWidgetItem* ) ) );
// project handling
connect( QgsProject::instance(), SIGNAL( readProject( const QDomDocument & ) ),
this, SLOT( readProject( const QDomDocument & ) ) );
connect( QgsProject::instance(), SIGNAL( writeProject( QDomDocument & ) ),
this, SLOT( writeProject( QDomDocument & ) ) );
// connect map layer registry signal to legend
connect( QgsMapLayerRegistry::instance(),
SIGNAL( layersWillBeRemoved( QStringList ) ),
this, SLOT( removeLayers( QStringList ) ) );
connect( QgsMapLayerRegistry::instance(), SIGNAL( removedAll() ),
this, SLOT( removeAll() ) );
connect( QgsMapLayerRegistry::instance(),
SIGNAL( layersAdded( QList<QgsMapLayer*> ) ),
this, SLOT( addLayers( QList<QgsMapLayer *> ) ) );
connect( mMapCanvas, SIGNAL( layersChanged() ),
this, SLOT( refreshCheckStates() ) );
// Initialise the line indicator widget.
mInsertionLine = new QWidget( viewport() );
hideLine();
mInsertionLine->setAutoFillBackground( true );
QPalette pal = mInsertionLine->palette();
pal.setColor( mInsertionLine->backgroundRole(), Qt::blue );
mInsertionLine->setPalette( pal );
setSortingEnabled( false );
setSelectionMode( QAbstractItemView::ExtendedSelection );
setDragEnabled( false );
setAutoScroll( true );
QFont f( "Arial", 10, QFont::Normal );
setFont( f );
QPalette palette;
palette.setColor( backgroundRole(), QColor( 192, 192, 192 ) );
setPalette( palette );
setColumnCount( 1 );
header()->setHidden( 1 );
setRootIsDecorated( true );
initPixmaps();
}
QgsLegend::~QgsLegend()
{
delete mInsertionLine;
}
#ifdef QGISDEBUG
void QgsLegend::showItem( QString msg, QTreeWidgetItem *item )
{
if ( !item )
{
QgsDebugMsg( msg + ": no item?" );
return;
}
QgsLegendItem *litem = dynamic_cast<QgsLegendItem *>( item );
QgsLegendGroup *group = dynamic_cast<QgsLegendGroup *>( item );
QgsLegendLayer *layer = dynamic_cast<QgsLegendLayer *>( item );
QString type;
if ( group )
type = "group";
else if ( layer )
type = "layer";
else if ( litem )
type = "litem";
else
type = "item";
QgsDebugMsgLevel( QString( "%1: %2 %3 row:%4" ).arg( msg ).arg( type ).arg( item->text( 0 ) ).arg( indexFromItem( item ).row() ), 3 );
}
#else
#define showItem(msg, item)
#endif
void QgsLegend::handleCurrentItemChanged( QTreeWidgetItem* current, QTreeWidgetItem* previous )
{
Q_UNUSED( current );
Q_UNUSED( previous );
QgsMapLayer *layer = currentLayer();
if ( mMapCanvas )
{
mMapCanvas->setCurrentLayer( layer );
}
blockSignals( true ); // or itemChanged() emitted
foreach ( QgsLegendLayer *ll, legendLayers() )
{
QFont itemFont = ll->font( 0 );
itemFont.setUnderline( ll->layer() == layer );
ll->setFont( 0, itemFont );
}
blockSignals( false );
emit currentLayerChanged( layer );
}
int QgsLegend::addGroupToCurrentItem( QString name, bool expand )
{
QTreeWidgetItem *parent = currentItem();
return addGroup( name, expand, parent );
}
int QgsLegend::addGroup( QString name, bool expand, QTreeWidgetItem* parent )
{
//avoid multiple refreshes of map canvas because of itemChanged signal
blockSignals( true );
bool nameEmpty = name.isEmpty();
QgsLegendGroup *parentGroup = dynamic_cast<QgsLegendGroup *>( parent );
QgsLegendGroup *group;
if ( parentGroup )
{
if ( nameEmpty )
name = getUniqueGroupName( tr( "sub-group" ), groups() );
group = new QgsLegendGroup( parentGroup, name );
}
else
{
if ( nameEmpty )
name = getUniqueGroupName( tr( "group" ), groups() );
group = new QgsLegendGroup( this, name );
if ( parent ) moveItem( group, parent );
// TODO: warn if parent != NULL or invisibleRootItem ?
}
QModelIndex groupIndex = indexFromItem( group );
setExpanded( groupIndex, expand );
setCurrentItem( group );
if ( nameEmpty )
openEditor();
blockSignals( false );
emit itemAdded( groupIndex );
// TODO: use QModelIndex::iternalId for an identifier instead of this mess ?
int itemCount = 0;
for ( QTreeWidgetItem* theItem = firstItem(); theItem; theItem = nextItem( theItem ) )
{
QgsLegendItem* legendItem = dynamic_cast<QgsLegendItem *>( theItem );
if ( legendItem->type() == QgsLegendItem::LEGEND_GROUP )
{
if ( legendItem == group ) return itemCount;
else itemCount++;
}
}
return itemCount; // bogus return
}
int QgsLegend::addGroup( QString name, bool expand, int groupIndex )
{
QTreeWidgetItem * parentItem = invisibleRootItem();
int itemCount = 0;
for ( QTreeWidgetItem* theItem = firstItem(); theItem; theItem = nextItem( theItem ) )
{
QgsLegendItem* legendItem = dynamic_cast<QgsLegendItem *>( theItem );
if ( legendItem->type() == QgsLegendItem::LEGEND_GROUP )
{
if ( itemCount == groupIndex )
{
// this is the matching group
parentItem = legendItem;
break;
}
else
{
itemCount = itemCount + 1;
}
}
}
return addGroup( name, expand, parentItem );
}
void QgsLegend::removeAll()
{
QgsDebugMsg( "Entering." );
clear();
mEmbeddedGroups.clear();
mPixmapWidthValues.clear();
mPixmapHeightValues.clear();
updateMapCanvasLayerSet();
setIconSize( mMinimumIconSize );
mDropTarget = 0;
}
void QgsLegend::setLayersVisible( bool visible )
{
if ( !mMapCanvas || mMapCanvas->isDrawing() )
{
return;
}
// Turn off rendering to improve speed.
mMapCanvas->freeze();
for ( QTreeWidgetItem* theItem = firstItem(); theItem; theItem = nextItem( theItem ) )
{
QgsLegendItem* litem = dynamic_cast<QgsLegendItem *>( theItem );
if ( litem && litem->type() == QgsLegendItem::LEGEND_LAYER )
{
theItem->setCheckState( 0, visible ? Qt::Checked : Qt::Unchecked );
handleItemChange( theItem, 0 );
}
}
// Turn on rendering (if it was on previously)
mMapCanvas->freeze( false );
QgsProject::instance()->dirty( true );
}
void QgsLegend::removeGroup( int groupIndex )
{
QgsLegendGroup * lg = NULL;
int itemCount = 0;
for ( QTreeWidgetItem* theItem = firstItem(); theItem; theItem = nextItem( theItem ) )
{
QgsLegendItem* legendItem = dynamic_cast<QgsLegendItem *>( theItem );
if ( legendItem->type() == QgsLegendItem::LEGEND_GROUP )
{
if ( itemCount == groupIndex )
{
// this is the matching group
lg = dynamic_cast<QgsLegendGroup*>( legendItem );
break;
}
else
{
itemCount = itemCount + 1;
}
}
}
if ( lg )
{
removeGroup( lg );
}
}
void QgsLegend::removeLayers( QStringList theLayers )
{
QgsDebugMsg( "Entering." );
foreach ( const QString &myId, theLayers )
{
bool invLayerRemoved = false;
for ( QTreeWidgetItem* theItem = firstItem();
theItem; theItem = nextItem( theItem ) )
{
QgsLegendItem *li = dynamic_cast<QgsLegendItem *>( theItem );
if ( li )
{
// save legend layer (parent of a legend layer file we're going to delete)
QgsLegendLayer* ll = qobject_cast<QgsLegendLayer *>( li );
if ( ll && ll->layer() && ll->layer()->id() == myId )
{
if ( !ll->isVisible() )
{
invLayerRemoved = true;
}
removeItem( ll );
delete ll;
break;
}
}
}
emit itemRemoved();
if ( invLayerRemoved )
emit invisibleLayerRemoved();
}
updateMapCanvasLayerSet();
adjustIconSize();
}
//deprecated delegates to removeLayers now
void QgsLegend::removeLayer( QString theLayer )
{
QStringList myList;
myList << theLayer;
removeLayers( myList );
}
void QgsLegend::mousePressEvent( QMouseEvent * e )
{
if ( e->button() == Qt::LeftButton )
{
mMousePressedFlag = true;
mDropTarget = itemAt( e->pos() );
if ( !mDropTarget )
{
setCurrentItem( 0 );
}
}
else if ( e->button() == Qt::RightButton )
{
QTreeWidgetItem* item = itemAt( e->pos() );
if ( !item )
{
setCurrentItem( 0 );
}
else if ( item != currentItem() )
{
if ( selectedItems().contains( item ) )
{
setCurrentItem( item, currentColumn(), QItemSelectionModel::NoUpdate );
}
else
{
clearSelection();
setCurrentItem( item );
}
}
else
{
// item is the current layer, but maybe previous selection was none
if ( !item->isSelected() )
{
item->setSelected( true );
}
}
handleRightClickEvent( item, e->globalPos() );
e->ignore();
return;
}
QTreeWidget::mousePressEvent( e );
} // contentsMousePressEvent
void QgsLegend::mouseMoveEvent( QMouseEvent * e )
{
if ( !mMousePressedFlag )
{
QgsDebugMsg( "mouse not pressed" );
return;
}
if ( mItemsBeingMoved.isEmpty() && !selectedItems().isEmpty() )
{
if ( mDropTarget == itemAt( e->pos() ) )
return;
mLayersPriorToMove = layerIDs();
QgsDebugMsg( "layers prior to move: " + mLayersPriorToMove.join( ", " ) );
// record which items were selected and hide them
foreach ( QTreeWidgetItem * item, selectedItems() )
{
//prevent to drag out content under groups that are embedded from other
//project files.
if ( parentGroupEmbedded( item ) )
{
continue;
}
item->setHidden( true );
mItemsBeingMoved << item;
}
// remove and unhide items, whose parent is already to be moved
foreach ( QTreeWidgetItem * item, mItemsBeingMoved )
{
QTreeWidgetItem *parent = item->parent();
bool parentHidden = false;
while ( !parentHidden && parent )
{
parentHidden = parent->isHidden();
parent = parent->parent();
}
if ( parentHidden )
{
mItemsBeingMoved.removeOne( item );
item->setHidden( false );
}
}
setCursor( Qt::SizeVerCursor );
}
if ( mItemsBeingMoved.isEmpty() )
{
QgsDebugMsg( "nothing to move" );
setCursor( QCursor( Qt::ArrowCursor ) );
return;
}
// change the cursor appropriate to if drop is allowed
QTreeWidgetItem* item = itemAt( e->pos() );
hideLine();
updateLineWidget();
scrollToItem( item );
mDropTarget = 0;
if ( item )
{
showItem( "moveMoveEvent" , item );
QgsLegendItem *litem = dynamic_cast<QgsLegendItem *>( item );
QgsLegendGroup *group = dynamic_cast<QgsLegendGroup *>( item );
QgsLegendLayer *layer = dynamic_cast<QgsLegendLayer *>( item );
while ( item->parent() && !group && !layer )
{
item = item->parent();
litem = dynamic_cast<QgsLegendItem *>( item );
group = dynamic_cast<QgsLegendGroup *>( item );
layer = dynamic_cast<QgsLegendLayer *>( item );
}
showItem( "layer/group" , item );
int line_x = visualItemRect( item ).left();
int line_y = 0;
if ( layer )
{
QTreeWidgetItem *lastItem = item->childCount() > 0 && item->isExpanded() ? item->child( item->childCount() - 1 ) : item;
int y0 = visualItemRect( item ).top() + 1;
int y1 = visualItemRect( lastItem ).bottom() - 2;
mDropTarget = layer;
//prevent inserting content into embedded groups
if ( !parentGroupEmbedded( litem ) )
{
if ( e->y() < ( y0 + y1 ) / 2 )
{
QgsDebugMsg( "insert before layer" );
mDropAction = BEFORE;
line_y = y0;
}
else
{
QgsDebugMsg( "insert after layer" );
mDropAction = AFTER;
line_y = y1;
}
}
}
else if ( group )
{
if ( yCoordAboveCenter( litem, e->y() ) ) //over center of item
{
QgsDebugMsg( "insert before group" );
//prevent inserting content into embedded groups
if ( !parentGroupEmbedded( item ) )
{
line_y = visualItemRect( item ).top() + 1;
mDropTarget = item;
mDropAction = BEFORE;
}
}
else // below center of item
{
if ( !groupEmbedded( item ) )
{
QgsDebugMsg( "insert into group" );
line_y = visualItemRect( item ).bottom() - 2;
mDropTarget = item;
mDropAction = INSERT;
}
}
}
else
{
line_y = visualItemRect( item ).bottom() - 2;
}
showLine( line_y, line_x );
setCursor( QCursor( Qt::SizeVerCursor ) );
}
else if ( !item
&& e->pos().y() >= 0 && e->pos().y() < viewport()->height()
&& e->pos().x() >= 0 && e->pos().x() < viewport()->width() )
{
// Outside the listed items, but check if we are in the empty area
// of the viewport, so we can drop after the last top level item.
mDropTarget = topLevelItem( topLevelItemCount() - 1 );
mDropAction = AFTER;
QgsDebugMsg( "insert after last layer/group" );
showLine( visualItemRect( lastVisibleItem() ).bottom() + 1, 0 );
setCursor( QCursor( Qt::SizeVerCursor ) );
}
else
{
QgsDebugMsg( "No item here" );
setCursor( QCursor( Qt::ForbiddenCursor ) );
}
}
void QgsLegend::updateGroupCheckStates( QTreeWidgetItem *item )
{
QgsLegendGroup *lg = dynamic_cast< QgsLegendGroup * >( item );
if ( !lg )
return;
for ( int i = 0; i < item->childCount(); i++ )
{
updateGroupCheckStates( item->child( i ) );
}
Qt::CheckState theState = lg->pendingCheckState();
if ( theState != lg->checkState( 0 ) )
{
blockSignals( true );
lg->setCheckState( 0, theState );
blockSignals( false );
}
}
void QgsLegend::mouseReleaseEvent( QMouseEvent * e )
{
QStringList layersPriorToEvent = layerIDs();
QTreeWidget::mouseReleaseEvent( e );
mMousePressedFlag = false;
if ( mItemsBeingMoved.isEmpty() )
{
//Trigger refresh because of check states on layers.
//If it comes from a check action on a group, it is not covered in handleItemChanges(),
//so we do it here
QgsLegendGroup *lg = dynamic_cast<QgsLegendGroup *>( currentItem() );
if ( lg && ( layersPriorToEvent != layerIDs() ) )
{
mMapCanvas->refresh();
}
return;
}
setCursor( QCursor( Qt::ArrowCursor ) );
hideLine();
// unhide
foreach ( QTreeWidgetItem * item, mItemsBeingMoved )
{
item->setHidden( false );
}
if ( mDropTarget )
{
if ( mDropAction == BEFORE )
{
showItem( "before => drop after", mDropTarget );
QTreeWidgetItem *prev = previousSibling( mDropTarget );
if ( prev )
{
mDropTarget = prev;
showItem( "prev sibling", mDropTarget );
foreach ( QTreeWidgetItem * item, mItemsBeingMoved )
{
moveItem( item, mDropTarget );
mDropTarget = item;
}
}
else
{
mDropTarget = mDropTarget->parent();
if ( mDropTarget )
{
showItem( "parent", mDropTarget );
mDropAction = INSERT;
}
else
{
mDropTarget = invisibleRootItem();
mDropAction = AFTER;
showItem( "root", mDropTarget );
}
}
}
if ( mDropAction == AFTER )
{
showItem( "drop after", mDropTarget );
foreach ( QTreeWidgetItem * item, mItemsBeingMoved )
{
moveItem( item, mDropTarget );
mDropTarget = item;
}
}
if ( mDropAction == INSERT )
{
showItem( "insert into", mDropTarget );
foreach ( QTreeWidgetItem * item, mItemsBeingMoved )
{
insertItem( item, mDropTarget );
}
}
mItemsBeingMoved.clear();
for ( int i = 0; i < topLevelItemCount(); i++ )
{
updateGroupCheckStates( topLevelItem( i ) );
}
}
else //no action
{
QgsDebugMsg( "Drop NO_ACTION" );
}
checkLayerOrderUpdate();
}
void QgsLegend::mouseDoubleClickEvent( QMouseEvent *e )
{
#ifdef Q_WS_MAC
// fix for when quick left-then-right clicks (when legend is out of focus)
// register as left double click: show contextual menu as user intended
if ( e->button() == Qt::RightButton )
{
mousePressEvent( e );
return;
}
#else
Q_UNUSED( e );
#endif
QSettings settings;
switch ( settings.value( "/qgis/legendDoubleClickAction", 0 ).toInt() )
{
case 0:
QgisApp::instance()->layerProperties();
break;
case 1:
QgisApp::instance()->attributeTable();
break;
default:
break;
}
}
void QgsLegend::handleRightClickEvent( QTreeWidgetItem* item, const QPoint& position )
{
if ( !mMapCanvas || mMapCanvas->isDrawing() )
{
return;
}
QMenu theMenu( tr( "Legend context" ), this );
QgsLegendItem* li = dynamic_cast<QgsLegendItem *>( item );
if ( li )
{
if ( li->type() == QgsLegendItem::LEGEND_LAYER )
{
qobject_cast<QgsLegendLayer*>( li )->addToPopupMenu( theMenu );
if ( li->parent() && !parentGroupEmbedded( li ) )
{
theMenu.addAction( tr( "&Make to Toplevel Item" ), this, SLOT( makeToTopLevelItem() ) );
}
}
else if ( li->type() == QgsLegendItem::LEGEND_GROUP )
{
theMenu.addAction( QgsApplication::getThemeIcon( "/mActionZoomToLayer.png" ),
tr( "Zoom to Group" ), this, SLOT( legendLayerZoom() ) );
// use QGisApp::removeLayer() to remove all selected layers+groups
theMenu.addAction( QgsApplication::getThemeIcon( "/mActionRemoveLayer.png" ), tr( "&Remove" ), QgisApp::instance(), SLOT( removeLayer() ) );
theMenu.addAction( QgsApplication::getThemeIcon( "/mActionSetCRS.png" ),
tr( "&Set Group CRS" ), this, SLOT( legendGroupSetCRS() ) );
}
if (( li->type() == QgsLegendItem::LEGEND_LAYER || li->type() == QgsLegendItem::LEGEND_GROUP ) && !groupEmbedded( li ) && !parentGroupEmbedded( li ) )
{
theMenu.addAction( tr( "Re&name" ), this, SLOT( openEditor() ) );
}
//
// Option to group layers, if the selection is more than one
//
if ( selectedLayers().length() > 1 )
{
theMenu.addAction( tr( "&Group Selected" ), this, SLOT( groupSelectedLayers() ) );
}
// ends here
}
if ( selectedLayers().length() == 1 )
{
QgisApp* app = QgisApp::instance();
theMenu.addAction( tr( "Copy Style" ), app, SLOT( copyStyle() ) );
if ( app->clipboard()->hasFormat( QGSCLIPBOARD_STYLE_MIME ) )
{
theMenu.addAction( tr( "Paste Style" ), app, SLOT( pasteStyle() ) );
}
}
theMenu.addAction( QgsApplication::getThemeIcon( "/folder_new.png" ), tr( "&Add New Group" ), this, SLOT( addGroupToCurrentItem() ) );
theMenu.addAction( QgsApplication::getThemeIcon( "/mActionExpandTree.png" ), tr( "&Expand All" ), this, SLOT( expandAll() ) );
theMenu.addAction( QgsApplication::getThemeIcon( "/mActionCollapseTree.png" ), tr( "&Collapse All" ), this, SLOT( collapseAll() ) );
QAction *updateDrawingOrderAction = theMenu.addAction( QgsApplication::getThemeIcon( "/mUpdateDrawingOrder.png" ), tr( "&Update Drawing Order" ), this, SLOT( toggleDrawingOrderUpdate() ) );
updateDrawingOrderAction->setCheckable( true );
updateDrawingOrderAction->setChecked( mUpdateDrawingOrder );
theMenu.exec( position );
}
void QgsLegend::initPixmaps()
{
mPixmaps.mOriginalPixmap = QgsApplication::getThemePixmap( "/mActionFileSmall.png" );
mPixmaps.mInOverviewPixmap = QgsApplication::getThemePixmap( "/mActionInOverview.png" );
mPixmaps.mEditablePixmap = QgsApplication::getThemePixmap( "/mIconEditable.png" );
mPixmaps.mProjectionErrorPixmap = QgsApplication::getThemePixmap( "/mIconProjectionProblem.png" );
}
Qt::CheckState QgsLegend::layerCheckState( QgsMapLayer * layer )
{
QgsLegendLayer * ll = findLegendLayer( layer );
return ll ? ll->checkState( 0 ) : Qt::Unchecked;
}
bool QgsLegend::layerIsExpanded( QgsMapLayer * layer )
{
QgsLegendLayer * ll = findLegendLayer( layer );
return ll->isExpanded();
}
QgsLegendGroup* QgsLegend::addEmbeddedGroup( const QString& groupName, const QString& projectFilePath, QgsLegendItem* parent )
{
mEmbeddedGroups.insert( groupName, projectFilePath );
//open project file, get layer ids in group, add the layers
QFile projectFile( projectFilePath );
if ( !projectFile.open( QIODevice::ReadOnly ) )
{
return 0;
}
QDomDocument projectDocument;
if ( !projectDocument.setContent( &projectFile ) )
{
return 0;
}
//store identify disabled layers of the embedded project
QSet<QString> embeddedIdentifyDisabledLayers;
QDomElement disabledLayersElem = projectDocument.documentElement().firstChildElement( "properties" ).firstChildElement( "Identify" ).firstChildElement( "disabledLayers" );
if ( !disabledLayersElem.isNull() )
{
QDomNodeList valueList = disabledLayersElem.elementsByTagName( "value" );
for ( int i = 0; i < valueList.size(); ++i )
{
embeddedIdentifyDisabledLayers.insert( valueList.at( i ).toElement().text() );
}
}
QDomElement legendElem = projectDocument.documentElement().firstChildElement( "legend" );
if ( legendElem.isNull() )
{
return 0;
}
QList<QDomNode> brokenNodes;
QList< QPair< QgsVectorLayer*, QDomElement > > vectorLayerList;
QSettings settings;
QDomNodeList legendGroupList = legendElem.elementsByTagName( "legendgroup" );
for ( int i = 0; i < legendGroupList.size(); ++i )
{
QDomElement legendElem = legendGroupList.at( i ).toElement();
if ( legendElem.attribute( "name" ) == groupName )
{
//embedded groups cannot be embedded again
if ( legendElem.attribute( "embedded" ) == "1" )
{
mEmbeddedGroups.remove( groupName );
return 0;
}
QgsLegendGroup* group = 0;
if ( parent )
{
group = new QgsLegendGroup( parent, groupName );
}
else
{
group = new QgsLegendGroup( this, groupName );
}
group->setEmbedded( true );
group->setProjectPath( projectFilePath );
QFont groupFont;
groupFont.setItalic( true );
group->setFont( 0, groupFont );
setCurrentItem( group );
QDomNodeList groupChildren = legendElem.childNodes();
for ( int j = 0; j < groupChildren.size(); ++j )
{
QDomElement childElem = groupChildren.at( j ).toElement();
bool visible = ( childElem.attribute( "checked" ).compare( "Qt::Checked", Qt::CaseInsensitive ) == 0 );
QString tagName = childElem.tagName();
if ( tagName == "legendlayer" )
{
QString layerId = childElem.firstChildElement( "filegroup" ).firstChildElement( "legendlayerfile" ).attribute( "layerid" );
QgsProject::instance()->createEmbeddedLayer( layerId, projectFilePath, brokenNodes, vectorLayerList, false );
QTreeWidgetItem* cItem = 0;
if ( settings.value( "/qgis/addNewLayersToCurrentGroup", false ).toBool() )
{
cItem = group->takeChild( 0 );
}
else
{
cItem = currentItem();
removeItem( cItem );
}
if ( cItem )
{
group->insertChild( group->childCount(), cItem );
}
if ( !visible )
{
cItem->setCheckState( 0, Qt::Unchecked );
}
//consider the layer might be identify disabled in its project
if ( embeddedIdentifyDisabledLayers.contains( layerId ) )
{
QStringList thisProjectIdentifyDisabledLayers = QgsProject::instance()->readListEntry( "Identify", "/disabledLayers" );
thisProjectIdentifyDisabledLayers.append( layerId );
QgsProject::instance()->writeEntry( "Identify", "/disabledLayers", thisProjectIdentifyDisabledLayers );
}
}
else if ( tagName == "legendgroup" )
{
addEmbeddedGroup( childElem.attribute( "name" ), projectFilePath, group );
}
}
checkLayerOrderUpdate();
return group;
}
}
return 0;
}
int QgsLegend::getItemPos( QTreeWidgetItem* item )
{
int counter = 1;
for ( QTreeWidgetItem* theItem = firstItem(); theItem; theItem = nextItem( theItem ) )
{
if ( theItem == item )
{
return counter;
}
++counter;
}
return -1;
}
//introduced in QGIS 1.8 - add layers in a batch
void QgsLegend::addLayers( QList<QgsMapLayer *> theLayerList )
{
QgsDebugMsg( "Entering." );
if ( !mMapCanvas || mMapCanvas->isDrawing() )
{
return;
}
QSettings settings;
//Note if the canvas was previously blank so we can
//zoom to all layers at the end if neeeded
bool myFirstLayerFlag = false;
QgsCoordinateReferenceSystem myPreviousCrs;
if ( layers().count() < 1 )
{
myFirstLayerFlag = true;
}
else
{
// remember CRS of present layer
myPreviousCrs = layers().first()->crs();
}
//iteratively add the layers to the canvas
for ( int i = 0; i < theLayerList.size(); ++i )
{
QgsMapLayer * layer = theLayerList.at( i );
QgsLegendLayer* llayer = new QgsLegendLayer( layer );
if ( !QgsProject::instance()->layerIsEmbedded( layer->id() ).isEmpty() )
{
QFont itemFont;
itemFont.setItalic( true );
llayer->setFont( 0, itemFont );
}
//set the correct check states
blockSignals( true );
llayer->setCheckState( 0, llayer->isVisible() ? Qt::Checked : Qt::Unchecked );
blockSignals( false );
QgsLegendGroup *lg = dynamic_cast<QgsLegendGroup *>( currentItem() );
if ( !lg && currentItem() )
{
lg = dynamic_cast<QgsLegendGroup *>( currentItem()->parent() );
}
int index = 0;
if ( lg )
{
index = lg->indexOfChild( currentItem() );
}
else
{
index = indexOfTopLevelItem( currentItem() );
}
if ( index < 0 )
{
index = 0;
}
if ( lg && settings.value( "/qgis/addNewLayersToCurrentGroup", false ).toBool() )
{
lg->insertChild( index, llayer );
}
else
{
insertTopLevelItem( index, llayer );
setCurrentItem( llayer );
}
setItemExpanded( llayer, true );
//don't expand raster items by default, there could be too many
refreshLayerSymbology( layer->id(), layer->type() != QgsMapLayer::RasterLayer );
updateMapCanvasLayerSet();
emit itemAdded( indexFromItem( llayer ) );
}
// first layer?
if ( myFirstLayerFlag )
{
QgsMapLayer * myFirstLayer = theLayerList.at( 0 );
if ( !mMapCanvas->mapRenderer()->hasCrsTransformEnabled() )
{
mMapCanvas->mapRenderer()->setDestinationCrs( myFirstLayer->crs() );
mMapCanvas->mapRenderer()->setMapUnits( myFirstLayer->crs().mapUnits() );
}
mMapCanvas->zoomToFullExtent();
mMapCanvas->clearExtentHistory();
}
else
{
if ( settings.value( "/Projections/otfTransformAutoEnable", true ).toBool() &&
!mMapCanvas->mapRenderer()->hasCrsTransformEnabled() )
{
// Verify if all layers have the same CRS
foreach ( QgsMapLayer *l, layers() )
{
if ( myPreviousCrs != l->crs() )
{
// Set to the previous de facto used so that extent does not change
mMapCanvas->mapRenderer()->setDestinationCrs( myPreviousCrs );
mMapCanvas->mapRenderer()->setProjectionsEnabled( true );
break;
}
}
}
}
//make the QTreeWidget item up-to-date
doItemsLayout();
}
//deprecated since 1.8 - delegates to addLayers
void QgsLegend::addLayer( QgsMapLayer * layer )
{
QList<QgsMapLayer *> myList;
myList << layer;
addLayers( myList );
}
void QgsLegend::setLayerVisible( QgsMapLayer * layer, bool visible )
{
QgsLegendLayer * ll = findLegendLayer( layer );
if ( ll )
{
ll->setCheckState( 0, visible ? Qt::Checked : Qt::Unchecked );
}
}
QgsLegendLayer* QgsLegend::currentLegendLayer()
{
QgsLegendItem* citem = dynamic_cast<QgsLegendItem *>( currentItem() );
if ( citem )
{
QgsLegendLayer* ll = qobject_cast<QgsLegendLayer *>( citem );
if ( ll )
{
return ll; //the current item is a legend layer, so return it
}
QgsLegendLayer* lpl = dynamic_cast<QgsLegendLayer *>( citem->parent() );
if ( lpl )
{
return lpl; //the parent of the current item is a legend layer, return it
}
}
return 0;
}
QgsMapLayer* QgsLegend::currentLayer()
{
QgsLegendLayer* ll = currentLegendLayer();
if ( ll )
{
return ll->layer();
}
else
{
return 0;
}
}
QList<QgsMapLayer *> QgsLegend::selectedLayers( bool inDrawOrder )
{
QList<QgsMapLayer *> layers;
if ( inDrawOrder )
{
foreach ( QgsLegendLayer *ll, legendLayers() )
{
if ( ll->isSelected() )
layers << ll->layer();
}
}
else
{
foreach ( QTreeWidgetItem * item, selectedItems() )
{
QgsLegendLayer *ll = dynamic_cast<QgsLegendLayer *>( item );
if ( ll )
layers << ll->layer();
}
}
return layers;
}
bool QgsLegend::selectedLayersEditable( bool modified )
{
bool hasEditable = false;
foreach ( QgsMapLayer * layer, selectedLayers() )
{
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer*>( layer );
if ( !vl )
{
continue;
}
if ( vl->isEditable() && ( !modified || ( modified && vl->isModified() ) ) )
{
hasEditable = true;
break;
}
}
return hasEditable;
}
QList<QgsLegendLayer *> QgsLegend::legendLayers()
{
if ( mUpdateDrawingOrder )
{
QList< QgsLegendLayer * > items;
QTreeWidgetItemIterator it( this );
while ( *it )
{
QgsLegendLayer *llayer = dynamic_cast<QgsLegendLayer *>( *it );
if ( llayer )
items.append( llayer );
++it;
}
return items;
}
else
{
int n = 0;
QMap< int, QgsLegendLayer * > items;
QTreeWidgetItemIterator it( this );
while ( *it )
{
QgsLegendLayer *llayer = dynamic_cast<QgsLegendLayer *>( *it );
if ( llayer )
{
QgsDebugMsgLevel( QString( "n=%1 o=%2 name=%3" ).arg( n ).arg( llayer->drawingOrder() ).arg( llayer->layer()->name() ), 3 );
items.insertMulti( llayer->drawingOrder(), llayer );
n++;
}
++it;
}
QList< QgsLegendLayer * > ls;
foreach ( int o, items.uniqueKeys() )
{
QgsDebugMsgLevel( QString( "o=%1" ).arg( o ), 3 );
QList< QgsLegendLayer *> values = items.values( o );
for ( int i = values.size() - 1; i >= 0; i-- )
{
QgsDebugMsgLevel( QString( " layer=%1" ).arg( values[i]->layer()->name() ), 3 );
ls << values[i];
}
}
return ls;
}
}
QList<DrawingOrderInfo> QgsLegend::drawingOrder()
{
QMap<int, DrawingOrderInfo> drawingOrder;
QSet<QgsLegendLayer*> embeddedGroupChildren;
int nEntries = 0;
QTreeWidgetItemIterator it( this );
while ( *it )
{
QgsLegendLayer* llayer = dynamic_cast<QgsLegendLayer *>( *it );
QgsLegendGroup* lgroup = dynamic_cast<QgsLegendGroup *>( *it );
if ( llayer )
{
if ( !embeddedGroupChildren.contains( llayer ) )
{
DrawingOrderInfo dInfo;
dInfo.name = llayer->layerName();
dInfo.id = llayer->layer()->id();
dInfo.checked = ( llayer->checkState( 0 ) == Qt::Checked );
dInfo.embeddedGroup = false;
if ( mUpdateDrawingOrder )
{
drawingOrder.insertMulti( nEntries, dInfo );
}
else
{
drawingOrder.insertMulti( llayer->drawingOrder(), dInfo );
}
++nEntries;
}
}
else if ( lgroup )
{
if ( lgroup->isEmbedded() && !( lgroup->parent() ) )
{
QList<QgsLegendLayer*> groupLayers = lgroup->legendLayers();
QList<QgsLegendLayer*>::const_iterator groupLayerIt = groupLayers.constBegin();
for ( ; groupLayerIt != groupLayers.constEnd(); ++groupLayerIt )
{
embeddedGroupChildren.insert( *groupLayerIt );
}
DrawingOrderInfo dInfo;
dInfo.name = lgroup->text( 0 );
dInfo.id = lgroup->projectPath();
dInfo.checked = ( lgroup->checkState( 0 ) != Qt::Unchecked );
dInfo.embeddedGroup = true;
if ( mUpdateDrawingOrder )
{
drawingOrder.insertMulti( nEntries, dInfo );
}
else
{
drawingOrder.insertMulti( lgroup->drawingOrder(), dInfo );
}
++nEntries;
}
}
++it;
}
return drawingOrder.values();
}
QList<QgsMapLayer *> QgsLegend::layers()
{
QList<QgsMapLayer *> ls;
foreach ( QgsLegendLayer *l, legendLayers() )
{
ls << l->layer();
}
return ls;
}
QList<QgsMapCanvasLayer> QgsLegend::canvasLayers()
{
QMap<int, QgsMapCanvasLayer> layers;
QSet<QgsLegendLayer*> embeddedGroupChildren;
int nEntries = 0;
QTreeWidgetItemIterator it( this );
while ( *it )
{
QgsLegendLayer* llayer = dynamic_cast<QgsLegendLayer *>( *it );
QgsLegendGroup* lgroup = dynamic_cast<QgsLegendGroup *>( *it );
if ( llayer && !embeddedGroupChildren.contains( llayer ) )
{
QgsMapCanvasLayer canvasLayer = llayer->canvasLayer();
if ( mUpdateDrawingOrder )
{
layers.insertMulti( nEntries + embeddedGroupChildren.size(), canvasLayer );
}
else
{
layers.insertMulti( llayer->drawingOrder(), canvasLayer );
}
}
else if ( lgroup )
{
if ( lgroup->isEmbedded() )
{
int groupDrawingOrder = lgroup->drawingOrder();
QList<QgsLegendLayer*> groupLayers = lgroup->legendLayers();
for ( int i = groupLayers.size() - 1; i >= 0; --i )
{
QgsLegendLayer* ll = groupLayers.at( i );
if ( !ll || embeddedGroupChildren.contains( ll ) )
{
continue;
}
if ( mUpdateDrawingOrder )
{
layers.insertMulti( nEntries, ll->canvasLayer() );
}
else
{
layers.insertMulti( groupDrawingOrder, ll->canvasLayer() );
}
embeddedGroupChildren.insert( ll );
}
}
}
++it;
++nEntries;
}
return layers.values();
}
void QgsLegend::setDrawingOrder( QList<QgsMapLayer *> layers )
{
QgsDebugMsg( "Entering." );
for ( int i = 0; i < layers.size(); i++ )
{
QgsLegendLayer *ll = findLegendLayer( layers[i] );
if ( !ll )
continue;
QgsDebugMsgLevel( QString( "setting order=%1 name=%2." ).arg( i ).arg( layers[i]->name() ), 3 );
ll->setDrawingOrder( i );
}
updateMapCanvasLayerSet();
}
void QgsLegend::setDrawingOrder( const QList<DrawingOrderInfo>& order )
{
QList<QgsMapCanvasLayer> layers;
QList<DrawingOrderInfo>::const_iterator orderIt = order.constBegin();
int i = 0;
for ( ; orderIt != order.constEnd(); ++orderIt )
{
if ( orderIt->embeddedGroup )
{
//find group
QgsLegendGroup* group = findLegendGroup( orderIt->name, orderIt->id );
if ( group )
{
group->setDrawingOrder( i );
QList<QgsLegendLayer*> groupLayers = group->legendLayers();
QList<QgsLegendLayer*>::iterator groupIt = groupLayers.begin();
for ( ; groupIt != groupLayers.end(); ++groupIt )
{
layers.push_back(( *groupIt )->canvasLayer() );
}
++i;
}
}
else
{
QgsLegendLayer *ll = findLegendLayer( orderIt->id );
if ( ll )
{
ll->setDrawingOrder( i );
layers.push_back( ll->canvasLayer() );
++i;
}
}
}
mMapCanvas->setLayerSet( layers );
}
bool QgsLegend::setCurrentLayer( QgsMapLayer *layer )
{
QgsLegendLayer *ll = findLegendLayer( layer );
if ( !ll )
return false;
setCurrentItem( ll );
clearSelection();
return true;
}
void QgsLegend::legendGroupRemove()
{
if ( !mMapCanvas || mMapCanvas->isDrawing() )
{
return;
}
// Turn off rendering to improve speed.
mMapCanvas->freeze();
QgsLegendGroup* lg = dynamic_cast<QgsLegendGroup *>( currentItem() );
if ( lg )
{
removeGroup( lg );
}
// Turn on rendering (if it was on previously)
mMapCanvas->freeze( false );
}
void QgsLegend::legendGroupSetCRS()
{
if ( !mMapCanvas || mMapCanvas->isDrawing() )
{
return;
}
QgsGenericProjectionSelector * mySelector = new QgsGenericProjectionSelector( this );
mySelector->setMessage();
if ( mySelector->exec() )
{
QgsCoordinateReferenceSystem crs( mySelector->selectedCrsId(), QgsCoordinateReferenceSystem::InternalCrsId );
QgsLegendGroup* lg = dynamic_cast<QgsLegendGroup *>( currentItem() );
setGroupCRS( lg, crs );
}
else
{
QApplication::restoreOverrideCursor();
}
delete mySelector;
}
void QgsLegend::removeGroup( QgsLegendGroup *lg )
{
if ( !mMapCanvas || mMapCanvas->isDrawing() )
{
return;
}
//delete the legend layers first
QTreeWidgetItem * child = lg->child( 0 );
while ( child )
{
QgsLegendLayer *cl = dynamic_cast<QgsLegendLayer *>( child );
QgsLegendGroup *cg = dynamic_cast<QgsLegendGroup *>( child );
if ( cl )
QgsMapLayerRegistry::instance()->removeMapLayers(
QStringList() << cl->layer()->id() );
else if ( cg )
removeGroup( cg );
child = lg->child( 0 );
}
delete lg;
emit itemRemoved();
adjustIconSize();
}
void QgsLegend::setGroupCRS( QgsLegendGroup *lg, const QgsCoordinateReferenceSystem &crs )
{
if ( !mMapCanvas || mMapCanvas->isDrawing() )
{
return;
}
foreach ( QgsLegendLayer *cl, lg->legendLayers() )
{
if ( cl )
{
cl->layer()->setCrs( crs );
}
}
}
void QgsLegend::moveLayer( QgsMapLayer *ml, int groupIndex )
{
if ( !ml )
return;
QgsLegendLayer *layer = findLegendLayer( ml->id() );
if ( !layer )
return;
int itemCount = 0;
QgsLegendGroup *group = NULL;
for ( QTreeWidgetItem* theItem = firstItem(); theItem; theItem = nextItem( theItem ) )
{
QgsLegendItem* legendItem = dynamic_cast<QgsLegendItem *>( theItem );
if ( legendItem->type() == QgsLegendItem::LEGEND_GROUP )
{
if ( itemCount == groupIndex )
{
// this is the matching group
group = dynamic_cast<QgsLegendGroup*>( legendItem );
break;
}
else
{
itemCount = itemCount + 1;
}
}
}
if ( group == NULL )
return;
insertItem( layer, group );
emit itemMovedGroup( dynamic_cast<QgsLegendItem*>( layer ), groupIndex );
}
void QgsLegend::legendLayerShowInOverview()
{
if ( !mMapCanvas || mMapCanvas->isDrawing() )
{
return;
}
QgsLegendItem* li = dynamic_cast<QgsLegendItem *>( currentItem() );
if ( !li )
return;
if ( li->type() == QgsLegendItem::LEGEND_LAYER )
return;
QgsLegendLayer* ll = qobject_cast<QgsLegendLayer *>( li );
if ( !ll )
return;
ll->showInOverview();
}
void QgsLegend::expandAll()
{
for ( QTreeWidgetItem* theItem = firstItem(); theItem; theItem = nextItem( theItem ) )
{
setExpanded( indexFromItem( theItem ), true );
}
}
void QgsLegend::collapseAll()
{
for ( QTreeWidgetItem* theItem = firstItem(); theItem; theItem = nextItem( theItem ) )
{
setExpanded( indexFromItem( theItem ), false );
}
}
bool QgsLegend::writeXML( QDomNode &legendnode, QDomDocument &document )
{
QList<QTreeWidgetItem*> items;
for ( int i = 0; i < topLevelItemCount(); i++ )
{
items << topLevelItem( i );
}
return writeXML( items, legendnode, document );
}
bool QgsLegend::writeXML( QList<QTreeWidgetItem *> items, QDomNode &node, QDomDocument &document )
{
foreach ( QTreeWidgetItem * currentItem, items )
{
QgsLegendItem *item = dynamic_cast<QgsLegendItem *>( currentItem );
if ( !item )
continue;
if ( item->type() == QgsLegendItem::LEGEND_GROUP )
{
QDomElement legendgroupnode = document.createElement( "legendgroup" );
legendgroupnode.setAttribute( "open", isItemExpanded( item ) ? "true" : "false" );
legendgroupnode.setAttribute( "name", item->text( 0 ) );
Qt::CheckState cstate = item->checkState( 0 );
if ( cstate == Qt::Checked )
{
legendgroupnode.setAttribute( "checked", "Qt::Checked" );
}
else if ( cstate == Qt::Unchecked )
{
legendgroupnode.setAttribute( "checked", "Qt::Unchecked" );
}
else if ( cstate == Qt::PartiallyChecked )
{
legendgroupnode.setAttribute( "checked", "Qt::PartiallyChecked" );
}
QHash< QString, QString >::const_iterator embedIt = mEmbeddedGroups.find( item->text( 0 ) );
if ( embedIt != mEmbeddedGroups.constEnd() )
{
legendgroupnode.setAttribute( "embedded", 1 );
legendgroupnode.setAttribute( "project", QgsProject::instance()->writePath( embedIt.value() ) );
QgsLegendGroup* group = dynamic_cast<QgsLegendGroup*>( item );
if ( group )
{
legendgroupnode.setAttribute( "drawingOrder", group->drawingOrder() );
}
}
else
{
QList<QTreeWidgetItem *> children;
for ( int i = 0; i < currentItem->childCount(); i++ )
{
children << currentItem->child( i );
}
writeXML( children, legendgroupnode, document );
}
node.appendChild( legendgroupnode );
}
else if ( item->type() == QgsLegendItem::LEGEND_LAYER )
{
QDomElement legendlayernode = document.createElement( "legendlayer" );
legendlayernode.setAttribute( "open", isItemExpanded( item ) ? "true" : "false" );
Qt::CheckState cstate = item->checkState( 0 );
if ( cstate == Qt::Checked )
{
legendlayernode.setAttribute( "checked", "Qt::Checked" );
}
else if ( cstate == Qt::Unchecked )
{
legendlayernode.setAttribute( "checked", "Qt::Unchecked" );
}
else if ( cstate == Qt::PartiallyChecked )
{
legendlayernode.setAttribute( "checked", "Qt::PartiallyChecked" );
}
legendlayernode.setAttribute( "name", item->text( 0 ) );
for ( int i = 0; i < item->childCount(); i++ )
{
QTreeWidgetItem *child = item->child( i );
QgsLegendItem *litem = dynamic_cast<QgsLegendItem *>( child );
if ( !litem )
{
QgsDebugMsg( "tree widget item not a legend item" );
continue;
}
if ( litem->type() == QgsLegendItem::LEGEND_PROPERTY_GROUP )
{
QDomElement legendpropertynode = document.createElement( "propertygroup" );
legendpropertynode.setAttribute( "open", isItemExpanded( item ) ? "true" : "false" );
legendlayernode.appendChild( legendpropertynode );
}
else if ( litem->type() == QgsLegendItem::LEGEND_SYMBOL_GROUP )
{
QDomElement legendsymbolnode = document.createElement( "symbolgroup" );
legendsymbolnode.setAttribute( "open", isItemExpanded( item ) ? "true" : "false" );
legendlayernode.appendChild( legendsymbolnode );
}
else
{
QgsDebugMsg( "unexpected legend item type " + QString::number( litem->type() ) );
}
}
node.appendChild( legendlayernode );
// save the information about layer
// emulate a legend layer file group and a legend layer file
// to keep it compatible with older projects
QgsLegendLayer *ll = dynamic_cast<QgsLegendLayer *>( item );
QgsMapLayer* layer = ll->layer();
legendlayernode.setAttribute( "drawingOrder", ll->drawingOrder() );
legendlayernode.setAttribute( "showFeatureCount", ll->showFeatureCount() );
QDomElement layerfilegroupnode = document.createElement( "filegroup" );
layerfilegroupnode.setAttribute( "open", isItemExpanded( item ) ? "true" : "false" );
layerfilegroupnode.setAttribute( "hidden", isItemHidden( item ) ? "true" : "false" );
legendlayernode.appendChild( layerfilegroupnode );
QDomElement legendlayerfilenode = document.createElement( "legendlayerfile" );
// layer id
legendlayerfilenode.setAttribute( "layerid", layer->id() );
layerfilegroupnode.appendChild( legendlayerfilenode );
//embedded layer?
if ( !QgsProject::instance()->layerIsEmbedded( layer->id() ).isEmpty() )
{
legendlayerfilenode.setAttribute( "embedded", "1" );
}
// visible flag
legendlayerfilenode.setAttribute( "visible", ll->isVisible() );
// show in overview flag
legendlayerfilenode.setAttribute( "isInOverview", ll->isInOverview() );
}
else
{
QgsDebugMsg( "unexpected legend item type " + QString::number( item->type() ) );
}
}
return true;
}
bool QgsLegend::readXML( QgsLegendGroup *parent, const QDomNode &node )
{
QgsRectangle bkMapExtent;
if ( mMapCanvas )
{
bkMapExtent = mMapCanvas->extent();
}
const QDomNodeList &l = node.childNodes();
for ( int i = 0; i < l.count(); i++ )
{
QDomNode child = l.at( i );
QDomElement childelem = child.toElement();
QString name = childelem.attribute( "name" );
//test every possibility of element...
if ( childelem.tagName() == "legendgroup" )
{
QgsLegendGroup* theGroup = 0;
if ( childelem.attribute( "embedded" ) == "1" )
{
theGroup = addEmbeddedGroup( name, QgsProject::instance()->readPath( childelem.attribute( "project" ) ) );
if ( childelem.hasAttribute( "drawingOrder" ) )
{
theGroup->setDrawingOrder( childelem.attribute( "drawingOrder" ).toInt() );
}
updateGroupCheckStates( theGroup );
}
else
{
if ( parent )
theGroup = new QgsLegendGroup( parent, name );
else
theGroup = new QgsLegendGroup( this, name );
}
if ( !theGroup )
{
continue;
}
//set the checkbox of the legend group to the right state
blockSignals( true );
QString checked = childelem.attribute( "checked" );
if ( checked == "Qt::Checked" )
{
theGroup->setCheckState( 0, Qt::Checked );
}
else if ( checked == "Qt::Unchecked" )
{
theGroup->setCheckState( 0, Qt::Unchecked );
}
else if ( checked == "Qt::PartiallyChecked" )
{
theGroup->setCheckState( 0, Qt::PartiallyChecked );
}
blockSignals( false );
readXML( theGroup, child );
if ( childelem.attribute( "open" ) == "true" )
{
expandItem( theGroup );
}
else
{
collapseItem( theGroup );
}
}
else if ( childelem.tagName() == "legendlayer" )
{
bool isOpen;
QgsLegendLayer* currentLayer = readLayerFromXML( childelem, isOpen );
if ( !currentLayer )
{
continue;
}
if ( currentLayer->layer() && !QgsProject::instance()->layerIsEmbedded( currentLayer->layer()->id() ).isEmpty() )
{
QFont itemFont;
itemFont.setItalic( true );
currentLayer->setFont( 0, itemFont );
}
// add to tree - either as a top-level node or a child of a group
if ( parent )
{
parent->addChild( currentLayer );
}
else
{
addTopLevelItem( currentLayer );
}
const QDomNodeList &cnl = child.childNodes();
for ( int j = 0; j < cnl.count(); j++ )
{
const QDomElement &childelem = cnl.at( j ).toElement();
if ( childelem.tagName() == "legendlayerfile" )
{
// do nothing, this has been handled in readLayerFromXML()
}
else if ( childelem.tagName() == "filegroup" )
{
// do nothing, this has been handled in readLayerFromXML()
}
else if ( childelem.tagName() == "propertygroup" )
{
QgsLegendPropertyGroup* thePropertyGroup = new QgsLegendPropertyGroup( currentLayer, "Properties" );
setItemExpanded( thePropertyGroup, childelem.attribute( "open" ) == "true" );
}
else
{
QgsDebugMsg( "unexpected legendlayer child " + childelem.tagName() );
}
}
// load symbology
refreshLayerSymbology( currentLayer->layer()->id() );
if ( isOpen )
{
expandItem( currentLayer );
}
else
{
collapseItem( currentLayer );
}
}
else
{
QgsDebugMsg( "unexpected legend child " + childelem.tagName() );
}
}
//restore canvas extent (could be changed by addLayer calls)
if ( !bkMapExtent.isEmpty() )
{
mMapCanvas->setExtent( bkMapExtent );
}
return true;
}
bool QgsLegend::readXML( QDomNode& legendnode )
{
clear(); //remove all items first
mEmbeddedGroups.clear();
mUpdateDrawingOrder = legendnode.toElement().attribute( "updateDrawingOrder", "true" ) == "true";
emit updateDrawingOrderChecked( mUpdateDrawingOrder );
emit updateDrawingOrderUnchecked( !mUpdateDrawingOrder );
return readXML( 0, legendnode );
}
QgsLegendLayer* QgsLegend::readLayerFromXML( QDomElement& childelem, bool& isOpen )
{
QDomElement fileGroupElem = childelem.firstChildElement( "filegroup" );
if ( fileGroupElem.isNull() )
return NULL; // we need a file group!
QDomElement fileElem = fileGroupElem.firstChildElement( "legendlayerfile" );
if ( fileElem.isNull() ) // we need a file element!
return NULL;
QgsMapLayer* theMapLayer = QgsMapLayerRegistry::instance()->mapLayer( fileElem.attribute( "layerid" ) );
if ( theMapLayer == NULL ) //the layer cannot be found (e.g. the file has been moved)
{
return NULL;
}
// create the item
QgsLegendLayer* ll = new QgsLegendLayer( theMapLayer );
ll->setDrawingOrder( childelem.attribute( "drawingOrder", "-1" ).toInt() );
ll->setShowFeatureCount( childelem.attribute( "showFeatureCount", "0" ).toInt(), false );
// load layer's visibility and 'show in overview' flag
ll->setInOverview( atoi( fileElem.attribute( "isInOverview" ).toUtf8() ) );
// expanded or collapsed
isOpen = childelem.attribute( "open" ) == "true";
//set the checkbox of the legend layer to the right state
blockSignals( true );
QString checked = childelem.attribute( "checked", "Qt::Checked" ); // Default is to show
if ( checked == "Qt::Checked" )
{
ll->setVisible( true );
ll->setCheckState( 0, Qt::Checked );
}
else if ( checked == "Qt::Unchecked" )
{
ll->setVisible( false );
ll->setCheckState( 0, Qt::Unchecked );
}
blockSignals( false );
return ll;
}
QgsLegendLayer* QgsLegend::findLegendLayer( const QString& layerKey )
{
QgsLegendLayer* theLegendLayer = 0;
for ( QTreeWidgetItem* theItem = firstItem(); theItem; theItem = nextItem( theItem ) )
{
theLegendLayer = dynamic_cast<QgsLegendLayer *>( theItem );
if ( theLegendLayer ) //item is a legend layer
{
if ( theLegendLayer->layer()->id() == layerKey )
{
return theLegendLayer;
}
}
}
return 0;
}
QgsLegendLayer* QgsLegend::findLegendLayer( const QgsMapLayer *layer )
{
QgsLegendLayer* theLegendLayer = 0;
for ( QTreeWidgetItem* theItem = firstItem(); theItem; theItem = nextItem( theItem ) )
{
theLegendLayer = dynamic_cast<QgsLegendLayer *>( theItem );
if ( theLegendLayer ) //item is a legend layer
{
if ( theLegendLayer->layer() == layer )
{
return theLegendLayer;
}
}
}
return 0;
}
QgsLegendGroup* QgsLegend::findLegendGroup( const QString& name, const QString& projectPath )
{
QgsLegendGroup* group = 0;
for ( QTreeWidgetItem* theItem = firstItem(); theItem; theItem = nextItem( theItem ) )
{
group = dynamic_cast<QgsLegendGroup*>( theItem );
if ( group )
{
if ( group->text( 0 ) == name && group->projectPath() == projectPath )
{
return group;
}
}
}
return 0;
}
void QgsLegend::adjustIconSize()
{
if ( mPixmapWidthValues.size() > 0 && mPixmapHeightValues.size() > 0 )
{
std::multiset<int>::const_reverse_iterator width_it = mPixmapWidthValues.rbegin();
std::multiset<int>::const_reverse_iterator height_it = mPixmapHeightValues.rbegin();
int maxWidth = *width_it;
int maxHeight = *height_it;
QSize currentIconSize = iconSize();
if ( maxWidth == currentIconSize.width() && maxHeight == currentIconSize.height() )
{
//no resizing necessary
return;
}
//keep the minimum size
if ( maxWidth < mMinimumIconSize.width() )
{
maxWidth = mMinimumIconSize.width();
}
if ( maxHeight < mMinimumIconSize.height() )
{
maxHeight = mMinimumIconSize.height();
}
setIconSize( QSize( maxWidth, maxHeight ) );
}
}
bool QgsLegend::yCoordAboveCenter( QgsLegendItem* it, int ycoord )
{
QRect rect = visualItemRect( it );
int height = rect.height();
int top = rect.top();
int mid = top + ( height / 2 );
if ( ycoord > mid ) //bottom, remember the y-coordinate increases downwards
{
return false;
}
else//top
{
return true;
}
}
bool QgsLegend::isLegendGroup( const QModelIndex &index )
{
return dynamic_cast<QgsLegendGroup *>( itemFromIndex( index ) );
}
QStringList QgsLegend::groups()
{
QStringList groupList;
QTreeWidgetItem *current = firstItem();
while ( current )
{
QgsLegendGroup *group = dynamic_cast<QgsLegendGroup *>( current );
if ( group )
{
groupList.append( group->text( 0 ) );
}
current = nextItem( current );
}
return groupList;
}
QList< GroupLayerInfo > QgsLegend::groupLayerRelationship()
{
QList< GroupLayerInfo > groupLayerList;
QList< QTreeWidgetItem * > items;
for ( int i = 0; i < topLevelItemCount(); i++ )
{
items << topLevelItem( i );
}
while ( !items.isEmpty() )
{
QTreeWidgetItem *currentItem = items.takeFirst();
QgsLegendLayer* lLayer = dynamic_cast<QgsLegendLayer*>( currentItem );
if ( lLayer )
{
if ( lLayer->layer() )
{
QList<QString> layerList;
layerList.push_back( lLayer->layer()->id() );
groupLayerList.push_back( qMakePair( QString(), layerList ) );
}
}
QgsLegendGroup* lGroup = dynamic_cast<QgsLegendGroup*>( currentItem );
if ( lGroup )
{
int nLayers = lGroup->childCount();
QList<QString> layerList;
for ( int i = 0; i < nLayers; ++i )
{
QTreeWidgetItem *gItem = lGroup->child( i );
QgsLegendLayer* lLayer = dynamic_cast<QgsLegendLayer*>( gItem );
if ( lLayer )
{
if ( lLayer->layer() )
{
layerList.push_back( lLayer->layer()->id() );
}
}
QgsLegendGroup* lGroup = dynamic_cast<QgsLegendGroup*>( gItem );
if ( lGroup )
{
layerList << lGroup->text( 0 );
items << lGroup;
}
}
groupLayerList.push_back( qMakePair( lGroup->text( 0 ), layerList ) );
}
}
return groupLayerList;
}
QTreeWidgetItem* QgsLegend::firstItem()
{
return topLevelItem( 0 );
}
/**Returns the next item (next sibling or next item on level above)*/
QTreeWidgetItem* QgsLegend::nextItem( QTreeWidgetItem* item )
{
QgsLegendItem* litem = dynamic_cast<QgsLegendItem *>( item );
if ( !litem )
{
return 0;
}
else if ( litem->childCount() > 0 )
{
return litem->child( 0 );
}
else if ( litem->nextSibling() )
{
return litem->nextSibling();
}
else if ( litem->parent() )
{
QTreeWidgetItem *parent = litem->parent();
while ( parent )
{
QgsLegendItem *sibling = dynamic_cast<QgsLegendItem *>( parent )->nextSibling();
if ( sibling )
return sibling;
parent = parent->parent();
}
}
return 0;
}
QTreeWidgetItem* QgsLegend::nextSibling( QTreeWidgetItem* item )
{
QModelIndex thisidx = indexFromItem( item );
QModelIndex nextsidx = thisidx.sibling( thisidx.row() + 1, thisidx.column() );
if ( nextsidx.isValid() )
{
return dynamic_cast<QgsLegendItem *>( itemFromIndex( nextsidx ) );
}
else
{
return 0;
}
}
QTreeWidgetItem* QgsLegend::previousSibling( QTreeWidgetItem* item )
{
QModelIndex thisidx = indexFromItem( item );
QgsDebugMsg( "prev sibling for row: " + QString::number( thisidx.row() ) );
QModelIndex prevsidx = thisidx.sibling( thisidx.row() - 1, thisidx.column() );
QgsDebugMsg( "row: " + QString::number( prevsidx.row() ) );
if ( prevsidx.isValid() )
{
return dynamic_cast<QgsLegendItem *>( itemFromIndex( prevsidx ) );
}
else
{
return 0;
}
}
void QgsLegend::insertItem( QTreeWidgetItem* move, QTreeWidgetItem* into )
{
QgsLegendItem* movedItem = dynamic_cast<QgsLegendItem *>( move );
QgsLegendItem* intoItem = dynamic_cast<QgsLegendItem *>( into );
if ( movedItem && intoItem )
{
QgsLegendItem* parentItem = dynamic_cast<QgsLegendItem *>( movedItem->parent() );
movedItem->storeAppearanceSettings();//store settings in the moved item and its children
removeItem( movedItem );
intoItem->insert( movedItem );
if ( parentItem )
{
parentItem->release( movedItem ); //give the former parent item the possibility to do cleanups
}
intoItem->receive( movedItem );
movedItem->restoreAppearanceSettings();//apply the settings again
emit itemMovedGroup( movedItem, indexFromItem( intoItem ).row() );
}
}
void QgsLegend::moveItem( QTreeWidgetItem* move, QTreeWidgetItem* after )
{
QModelIndex oldIndex = indexFromItem( move );
QgsDebugMsgLevel( QString( "Moving layer : %1 (%2)" ).arg( move->text( 0 ) ).arg( move->type() ), 3 );
if ( after )
{
QgsDebugMsgLevel( QString( "after layer : %1 (%2)" ).arg( after->text( 0 ) ).arg( after->type() ), 3 );
}
else
{
QgsDebugMsgLevel( "as toplevel item", 3 );
}
static_cast<QgsLegendItem*>( move )->storeAppearanceSettings();//store settings in the moved item and its childern
if ( move->parent() )
{
move->parent()->takeChild( move->parent()->indexOfChild( move ) );
}
else //move is toplevel item
{
takeTopLevelItem( indexOfTopLevelItem( move ) );
}
if ( after )
{
if ( after->parent() )
{
after->parent()->insertChild( after->parent()->indexOfChild( after ) + 1, move );
}
else //toplevel item
{
insertTopLevelItem( indexOfTopLevelItem( after ) + 1, move );
}
}
else
{
insertTopLevelItem( 0, move );
}
static_cast<QgsLegendItem*>( move )->restoreAppearanceSettings();//apply the settings again
emit itemMoved( oldIndex, indexFromItem( move ) );
}
void QgsLegend::removeItem( QTreeWidgetItem* item )
{
if ( item->parent() )
{
item->parent()->takeChild( item->parent()->indexOfChild( item ) );
}
else
{
takeTopLevelItem( indexOfTopLevelItem( item ) );
}
}
void QgsLegend::updateMapCanvasLayerSet()
{
QgsDebugMsg( "Entering." );
QList<QgsMapCanvasLayer> layers = canvasLayers();
mMapCanvas->setLayerSet( layers );
}
void QgsLegend::updateOverview()
{
mMapCanvas->updateOverview();
}
void QgsLegend::enableOverviewModeAllLayers( bool isInOverview )
{
for ( QTreeWidgetItem* theItem = firstItem(); theItem; theItem = nextItem( theItem ) )
{
QgsLegendLayer* ll = dynamic_cast<QgsLegendLayer *>( theItem );
if ( ll )
{
ll->setInOverview( isInOverview );
}
}
updateMapCanvasLayerSet();
updateOverview();
}
QStringList QgsLegend::layerIDs()
{
QStringList layers;
for ( QTreeWidgetItem* theItem = firstItem(); theItem; theItem = nextItem( theItem ) )
{
QgsLegendItem *li = dynamic_cast<QgsLegendItem *>( theItem );
QgsLegendLayer* ll = qobject_cast<QgsLegendLayer *>( li );
if ( ll )
{
if ( ll->checkState( 0 ) == Qt::Checked )
{
QgsMapLayer *lyr = ll->layer();
layers.push_front( lyr->id() );
}
}
}
#ifdef QGISDEBUG
QgsDebugMsg( "QgsLegend::layerIDs()" );
foreach ( QString id, layers )
{
QgsDebugMsg( id );
}
#endif
return layers;
}
void QgsLegend::refreshLayerSymbology( QString key, bool expandItem )
{
QgsLegendLayer* theLegendLayer = findLegendLayer( key );
if ( !theLegendLayer )
{
return;
}
//store the current item
QTreeWidgetItem* current = currentItem();
// in case the current item is a child of the layer, use the layer as current item
// because otherwise we would set an invalid item as current item
// (in refreshSymbology the symbology items are removed and new ones are added)
if ( current && current->parent() == theLegendLayer )
current = current->parent();
double widthScale = 1.0;
if ( mMapCanvas && mMapCanvas->map() )
{
widthScale = mMapCanvas->map()->paintDevice().logicalDpiX() / 25.4;
}
theLegendLayer->refreshSymbology( key, widthScale );
//restore the current item again
setCurrentItem( current );
adjustIconSize();
setItemExpanded( theLegendLayer, expandItem );//make sure the symbology items are visible
}
void QgsLegend::refreshLayerSymbology( QString key, QgsLegendItem::Expansion expandItem )
{
bool expand = true;
if ( expandItem == QgsLegendItem::DontChange )
{
QgsLegendLayer* theLegendLayer = findLegendLayer( key );
if ( !theLegendLayer )
{
return;
}
expand = theLegendLayer->isExpanded();
}
else if ( expandItem == QgsLegendItem::Collapse )
{
expand = false;
}
return refreshLayerSymbology( key, expand );
}
void QgsLegend::addPixmapWidthValue( int width )
{
mPixmapWidthValues.insert( width );
}
void QgsLegend::addPixmapHeightValue( int height )
{
mPixmapHeightValues.insert( height );
}
void QgsLegend::removePixmapWidthValue( int width )
{
std::multiset<int>::iterator it = mPixmapWidthValues.find( width );
if ( it != mPixmapWidthValues.end() )
{
mPixmapWidthValues.erase( it );
}
//todo: adapt the icon size if width is the largest value and the size of the next element is higher than the minimum
}
void QgsLegend::removePixmapHeightValue( int height )
{
std::multiset<int>::iterator it = mPixmapHeightValues.find( height );
if ( it != mPixmapHeightValues.end() )
{
mPixmapHeightValues.erase( height );
}
//todo: adapt the icon size if height is the largest value and the size of the next element is higher than the minimum
}
void QgsLegend::handleCloseEditor( QWidget * editor, QAbstractItemDelegate::EndEditHint hint )
{
Q_UNUSED( editor );
Q_UNUSED( hint );
QgsLegendItem *item = dynamic_cast<QgsLegendItem *>( currentItem() );
if ( item )
{
item->afterEdit();
}
}
void QgsLegend::handleItemChange( QTreeWidgetItem* item, int column )
{
Q_UNUSED( column );
if ( !item )
{
return;
}
QgsLegendLayer *ll = dynamic_cast<QgsLegendLayer *>( item );
QgsLegendGroup *lg = dynamic_cast<QgsLegendGroup *>( item );
if ( !ll && !lg )
{
return;
}
#ifdef QGISDEBUG
if ( item->checkState( 0 ) == Qt::Checked )
showItem( "handleItemChange[checked]", item );
else if ( item->checkState( 0 ) == Qt::Unchecked )
showItem( "handleItemChange[unchecked]", item );
else if ( item->checkState( 0 ) == Qt::PartiallyChecked )
showItem( "handleItemChange[partially]", item );
else
showItem( "handleItemChange[?]", item );
#endif
if ( ll )
{
//if the text of a QgsLegendLayer has changed, change the display names of all its maplayers
if ( ll->layerName() != ll->layer()->name() )
{
ll->layer()->setLayerName( ll->layerName() );
}
}
bool changing = mChanging;
mChanging = true;
bool mapCanvasFrozen = mMapCanvas->isFrozen(); //save freeze state
if ( !changing )
{
mMapCanvas->freeze();
if ( item->isSelected() )
{
foreach ( QTreeWidgetItem * i, selectedItems() )
{
if ( i != item )
{
i->setCheckState( 0, item->checkState( 0 ) );
}
}
}
}
if ( ll )
{
ll->setVisible( ll->checkState( 0 ) == Qt::Checked );
}
if ( lg && lg->checkState( 0 ) != Qt::PartiallyChecked )
{
Qt::CheckState theState = lg->checkState( 0 );
for ( int i = 0; i < item->childCount(); i++ )
{
QTreeWidgetItem *child = item->child( i );
if ( child->checkState( 0 ) != item->checkState( 0 ) )
child->setCheckState( 0, theState );
}
}
// propagate updates to upper groups
for (
QgsLegendGroup *plg = dynamic_cast<QgsLegendGroup *>( item->parent() );
plg;
plg = dynamic_cast<QgsLegendGroup *>( plg->parent() )
)
{
Qt::CheckState theState = plg->pendingCheckState();
if ( theState != plg->checkState( 0 ) )
{
blockSignals( true );
plg->setCheckState( 0, theState );
blockSignals( false );
}
}
if ( !changing )
{
// If it was on, turn it back on, otherwise leave it
// off, as turning it on causes a refresh.
mMapCanvas->freeze( mapCanvasFrozen );
// update layer set
updateMapCanvasLayerSet();
}
mChanging = changing;
}
void QgsLegend::openEditor()
{
QgsLegendItem* theItem = dynamic_cast<QgsLegendItem*>( currentItem() );
if ( theItem )
{
if ( !groupEmbedded( theItem ) && !parentGroupEmbedded( theItem ) )
{
theItem->beforeEdit();
editItem( theItem, 0 );
}
}
}
void QgsLegend::makeToTopLevelItem()
{
QgsLegendItem* theItem = dynamic_cast<QgsLegendItem *>( currentItem() );
if ( theItem )
{
if ( !parentGroupEmbedded( theItem ) )
{
theItem->storeAppearanceSettings();
removeItem( theItem );
addTopLevelItem( theItem );
theItem->restoreAppearanceSettings();
}
}
}
void QgsLegend::legendLayerZoom()
{
if ( !mMapCanvas )
{
return;
}
QgsRectangle extent;
QgsLegendItem* li = dynamic_cast<QgsLegendItem *>( currentItem() );
if ( !li )
return;
if ( li->type() == QgsLegendItem::LEGEND_LAYER )
{
QgsLegendLayer* currentLayer = dynamic_cast<QgsLegendLayer *>( currentItem() );
if ( !currentLayer )
return;
QgsMapLayer* theLayer = currentLayer->layer();
extent = theLayer->extent();
//transform extent if otf-projection is on
if ( mMapCanvas->hasCrsTransformEnabled() )
{
QgsMapRenderer* renderer = mMapCanvas->mapRenderer();
if ( renderer )
{
extent = renderer->layerExtentToOutputExtent( theLayer, extent );
}
}
}
else if ( li->type() == QgsLegendItem::LEGEND_GROUP )
{
QgsLegendGroup* currentGroup = dynamic_cast<QgsLegendGroup *>( currentItem() );
QgsRectangle layerExtent;
QList<QgsLegendLayer*> layers = currentGroup->legendLayers();
for ( int i = 0; i < layers.size(); ++i )
{
QgsMapLayer* theLayer = layers.at( i )->layer();
layerExtent = theLayer->extent();
//transform extent if otf-projection is on
if ( mMapCanvas->hasCrsTransformEnabled() )
{
QgsMapRenderer* renderer = mMapCanvas->mapRenderer();
if ( renderer )
{
layerExtent = renderer->layerExtentToOutputExtent( theLayer, 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
mMapCanvas->setExtent( extent );
mMapCanvas->refresh();
}
void QgsLegend::legendLayerZoomNative()
{
//find current Layer
QgsLegendLayer* currentLayer = dynamic_cast<QgsLegendLayer *>( currentItem() );
if ( !currentLayer )
return;
QgsRasterLayer *layer = qobject_cast<QgsRasterLayer *>( currentLayer->layer() );
if ( layer )
{
QgsDebugMsg( "Raster units per pixel : " + QString::number( layer->rasterUnitsPerPixel() ) );
QgsDebugMsg( "MapUnitsPerPixel before : " + QString::number( mMapCanvas->mapUnitsPerPixel() ) );
layer->setCacheImage( NULL );
if ( mMapCanvas->hasCrsTransformEnabled() )
{
// get legth of central canvas pixel width in source raster crs
QgsRectangle e = mMapCanvas->extent();
QgsMapRenderer* r = mMapCanvas->mapRenderer();
QgsPoint p1( e.center().x(), e.center().y() );
QgsPoint p2( e.center().x() + e.width() / r->width(), e.center().y() + e.height() / r->height() );
QgsCoordinateTransform ct( r->destinationCrs(), layer->crs() );
p1 = ct.transform( p1 );
p2 = ct.transform( p2 );
double width = sqrt( p1.sqrDist( p2 ) ); // width of reprojected pixel
// This is not perfect of course, we use the resolution in just one direction
mMapCanvas->zoomByFactor( qAbs( layer->rasterUnitsPerPixel() / width ) );
}
else
{
mMapCanvas->zoomByFactor( qAbs( layer->rasterUnitsPerPixel() / mMapCanvas->mapUnitsPerPixel() ) );
}
mMapCanvas->refresh();
QgsDebugMsg( "MapUnitsPerPixel after : " + QString::number( mMapCanvas->mapUnitsPerPixel() ) );
}
}
void QgsLegend::legendLayerStretchUsingCurrentExtent()
{
//find current Layer
QgsLegendLayer* currentLayer = dynamic_cast<QgsLegendLayer *>( currentItem() );
if ( !currentLayer )
return;
QgsRasterLayer *layer = qobject_cast<QgsRasterLayer *>( currentLayer->layer() );
if ( layer )
{
// Note: Do we really want to do these next clauses? The user will get a surprise when the
// drawing style they are using suddenly changes....! TS
if ( layer->drawingStyle() == QgsRasterLayer::SingleBandPseudoColor )
{
layer->setDrawingStyle( QgsRasterLayer::SingleBandGray );
}
else if ( layer->drawingStyle() == QgsRasterLayer::MultiBandSingleBandPseudoColor )
{
layer->setDrawingStyle( QgsRasterLayer::MultiBandSingleBandGray );
}
if ( layer->contrastEnhancementAlgorithmAsString() == "NoEnhancement" )
{
layer->setContrastEnhancementAlgorithm( "StretchToMinimumMaximum" );
}
layer->setMinimumMaximumUsingLastExtent();
layer->setCacheImage( NULL );
refreshLayerSymbology( layer->id() );
mMapCanvas->refresh();
}
}
void QgsLegend::readProject( const QDomDocument & doc )
{
QDomNodeList nodes = doc.elementsByTagName( "legend" );
if ( nodes.count() )
{
QDomNode node = nodes.item( 0 );
readXML( node );
// update canvas layers to match the order defined by legend
updateMapCanvasLayerSet();
}
else
{
QgsDebugMsg( "Couldn't read legend information from project" );
}
}
void QgsLegend::writeProject( QDomDocument & doc )
{
QDomNodeList nl = doc.elementsByTagName( "qgis" );
if ( !nl.count() )
{
QgsDebugMsg( "Unable to find qgis element in project file" );
return;
}
QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element ok
QDomElement mapcanvasNode = doc.createElement( "legend" );
mapcanvasNode.setAttribute( "updateDrawingOrder", mUpdateDrawingOrder ? "true" : "false" );
qgisNode.appendChild( mapcanvasNode );
writeXML( mapcanvasNode, doc );
}
bool QgsLegend::checkLayerOrderUpdate()
{
QStringList layersAfterRelease = layerIDs(); //test if canvas redraw is really necessary
if ( layersAfterRelease != mLayersPriorToMove )
{
// z-order has changed - update layer set
updateMapCanvasLayerSet();
emit zOrderChanged();
return true;
}
return false;
}
void QgsLegend::hideLine()
{
mInsertionLine->setGeometry( 0, -100, 1, 1 );
}
void QgsLegend::showLine( int y, int left )
{
mInsertionLine->setGeometry( left, y, viewport()->width(), 2 );
}
void QgsLegend::updateLineWidget()
{
mInsertionLine->repaint();
}
QTreeWidgetItem * QgsLegend::lastVisibleItem()
{
QTreeWidgetItem *current;
QTreeWidgetItem *next;
current = topLevelItem( topLevelItemCount() - 1 );
while (( next = itemBelow( current ) ) )
{
current = next;
}
return current;
}
void QgsLegend::refreshCheckStates()
{
if ( !mMapCanvas )
{
return;
}
QList<QgsMapLayer*> lst = mMapCanvas->layers();
for ( QTreeWidgetItem* item = firstItem(); item; item = nextItem( item ) )
{
QgsLegendLayer* ll = dynamic_cast<QgsLegendLayer *>( item );
if ( ll )
{
ll->setCheckState( 0, lst.contains( ll->layer() ) ? Qt::Checked : Qt::Unchecked );
}
}
}
void QgsLegend::removeSelectedLayers()
{
// Turn off rendering to improve speed.
mMapCanvas->freeze();
foreach ( QTreeWidgetItem * item, selectedItems() )
{
QgsLegendGroup* lg = dynamic_cast<QgsLegendGroup *>( item );
if ( lg )
{
removeGroup( lg );
continue;
}
QgsLegendLayer *ll = dynamic_cast<QgsLegendLayer *>( item );
if ( ll && ll->layer() )
{
QgsMapLayerRegistry::instance()->removeMapLayers(
QStringList() << ll->layer()->id() );
continue;
}
}
// Turn on rendering (if it was on previously)
mMapCanvas->freeze( false );
}
void QgsLegend::setCRSForSelectedLayers( const QgsCoordinateReferenceSystem &crs )
{
// Turn off rendering to improve speed.
mMapCanvas->freeze();
foreach ( QTreeWidgetItem * item, selectedItems() )
{
QgsLegendGroup* lg = dynamic_cast<QgsLegendGroup *>( item );
if ( lg )
{
setGroupCRS( lg, crs );
continue;
}
QgsLegendLayer *ll = dynamic_cast<QgsLegendLayer *>( item );
if ( ll && ll->layer() )
{
ll->layer()->setCrs( crs );
continue;
}
}
// Turn on rendering (if it was on previously)
mMapCanvas->freeze( false );
}
bool QgsLegend::parentGroupEmbedded( QTreeWidgetItem* item ) const
{
if ( !item )
{
return false;
}
QgsLegendItem* lItem = dynamic_cast<QgsLegendItem*>( item );
if ( lItem && lItem->parent() )
{
QgsLegendGroup* parentGroup = dynamic_cast<QgsLegendGroup*>( lItem->parent() );
if ( parentGroup && parentGroup->type() == QgsLegendItem::LEGEND_GROUP
&& mEmbeddedGroups.contains( parentGroup->text( 0 ) ) )
{
return true;
}
}
return false;
}
bool QgsLegend::groupEmbedded( QTreeWidgetItem* item ) const
{
if ( !item )
{
return false;
}
QgsLegendGroup* gItem = dynamic_cast<QgsLegendGroup*>( item );
if ( !gItem )
{
return false;
}
return mEmbeddedGroups.contains( gItem->text( 0 ) );
}
void QgsLegend::setUpdateDrawingOrder( bool updateDrawingOrder )
{
if ( mUpdateDrawingOrder == updateDrawingOrder )
return;
mUpdateDrawingOrder = updateDrawingOrder;
QgsProject::instance()->dirty( true );
updateMapCanvasLayerSet();
emit updateDrawingOrderChecked( mUpdateDrawingOrder );
emit updateDrawingOrderUnchecked( !mUpdateDrawingOrder );
}
bool QgsLegend::updateDrawingOrder()
{
return mUpdateDrawingOrder;
}
void QgsLegend::toggleDrawingOrderUpdate()
{
setUpdateDrawingOrder( !mUpdateDrawingOrder );
}
void QgsLegend::groupSelectedLayers()
{
//avoid multiple refreshes of map canvas because of itemChanged signal
blockSignals( true );
QTreeWidgetItem * parent = 0;
foreach ( QTreeWidgetItem* item, selectedItems() )
{
parent = item->parent();
}
QgsLegendGroup *group;
if ( parent )
{
group = new QgsLegendGroup( parent,
getUniqueGroupName( tr( "sub-group" ), groups() ) );
}
else
{
group = new QgsLegendGroup( this,
getUniqueGroupName( tr( "group" ), groups() ) );
}
// save old indexes so we can notify changes
QList< QModelIndex > oldIndexes;
QList< QTreeWidgetItem* > selected;
foreach ( QTreeWidgetItem * item, selectedItems() )
{
QgsLegendLayer* layer = dynamic_cast<QgsLegendLayer *>( item );
if ( layer )
{
oldIndexes.append( indexFromItem( item ) );
selected.append( item );
}
}
foreach ( QTreeWidgetItem * item, selected )
{
insertItem( item, group );
}
editItem( group, 0 );
blockSignals( false );
// notify that group was added and that items were moved
emit itemAdded( indexFromItem( group ) );
for ( int i = 0; i < selected.size(); i++ )
{
emit itemMoved( oldIndexes[i], indexFromItem( selected[i] ) );
}
}