class Legend( QtGui.QTreeWidget ):
    """
      Provide a widget that manages map layers and their symbology as tree items
    """
    # b called from mainwindow.py and passed mainwindow as parent
    def __init__( self, parent ):
        QtGui.QTreeWidget.__init__( self, parent )
        
        # so self.pyQGisApp is the mainwindow
        self.pyQGisApp = parent
        self.canvas = None
        # b returns a list of layer objects that appear in the legend
        self.layers = self.getLayerSet()

        self.bMousePressedFlag = False
        self.itemBeingMoved = None

        # QTreeWidget properties
        self.setSortingEnabled( False )
        self.setDragEnabled( False )
        self.setAutoScroll( True )
        self.setHeaderHidden( True )
        self.setRootIsDecorated( True )
        self.setContextMenuPolicy( QtCore.Qt.CustomContextMenu )
        # b context menu requested with a right click on a legend item
        # b apparently passes a QPoint to self.showMenu
        self.connect( self, QtCore.SIGNAL( "customContextMenuRequested(QPoint)" ),
            self.showMenu )
        # b Signal emitted by QgsMapLayerRegistry
        # b QgsMapLayerRegistry.instance() is pointer to new layer
        # b addLayerToLegend calls LegendItem() to create new QTreeWidgetItem 
        self.connect( QgsMapLayerRegistry.instance(), QtCore.SIGNAL("layerWasAdded(QgsMapLayer *)"),
            self.addLayerToLegend)
        # b this connection does not seem to be implemented in CAPS
        self.connect( QgsMapLayerRegistry.instance(), QtCore.SIGNAL( "removedAll()" ),
            self.removeAll )
        self.connect( self, QtCore.SIGNAL("itemChanged(QTreeWidgetItem *,int)"),
            self.updateLayerStatus )
        self.connect( self, QtCore.SIGNAL( "currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)" ),
            self.currentItemChanged )
    # b called from mainwindow method createLegendWidget
    def setCanvas( self, canvas ):
        """ Set the base canvas """
        self.canvas = canvas

    def showMenu( self, pos ):
        """ Show a context menu for the active layer in the legend """
        item = self.itemAt( pos )
        if item:
            if self.isLegendLayer( item ):
                # b a QTreeWidget method 'setCurrentItem
                self.setCurrentItem( item )
                self.menu = self.getMenu( item.isVect, item.canvasLayer )
                self.menu.popup( QtCore.QPoint( self.mapToGlobal( pos ).x() + 5, self.mapToGlobal( pos ).y() ) )

    def getMenu( self, isVect, canvasLayer ):
        """ Create a context menu for a layer """
        menu = QtGui.QMenu()
        menu.addAction( QtGui.QIcon( resources_prefix + "mActionZoomToLayer.png" ), "&Zoom to layer extent", self.zoomToLayer )
        
        # Removed by BE.  Not needed in CAPS.
        #self.actionOV = menu.addAction( "Show in &overview", self.showInOverview )
        #self.actionOV.setCheckable( True )
        #self.actionOV.setChecked( canvasLayer.isInOverview() )
        
        # b after all other GIS functions are done (i.e save project and edit tools)
        # b then modify the dlgLayerProperties dialog to change line thickness and 
        # b use alternate symbols 
        #menu.addSeparator()
        #menu.addAction( QtGui.QIcon( resources_prefix + "mIconProperties.png" ), "&Properties...", self.setLayerProperties )
        
        if isVect :
            # b see the layerSymbology slot for modifying symbology
            menu.addAction( QtGui.QIcon( resources_prefix + "symbology.png" ), "&Symbology...", self.layerSymbology )
            
        #Removed by BE.  Not needed in CAPS
        #menu.addAction( QtGui.QIcon( resources_prefix + "mActionFileOpen.png" ), "&Load style...", self.loadSymbologyFile )
        #menu.addAction( QtGui.QIcon( resources_prefix + "mActionFileSave.png" ), "Save s&tyle as...", self.saveSymbologyFile )

        menu.addSeparator()
        menu.addAction( QtGui.QIcon( resources_prefix + "collapse.png" ), "&Collapse all", self.collapseAll )
        menu.addAction( QtGui.QIcon( resources_prefix + "expand.png" ), "&Expand all", self.expandAll )

        menu.addSeparator()
        menu.addAction( QtGui.QIcon( resources_prefix + "mActionRemoveLayer.png" ), "&Remove layer", self.removeCurrentLayer )
        return menu

    # b This reimplemented event handler just sets the bMousePressedFlag 
    # b to true and then passes the event back to Qt to handle
    # b "event" is the info passed by the Qt class
    # b in this case QMouseEvent
    def mousePressEvent(self, event):
        """ Mouse press event to manage the layers drag """
        if ( event.button() == QtCore.Qt.LeftButton ):
            self.lastPressPos = event.pos()
            self.bMousePressedFlag = True
        QtGui.QTreeWidget.mousePressEvent( self, event )

    
    def mouseMoveEvent(self, event):
        """ Mouse move event to manage the layers drag """
        if ( self.bMousePressedFlag ):
            # Set the flag back such that the else if(mItemBeingMoved)
            # code part is passed during the next mouse moves
            self.bMousePressedFlag = False

            # Remember the item that has been pressed
            item = self.itemAt( self.lastPressPos )
            if ( item ):
                # b local "isLegendLayer" method just checks for a parent
                # b to establish that the "item" is a layer
                # b top level items in the QTreeWidget have no parent
                if ( self.isLegendLayer( item ) ):
                    # b local variable "self.itemBeingMoved"
                    self.itemBeingMoved = item
                    # b storeInitialPosition just calls getLayerIDs and stores in a local private variable
                    self.storeInitialPosition() # Store the initial layers order
                    self.setCursor( QtCore.Qt.SizeVerCursor )
                else:
                    self.setCursor( QtCore.Qt.ForbiddenCursor )
        elif ( self.itemBeingMoved ):
            p = QtCore.QPoint( event.pos() )
            self.lastPressPos = p

            # Change the cursor
            item = self.itemAt( p )
            origin = self.itemBeingMoved
            dest = item

            if not item:
                self.setCursor( QtCore.Qt.ForbiddenCursor )

            if ( item and ( item != self.itemBeingMoved ) ):
                if ( self.yCoordAboveCenter( dest, event.y() ) ): # Above center of the item
                    if self.isLegendLayer( dest ): # The item is a layer
                        if ( origin.nextSibling() != dest ):
                            self.moveItem( dest, origin )
                        self.setCurrentItem( origin )
                        # b just sets mouse cursor for the widget when layer is dragged
                        # b Qt has a large number of cursors available
                        self.setCursor( QtCore.Qt.SizeVerCursor )
                    else:
                        self.setCursor( QtCore.Qt.ForbiddenCursor )
                else: # Below center of the item
                    if self.isLegendLayer( dest ): # The item is a layer
                        if ( self.itemBeingMoved != dest.nextSibling() ):
                            self.moveItem( origin, dest )
                        self.setCurrentItem( origin )
                        self.setCursor( QtCore.Qt.SizeVerCursor )
                    else:
                        self.setCursor( QtCore.Qt.ForbiddenCursor )

    def mouseReleaseEvent( self, event ):
        """ Mouse release event to manage the layers drag """
        QtGui.QTreeWidget.mouseReleaseEvent( self, event )
        self.setCursor( QtCore.Qt.ArrowCursor )
        self.bMousePressedFlag = False

        if ( not self.itemBeingMoved ):
            #print "*** Legend drag: No itemBeingMoved ***"
            return

        dest = self.itemAt( event.pos() )
        origin = self.itemBeingMoved
        if ( ( not dest ) or ( not origin ) ): # Release out of the legend
            self.checkLayerOrderUpdate()
            return

        self.checkLayerOrderUpdate()
        self.itemBeingMoved = None

    def addLayerToLegend( self, canvasLayer ):
        """ Slot. Create and add a legend item based on a layer """
        # b canvasLayer is actually a layer not a canvas layer
        # passed from the a connection as a QgsMapLayerRegistry.instance()
        # b this turns the layer into a QgsMapCanvasLayer
        legendLayer = LegendItem( self, QgsMapCanvasLayer( canvasLayer ) )
        self.addLayer( legendLayer )

    # don't know why this isn't incorporated into the above method
    def addLayer( self, legendLayer ):
        """ Add a legend item to the legend widget """
        self.insertTopLevelItem ( 0, legendLayer )
        self.expandItem( legendLayer )
        self.setCurrentItem( legendLayer )
        self.updateLayerSet()

    # b this method sets visibility based on the checkState of the widget
    def updateLayerStatus( self, item ):
        """ Update the layer status """
        if ( item ):
            if self.isLegendLayer( item ): # Is the item a layer item?
                for i in self.layers:
                    if i.layer().getLayerID() == item.layerId:
                        if item.checkState( 0 ) == QtCore.Qt.Unchecked:
                            i.setVisible( False )
                        else:
                            i.setVisible( True )
                        self.canvas.setLayerSet( self.layers )
                        return
    # the "currentItem is the item selected, not the item checked
    def currentItemChanged( self, newItem, oldItem ):
        """ Slot. Capture a new currentItem and emit a SIGNAL to inform the new type 
            It could be used to activate/deactivate GUI buttons according the layer type
        """
        layerType = None

        if self.currentItem():
            if self.isLegendLayer( newItem ):
                layerType = newItem.canvasLayer.layer().type()
                self.canvas.setCurrentLayer( newItem.canvasLayer.layer() )
            else:
                layerType = newItem.parent().canvasLayer.layer().type()
                self.canvas.setCurrentLayer( newItem.parent().canvasLayer.layer() )

        self.emit( QtCore.SIGNAL( "activeLayerChanged" ), layerType )
    
    # b not sure why we couldn't just call zoomToLegendLayer from the calling action
    def zoomToLayer( self ):
        """ Slot. Manage the zoomToLayer action in the context Menu """
        self.zoomToLegendLayer( self.currentItem() )

    def removeCurrentLayer( self ):
        """ Slot. Manage the removeCurrentLayer action in the context Menu """
        QgsMapLayerRegistry.instance().removeMapLayer( self.currentItem().canvasLayer.layer().getLayerID() )
        self.removeLegendLayer( self.currentItem() )
        self.updateLayerSet()
    
    # b instantiates properties dialog with mainwindow as parent and passes a qgis layer
    # b disabled by BE.  Not needed in CAPS at the moment, but may be reactivated
    # b to set line thickness and color
    def setLayerProperties( self ):
        """ Slot. Open a dialog to set the layer properties """
        if self.currentItem():
            item = self.currentItem()
            self.dlgProperties = None
            self.dlgProperties = LayerProperties( self.pyQGisApp, item.canvasLayer.layer() )
            self.connect( self.dlgProperties, QtCore.SIGNAL( "layerNameChanged(PyQt_PyObject)" ), self.updateLayerName )
            self.dlgProperties.mostrar()

    # b called by the properties dialog to rename the layer
    # b We do not want to allow renaming in CAPS.  Base layers and editing layers
    # b should have fixed names in CAPS
    def updateLayerName( self, layer ):
        """ Update the layer name in the legend """
        for i in range( self.topLevelItemCount() ):
            if self.topLevelItem( i ).layerId == layer.getLayerID():
                layer.setLayerName( self.createUniqueName( unicode( layer.name() ) ) )
                self.topLevelItem( i ).setText( 0, layer.name() )
                break
    
    # b refer to this method to change line thickness and point symbols
    # b also refer to http://www.qgis.org/pyqgis-cookbook/vector.html#appearance-symbology-of-vector-layers
    def layerSymbology( self ):
        """ Change the features color of a vector layer """
        legendLayer = self.currentItem()
        
        if legendLayer.isVect == True:
            geom = legendLayer.canvasLayer.layer().geometryType() # QGis Geometry
            for i in self.layers: #self.layers is a list of QgsCanvasLayer objects
                if i.layer().getLayerID() == legendLayer.layerId:
                    if geom == 1: # Line
                        color = QtGui.QColorDialog.getColor( i.layer().renderer().symbols()[ 0 ].color(), self.pyQGisApp )
                    else:
                        color = QtGui.QColorDialog.getColor( i.layer().renderer().symbols()[ 0 ].fillColor(), self.pyQGisApp )
                    break

            if color.isValid():
                pm = QtGui.QPixmap()
                iconChild = QtGui.QIcon()
                if geom == 1: # Line
                    legendLayer.canvasLayer.layer().renderer().symbols()[ 0 ].setColor( color )                                       
                else:  
                    legendLayer.canvasLayer.layer().renderer().symbols()[ 0 ].setFillColor( color )

                self.refreshLayerSymbology( legendLayer.canvasLayer.layer() )

    # b not used in caps, but worth looking at sometime
    # b this is related to saving "scenarios" perhaps
    def loadSymbologyFile( self ):
        """ Load a QML file to set the layer style """
        settings = QtCore.QSettings()
        ultimaRutaQml = settings.value( 'Paths/qml', QtCore.QVariant('.') ).toString()
        symPath = QtGui.QFileDialog.getOpenFileName(self, "Open a style file (.qml)",
            ultimaRutaQml, "QGIS Layer Style File (*.qml)")

        if isfile( symPath ):
            res = self.currentItem().canvasLayer.layer().loadNamedStyle( symPath )

            if res[ 1 ]:
                self.refreshLayerSymbology( self.currentItem().canvasLayer.layer() )
                self.showMessage( self, 'Load style',
                    'The style file has been succesfully applied to the layer ' +
                    self.currentItem().text( 0 ) + '.', QtGui.QMessageBox.Information )
            else:
                self.showMessage( self, 'Load style',
                    'It was not possible to load the style file. Make sure the file ' \
                    'matches the structure of the layer ' + self.currentItem().text( 0 ) + '.',
                    QtGui.QMessageBox.Warning )

            symInfo = QtCore.QFileInfo( symPath )
            settings.setValue( 'Paths/qml', QtCore.QVariant( symInfo.absolutePath() ) )

    # not used in CAPS
    def saveSymbologyFile( self ):
        """ Save a QML file to set the layer style """
        settings = QtCore.QSettings()
        ultimaRutaQml = settings.value( 'Paths/qml', QtCore.QVariant('.') ).toString()
        symPath = QtGui.QFileDialog.getSaveFileName( self, "Save layer properties as a style file",
            ultimaRutaQml, "QGIS Layer Style File (*.qml)" )

        if symPath:
            symInfo = QtCore.QFileInfo( symPath )
            settings.setValue( 'Paths/qml', QtCore.QVariant( symInfo.absolutePath() ) )

            if not symPath.toUpper().endsWith( '.QML' ):
                symPath += '.qml'

            res = self.currentItem().canvasLayer.layer().saveNamedStyle( symPath )

            if res[ 1 ]:
                self.refreshLayerSymbology( self.currentItem().canvasLayer.layer() )
                self.showMessage( self, 'Save style file',
                    'The style file has been saved succesfully.',
                    QtGui.QMessageBox.Information )
            else:
                self.showMessage( self, 'Save style file',
                    res[ 0 ], QtGui.QMessageBox.Warning )

    # b OK. if I need to display a lot of messages, this is how to do it.
    def showMessage( self, parent, title, desc, icon=None ):
        """ Method to create messages to be shown to the users """
        msg = QtGui.QMessageBox( parent )
        msg.setText( desc )
        msg.setWindowTitle( title )
        if icon:
            msg.setIcon( icon )
        msg.addButton( 'Close', QtGui.QMessageBox.RejectRole )
        msg.exec_()

    def zoomToLegendLayer( self, legendLayer ):
        """ Zoom the map to a layer extent """
        for i in self.layers:
            if i.layer().getLayerID() == legendLayer.layerId:
                extent = i.layer().extent()
                extent.scale( 1.05 )
                self.canvas.setExtent( extent )
                self.canvas.refresh()
                break
    
    # b interesting that it needs to set a new "CurrentItem" before deleting
    def removeLegendLayer( self, legendLayer ):
        """ Remove a layer item in the legend """
        if self.topLevelItemCount() == 1:
            self.clear()
        else: # Manage the currentLayer before the remove
            indice = self.indexOfTopLevelItem( legendLayer )
            if indice == 0:
                newCurrentItem = self.topLevelItem( indice + 1 )
            else:
                newCurrentItem = self.topLevelItem( indice - 1 )

            self.setCurrentItem( newCurrentItem )
            self.takeTopLevelItem( self.indexOfTopLevelItem( legendLayer ) )

    # b note the "blockSignals" Qt method! 
    def setStatusForAllLayers( self, visible ):
        """ Show/Hide all layers in the map """
        # Block SIGNALS to avoid setLayerSet for each item status changed
        self.blockSignals( True )

        status = QtCore.Qt.Checked if visible else QtCore.Qt.Unchecked
        for i in range( self.topLevelItemCount() ):
            self.topLevelItem( i ).setCheckState( 0, status )
            self.topLevelItem( i ).canvasLayer.setVisible( visible )

        self.blockSignals( False )

        self.updateLayerSet() # Finally, update the layer set

   # b this method does not appear to be implemented in CAPS
    def removeAll( self ):
        """ Remove all legend items """
        self.clear()
        self.updateLayerSet()

    # b don't need overview for CAPS
    def allLayersInOverview( self, visible ):
        """ Show/Hide all layers in Overview """
        for i in range( self.topLevelItemCount() ):
            self.topLevelItem( i ).canvasLayer.setInOverview( visible )
        self.updateLayerSet()
        self.canvas.updateOverview()

    def showInOverview( self ):
        """ Toggle the active layer in Overview Map """
        self.currentItem().canvasLayer.setInOverview( not( self.currentItem().canvasLayer.isInOverview() ) )
        self.updateLayerSet()
        self.canvas.updateOverview()
    
    # b updates the QgsMapCanvas layer set
    def updateLayerSet( self ):
        """ Update the LayerSet and set it to canvas """
        self.layers = self.getLayerSet()
        self.canvas.setLayerSet( self.layers )
    
    # b used to set the variable self.layers equal to the layer list
    def getLayerSet( self ):
        """ Get the LayerSet by reading the layer items in the legend """
        layers = []
        # b in the LegendItem class the QTreeWidgetItems are 
        # b set to be qgis QgsCanvasLayers with method
        # b addLayerToLegend
        for i in range( self.topLevelItemCount() ):
            layers.append( self.topLevelItem( i ).canvasLayer )
        return layers
    
    # this returns the active layer even if the layer symbols icons are selected
    def activeLayer( self ):
        """ Return the selected layer """
        if self.currentItem():
            if self.isLegendLayer( self.currentItem() ):
                return self.currentItem().canvasLayer
            else:
                return self.currentItem().parent().canvasLayer
        else:
            return None

    def collapseAll( self ):
        """ Collapse all layer items in the legend """
        for i in range( self.topLevelItemCount() ):
            item = self.topLevelItem( i )
            self.collapseItem( item )

    def expandAll( self ):
        """ Expand all layer items in the legend """
        for i in range( self.topLevelItemCount() ):
            item = self.topLevelItem( i )
            self.expandItem( item )

    # b interesting method because of its simplicity.  A top level item in
    # b QTreeWidget doesn't have a parent.  So this returns "True" if the 
    # layer is a top level item and thus a qgis layer
    def isLegendLayer( self, item ):
        """ Check if a given item is a layer item """
        return not item.parent()

    # related to the drag and drop functionality of the layer pane
    def storeInitialPosition( self ):
        """ Store the layers order """
        self.__beforeDragStateLayers = self.getLayerIDs()

    # b OK. So I can easily get a list of layer Id's
    def getLayerIDs( self ):
        """ Return a list with the layers ids """
        layers = []
        for i in range( self.topLevelItemCount() ):
            item = self.topLevelItem( i )
            layers.append( item.layerId )
        return layers

    def nextSibling( self, item ):
        """ Return the next layer item based on a given item """
        for i in range( self.topLevelItemCount() ):
            if item.layerId == self.topLevelItem( i ).layerId:
                break
        if i < self.topLevelItemCount():                                            
            return self.topLevelItem( i + 1 )
        else:
            return None

    def moveItem( self, itemToMove, afterItem ):
        """ Move the itemToMove after the afterItem in the legend """
        itemToMove.storeAppearanceSettings() # Store settings in the moved item
        self.takeTopLevelItem( self.indexOfTopLevelItem( itemToMove ) )
        self.insertTopLevelItem( self.indexOfTopLevelItem( afterItem ) + 1, itemToMove )
        itemToMove.restoreAppearanceSettings() # Apply the settings again

    def checkLayerOrderUpdate( self ):
        """
            Check if the initial layers order is equal to the final one.
            This is used to refresh the legend in the release event.
        """
        self.__afterDragStateLayers = self.getLayerIDs()
        if self.__afterDragStateLayers != self.__beforeDragStateLayers:
            self.updateLayerSet()
            #print "*** Drag legend layer done. Updating canvas ***"

    def yCoordAboveCenter( self, legendItem, ycoord ):
        """
            Return a bool to know if the ycoord is in the above center of the legendItem

            legendItem: The base item to get the above center and the below center
            ycoord: The coordinate of the comparison
        """
        rect = self.visualItemRect( legendItem )
        height = rect.height()
        top = rect.top()
        mid = top + ( height / 2 )
        if ( ycoord > mid ): # Bottom, remember the y-coordinate increases downwards
            return False
        else: # Top
            return True

    def normalizeLayerName( self, name ):
        """ Create an alias to put in the legend and avoid to repeat names """
        # Remove the extension
        if len( name ) > 4:
            if name[ -4 ] == '.':
                name = name[ :-4 ]
        return self.createUniqueName( name )

    def createUniqueName( self, name ):
        """ Avoid to repeat layers names """
        import re
        name_validation = re.compile( "\s\(\d+\)$", re.UNICODE ) # Strings like " (1)"

        bRepetida = True
        i = 1
        while bRepetida:
            bRepetida = False

            # If necessary add a sufix like " (1)" to avoid to repeat names in the legend
            for j in range( self.topLevelItemCount() ):
                item = self.topLevelItem( j )
                if item.text( 0 ) == name:
                    bRepetida = True
                    if name_validation.search( name ): # The name already have numeration
                        name = name[ :-4 ]  + ' (' + str( i ) + ')'
                    else: # Add numeration because the name doesn't have it
                        name = name + ' (' + str( i ) + ')'
                    i += 1
        return name

    def refreshLayerSymbology( self, layer ):
        """ Refresh the layer symbology. For plugins. """
        for i in range( self.topLevelItemCount() ):
            item = self.topLevelItem( i )
            if layer.getLayerID() == item.layerId:
                item.vectorLayerSymbology( layer )
                self.canvas.refresh()
                break

    def removeLayers( self, layerIds ):
        """ Remove layers from their ids. For plugins. """
        for layerId in layerIds:
            QgsMapLayerRegistry.instance().removeMapLayer( layerId )

            # Remove the legend item
            for i in range( self.topLevelItemCount() ):
                item = self.topLevelItem( i )
                if layerId == item.layerId:
                    self.removeLegendLayer( item )
                    break

        self.updateLayerSet()
Example #2
0
class Legend( QTreeWidget ):
    """
      Provide a widget that manages map layers and their symbology as tree items
    """
    def __init__( self, parent ):
        QTreeWidget.__init__( self, parent )

        self.pyQGisApp = parent
        self.canvas = None
        self.layers = self.getLayerSet()

        self.bMousePressedFlag = False
        self.itemBeingMoved = None

        # QTreeWidget properties
        self.setSortingEnabled( False )
        self.setDragEnabled( False )
        self.setAutoScroll( True )
        self.setHeaderHidden( True )
        self.setRootIsDecorated( True )
        self.setContextMenuPolicy( Qt.CustomContextMenu )

        self.connect( self, SIGNAL( "customContextMenuRequested(QPoint)" ),
            self.showMenu )
        self.connect( QgsMapLayerRegistry.instance(), SIGNAL("layerWasAdded(QgsMapLayer *)"),
            self.addLayerToLegend)
        self.connect( QgsMapLayerRegistry.instance(), SIGNAL( "removedAll()" ),
            self.removeAll )
        self.connect( self, SIGNAL("itemChanged(QTreeWidgetItem *,int)"),
            self.updateLayerStatus )
        self.connect( self, SIGNAL( "currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)" ),
            self.currentItemChanged )

    def setCanvas( self, canvas ):
        """ Set the base canvas """
        self.canvas = canvas

    def showMenu( self, pos ):
        """ Show a context menu for the active layer in the legend """
        item = self.itemAt( pos )
        if item:
            if self.isLegendLayer( item ):
                self.setCurrentItem( item )
                self.menu = self.getMenu( item.isVect, item.canvasLayer )
                self.menu.popup( QPoint( self.mapToGlobal( pos ).x() + 5, self.mapToGlobal( pos ).y() ) )

    def getMenu( self, isVect, canvasLayer ):
        """ Create a context menu for a layer """
        menu = QMenu()
        menu.addAction( QIcon( resources_prefix + "mActionZoomToLayer.png" ), "&Zoom to layer extent", self.zoomToLayer )

        self.actionOV = menu.addAction( "Show in &overview", self.showInOverview )
        self.actionOV.setCheckable( True )
        self.actionOV.setChecked( canvasLayer.isInOverview() )

        menu.addSeparator()
        menu.addAction( QIcon( resources_prefix + "mIconProperties.png" ), "&Properties...", self.setLayerProperties )
        if isVect :
            menu.addAction( QIcon( resources_prefix + "symbology.png" ), "&Symbology...", self.layerSymbology )
            menu.addAction( QIcon( resources_prefix + "mActionFileOpen.png" ), "&Load style...", self.loadSymbologyFile )
            menu.addAction( QIcon( resources_prefix + "mActionFileSave.png" ), "Save s&tyle as...", self.saveSymbologyFile )

        menu.addSeparator()
        menu.addAction( QIcon( resources_prefix + "collapse.png" ), "&Collapse all", self.collapseAll )
        menu.addAction( QIcon( resources_prefix + "expand.png" ), "&Expand all", self.expandAll )

        menu.addSeparator()
        menu.addAction( QIcon( resources_prefix + "removeLayer.png" ), "&Remove layer", self.removeCurrentLayer )
        return menu

    def mousePressEvent(self, event):
        """ Mouse press event to manage the layers drag """
        if ( event.button() == Qt.LeftButton ):
            self.lastPressPos = event.pos()
            self.bMousePressedFlag = True
        QTreeWidget.mousePressEvent( self, event )

    def mouseMoveEvent(self, event):
        """ Mouse move event to manage the layers drag """
        if ( self.bMousePressedFlag ):
            # Set the flag back such that the else if(mItemBeingMoved)
            # code part is passed during the next mouse moves
            self.bMousePressedFlag = False

            # Remember the item that has been pressed
            item = self.itemAt( self.lastPressPos )
            if ( item ):
                if ( self.isLegendLayer( item ) ):
                    self.itemBeingMoved = item
                    self.storeInitialPosition() # Store the initial layers order
                    self.setCursor( Qt.SizeVerCursor )
                else:
                    self.setCursor( Qt.ForbiddenCursor )
        elif ( self.itemBeingMoved ):
            p = QPoint( event.pos() )
            self.lastPressPos = p

            # Change the cursor
            item = self.itemAt( p )
            origin = self.itemBeingMoved
            dest = item

            if not item:
                self.setCursor( Qt.ForbiddenCursor )

            if ( item and ( item != self.itemBeingMoved ) ):
                if ( self.yCoordAboveCenter( dest, event.y() ) ): # Above center of the item
                    if self.isLegendLayer( dest ): # The item is a layer
                        if ( origin.nextSibling() != dest ):
                            self.moveItem( dest, origin )
                        self.setCurrentItem( origin )
                        self.setCursor( Qt.SizeVerCursor )
                    else:
                        self.setCursor( Qt.ForbiddenCursor )
                else: # Below center of the item
                    if self.isLegendLayer( dest ): # The item is a layer
                        if ( self.itemBeingMoved != dest.nextSibling() ):
                            self.moveItem( origin, dest )
                        self.setCurrentItem( origin )
                        self.setCursor( Qt.SizeVerCursor )
                    else:
                        self.setCursor( Qt.ForbiddenCursor )

    def mouseReleaseEvent( self, event ):
        """ Mouse release event to manage the layers drag """
        QTreeWidget.mouseReleaseEvent( self, event )
        self.setCursor( Qt.ArrowCursor )
        self.bMousePressedFlag = False

        if ( not self.itemBeingMoved ):
            #print "*** Legend drag: No itemBeingMoved ***"
            return

        dest = self.itemAt( event.pos() )
        origin = self.itemBeingMoved
        if ( ( not dest ) or ( not origin ) ): # Release out of the legend
            self.checkLayerOrderUpdate()
            return

        self.checkLayerOrderUpdate()
        self.itemBeingMoved = None

    def addLayerToLegend( self, canvasLayer ):
        """ Slot. Create and add a legend item based on a layer """
        legendLayer = LegendItem( self, QgsMapCanvasLayer( canvasLayer ) )
        self.addLayer( legendLayer )

    def addLayer( self, legendLayer ):
        """ Add a legend item to the legend widget """
        self.insertTopLevelItem ( 0, legendLayer )
        self.expandItem( legendLayer )
        self.setCurrentItem( legendLayer )
        #self.updateLayerSet()

    def updateLayerStatus( self, item ):
        """ Update the layer status """
        if ( item ):
            if self.isLegendLayer( item ): # Is the item a layer item?
                for i in self.layers:
                    if i.layer().getLayerID() == item.layerId:
                        if item.checkState( 0 ) == Qt.Unchecked:
                            i.setVisible( False )
                        else:
                            i.setVisible( True )
                        self.canvas.setLayerSet( self.layers )
                        return

    def currentItemChanged( self, newItem, oldItem ):
        """ Slot. Capture a new currentItem and emit a SIGNAL to inform the new type 
            It could be used to activate/deactivate GUI buttons according the layer type
        """
        layerType = None

        if self.currentItem():
            if self.isLegendLayer( newItem ):
                layerType = newItem.canvasLayer.layer().type()
                self.canvas.setCurrentLayer( newItem.canvasLayer.layer() )
            else:
                layerType = newItem.parent().canvasLayer.layer().type()
                self.canvas.setCurrentLayer( newItem.parent().canvasLayer.layer() )

        self.emit( SIGNAL( "activeLayerChanged" ), layerType )

    def zoomToLayer( self ):
        """ Slot. Manage the zoomToLayer action in the context Menu """
        self.zoomToLegendLayer( self.currentItem() )

    def removeCurrentLayer( self ):
        """ Slot. Manage the removeCurrentLayer action in the context Menu """
        QgsMapLayerRegistry.instance().removeMapLayer( self.currentItem().canvasLayer.layer().getLayerID() )
        self.removeLegendLayer( self.currentItem() )
        self.updateLayerSet()

    def setLayerProperties( self ):
        """ Slot. Open a dialog to set the layer properties """
        if self.currentItem():
            item = self.currentItem()
            self.dlgProperties = None
            self.dlgProperties = LayerProperties( self.pyQGisApp, item.canvasLayer.layer() )
            self.connect( self.dlgProperties, SIGNAL( "layerNameChanged(PyQt_PyObject)" ), self.updateLayerName )
            self.dlgProperties.mostrar()

    def updateLayerName( self, layer ):
        """ Update the layer name in the legend """
        for i in range( self.topLevelItemCount() ):
            if self.topLevelItem( i ).layerId == layer.getLayerID():
                layer.setLayerName( self.createUniqueName( unicode( layer.name() ) ) )
                self.topLevelItem( i ).setText( 0, layer.name() )
                break

    def layerSymbology( self ):
        """ Change the features color of a vector layer """
        legendLayer = self.currentItem()
        
        if legendLayer.isVect == True:
            geom = legendLayer.canvasLayer.layer().geometryType() # QGis Geometry
            for i in self.layers:
                if i.layer().getLayerID() == legendLayer.layerId:
                    if geom == 1: # Line
                        color = QColorDialog.getColor( i.layer().renderer().symbols()[ 0 ].color(), self.pyQGisApp )
                    else:
                        color = QColorDialog.getColor( i.layer().renderer().symbols()[ 0 ].fillColor(), self.pyQGisApp )
                    break

            if color.isValid():
                pm = QPixmap()
                iconChild = QIcon()
                if geom == 1: # Line
                    legendLayer.canvasLayer.layer().renderer().symbols()[ 0 ].setColor( color )                                       
                else:  
                    legendLayer.canvasLayer.layer().renderer().symbols()[ 0 ].setFillColor( color )

                self.refreshLayerSymbology( legendLayer.canvasLayer.layer() )

    def loadSymbologyFile( self ):
        """ Load a QML file to set the layer style """
        settings = QSettings()
        ultimaRutaQml = settings.value( 'Paths/qml', QVariant('.') ).toString()
        symPath = QFileDialog.getOpenFileName(self, "Open a style file (.qml)",
            ultimaRutaQml, "QGIS Layer Style File (*.qml)")

        if isfile( symPath ):
            res = self.currentItem().canvasLayer.layer().loadNamedStyle( symPath )

            if res[ 1 ]:
                self.refreshLayerSymbology( self.currentItem().canvasLayer.layer() )
                self.showMessage( self, 'Load style',
                    'The style file has been succesfully applied to the layer ' +
                    self.currentItem().text( 0 ) + '.', QMessageBox.Information )
            else:
                self.showMessage( self, 'Load style',
                    'It was not possible to load the style file. Make sure the file ' \
                    'matches the structure of the layer ' + self.currentItem().text( 0 ) + '.',
                    QMessageBox.Warning )

            symInfo = QFileInfo( symPath )
            settings.setValue( 'Paths/qml', QVariant( symInfo.absolutePath() ) )

    def saveSymbologyFile( self ):
        """ Save a QML file to set the layer style """
        settings = QSettings()
        ultimaRutaQml = settings.value( 'Paths/qml', QVariant('.') ).toString()
        symPath = QFileDialog.getSaveFileName( self, "Save layer properties as a style file",
            ultimaRutaQml, "QGIS Layer Style File (*.qml)" )

        if symPath:
            symInfo = QFileInfo( symPath )
            settings.setValue( 'Paths/qml', QVariant( symInfo.absolutePath() ) )

            if not symPath.toUpper().endsWith( '.QML' ):
                symPath += '.qml'

            res = self.currentItem().canvasLayer.layer().saveNamedStyle( symPath )

            if res[ 1 ]:
                self.refreshLayerSymbology( self.currentItem().canvasLayer.layer() )
                self.showMessage( self, 'Save style file',
                    'The style file has been saved succesfully.',
                    QMessageBox.Information )
            else:
                self.showMessage( self, 'Save style file',
                    res[ 0 ], QMessageBox.Warning )

    def showMessage( self, parent, title, desc, icon=None ):
        """ Method to create messages to be shown to the users """
        msg = QMessageBox( parent )
        msg.setText( desc )
        msg.setWindowTitle( title )
        if icon:
            msg.setIcon( icon )
        msg.addButton( 'Close', QMessageBox.RejectRole )
        msg.exec_()

    def zoomToLegendLayer( self, legendLayer ):
        """ Zoom the map to a layer extent """
        for i in self.layers:
            if i.layer().getLayerID() == legendLayer.layerId:
                extent = i.layer().extent()
                extent.scale( 1.05 )
                self.canvas.setExtent( extent )
                self.canvas.refresh()
                break

    def removeLegendLayer( self, legendLayer ):
        """ Remove a layer item in the legend """
        if self.topLevelItemCount() == 1:
            self.clear()
        else: # Manage the currentLayer before the remove
            indice = self.indexOfTopLevelItem( legendLayer )
            if indice == 0:
                newCurrentItem = self.topLevelItem( indice + 1 )
            else:
                newCurrentItem = self.topLevelItem( indice - 1 )

            self.setCurrentItem( newCurrentItem )
            self.takeTopLevelItem( self.indexOfTopLevelItem( legendLayer ) )

    def setStatusForAllLayers( self, visible ):
        """ Show/Hide all layers in the map """
        # Block SIGNALS to avoid setLayerSet for each item status changed
        self.blockSignals( True )

        status = Qt.Checked if visible else Qt.Unchecked
        for i in range( self.topLevelItemCount() ):
            self.topLevelItem( i ).setCheckState( 0, status )
            self.topLevelItem( i ).canvasLayer.setVisible( visible )

        self.blockSignals( False )

        self.updateLayerSet() # Finally, update the layer set

    def removeAll( self ):
        """ Remove all legend items """
        self.clear()
        self.updateLayerSet()

    def allLayersInOverview( self, visible ):
        """ Show/Hide all layers in Overview """
        for i in range( self.topLevelItemCount() ):
            self.topLevelItem( i ).canvasLayer.setInOverview( visible )
        self.updateLayerSet()
        self.canvas.updateOverview()

    def showInOverview( self ):
        """ Toggle the active layer in Overview Map """
        self.currentItem().canvasLayer.setInOverview( not( self.currentItem().canvasLayer.isInOverview() ) )
        self.updateLayerSet()
        self.canvas.updateOverview()

    def updateLayerSet( self ):
        """ Update the LayerSet and set it to canvas """
        self.layers = self.getLayerSet()
        self.canvas.setLayerSet( self.layers )

    def getLayerSet( self ):
        """ Get the LayerSet by reading the layer items in the legend """
        layers = []
        for i in range( self.topLevelItemCount() ):
            layers.append( self.topLevelItem( i ).canvasLayer )
        return layers

    def activeLayer( self ):
        """ Return the selected layer """
        if self.currentItem():
            if self.isLegendLayer( self.currentItem() ):
                return self.currentItem().canvasLayer
            else:
                return self.currentItem().parent().canvasLayer
        else:
            return None

    def collapseAll( self ):
        """ Collapse all layer items in the legend """
        for i in range( self.topLevelItemCount() ):
            item = self.topLevelItem( i )
            self.collapseItem( item )

    def expandAll( self ):
        """ Expand all layer items in the legend """
        for i in range( self.topLevelItemCount() ):
            item = self.topLevelItem( i )
            self.expandItem( item )

    def isLegendLayer( self, item ):
        """ Check if a given item is a layer item """
        return not item.parent()

    def storeInitialPosition( self ):
        """ Store the layers order """
        self.__beforeDragStateLayers = self.getLayerIDs()

    def getLayerIDs( self ):
        """ Return a list with the layers ids """
        layers = []
        for i in range( self.topLevelItemCount() ):
            item = self.topLevelItem( i )
            layers.append( item.layerId )
        return layers

    def nextSibling( self, item ):
        """ Return the next layer item based on a given item """
        for i in range( self.topLevelItemCount() ):
            if item.layerId == self.topLevelItem( i ).layerId:
                break
        if i < self.topLevelItemCount():                                            
            return self.topLevelItem( i + 1 )
        else:
            return None

    def moveItem( self, itemToMove, afterItem ):
        """ Move the itemToMove after the afterItem in the legend """
        itemToMove.storeAppearanceSettings() # Store settings in the moved item
        self.takeTopLevelItem( self.indexOfTopLevelItem( itemToMove ) )
        self.insertTopLevelItem( self.indexOfTopLevelItem( afterItem ) + 1, itemToMove )
        itemToMove.restoreAppearanceSettings() # Apply the settings again

    def checkLayerOrderUpdate( self ):
        """
            Check if the initial layers order is equal to the final one.
            This is used to refresh the legend in the release event.
        """
        self.__afterDragStateLayers = self.getLayerIDs()
        if self.__afterDragStateLayers != self.__beforeDragStateLayers:
            self.updateLayerSet()
            #print "*** Drag legend layer done. Updating canvas ***"

    def yCoordAboveCenter( self, legendItem, ycoord ):
        """
            Return a bool to know if the ycoord is in the above center of the legendItem

            legendItem: The base item to get the above center and the below center
            ycoord: The coordinate of the comparison
        """
        rect = self.visualItemRect( legendItem )
        height = rect.height()
        top = rect.top()
        mid = top + ( height / 2 )
        if ( ycoord > mid ): # Bottom, remember the y-coordinate increases downwards
            return False
        else: # Top
            return True

    def normalizeLayerName( self, name ):
        """ Create an alias to put in the legend and avoid to repeat names """
        # Remove the extension
        if len( name ) > 4:
            if name[ -4 ] == '.':
                name = name[ :-4 ]
        return self.createUniqueName( name )

    def createUniqueName( self, name ):
        """ Avoid to repeat layers names """
        import re
        name_validation = re.compile( "\s\(\d+\)$", re.UNICODE ) # Strings like " (1)"

        bRepetida = True
        i = 1
        while bRepetida:
            bRepetida = False

            # If necessary add a sufix like " (1)" to avoid to repeat names in the legend
            for j in range( self.topLevelItemCount() ):
                item = self.topLevelItem( j )
                if item.text( 0 ) == name:
                    bRepetida = True
                    if name_validation.search( name ): # The name already have numeration
                        name = name[ :-4 ]  + ' (' + str( i ) + ')'
                    else: # Add numeration because the name doesn't have it
                        name = name + ' (' + str( i ) + ')'
                    i += 1
        return name

    def refreshLayerSymbology( self, layer ):
        """ Refresh the layer symbology. For plugins. """
        for i in range( self.topLevelItemCount() ):
            item = self.topLevelItem( i )
            if layer.getLayerID() == item.layerId:
                item.vectorLayerSymbology( layer )
                self.canvas.refresh()
                break

    def removeLayers( self, layerIds ):
        """ Remove layers from their ids. For plugins. """
        for layerId in layerIds:
            QgsMapLayerRegistry.instance().removeMapLayer( layerId )

            # Remove the legend item
            for i in range( self.topLevelItemCount() ):
                item = self.topLevelItem( i )
                if layerId == item.layerId:
                    self.removeLegendLayer( item )
                    break

        self.updateLayerSet()