from qgis.PyQt.QtCore import (QCoreApplication, QEvent, QFile, QFileInfo, QObject, QRegularExpression, QSize, Qt, QTimer)
from qgis.PyQt.QtGui import (QBrush, QColor, QCursor, QFont, QFontMetrics, QIcon, QKeySequence, QPalette, QPixmap, QStandardItem, QStandardItemModel)
from qgis.PyQt.QtWidgets import (QAbstractItemView, QAction, QApplication, QButtonGroup, QCheckBox, QComboBox, QDialog, QFileDialog, QFrame, QGroupBox, QInputDialog,
                                 QLabel, QLineEdit, QListView, QListWidget, QMenu, QMessageBox, QPushButton, QProgressBar, QProxyStyle, QStyle, QStyledItemDelegate,
                                 QSizePolicy, QTableView, QTextEdit, QToolBar, QToolTip, QTreeWidget, QTreeWidgetItem, QVBoxLayout)

from qgis.PyQt.QtPrintSupport import (QPrintPreviewDialog, QPrinter)
from qgis.core import (Qgis, QgsApplication, QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsFeature, QgsFeatureRequest, QgsGeometry, QgsLayerTree,
                       QgsLayerTreeGroup, QgsPointXY, QgsProject, QgsRasterLayer, QgsSpatialIndex, QgsVectorLayer)
from qgis.core import (QgsExpression, QgsExpressionContext, QgsExpressionContextScope, QgsMapLayerProxyModel)
from qgis.gui import (QgsMapCanvas, QgsMapCanvasItem, QgsMapTool, QgsMapToolPan, QgsMapToolZoom, QgsRubberBand)
from os.path import (basename, dirname, exists, isfile, join, splitext)
from sys import path as sysPath
from xml.dom.minidom import parseString


from ast import literal_eval
from webbrowser import open as webbrowseropen
from urllib.parse import urlparse
from urllib.parse import parse_qs

from qgis.core import (QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsLayerTreeLayer, QgsLayerTreeNode)

try:
    from osgeo.gdal import Open as gdalopen
    from osgeo.gdal import GetDriver as gdal_GetDriver
    from osgeo.gdal import GetDriverCount as gdal_GetDriverCount
    from osgeo.gdal import GetDriverByName as gdal_GetDriverByName
    from osgeo.gdal import (DMD_LONGNAME, DMD_MIMETYPE, DMD_EXTENSION)
    from osgeo.ogr import GetDriver as ogr_GetDriver
    from osgeo.ogr import GetDriverCount as ogr_GetDriverCount
except:
    from gdal import Open as gdalopen
    from gdal import GetDriver as gdal_GetDriver
    from gdal import GetDriverCount as gdal_GetDriverCount
    from gdal import GetDriverByName as gdal_GetDriverByName
    from gdal import (DMD_LONGNAME, DMD_MIMETYPE, DMD_EXTENSION)
    from ogr import GetDriver as ogr_GetDriver
    from ogr import GetDriverCount as ogr_GetDriverCount

import os
from math import (floor, log, pow)

#NEW 3.6.4
def zoomPreviousExtent(posExtent, listHistoExtents, mapCanvas):
    if posExtent > 0 :
       mapCanvas.blockSignals(True)
       posExtent = posExtent-1 
       mapCanvas.setExtent(listHistoExtents[posExtent])
       mapCanvas.refresh()       
       mapCanvas.blockSignals(False)
    return posExtent
       
def zoomNextExtent(posExtent, listHistoExtents, mapCanvas):
    if posExtent < len(listHistoExtents)-1 :
       mapCanvas.blockSignals(True)
       posExtent = posExtent + 1
       mapCanvas.setExtent(listHistoExtents[posExtent])
       mapCanvas.refresh()
       mapCanvas.blockSignals(False)
    return posExtent    



def iff(a, b, c):
    if a: return b
    else: return c
    
def cleanUri(uri):
    pos = uri.find("http")
    startUri = uri[:pos]      

    uri = uri.replace(startUri, "")
    uri = uri.replace("{", "%7B")
    uri = uri.replace("}", "%7D")
    uri = uri.replace("=", "%3D")
    uri = uri.replace("&", "%26")

    uri = startUri+uri
        
    return uri

#NEW 3.7.0
class CheckableComboBox(QComboBox):

    # Subclass Delegate to increase item height
    class Delegate(QStyledItemDelegate):
        def sizeHint(self, option, index):
            size = super().sizeHint(option, index)
            size.setHeight(24)
            return size

    def __init__(self, iface, parent, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        self.iface = iface
        self.parent = parent

        self.currentLayer = None
        self.selLayers = []
        self.index = None
        self.pathIconGridRaster = getThemeIcon("gridrastersel.png").replace("\\", "/")
        self.iconGridRaster = QIcon(self.pathIconGridRaster)
        
        self.canvas = self.iface.mapCanvas()
        try : self.rbCanvas = QgsRubberBand(self.canvas, True)
        except : self.rbCanvas = QgsRubberBand(self.canvas)
        self.rbCanvas.setColor(QColor(255, 0, 0, 255))
        self.rbCanvas.setWidth(3)
        self.rbCanvas.setFillColor(QColor(255, 0, 0, 100))

        self.projectAlr = self.parent.projectAlr

        self.mapViewerWmsGrid = QgsMapCanvas(self)
        self.mapViewerWmsGrid.setObjectName("mapViewerWmsGrid")
        
        self.crs = QgsCoordinateReferenceSystem()
        self.crs.createFromOgcWmsCrs("EPSG:2154")
        self.transformer = QgsCoordinateTransform() 
        self.transformer.setSourceCrs(self.crs)

        self.rbTool = QgsRubberBand(self.mapViewerWmsGrid, Qgis.GeometryType.Point)

        self.setMinimumWidth(340)
        self.setMaximumWidth(340)
        
        self.model = QStandardItemModel()
        self.view = QTableView()
        self.view.setModel( self.model )
        self.view.horizontalHeader().setVisible(False)
        self.view.verticalHeader().setVisible(False)
        self.view.setColumnHidden(1, True)
        self.view.horizontalHeader().setDefaultSectionSize(400)
        self.view.verticalHeader().setDefaultSectionSize(20)
        #self.view.setStyleSheet("QTableView {min-width: 400px; max-width: 500px; selection-background-color: qlineargradient(x1: 0, y1: 0, x2: 0.5, y2: 0.5, stop: 0 #FFFFFF, stop: 1 #000000);}")
        self.view.setShowGrid(False)
        
        self.setEditable(False)
        self.setEnabled(True)
        self.setModel(self.model)
        self.setView(self.view)

        self.barInfo = self.iface.messageBar() 
        self.barInfo.setSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed)

        
        #CREATE THE MENU
        self.menu = QMenu("Actions")
        
        self.actionReloadGrids = QAction(QIcon(getThemeIcon("reload.png")), "Reload grid file", self.iface.mainWindow())
        self.actionReloadGrids.setObjectName("actionReloadGrids")
        self.actionReloadGrids.setShortcut(QKeySequence("SHIFT+R"))
        
        self.actionCheckGrids = QAction(QIcon(getThemeIcon("checked.png")), "Check the grids", self.iface.mainWindow())
        self.actionCheckGrids.setObjectName("actionCheckGrids")
        self.actionCheckGrids.setShortcut(QKeySequence("SHIFT+C"))

        self.actionCleanSelectedGrids = QAction(QIcon(getThemeIcon("deleteall.png")), "Clean the selection", self.iface.mainWindow())
        self.actionCleanSelectedGrids.setObjectName("actionCleanSelectedGrids")
        self.actionCleanSelectedGrids.setShortcut(QKeySequence("SHIFT+X"))

        self.actionReloadGrids.triggered.connect(self.parent.loadXmlGridFile)
        self.actionCheckGrids.triggered.connect(self.parent.checkGridFiles)
        self.actionCleanSelectedGrids.triggered.connect(self.delSelectedGrids)

        self.actionZoomToGroup = QAction(QIcon(getThemeIcon("zoomtogroup.png")), translateMsg("Zoom to the group"), self.iface.mainWindow())
        self.actionZoomToGroup.setObjectName("actionZoomToGroup")
        self.actionZoomToGroup.setShortcut(QKeySequence("SHIFT+Z"))

        self.actionCleanGroup = QAction(QIcon(getThemeIcon("cleangroup.png")), translateMsg("Clean group"), self.iface.mainWindow())
        self.actionCleanGroup.setObjectName("actionCleanGroup")
        self.actionCleanGroup.setShortcut(QKeySequence("SHIFT+X"))

        self.actionCleanLayers = QAction(QIcon(getThemeIcon("cleanlayers.png")), "%s\n(CTRL+SHIFT+X)" % translateMsg("Remove layers"), self.iface.mainWindow())
        self.actionCleanLayers.setObjectName("actionCleanLayers")
        self.actionCleanLayers.setCheckable(True)
        self.actionCleanLayers.setWhatsThis("actionCleanLayers")
        self.actionCleanLayers.setShortcut(QKeySequence("CTRL+SHIFT+X"))
        #self.actionCleanLayers.triggered.connect(self.parent.updateToolRemove)


        self.toolsHotLink = QMenu("%s" % translateMsg("Load a raster file"))

        dicoCtrls = {"actionToolHotLinkRaster" : ("selone.png", "Load a raster file"),
                     "actionToolHotLinkRasterRect" : ("selrect.png", "Selection with a rectangle"),
                     "actionToolHotLinkRasterFree" : ("selfree.png", "Selection with free hand"),
                     "actionToolHotLinkRasterPoly" : ("selpoly.png", "Selection with polygon"),
                     "actionToolHotLinkRasterCircleByCenter" : ("selcenter.png", "Selection with circle (center)"),
                     "actionToolHotLinkRasterCircleByRadius" : ("selradius.png", "Selection with circle (radius)")
                     }

        for key in dicoCtrls :
            icon = getThemeIcon(dicoCtrls[key][0])  
            text = translateMsg(dicoCtrls[key][1])
            action = QAction(QIcon(icon), text, self.toolsHotLink)
            action.setObjectName(key)
            self.toolsHotLink.addAction(action)
            action.triggered.connect(self.hotLinkRaster)
            setattr(self, key, action)
            if key in ("actionToolHotLinkRaster", "actionToolHotLinkRasterPoly") : self.toolsHotLink.addSeparator()
        
        self.toolHotLinkRaster = toolHotLink(self, self.canvas, False, False)
        self.toolHotLinkRemoveRaster = toolHotLink(self, self.canvas, False, True)
        self.toolHotLinkRasterRect = toolHotLinkRect(self, self.canvas, False)
        self.toolHotLinkRasterFree = toolHotLinkFree(self, self.canvas, False)
        self.toolHotLinkRasterPoly = toolHotLinkPoly(self, self.canvas, False)
        self.toolHotLinkRasterCircleByCenter = toolHotLinkCircleByCenter(self, self.canvas, False)
        self.toolHotLinkRasterCircleByRadius = toolHotLinkCircleByRadius(self, self.canvas, False)        

        self.actionToolsSel = QAction(QIcon(getThemeIcon("selone.png")), translateMsg("Selection's tools"), self.iface.mainWindow()) 
        self.actionToolsSel.setObjectName("mActionToolsSel")
        self.actionToolsSel.setCheckable(True)
        self.actionToolsSel.setChecked(False)
        self.actionToolsSel.setWhatsThis("actionToolHotLinkRaster")
        self.actionToolsSel.setToolTip(translateMsg("Selection's tools"))
        self.actionToolsSel.setMenu(self.toolsHotLink)
        self.actionToolsSel.triggered.connect(self.updateToolByAction)


        self.actionCleanLayers.setChecked(False)
        self.actionToolsSel.setChecked(False)        
        
        self.cmenu = QMenu(self)
        self.actionsGrid = self.cmenu.addAction(self.actionToolsSel)
        self.cmenu.addSeparator()
        self.zoomToGroup = self.cmenu.addAction(self.actionZoomToGroup)
        self.cleanTheGroupt = self.cmenu.addAction(self.actionCleanGroup)
        self.cmenu.addSeparator()
        self.delForGrid = self.cmenu.addAction(self.actionCleanLayers)
        
        self.menu.addAction(self.actionReloadGrids)
        self.menu.addAction(self.actionCheckGrids)
        self.menu.addSeparator()
        self.menu.addAction(self.actionCleanSelectedGrids)

        # Make the combo editable to set a custom text, but readonly
        self.setEditable(True)
        self.lineEdit().setReadOnly(True)
        # Make the lineedit the same color as QPushButton
        palette = QApplication.palette()
        palette.setBrush(QPalette.ColorRole.Base, palette.button())
        self.lineEdit().setPalette(palette)

        # Use custom delegate
        self.setItemDelegate(CheckableComboBox.Delegate())

        # Update the text when an item is toggled
        self.model.dataChanged.connect(self.updateText)

        # Hide and show popup when clicking the line edit
        self.lineEdit().installEventFilter(self)
        self.closeOnLineEditClick = False

        # Prevent popup from closing when clicking on an item
        self.view.viewport().installEventFilter(self)
        
        self.imgNodeExpanded = getThemeIcon("movedown.png").replace("\\", "/")
        self.maxSelected = 3
       
        self.setIconSize(QSize(18, 18)) 
        #self.setStyleSheet(self.styleComboBox)

        #CREATE THE BUTTON WITH MENU
        self.butActions = funnyPushButton(self) 
        self.butActions.initPushButton(40, 23, 40, 23, "wmsServer", "", translateMsg("Actions for files"), True, getThemeIcon("actions.png"), 40, 23, True)
        self.butActions.setMenu(self.menu)
        

    def resizeEvent(self, event):
        # Recompute text to elide as needed
        self.updateText()
        super().resizeEvent(event)
        self.butActions.setGeometry(self.width()-63, 0, 30, 22)

    def hotLinkRaster(self):
        self.updateTool(self.iface.sender().objectName(), self.iface.sender())      
        self.actionToolsSel.setIcon(self.iface.sender().icon())
        self.actionToolsSel.setChecked(True)


    def updateToolByAction(self): self.updateTool(self.actionToolsSel.whatsThis(), None)

    def updateTool(self, nameTool, obj):
        if self.view.isVisible() :
           item = self.model.item(self.index.row())
           #print("%s : %s" % (self.index.row(), item.data()))
           self.getLayerGrid(item.data())
           #self.parent.zoomToCurrentLayer()

    def getLayerGrid(self, fileName):
           if self.currentLayer :
              try : self.projectAlr.removeMapLayers([self.currentLayer.id()])
              except : pass
              
           self.currentLayer = QgsVectorLayer(fileName, basename(fileName), "ogr")
           self.spatialIndex = getSpatialIndex(self.currentLayer)
           getQml(self.currentLayer)

           self.currentLayer.select([])           

           self.hasExtentForGeom, self.indexForExtentGeom = checkExtentField(self.currentLayer)           

        

    def getSelLayers(self): return self.selLayers
    
    def loadTheItems(self, grids):
        for key in grids :
            item = QStandardItem(grids[key]['usualName'])
            item.setData(key, Qt.ItemDataRole.UserRole)
            self.addItem(item.text(), key)

    def eventFilter(self, object, event):

        #BLOC SIGNALS ON CHANGE WHILE : self.view.isVisible()
        if object == self.lineEdit():
            if event.type() == QEvent.Type.MouseButtonRelease:
                if self.closeOnLineEditClick:
                    self.hidePopup()
                else:
                    self.showPopup()
                return True
            return False

        if object == self.view.viewport():
            if event.type() == QEvent.Type.MouseButtonRelease:

                self.index = None
                index = self.view.indexAt(event.pos())
                item = self.model.item(index.row())
                
                if event.button() == Qt.MouseButton.LeftButton:
                    nbSelected = self.getNbSelected()
                    if item.checkState() == Qt.CheckState.Checked : item.setCheckState(Qt.Unchecked)
                    else: 
                        if nbSelected < self.maxSelected : item.setCheckState(Qt.CheckState.Checked)
                        else : self.barInfo.pushMessage("Warning", "Nombre de grilles max sélectionnables atteint [3] !", Qgis.Warning, 3)
                    return True
                else :
                    if item.checkState() == Qt.CheckState.Checked : self.index = index ; action = self.cmenu.exec_(self.mapToGlobal(event.pos()))
        
        return False
 
  
    def getNbSelected(self):
        counter = 0
        for i in range(self.model.rowCount()):
            if self.model.item(i).checkState() == Qt.CheckState.Checked : counter+= 1
        return counter
 
    def delSelectedGrids(self):
        for i in range(self.model.rowCount()):
            item = self.model.item(i)
            if item.checkState() == Qt.CheckState.Checked : item.setCheckState(Qt.Unchecked)        
        self.selLayers = []
        self.actionCleanLayers.setChecked(False)
        self.actionToolsSel.setChecked(False)

    def showPopup(self):
        super().showPopup()
        # When the popup is displayed, a click on the lineedit should close it
        self.closeOnLineEditClick = True

    def hidePopup(self):
        super().hidePopup()
        # Used to prevent immediate reopening when clicking on the lineEdit
        self.startTimer(100)
        # Refresh the display text when closing
        self.updateText()

    def timerEvent(self, event):
        # After timeout, kill timer, and reenable click on line edit
        self.killTimer(event.timerId())
        self.closeOnLineEditClick = False

    def populateComboWidget(self, comboCurrentLayer):
        comboCurrentLayer.clear()
        for i in range(self.model.rowCount()):
            if self.model.item(i).checkState() == Qt.CheckState.Checked : 
                text = self.model.item(i).text()
                key = self.model.item(i).data()                
                item = QStandardItem()
                comboCurrentLayer.addItem(text, key)

        self.parent.comboCurrentLayer.blockSignals(False)        
        if comboCurrentLayer.model().rowCount() > 0 :
            comboCurrentLayer.setCurrentIndex(0)
            self.parent.updateMapViewerWithGrid()
                


    def updateText(self):
        nbSelected = self.getNbSelected()
        texts, toolTip, index = [], "", 1
        self.selLayers = []
        for i in range(self.model.rowCount()):
            if self.model.item(i).checkState() == Qt.CheckState.Checked : 
                texts.append(self.model.item(i).text())
                retLine = "" if toolTip == "" else "\n"
                toolTip+= "%s%s - %s" % (retLine, index, self.model.item(i).text())
                self.selLayers.append(self.model.item(i).data())
                index+= 1
        text = ", ".join(texts)
        text = "[%s] %s" % (nbSelected, text)

        # Compute elided text (with "...")
        metrics = QFontMetrics(self.lineEdit().font())
        elidedText = metrics.elidedText(text, Qt.TextElideMode.ElideRight, self.lineEdit().width())
        self.lineEdit().setText(elidedText)
        self.lineEdit().setStyleSheet("""QLineEdit {background: #f3f3f3; background-image: url(%s); background-repeat: no-repeat; background-position: left; color: #252424; font-size: 12px; padding: 2 2 2 30;}""" % self.pathIconGridRaster)
        self.lineEdit().setToolTip(toolTip)

        self.parent.comboCurrentLayer.blockSignals(True)
        self.populateComboWidget(self.parent.comboCurrentLayer)

        #if self.view.isVisible() : print(self.getSelLayers())
        
        return self.selLayers

    def addItem(self, text, data=None):
        item = QStandardItem()
        item.setText(text)
        if data is None:
            item.setData(text)
        else:
            item.setData(data)
        item.setFlags(Qt.ItemFlag.ItemIsEnabled | Qt.ItemIsUserCheckable)
        item.setData(Qt.Unchecked, Qt.CheckStateRole)
        icon = QIcon(getThemeIcon("gridraster.png"))
        item.setIcon(icon)
        self.model.appendRow(item)

    def addItems(self, texts, datalist=None):
        for i, text in enumerate(texts):
            try:
                data = datalist[i]
            except (TypeError, IndexError):
                data = None
            self.addItem(text, data)

    def currentData(self):
        # Return the list of selected items data
        res = []
        for i in range(self.model.rowCount()):
            if self.model.item(i).checkState() == Qt.CheckState.Checked:
                res.append(self.model.item(i).data())
        return res


class toolHotLink(QgsMapTool):
    def __init__(self, parent, mapViewer, uiAlr, toRemove):
        QgsMapTool.__init__(self, mapViewer)
      
        self.parent = parent        
        self.mapViewer = mapViewer        
        self.sb = self.parent.iface.mainWindow().statusBar()
        self.cp = None
        self.context = QgsExpressionContext()
        self.scope = QgsExpressionContextScope()
        self.uiAlr  = uiAlr
        self.toRemove = toRemove
        self.setCursor(QCursor(QPixmap(getThemeIcon("toolhotlink.png")))) if not self.toRemove else self.setCursor(QCursor(QPixmap(getThemeIcon("toolhotdelete.png"))))

        if self.uiAlr :  self.rb1, self.rb2 = self.parent.rbTool, self.parent.rbCanvas 
        else :           self.rb1, self.rb2 = self.parent.rbCanvas, self.parent.rbTool


        self.timerMapTips = QTimer( self.mapViewer )
        if self.parent.currentLayer :
           if self.parent.currentLayer.geometryType() == 2 : self.timerMapTips.timeout.connect( self.showMapTip )
        
        self.status=0        
         
    def getRemove(self) : return self.toRemove
    
    def canvasReleaseEvent(self, e):  pass 

    def canvasMoveEvent(self, e):
        if self.mapViewer.underMouse(): 
            QToolTip.hideText()
            self.timerMapTips.start(1000)
            self.cp = e.pos()
            self.rb1.reset()
            self.rb2.reset()            
            self.showExtent()

    def canvasReleaseEvent(self, e):
        if not e.button() == Qt.MouseButton.LeftButton: return
        if not self.parent.currentLayer : return
       
        cp = self.toLayerCoordinates(self.parent.currentLayer, e.pos())
       
        g = QgsGeometry().fromPointXY(QgsPointXY(cp.x(), cp.y()))
   
        if self.parent.currentLayer.geometryType() == 0 : g = g.buffer(250, 12)

        key = self.parent.getKeyForGridLayer(self.parent.listFiles.currentIndex())
        index = self.parent.getIndexLayerField(key) 
        if index < 0 : return
        
        selectedSet, featIds = [], []
        selectedSet, featIds = self.parent.intersectsOp(self.parent.currentLayer, [], [], index, g)

        if not self.toRemove : self.parent.loadRaster(selectedSet)
        else :
            self.parent.removeRaster(selectedSet)
            self.parent.parent.updateToolRemove()

    def deactivate(self):   
        self.timerMapTips.stop()
        self.rb1.reset()
        self.rb2.reset()

    def evaluateExpression(self, context, feat, expression):
            expression = QgsExpression(expression)
            value = expression.evaluate(context)
            return value

    def showExtent( self ):
        if not self.parent.currentLayer : return
        self.timerMapTips.stop()
        
        if self.mapViewer.underMouse():
           cp = self.toLayerCoordinates(self.parent.currentLayer, self.cp) 
           g = QgsGeometry().fromPointXY(QgsPointXY(cp.x(), cp.y()))
           g = g.buffer(250, 12)

           features = self.parent.currentLayer.getFeatures(QgsFeatureRequest())

           for feat in features :        
                  geom = feat.geometry()
                  if geom and g:
                     if geom.intersects(g):
                        self.timerMapTips.start(1000)
                        if self.parent.currentLayer.geometryType() == 0 :
                           if self.parent.hasExtentForGeom :
                              wktGeom = feat[self.parent.indexForExtentGeom]
                              geom = QgsGeometry().fromWkt("%s" % wktGeom)
                           else :  
                              point = g.centroid().asPoint()
                              geom = QgsGeometry().fromPointXY(point).buffer(500, 12)
                             
                        self.parent.rbGeom(self.rb1, geom, self.parent.currentLayer.crs(), True)
                        self.parent.rbGeom(self.rb2, geom, self.parent.currentLayer.crs(), True)

                        break

    def showMapTip(self):

        if not self.parent.currentLayer : return
        self.timerMapTips.stop()
        if self.mapViewer.underMouse():
           
           key = self.parent.currentLayer.source()

           expression = None
           if self.parent.dictGridRaster != [] :
              if dicoHasKey(self.parent.dictGridRaster, key):  expression = self.parent.dictGridRaster[key]["listFields"] 

              if not self.cp : return

              cp = self.toLayerCoordinates(self.parent.currentLayer, self.cp)
              g = QgsGeometry().fromPointXY(QgsPointXY(cp.x(), cp.y()))

              if self.parent.currentLayer.geometryType() == 0 : g = g.buffer(250, 12)                  

              key = self.parent.getKeyForGridLayer(self.parent.listFiles.currentIndex())
              index = self.parent.getIndexLayerField(key)
              if index < 0 : return              
              
              features = self.parent.currentLayer.getFeatures(QgsFeatureRequest())

              for feat in features :        
                  geom = feat.geometry()
                  self.scope.setFeature(feat)
                  self.context.appendScope(self.scope)

                  if geom and g:
                     if geom.intersects(g):
                            if index < len(self.parent.currentLayer.fields()) : expression = feat[index]
                            else :  expression = self.evaluateExpression(self.context, feat, expression)
                            break

              QToolTip.showText( self.mapViewer.mapToGlobal( self.mapViewer.mouseLastXY() ), expression, self.mapViewer )

              msg = translateMsg("Load a raster file") if not self.toRemove else translateMsg("Remove raster file")
              self.sb.showMessage("%s ? %s" % (msg, expression))

              
class toolHotLinkRect(QgsMapTool):
    def __init__(self, parent, mapViewer, uiAlr):
        QgsMapTool.__init__(self, mapViewer)
        self.parent = parent
       
        self.mapViewer = mapViewer
        self.status = 0
        self.uiAlr  = uiAlr
        if self.uiAlr :  self.rb1, self.rb2 = self.parent.rbTool, self.parent.rbCanvas 
        else :           self.rb1, self.rb2 = self.parent.rbCanvas, self.parent.rbTool

        self.setCursor(QCursor(QPixmap(getThemeIcon("toolhotlink.png"))))        
        self.sb = self.parent.iface.mainWindow().statusBar()
        msg = translateMsg("Draw a rectangle : Place the top left point and move the mouse")
        self.sb.showMessage(msg)
        
        self.timerMapTips = QTimer( self.mapViewer )

    def deactivate(self): self.rb1.reset() ; self.rb2.reset();
         

    def canvasPressEvent(self,e):
        if not e.button() == Qt.MouseButton.LeftButton: return
        self.status = 1 if self.status == 0 else 0
        if self.status == 1 :
            self.topleft = self.toMapCoordinates(e.pos())
            geom = rbRectangleByPoint(self.rb1, self.topleft, self.topleft)
            if self.uiAlr : self.parent.rbGeom(self.rb2, geom, self.parent.crs, True)
            else : self.parent.rbGeom(self.rb2, geom, self.parent.currentLayer.crs(), True)            
        else : self.rb1.reset()            
        

    def canvasMoveEvent(self,e):
        if not self.status == 1: return
        cp = self.toMapCoordinates(e.pos())
        geom = rbRectangleByPoint(self.rb1, self.topleft, cp)
        if self.uiAlr : self.parent.rbGeom(self.rb2, geom, self.parent.crs, True)
        else : self.parent.rbGeom(self.rb2, geom, self.parent.currentLayer.crs(), True)          
        msg = translateMsg("Drawing rectangle, extent : width %s - height %s")
        self.sb.showMessage(msg % (round(abs(self.topleft.x()-cp.x()), 2), round(abs(self.topleft.y()-cp.y()), 2))) 

    def canvasReleaseEvent(self,e):
        if not e.button() == Qt.MouseButton.LeftButton: return
        if not self.status == 1: return
        cp = self.toMapCoordinates(e.pos())
        if float(cp.x())!= float(self.topleft.x()) and float(cp.y()) != float(self.topleft.y()) : self.rb2.reset(); getRasterForSelection(self, self.rb1, self.mapViewer, self.parent.currentLayer)
 


class toolHotLinkFree(QgsMapTool):
    def __init__(self, parent, mapViewer, uiAlr):
        QgsMapTool.__init__(self, mapViewer)
        self.parent = parent
        self.mapViewer = mapViewer
        self.uiAlr  = uiAlr
        if self.uiAlr :  self.rb1, self.rb2 = self.parent.rbTool, self.parent.rbCanvas 
        else :           self.rb1, self.rb2 = self.parent.rbCanvas, self.parent.rbTool

        if self.uiAlr : self.transform = QgsCoordinateTransform(self.parent.crs, QgsProject(self.parent.iface).instance().crs(), QgsProject(self.parent.iface).instance())
        else :  self.transform = QgsCoordinateTransform(QgsProject(self.parent.iface).instance().crs(), self.parent.crs, self.parent.projectAlr)
        
        self.setCursor(QCursor(QPixmap(getThemeIcon("toolhotlink.png"))))        
        self.sb = self.parent.iface.mainWindow().statusBar()
        msg = translateMsg("Draw a free polygon")
        self.sb.showMessage(msg)        
        self.firstpoint = False 


    def deactivate(self): self.rb1.reset(); self.rb2.reset();
         
    
    def canvasPressEvent(self,e):
        point = self.toMapCoordinates(e.pos())
        self.rb1.addPoint(point)
        self.rb2.addPoint(self.transform.transform(point))
        self.firstpoint = True
        
    
    def canvasMoveEvent(self,e):
        if self.firstpoint :
           point = self.toMapCoordinates(e.pos())
           self.rb1.addPoint(point)
           self.rb2.addPoint(self.transform.transform(point))
           msg = translateMsg("Drawing Free Polygon : %s points")
           self.sb.showMessage(msg % self.rb1.numberOfVertices())      
        
    def canvasReleaseEvent(self,e):
        if not self.rb1: return
        if self.rb1.numberOfVertices() > 2 :  self.rb2.reset(); getRasterForSelection(self, self.rb1, self.mapViewer, self.parent.currentLayer);  self.firstpoint = False
            

class toolHotLinkPoly(QgsMapTool):
    def __init__(self, parent, mapViewer, uiAlr):
        QgsMapTool.__init__(self, mapViewer)
        self.parent = parent
        self.mapViewer = mapViewer
        self.uiAlr  = uiAlr
        if self.uiAlr :  self.rb1, self.rb2 = self.parent.rbTool, self.parent.rbCanvas 
        else :           self.rb1, self.rb2 = self.parent.rbCanvas, self.parent.rbTool

        if self.uiAlr : self.transform = QgsCoordinateTransform(self.parent.crs, QgsProject(self.parent.iface).instance().crs(), QgsProject(self.parent.iface).instance())
        else :  self.transform = QgsCoordinateTransform(QgsProject(self.parent.iface).instance().crs(), self.parent.crs, self.parent.projectAlr) 

        self.setCursor(QCursor(QPixmap(getThemeIcon("toolhotlink.png"))))                
        self.sb = self.parent.iface.mainWindow().statusBar()
        msg = translateMsg("Draw a Polygon : Left click to place points, right click to complete the entry")
        self.sb.showMessage(msg)        

    def deactivate(self): self.rb1.reset(); self.rb2.reset();
        

    def canvasMoveEvent(self,e):
        if self.rb1.numberOfVertices() > 0:
            self.rb1.removeLastPoint(0)
            self.rb2.removeLastPoint(0)
            point = self.toMapCoordinates(e.pos())
            self.rb1.addPoint(point)
            self.rb2.addPoint(self.transform.transform(point))
            msg = translateMsg("Drawing Polygon : %s points")
            self.sb.showMessage(msg % self.rb1.numberOfVertices())  
        
    
    def canvasPressEvent(self,e):
        if e.button() == Qt.MouseButton.LeftButton :
           point = self.toMapCoordinates(e.pos())   
           self.rb1.addPoint(point)
           self.rb2.addPoint(self.transform.transform(point))
        else:
            if self.rb1.numberOfVertices() > 2 :  self.rb2.reset(); getRasterForSelection(self, self.rb1, self.mapViewer, self.parent.currentLayer)

        

class toolHotLinkCircleByCenter(QgsMapTool):
    def __init__(self, parent, mapViewer, uiAlr):
        QgsMapTool.__init__(self, mapViewer)
        self.parent = parent
        self.mapViewer = mapViewer
        self.uiAlr  = uiAlr
        if self.uiAlr :  self.rb1, self.rb2 = self.parent.rbTool, self.parent.rbCanvas 
        else :           self.rb1, self.rb2 = self.parent.rbCanvas, self.parent.rbTool

        self.setCursor(QCursor(QPixmap(getThemeIcon("toolhotlink.png"))))
        self.sb = self.parent.iface.mainWindow().statusBar()
        msg = translateMsg("Draw a circle : Place the center and move the mouse to set the radius")
        self.sb.showMessage(msg)        
        self.status = 0

    def deactivate(self) : self.rb1.reset(); self.rb2.reset();

    def canvasPressEvent(self,e):
        if not e.button() == Qt.MouseButton.LeftButton: return
        self.status = 1
        self.center = self.toMapCoordinates(e.pos())
        geom = rbcircle(self.rb1, self.center, self.center, 40)
        if self.uiAlr : self.parent.rbGeom(self.rb2, geom, self.parent.crs, True)
        else : self.parent.rbGeom(self.rb2, geom, self.parent.currentLayer.crs(), True)
    
    def canvasMoveEvent(self,e):
        if not self.status == 1: return
        cp = self.toMapCoordinates(e.pos())
        geom = rbcircle(self.rb1, self.center, cp, 40)
        if self.uiAlr : self.parent.rbGeom(self.rb2, geom, self.parent.crs, True)
        else : self.parent.rbGeom(self.rb2, geom, self.parent.currentLayer.crs(), True)
        
        r = sqrt(self.center.sqrDist(cp))
        msg = translateMsg("Drawing circle - Center : (%s - %s), radius : %s")
        self.sb.showMessage(msg % (round(self.center.x(), 2), round(self.center.y(),2), round(r, 2))) 

    def canvasReleaseEvent(self,e):
        if not e.button() == Qt.MouseButton.LeftButton: return
        self.edge = self.toMapCoordinates(e.pos())

        self.rb2.reset()

        if int(self.edge.x())== int(self.center.x()) and int(self.edge.y()) == int(self.center.y()) :
           self.rb1.reset()
           self.rb1.addPoint(QgsPointXY(self.center.x(),self.center.y()))
           self.rb1.addPoint(QgsPointXY(self.center.x(),self.center.y()+1))
           self.rb1.addPoint(QgsPointXY(self.center.x()+1,self.center.y()+1))
           self.rb1.addPoint(QgsPointXY(self.center.x()+1,self.center.y()))

        getRasterForSelection(self, self.rb1, self.mapViewer, self.parent.currentLayer)   



class toolHotLinkCircleByRadius(QgsMapTool):
    def __init__(self, parent, mapViewer, uiAlr):
        QgsMapTool.__init__(self, mapViewer)
        self.parent = parent
        self.mapViewer = mapViewer
        self.uiAlr  = uiAlr
        if self.uiAlr :  self.rb1, self.rb2 = self.parent.rbTool, self.parent.rbCanvas 
        else :           self.rb1, self.rb2 = self.parent.rbCanvas, self.parent.rbTool
        
        self.setCursor(QCursor(QPixmap(getThemeIcon("toolhotlink.png"))))
        self.sb = self.parent.iface.mainWindow().statusBar()
        msg = translateMsg("Draw a circle : Place the first top left point, and move the mouse to fix the diameter")
        self.sb.showMessage(msg)
        self.status = 0 

    def deactivate(self): self.rb1.reset(); self.rb2.reset();
        
    def canvasPressEvent(self,e):
        if not e.button() == Qt.MouseButton.LeftButton: return
        self.status = 1
        self.p1 = self.toMapCoordinates(e.pos())
        geom = rbcircle(self.rb1, self.p1, self.p1, 40)
        if self.uiAlr : self.parent.rbGeom(self.rb2, geom, self.parent.crs, True)
        else : self.parent.rbGeom(self.rb2, geom, self.parent.currentLayer.crs(), True)
    
    def center(self,p2):
        return QgsPointXY((self.p1.x()+p2.x())/2.0,(self.p1.y()+p2.y())/2.0)
        
    def canvasMoveEvent(self,e):
        if not self.status == 1: return
        cp = self.toMapCoordinates(e.pos())
        center =  self.center(cp)
        geom = rbcircle(self.rb1, center, cp, 40)
        if self.uiAlr : self.parent.rbGeom(self.rb2, geom, self.parent.crs, True)
        else : self.parent.rbGeom(self.rb2, geom, self.parent.currentLayer.crs(), True)
        
        r = sqrt(center.sqrDist(cp))
        msg = translateMsg("Drawing circle - Center : (%s - %s), radius : %s")
        self.sb.showMessage(msg % (round(center.x(), 2), round(center.y(),2), round(r, 2)))        

    def canvasReleaseEvent(self,e):
        if not e.button() == Qt.MouseButton.LeftButton: return
        self.edge = self.toMapCoordinates(e.pos())

        self.rb2.reset()

        if int(self.edge.x())== int(self.p1.x()) and int(self.edge.y()) == int(self.p1.y()) :
           self.rb1.reset()
           self.rb1.addPoint(QgsPoint(self.p1.x(),self.p1.y()))
           self.rb1.addPoint(QgsPoint(self.p1.x(),self.p1.y()+1))
           self.rb1.addPoint(QgsPoint(self.p1.x()+1,self.p1.y()+1))
           self.rb1.addPoint(QgsPoint(self.p1.x()+1,self.p1.y()))

        getRasterForSelection(self, self.rb1, self.mapViewer, self.parent.currentLayer)    


def checkExtentField(layer):
    fieldsNames = layer.fields().names()
    if "extent" in fieldsNames :
       indexForExtentGeom = fieldsNames.index("extent")       
       features = layer.getFeatures(QgsFeatureRequest())
       for feat in features :        
           wktGeom = feat[indexForExtentGeom]
           try :
               geom = QgsGeometry().fromWkt("%s" % wktGeom)
               return True, indexForExtentGeom
           except : return False, -1
    else : return False, -1

def zoomToCurrentLayer(layer, mapCanvas, qgsProject) :
    if layer :
       extent = layer.extent()
       if layer.crs().authid() != qgsProject.crs().authid():
          tr = QgsCoordinateTransform(layer.crs(), qgsProject.crs(), qgsProject) 
          extent = tr.transform(extent)
       mapCanvas.setExtent(extent)
       mapCanvas.refresh()


def getQml(layer):
    theQml = None
    if layer :
       styleQml = os.path.splitext(layer.source())[0]+'.qml'
       if exists(styleQml):  #not
          if layer.geometryType() == 0 : styleQml = "%s/qml/stylepoints.qml" % dirname(__file__) 
          else : styleQml = "%s/qml/style.qml" % dirname(__file__)   
          if exists(styleQml) : theQml = styleQml # layer.loadNamedStyle(styleQml, True)
       else : theQml = styleQml #layer.loadNamedStyle(styleQml, True)
    return theQml    

def getSpatialIndex(layer):
    spatialIndex = None
    if layer :
       spatialIndex = QgsSpatialIndex()
       feat = QgsFeature()
       features = layer.dataProvider().getFeatures()
       while features.nextFeature(feat): spatialIndex.addFeature(feat)
    return spatialIndex


def getRasterForSelection(self, rb, mapViewer, currentLayer):

    geom = getGeomFromRb(self, rb, mapViewer, currentLayer)
    key = self.parent.getKeyForGridLayer(self.parent.listFiles.currentIndex())
    index = self.parent.getIndexLayerField(key)
    if index < 0 : return

    selectedSet, featIds = [], []
    selectedSet, featIds = self.parent.intersectsOp(currentLayer, [], [], index, geom)
    self.parent.loadRaster(selectedSet)

    self.sb.showMessage( "%s : %s %s" % (translateMsg("Load the raster files for selected objects"),  len(selectedSet), translateMsg("files")))
  
                  
def getGeomFromRb(self, rb, mapViewer, currentLayer):
    coords = []  
    [coords.append(QgsMapTool(mapViewer).toLayerCoordinates(currentLayer, rb.getPoint(0,i))) for i in range(rb.numberOfVertices())]
    geom = QgsGeometry().fromPolygonXY([coords])

    rb.reset()
    self.status = 0

    return geom

def rbRectangleByPoint(rb, topleft, bottomright):
    rb.reset()
    rb.addPoint(QgsPointXY(topleft.x(),topleft.y()))
    rb.addPoint(QgsPointXY(bottomright.x(),topleft.y()))
    rb.addPoint(QgsPointXY(bottomright.x(),bottomright.y()))
    rb.addPoint(QgsPointXY(topleft.x(),bottomright.y()))
    rb.addPoint(QgsPointXY(topleft.x(),topleft.y()))
    
    coords = []
    [coords.append(rb.getPoint(0, i)) for i in range(rb.numberOfVertices())]
    g = QgsGeometry().fromPolygonXY([coords])

    return g
         
def rbcircle(rb, center, edgePoint, N):
  
    r = sqrt(center.sqrDist(edgePoint))
    rb.reset()
    for itheta in range(N+1):
        theta = itheta*(2.0 * pi/N)
        rb.addPoint(QgsPointXY(center.x()+r*cos(theta),center.y()+r*sin(theta)))

    coords = []
    [coords.append(rb.getPoint(0,ki)) for ki in range(rb.numberOfVertices())]
    g = QgsGeometry().fromPolygonXY([coords])
       
    return g






class myGroupBoxButtons(QGroupBox):
      def __init__(self, parent, *args):
          super(myGroupBoxButtons, self).__init__(*args)
          self.parent = parent

          self.setMinimumHeight(60)
          self.setMaximumHeight(60)
          self.setStyleSheet("QGroupBox { border: 0px solid black;}")

          self.parent.dlg.saveButton = QPushButton(self)
          self.parent.dlg.saveButton.setText(translateMsg("Save"))
          self.parent.dlg.saveButton.setToolTip(translateMsg("Save"))
          self.parent.dlg.saveButton.setObjectName("saveButton")
          self.parent.dlg.saveButton.setShortcut(QKeySequence("CTRL+S"))

          self.parent.dlg.saveCloseButton = QPushButton(self)
          self.parent.dlg.saveCloseButton.setText(translateMsg("Save and close"))
          self.parent.dlg.saveCloseButton.setToolTip(translateMsg("Save and close"))
          self.parent.dlg.saveCloseButton.setObjectName("saveCloseButton")
          self.parent.dlg.saveCloseButton.setShortcut(QKeySequence("CTRL+MAJ+S"))

          
          self.parent.dlg.closeButton = QPushButton(self)
          self.parent.dlg.closeButton.setText(translateMsg("Close"))
          self.parent.dlg.closeButton.setToolTip(translateMsg("Close"))
          self.parent.dlg.closeButton.setObjectName("closeButton")
          self.parent.dlg.closeButton.setShortcut(QKeySequence("CTRL+Q"))
          

          self.parent.dlg.saveButton.clicked.connect(self.parent.setValues)
          self.parent.dlg.saveCloseButton.clicked.connect(self.saveAndClose)
          self.parent.dlg.closeButton.clicked.connect(self.parent.dlg.reject)

      def saveAndClose(self):
          self.parent.setValues()
          self.parent.dlg.reject()

      def resizeEvent(self,ev):
          middle = int( self.width()/2) 
          self.parent.dlg.saveButton.setGeometry(10, 30, 100, 24)
          self.parent.dlg.saveCloseButton.setGeometry(middle-100, 30, 200, 24)
          self.parent.dlg.closeButton.setGeometry(self.width() - 110, 30, 100, 24)
          

class myLineEdit(QLineEdit):
      def __init__(self, dlg=None, dockWidget=None):
          super(myLineEdit, self).__init__()
          self.dlg = dlg
          self.dockWidget = dockWidget
          self.setDragEnabled(True)
          self.setAcceptDrops(True)
          self.buttonWizard = None
          self.iconDir = join(dirname(__file__),"icons/")

      def initType(self, key, context):
          if key == "uri" :
             self.buttonWizard = funnyPushButton(self) 
             self.buttonWizard.initPushButton(24, 24, 400, 0, "buttonWizard", "",  translateMsg("Input assistant"), True, getThemeIcon("actions.png"), 24, 24, True)

             icon = QIcon(getThemeIcon("urlweb2.png"))
            
             menuAddTags = QMenu() 
             menuAddTags.setStyleSheet("QMenu {color: white; background-color:qlineargradient(x1:0, y1:0, x2:0, y2:1, stop: 0 #cccccc, stop: 1 #5D5D5D);}")
             #NEW 3.4.0
             icon = QIcon(getThemeIcon("urlweb2.png"))
             self.testUrlInQGIS = menuAddTags.addAction(icon, '{:<30}'.format(translateMsg("Test in QGIS")), self.openUrlInQGIS)
             iconClean = QIcon(getThemeIcon("closenb.png"))
             self.getCleanUrlInQGIS = menuAddTags.addAction(iconClean, '{:<30}'.format(translateMsg("None")), self.cleanUrlInQgis)             
             menuAddTags.addSeparator()
             self.testUrlInWeb = menuAddTags.addAction(icon, '{:<30}'.format(translateMsg("Test in a web browser")), self.openWebUrl)
             self.buttonWizard.setMenu(menuAddTags)
                   
          if key == "iconGroupData" :
             self.buttonWizard = funnyPushButton(self) 
             self.buttonWizard.initPushButton(24, 24, 400, 0, "buttonWizard", "", translateMsg("Input assistant"), True, getThemeIcon("files.png"), 24, 24, True)
             self.buttonWizard.clicked.connect(self.getIcon)             
             self.setReadOnly(True)

          if key in ("groupe", "labelGroupData", "labeluri") : self.regex = QRegularExpression(r"^[^>$*<%+=@¤~#]*$", Qt.CaseSensitivity.CaseSensitive)
             
          elif key == "uri" :
              if context == 1 : self.regex = QRegularExpression(r"^(http|https|ftp)\://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))*$", Qt.CaseSensitivity.CaseInsensitive)
              else : self.regex = QRegularExpression(r"^(.+?)/([\w]+\.log)$", Qt.CaseSensitivity.CaseInsensitive) 
          self.key = key
          self.context = context
          self.slotCtrl = self.buttonWizard


      def setSlotCtrl(self, slotCtrl): self.slotCtrl = slotCtrl    
      def mouseDoubleClickEvent(self, event):
          if self.slotCtrl : self.slotCtrl.clicked.emit()

      #NEW 3.4.0
      def cleanUrlInQgis(self) :
          if self.dockWidget : 
            groupRaster = self.dockWidget.getWmsGroupRaster() 
            if not groupRaster : return
            self.dockWidget.cleanWmsGroupRaster()
            
      def openUrlInQGIS(self):
          if self.dockWidget : 
            groupRaster = self.dockWidget.getWmsGroupRaster() 
            if not groupRaster : return
            self.dockWidget.cleanWmsGroupRaster()
            
            nameLayer = "WMS"
            urlWms = self.text()

            if not urlWms : return
            
            urlWms = cleanUri(urlWms)        

            rasterLayer = QgsRasterLayer(urlWms , "TEST - %s" %  nameLayer, "WMS")
            mapLayer = QgsProject.instance().addMapLayer(rasterLayer, False) 
            nodeLayer = QgsLayerTreeLayer(rasterLayer)
            groupRaster.insertChildNode(0, nodeLayer)


            if urlWms.find("traffic") > -1 :
               if mapLayer :
                  mapLayer.setAutoRefreshInterval(60000)                        
                  mapLayer.setAutoRefreshEnabled(True)
              
          

      def openWebUrl(self):
          url = self.text()
          #NEW 3.4.0
          posUrl = url.find("&url=")
          if posUrl > -1 : url = url[posUrl+5:]
          try : webbrowseropen(url)
          except : pass            


      def checkIconFile(self):
          cond = exists(join(dirname(__file__),"icons/%s" % self.text()))   
          self.setStyleSheet("background-color:#AEEE00;") if cond else self.setStyleSheet("background-color:red;")
      
      def verifiyPath(self, path):
          if path != self.iconDir :
             self.sender().setDirectory(self.iconDir)
      
      def getIcon(self):
            if self.sender() == None: return           

            fileDialog = QFileDialog(None, translateMsg("Extension images folder"))
            mimeTypeFilters = (("image/png", "image/icon", "image/jpeg"))
            fileDialog.setMimeTypeFilters(mimeTypeFilters)
            fileDialog.setViewMode(QFileDialog.ViewMode.Detail)
            fileDialog.setFileMode(QFileDialog.FileMode.ExistingFiles)  
            fileDialog.setAcceptMode(QFileDialog.DialogLabel.AcceptOpen)
            fileDialog.setDirectory(self.iconDir)
            fileDialog.setFileMode(QFileDialog.FileMode.ExistingFiles)
            fileDialog.currentChanged.connect(self.verifiyPath)

            if fileDialog.exec():
                if  fileDialog.selectedFiles() != [] :
                           fileName = "%s" % (fileDialog.selectedFiles()[0])
                           self.sender().parent().setText(basename(fileName))            



      def keyPressEvent(self, event) : QLineEdit.keyPressEvent(self, event)

      #NEW 3.4.0
      def checkRegExp(self):
          if self.isEnabled() :
             self.setStyleSheet("background-color:red;")
             if self.text() == "" : return
             if self.key == "iconGroupData" :
                cond = exists(join(dirname(__file__),"icons/%s" % self.text()))
                if not cond : cond = exists(self.text())
                self.setStyleSheet("background-color:#AEEE00;") if cond else self.setStyleSheet("background-color:red;")   
                return

             if self.key in ("groupe", "labelGroupData", "labeluri") :             
                 if self.regex.exactMatch(self.text()) : self.setStyleSheet("background-color:#AEEE00;")
             else :
                 if self.regex.exactMatch(self.text()) : self.setStyleSheet("background-color:#AEEE00;")
                 if self.key == "uri" and self.context == 1 :
                    if self.text().find("&url=http") > 0 : self.setStyleSheet("background-color:#AEEE00;") 
                 else : self.setStyleSheet("background-color:#AEEE00;")                    

      def resizeEvent(self, ev):
          if self.buttonWizard :  self.buttonWizard.setGeometry(self.width()-30, 1, 24, 24)


class legendTreeWidget( QTreeWidget ):
    def __init__(self, settings, parent=None, context=0, iconData=None, comboBox=None, dockWidget=None):
        QTreeWidget.__init__(self, parent)

        self.vectors, self.rasters, self.alrRasters = None, None, None
        self.parent = parent
        self.dockWidget = dockWidget

        self.context = context
        self.iconData = iconData
        self.comboBox = comboBox
        self.ihmWindow = self.parent.parent()#.parent()
        self.newNode = False

        self.setItemsExpandable(True)
        self.setAnimated(True)
        self.setDragEnabled(True)
        self.setDropIndicatorShown(True)
        self.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove)
        self.header().setDefaultSectionSize(150)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
        self.setMinimumWidth(300)
        self.setGeometry(500, 25, 300, 400)
        self.settings = settings

        self.groupIcon = QIcon(getThemeIcon("group.png"))


        self.toolBarWidget = QToolBar(self) 
        self.toolBarWidget.setObjectName("toolBarWidget")
        self.toolBarWidget.setGeometry(300, 30, 220, 40)
        self.toolBarWidget.setAllowedAreas(Qt.ToolBarArea.TopToolBarArea | Qt.ToolBarArea.BottomToolBarArea)
        self.toolBarWidget.setStyleSheet("QToolBar {icon-size: 32px, 32px; background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #E1E1E1, stop: 0.4 #9B9B9B, stop: 0.5 #D8D8D8, stop: 1.0 #D3D3D3);  border-width: 2px; border-radius: 10px; border-color: beige;}")


        self.buttonReloadWms = funnyPushButton(self.toolBarWidget) 
        self.buttonReloadWms.initPushButton(24, 24, 10, 5, "buttonExpandAllNodes", "", translateMsg("Reload WMS services"), True, getThemeIcon("refresh.png"), 24, 24, True)

        self.buttonExpandAllNodes = funnyPushButton(self.toolBarWidget) 
        self.buttonExpandAllNodes.initPushButton(24, 24, 45, 5, "buttonExpandAllNodes", "", translateMsg("Expand all nodes"), True, getThemeIcon("nodes_expand.png"), 24, 24, True)

        self.buttonReduceAllNodes = funnyPushButton(self.toolBarWidget) 
        self.buttonReduceAllNodes.initPushButton(24, 24, 80, 5, "buttonReduceAllNodes", "", translateMsg("Close all nodes"), True, getThemeIcon("nodes_reduce.png"), 24, 24, True)

        self.buttonAddGroup = funnyPushButton(self.toolBarWidget) 
        self.buttonAddGroup.initPushButton(24, 24, 115, 5, "buttonAddGroup", "", translateMsg("Add a group"), True, getThemeIcon("addgroup.png"), 24, 24, True)

        self.buttonEditNode = funnyPushButton(self.toolBarWidget) 
        self.buttonEditNode.initPushButton(24, 24, 150, 5, "buttonEditNode", "", translateMsg("Modify"), True, getThemeIcon("edit.png"), 24, 24, True)

        self.buttonDelNode = funnyPushButton(self.toolBarWidget) 
        self.buttonDelNode.initPushButton(24, 24, 185, 5, "buttonDelNode", "", translateMsg("Delete"), True, getThemeIcon("del.png"), 24, 24, True)

        self.buttonReloadWms.clicked.connect(self.reloadServicesWms)
        self.buttonExpandAllNodes.clicked.connect(self.expandTreeWidget)
        self.buttonReduceAllNodes.clicked.connect(self.expandTreeWidget)
        self.buttonEditNode.clicked.connect(self.callEditItem)
        self.buttonAddGroup.clicked.connect(self.addGroup)
        self.buttonDelNode.clicked.connect(self.delNode) 


        root = self.invisibleRootItem()
        root.setData(0, Qt.ItemDataRole.WhatsThisRole,"root")
        self.setRootIsDecorated( False )
        self.setHeaderLabels([translateMsg("Values")])

        styleSheet = "QTreeWidget { border: 1px solid;  border-radius: 5px; opacity: 0.5; background-color: rgba(255, 255, 255, 50);}"
        styleSheet+= "QTreeWidget::item:selected {background-color: #5D5D5D; color: rgb(255,255,255); }"

        self.imgChecked = getThemeIcon("checked.png").replace("\\","/")
        self.imgUnChecked = getThemeIcon("unchecked.png").replace("\\","/")

        styleSheet+= "QTreeWidget {opacity: 0.5;  background-color: rgba(255, 255, 255, 210);}"
        styleSheet+= "QTreeWidget::indicator:checked {image: url(%s);}" % self.imgChecked
        styleSheet+= "QTreeWidget::indicator:unchecked  {image: url(%s);}" % self.imgUnChecked


        self.setStyleSheet(styleSheet)
        self.setColumnWidth(0, 280)
        self.setColumnCount(1)
        
        self.styleSlider = """
        QSlider::groove:horizontal {border: 1px solid #bbb; background: white; height: 10px; border-radius: 4px;}
        QSlider::sub-page:horizontal {background: qlineargradient(x1: 0, y1: 0,    x2: 0, y2: 1,  stop: 0 #66e, stop: 1 #bbf); background: qlineargradient(x1: 0, y1: 0.2, x2: 1, y2: 1,  stop: 0 #bbf, stop: 1 #55f);border: 1px solid #777;height: 10px;border-radius: 4px;}
        QSlider::add-page:horizontal {background: #fff; border: 1px solid #777; height: 10px; border-radius: 4px;}
        QSlider::handle:horizontal {background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #eee, stop:1 #ccc); border: 1px solid #777; width: 13px; margin-top: -2px; margin-bottom: -2px; border-radius: 4px;}
        QSlider::handle:horizontal:hover {background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #fff, stop:1 #ddd); border: 1px solid #444; border-radius: 4px;}
        QSlider::sub-page:horizontal:disabled {background: #bbb; border-color: #999;}
        QSlider::add-page:horizontal:disabled {background: #eee; border-color: #999;}
        QSlider::handle:horizontal:disabled {background: #eee; border: 1px solid #aaa; border-radius: 4px;}
        """

        self.itemClicked.connect(self.treeWidgetItemChanged)
        self.parent.installEventFilter(self)

        self.setCurrentItem(self.topLevelItem(0))
        self.treeWidgetItemChanged(self.currentItem(), 0)

    def reloadServicesWms(self):
        self.dockWidget.reloadServicesWms()
        self.dockWidget.barInfo.pushMessage("%s :" % translateMsg("Information"), translateMsg("WMS services have been reloaded !"), Qgis.Info, 3)

    def setContext(self, context, iconData):
        self.context = context
        self.iconData = iconData

    def eventFilter(self, obj, event):
        if obj is self.parent:
            
           if event.type() == QEvent.Type.Resize: self.resizeObjects()
        return QObject.eventFilter(self, obj, event)
           
    def expandTreeWidget(self):
        if self.sender().objectName() == "buttonExpandAllNodes" : self.expandAll()
        else : self.collapseAll()

    def resizeObjects(self):
        self.setGeometry(10, 25, self.parent.width()-30, self.parent.height()-70) 
        self.setColumnWidth(0, self.parent.width()-150)
        self.setColumnWidth(1, 80)
              
    #NEW 3.4.0
    def addGroup(self):
        i = 0
        groupData = "%s %s %s" % (translateMsg("New"), translateMsg("Group").lower(), i)
        while not self.checkUnicityName(groupData) :
              i+= 1
              groupData = "%s %s %s" % (translateMsg("New"), translateMsg("Group").lower(), i)      
        self.addItem([groupData], "groupdata", -1, self.groupIcon, "groupdata", None, {"labelGroupData" : groupData, "iconGroupData" : "group.png"})


    def getGroupParent(self, nameGroup): 
        results = self.findItems(nameGroup, Qt.MatchContains|Qt.MatchRecursive, 0)
        for item in results :
            return item
        return None


    def delNode(self): 
        if not self.currentItem() : return  
        if self.currentItem().parent() : self.currentItem().parent().removeChild(self.currentItem())
        else : 
           group = self.currentItem()
           self.removeAllItems(group)
           self.invisibleRootItem().removeChild(self.currentItem())

        self.model().submit()
        self.repaint()
        self.treeWidgetItemChanged(self.currentItem(), 0)

    def removeAllItems(self, group):
        for j in range(group.childCount()-1, -1, -1,) : group.removeChild(group.child(j))
  
    def checkUnicityName(self, text):
        checkUnicityName = True  
        root = self.invisibleRootItem()
        for i in range(root.childCount()):
            group = root.child(i)
            if group.data(0, Qt.ItemDataRole.DisplayRole) == text : checkUnicityName = False ; break
        return checkUnicityName

    def verifyUnicity(self):
        verifyUnicity = True
        text = self.sender().text()
        itemGroup = self.currentItem()
        mainCtrl = self.dlg if self.dlg else self
        
        root = self.invisibleRootItem()
        for i in range(root.childCount()):
            group = root.child(i)
            if group != itemGroup :
               if group.data(0, Qt.ItemDataRole.DisplayRole) == text : verifyUnicity = False ; break

        if not verifyUnicity : QMessageBox.information(mainCtrl, translateMsg("Warning"), translateMsg("The key <b><u>%s</u></b> already exists !") % text )
        if self.dlg :
           self.dlg.saveButton.setEnabled(verifyUnicity)
           self.dlg.saveCloseButton.setEnabled(verifyUnicity)
           

        return verifyUnicity

    def treeWidgetItemChanged(self, item, column):
        if item :
           self.newNode = False   
           self.buttonDelNode.setEnabled(True)
           self.buttonEditNode.setEnabled(True)
           if item.parent() :
              self.buttonEditNode.setIcon(QIcon(getThemeIcon("edit.png")))
              self.buttonEditNode.setToolTip(translateMsg("Edit item"))
              self.buttonDelNode.setToolTip(translateMsg("Delete item"))
           else :
              self.newNode = True   
              self.buttonEditNode.setIcon(QIcon(getThemeIcon("add.png")))
              self.buttonEditNode.setToolTip(translateMsg("Add an item"))
              self.buttonDelNode.setToolTip(translateMsg("Delete group"))
        else :
              self.buttonDelNode.setEnabled(False)
              self.buttonEditNode.setEnabled(False)
              self.buttonEditNode.setIcon(QIcon(getThemeIcon("noaction.png")))
              self.buttonEditNode.setToolTip(translateMsg("Select an item"))
              self.buttonDelNode.setToolTip(translateMsg("Select an item"))

    def setPropertiesItem(self, item, itemParent):
        self.dlg = QDialog()
        self.dlg.item = item
        self.dlg.setMinimumWidth(480)
        self.dlg.setMaximumWidth(800)
        self.dlg.setMaximumHeight(320)
        self.dlg.setWindowFlags(Qt.WindowType.WindowMaximizeButtonHint | Qt.WindowType.WindowStaysOnTopHint | Qt.WindowType.WindowCloseButtonHint)

        if itemParent and not self.newNode :
              self.dlg.setWindowTitle("%s : %s - %s" % (translateMsg("Property for the item"), translateMsg("Group"), itemParent.data(0, Qt.ItemDataRole.DisplayRole)))
              self.dlg.setGeometry(200, 200, 480, 180)
        else :
              self.dlg.setWindowTitle("%s : %s - %s" % (translateMsg("Property for the item"), translateMsg("Item"), item.data(0, Qt.ItemDataRole.DisplayRole))) if item else self.dlg.setWindowTitle("%s : %s - %s %s" % (translateMsg("Property for the item"), translateMsg("Item"), translateMsg("New"), translateMsg("Item").lower())) 
              self.dlg.setGeometry(200, 200, 480, 320)

        self.dlg.setWindowIcon(self.currentItem().icon(0)) if not self.newNode else self.dlg.setWindowIcon(QIcon(getThemeIcon("wms.png")))
        layout = QVBoxLayout(self.dlg)

        role = Qt.WhatsThisRole

        if item :
           values = self.dlg.item.data(0, role)
           dictValues = literal_eval(values)
        else :
           if itemParent :
              values = itemParent.data(0, role)
              dictValues = literal_eval(values)
           if self.newNode : dictValues = {"labeluri" : '', "uri" : '', "paramuri" : '0'}  

        posY = 10
       
        for key in dictValues.keys():
              
            label = QLabel(self.dlg)
            label.setText("%s :" % key)

            label.setMinimumHeight(30)
            label.setMaximumHeight(30)                
            label.setAlignment(Qt.AlignmentFlag.AlignCenter)
            label.setGeometry(10, posY, 100, 30)
            label.setStyleSheet("QLabel { font-size: 11px;  color : black; background-color: rgba(160, 160, 160, 125); border: 1px solid black;  border-radius: 4px;  padding: 8px;}")

            layout.addWidget(label)

            value = myLineEdit(self.dlg, self.dockWidget)
            value.setObjectName(key)
            value.initType(key, self.context)
            value.setMinimumHeight(25)
            value.setMaximumHeight(25)
            value.setGeometry(110, posY, 350, 25)
            value.setText(dictValues[key])
        
                            
            if key == "paramuri" : value.setEnabled(False)
            else :
               if key == "uri" : value.textChanged.connect(self.ctrlValue)
               elif key == "iconGroupData" : pass 
               else : value.textChanged.connect(value.checkRegExp)
               #NEW 3.4.0
               if itemParent and key == "labelGroupData" : value.textChanged.connect(self.verifyUnicity) #editingFinished

            if key != "uri" : value.checkRegExp()

            layout.addWidget(value)
            
            posY+= 30

        self.ctrlValue()

        groupButtons = myGroupBoxButtons(self)

        layout.addWidget(groupButtons)
                        
        self.dlg.exec()


    def ctrlValue(self):
        obj = self.dlg.findChild(myLineEdit, "uri")
        if obj :          
           obj.checkRegExp()    
           self.countBalises(obj)
           
    def countBalises(self, obj):
        text = obj.text()
        baliseO, baliseF = text.count("["), text.count("]") 
        objToUpdate = self.dlg.findChild(myLineEdit, "paramuri")           
        if objToUpdate :
           if baliseO == baliseF :
              objToUpdate.setStyleSheet("background-color:#AEEE00;")
              objToUpdate.setText("%s" % baliseO)
           else :
              objToUpdate.setStyleSheet("background-color:red;")
              objToUpdate.setText("0")


    def setValues(self):
        if not self.dlg : return
        values = {}
        for child in self.dlg.children():
            if type(child) == myLineEdit : values[child.objectName()] = child.text()

        verifyLabel = True    
            
        if self.currentItem().parent() or self.newNode :    
            if values["labeluri"] == "" : verifyLabel = False 
        else :
            try :  
               if values["labelGroupData"] == "" : verifyLabel = False  
            except :
               try :
                     if values["groupe"] == "" : verifyLabel = False
               except : return

        if not verifyLabel : QMessageBox.information(self.dlg, translateMsg("Warning"), translateMsg("The name cannot be an empty string !")); return    
        
        if not self.newNode :
           #NEW 3.3.2
           if dicoHasKey(values, "labelGroupData") :
              name = values["labeluri"] if self.currentItem().parent() else values["labelGroupData"]
              iconItem = self.getIconForItem(values["uri"]).replace("\\", "/") if self.currentItem().parent() else QIcon(getThemeIcon(values["iconGroupData"]))
           else : return   
     
           self.currentItem().setData(0, Qt.WhatsThisRole, "%s" % values)   
           self.currentItem().setData(0, Qt.ItemDataRole.DisplayRole, name)           
           self.currentItem().setIcon(0, QIcon(iconItem))
           self.dlg.setWindowIcon(self.currentItem().icon(0))
        else :           
           value = values["uri"]
           iconItem = self.getIconForItem(value).replace("\\", "/")
           self.addItem(['{:<100}'.format(values["labeluri"])], translateMsg("Value"), self.currentItem().childCount(), iconItem, translateMsg("Value"), self.currentItem(), values)
           
        self.model().submit()
        self.repaint()

        if self.newNode : self.newNode = False; self.dlg.close()

    def getIconForItem(self, value):
        iconItem = getThemeIcon("unknow.png")  
        if self.context == 0 :
              if exists(value) :
                 sourceFile = QFileInfo(value)
                 if sourceFile.isFile():
                    iconMime = getThemeIcon("%s.png" % sourceFile.suffix().lower())
                    if exists(iconMime) : iconItem = iconMime
                    else :   iconItem = getThemeIcon("unknow.png")  
                 else : iconItem = getThemeIcon("folder.png")                 
              else : iconItem = getThemeIcon("unknow.png")
        else :
              iconItem = getThemeIcon("urlweb.png")
        return iconItem      
      

    def editItem(self, item) :
        if not item : return
        if item.parent() : self.setPropertiesItem(item, None)
        else : self.setPropertiesItem(None, item)

    def callEditItem(self) :
        if not self.currentItem().parent() : self.newNode = True
        self.editItem(self.currentItem())
        
    def mouseDoubleClickEvent(self, event):
        if not self.currentItem().parent() : self.newNode = False  
        self.editItem(self.currentItem())

    def dragMoveEvent(self, event):
        role = Qt.WhatsThisRole
        itemToDropIn = self.itemAt(event.pos())
        itemBeingDragged = self.currentItem()
        dictValues = literal_eval(itemBeingDragged.data(0,role))
        okList = dictValues["labeluri"]

        dropInValues = None
        if itemToDropIn is None: itemToDropIn = self.invisibleRootItem()
        else : dropInValues = literal_eval(itemToDropIn.data(0, role))
        
        if not dropInValues : return
        return

        if dropInValues["labeluri"] == okList :    
            super(legendTreeWidget, self).dragMoveEvent(event)            
            return
        else:
            parent = itemToDropIn.parent()
            if parent is None : parent=self.invisibleRootItem()
            if parent.data(0,role) in okList:
                super(legendTreeWidget, self).dragMoveEvent(event)
                return
        event.ignore()

    def dropEvent(self, event):
        
        role = Qt.ItemDataRole.UserRole  

        itemBeingDragged = self.currentItem()
        okList = self.settings[itemBeingDragged.data(0, role)][0]
 
        oldParent = itemBeingDragged.parent()
        if oldParent is None: oldParent = self.invisibleRootItem()
        oldIndex = oldParent.indexOfChild(itemBeingDragged)

        super(legendTreeWidget,self).dropEvent(event)

        newParent = itemBeingDragged.parent()
        if newParent is None : newParent = self.invisibleRootItem()

        if not newParent.data(0, role) in okList :
            newParent.removeChild(itemBeingDragged)
            oldParent.insertChild(oldIndex, itemBeingDragged)

        self.setCurrentItem(itemBeingDragged)
            

    def setDataItem(self, groupText, findText, itemText, layerId, itemIcon, groupKey, category, group):
        group = self.getGroupParent(groupText)
        item = None
        if group :
           for i in range(group.childCount()):
               if group.child(i).text(0).startswith(findText) :
                  group.child(i).setData(0, Qt.ItemDataRole.DisplayRole, itemText)
                  group.child(i).setStatusTip(0, "%s|%s" % (category, layerId))
                  item = group.child(i)
                  break

    def addItem(self, strings, category, fileId, iconlayer, parentname=None, parent=None, values=None):
        if category not in self.settings:  return False
        if parent is None : parent = self.invisibleRootItem()
        
        item = QTreeWidgetItem(parent, strings)
        item.setData(0, Qt.ItemDataRole.UserRole , category)
        item.setData(0, Qt.ItemDataRole.WhatsThisRole, "%s" % values)
        item.setStatusTip(0, "%s|%s" % (category, fileId)) if parent else item.setStatusTip(0, "%s|%s" % (category, parentname))
        item.setExpanded(True)
        item.setFlags(self.settings[category][1])
        
        if type(iconlayer) == str : iconlayer = QIcon(iconlayer)
        item.setIcon(0, iconlayer)
   
        return item

    def removeItemWithId(self, groupText, layerId):
        group = self.getGroupParent(groupText)
        if group :
           for i in range(group.childCount()):
               item = group.child(i)
               infos = item.statusTip(0).split("|")
               if layerId == infos[1] :
                  group.removeChild(item)
                  break

#END NEW 3.4.0
                
def translateMsg(message): return QCoreApplication.translate('autoLoaderRasterPlugin', message)

def convertSize(size):
   size_name = ("", "K.", "M.", "G.", "T.", "P.", "E.", "Z.", "Y.")
   i = 0
   if size > 0 :
      i = int(floor(log(size, 1000)))
      p = pow(1000, i)
      s = round(size/p, 2)
   else :
      s = 0
   return '%s %s ' % (s, size_name[i])
    
##### ======= FUNCTIONS QTREEWIDGET ======== ####
def visitTreeElt(listItems, item, level, maxlevel):
  listItems.append([level, item.text(0), item.whatsThis(0), item.text(1)])
  for i in range(item.childCount()):
      if maxlevel < level+1 : maxlevel = level+1  
      maxlevel = visitTreeElt(listItems, item.child(i), level+1, maxlevel)
  return maxlevel    

def visitTreeList(tree) :
  listItems = []
  maxlevel = 0
  for i in range(tree.topLevelItemCount()) :
      maxlevel = visitTreeElt(listItems, tree.topLevelItem(i), 0, 1)
  return listItems, maxlevel
    
def legendSymbolItemChecked(node, ruleKey):
    isCheckedElt = False
    if Qgis.QGIS_VERSION_INT <= 31299 : isCheckedElt = node.layer().renderer().legendSymbolItemChecked(ruleKey)
    else :
          try : isCheckedElt = node.layer().featureRenderer().legendSymbolItemChecked(ruleKey)
          except : isCheckedElt = node.layer().renderer().legendSymbolItemChecked(ruleKey) #NEW AFTER QGIS 3.4
    return isCheckedElt

def conditionnalAddChild(treeWidget, parent, legendItem) :    
    if not parent : treeWidget.addTopLevelItem(legendItem) 
    else : parent.addChild(legendItem)

def createTreeWidgetItem(iconPath, icon, text):
    legendItem = QTreeWidgetItem()
    legendItem.setIcon(0, icon)
    legendItem.setText(0, text)
    legendItem.setWhatsThis(0, iconPath)
    return legendItem    
##### ======= END FUNCTIONS QTREEWIDGET ======== ####    

##### =======    FUNCTIONS LISTLAYERS   ======== #### 
def getListLayers(self, targetList, model, comboBox):
   root = QgsProject.instance().layerTreeRoot()
   targetList.clear()
   comboBox.clear()
   
   targetList[0], maxDim, dim = [], 0, 400

   i = 0
   if root != None :
       nameGroup = ""
       for layer in root.findLayers() : 
           if type(layer.layer()) == QgsRasterLayer :
              if nameGroup != layer.parent().name():
                 nameGroup = layer.parent().name() 
                 try : comboBox.addGroup(model, nameGroup)
                 except : pass
              if not dicoHasKey(targetList, layer.layerId()):
                 dim = addItemInModel(self, targetList, model, layer)
                 maxDim = max(maxDim, dim)
   
   comboBox.setModelColumn(0)
   comboBox.show()
   comboBox.setCurrentIndex(-1)
   
   return maxDim

def addItemInModel(self, targetList, model, layer):
   item = QStandardItem(QIcon(getThemeIcon("raster.png")), "%s, [ %s ]" % (layer.name(), layer.layer().crs().authid()))
   item.setData("%s" % layer.layerId(), Qt.ItemDataRole.UserRole)
   font = QFont('Times New Roman', 9)
   font.setItalic(True)        
   item.setFont(font)   
   model.appendRow(item) 
   targetList[layer.layerId()] = []
   return len(layer.name())

def getComboLayer(self, comboBox, index):
   targetLayer = None
   if index != -1 :
       modelIndex = comboBox.model().index(index, 0)
       layerId = comboBox.model().data(modelIndex, Qt.ItemDataRole.UserRole)
       root = QgsProject.instance().layerTreeRoot()
       if root != None :
               for layer in root.findLayers() :
                   if layer.layerId() == layerId: targetLayer = layer.layer(); break;
   return targetLayer

def getComboLayerIndex(self, comboBox, text):
   model, index = comboBox.model(), 0
   for i in range(comboBox.count()):
       if model.item(i).text() == text :    
          index = i  
          break
   return index
##### =======  END FUNCTIONS LISTLAYERS ======== ####


def textForMenu(pattern, text, maxDim, shorcuts):
    return pattern % (text, shorcuts.ljust(maxDim-len(text)-len(shorcuts))) if pattern else text 

def fixeMaxDim(listItems):
    maxDim = 40
    if listItems != []:
       for item in listItems: maxDim = max(maxDim, len(item))
    return maxDim

def iff(a, b, c):
    if a: return b
    else: return c

def makeListFormatsExtensions(self, vector, raster, includeJoker):
    listFormatsExtensions, listFormatsExtensionsVector, listFormatsExtensionsAll = [], [], []

    for iDriver in range(ogr_GetDriverCount()):
        poDriver = ogr_GetDriver(iDriver)
        poValue = poDriver.GetName()
        driver = gdal_GetDriverByName("%s" % (poValue))
        listFormatsExtensionsVector = setEltListFormatsExtensions(self, listFormatsExtensionsVector, driver, poValue)
        

    for iDriver in range(gdal_GetDriverCount()):
        poDriver = gdal_GetDriver(iDriver)
        poValue = poDriver.ShortName
        driver = gdal_GetDriverByName("%s" % (poValue))
        listFormatsExtensionsAll = setEltListFormatsExtensions(self, listFormatsExtensionsAll, driver, poValue)

    if vector and raster : listFormatsExtensions =  listFormatsExtensionsAll
    else :
        if vector : listFormatsExtensions = listFormatsExtensionsVector
        elif raster :
            listFormatsExtensions = [value for value in listFormatsExtensionsAll if value not in listFormatsExtensionsVector] 
            if not "jp2" in listFormatsExtensions : listFormatsExtensions.append('jp2')
            
    if includeJoker : listFormatsExtensions.append('*')
    return sorted(listFormatsExtensions)

def setEltListFormatsExtensions(self, listFormatsExtensions, driver, poValue):
       if driver :
            poDriverMetadata = driver.GetMetadata()
            if dicoHasKey(poDriverMetadata, DMD_EXTENSION):
                if poDriverMetadata[DMD_EXTENSION]!= '' and not poDriverMetadata[DMD_EXTENSION] in  listFormatsExtensions : listFormatsExtensions.append(poDriverMetadata[DMD_EXTENSION])
       else :
           if poValue != '' and not poValue in  listFormatsExtensions : listFormatsExtensions.append[poValue]
       return listFormatsExtensions


def getExtension(fileName):
    from os.path import (basename, splitext)
    textension = splitext(fileName)
    extension = textension[len(textension)-1].lower()
    return extension

def windowIcon(qtWindow, icon):
    if not qtWindow : return
    icon = getThemeIcon(icon)
    if icon != "" : qtWindow.setWindowIcon(QIcon(icon)) 

def changeButtonIcon(self, button, icon, iconWidth, iconHeight):
    icon = getThemeIcon(icon)
    button.setIcon(QIcon(icon))

def getThemeIcon(theName):
    dirs = list(sysPath)
    dirs.insert(0, "%s%s" % (cleanPath(dirname(__file__)),"icons/"))
    dirs.insert(0, "%s%s" % (cleanPath(QgsApplication.pluginPath()),"autoLoaderRaster/icons/"))
    for dir in dirs :
        myPath = cleanPath(dir)
        myIconPath = "%s%s" % (myPath, theName)
        if QFile.exists(myIconPath): return myIconPath
    return ""
    
def cleanPath(path):
    path = "%s" % (path)
    a = len(path)
    letter = "/"
    b = path.rfind(letter, 0, a)
    return ("%s/" % (path)) if a != b else path 
    
def fixeLabelsFileDialog(self, dlgFile, context, hasName):
    dlgFile.setLabelText(QFileDialog.DialogLabel.Accept, translateMsg("Open")) if context == 0 else dlgFile.setLabelText(QFileDialog.DialogLabel.Accept, translateMsg("Save"))
    dlgFile.setLabelText(QFileDialog.DialogLabel.Reject, translateMsg("Cancel"))        
    dlgFile.setLabelText(QFileDialog.DialogLabel.FileName, "%s : " % (translateMsg("Name"))) if hasName else dlgFile.setLabelText(QFileDialog.DialogLabel.FileName, translateMsg("Folder : "))
    dlgFile.setLabelText(QFileDialog.DialogLabel.FileType, "%s : " % translateMsg("Filters"))
    dlgFile.setLabelText(QFileDialog.DialogLabel.LookIn,   "%s : " % translateMsg("Folder"))

def dicoHasKey(dico, key):
    cond = False
    try : cond = dico.has_key(key)
    except : cond = (key in dico)
    return cond

def prettifyXml(xml):
    return parseString(xml).toprettyxml()


class textEditFolder(QTextEdit):
    def __init__(self, * args):
        QTextEdit.__init__(self, * args)

    def initTextEdit(self, SizeW, SizeH, posX, posY, name, isGeom, undoRedo, isReadOnly, isRichText, isSizable):
        if not isSizable:  self.setMaximumSize(SizeW, SizeH)
        if isGeom: self.setGeometry(posX, posY, SizeW, SizeH)
        self.setUndoRedoEnabled(undoRedo)
        self.setReadOnly(isReadOnly)
        self.setAcceptRichText(isRichText)
        self.setObjectName(name)
        self.setAccessibleName(name)
        self.setAcceptDrops(True)

class editor(QTextEdit):
    def __init__(self, parent, withProgressBar):
        super(editor, self).__init__(parent)
        self.setWindowTitle("QTextEdit Standard Output Redirection")
        self.setStyleSheet("color: white; background-color: black;")
        self.setAcceptRichText(True)
        self.setObjectName(parent.objectName())
        self.parent = parent
        self.progressBar = None

        if withProgressBar :
            self.progressBar = progressBar(self)
            self.progressBar.initTextEdit(200, 25, 200, 140, "progressBar", True, 0, True, Qt.AlignmentFlag.AlignCenter, 0)
            self.progressBar.setStyleSheet("progressBar {border: 2px solid grey; border-radius: 5px; text-align: center;}"
                                            "progressBar::chunk {background-color: #6C96C6; width: 20px;}")


class funnyPushButton(QPushButton):
     def __init__(self, *args): super(funnyPushButton, self).__init__(*args)
            
     def initPushButton(self, sizeWidth, sizeHeight, coordX, coordY, name, text, toolTip, isGeom, icon, iconWidth, iconHeight, isStyleSheeted):
            self.setMinimumSize(sizeWidth, sizeHeight)
            self.setMaximumSize(sizeWidth, sizeHeight)
            self.iconWidth = iconWidth
            self.iconHeight = iconHeight
            self.selfFocused = False
            self.subMenuVisble = False
            if isGeom : self.setGeometry(coordX, coordY, sizeWidth, sizeHeight)
            if icon != "" :
               self.setIcon(QIcon(icon))
               self.setIconSize(QSize(self.iconWidth , self.iconHeight))
            self.setToolTip(toolTip)
            if isStyleSheeted : self.setStyleSheet(" QPushButton {border-width: 0px; border-radius: 10px;  border-color: beige;}")
            self.setObjectName(name)
            if text != "" : self.setText(text)

     def enterEvent(self, event):
          if not self.selfFocused :
             #NEW 3.4.1 - QSIZE BUG QGIS >= 3.36 
             self.setIconSize(QSize(int(self.iconWidth *0.8), int(self.iconHeight *0.8)))
             self.selfFocused = True
          
     def leaveEvent(self, event):
        if self.subMenuVisble == False : self.restoreButton()
     
     def restoreButton(self):
           self.selfFocused = False    
           self.setIconSize(QSize(self.iconWidth, self.iconHeight))
           
      
class labeller(QLabel):
     def __init__(self, name, text, styleSheet, alignment, *args):
             super(labeller, self).__init__(*args)
             self.setObjectName(name)
             self.setText(text)
             self.setMyStyleSheet(styleSheet)
             """
             if styleSheet == "special": self.setStyleSheet("QLabel {display: block;  border: 2px solid gray;  border-radius: 10px; text-align: center;  line-height: 10px; background-color: #C0C0C0; border: 1px solid black; color: black}")
             elif styleSheet == "infos": self.setStyleSheet("QLabel {font-size:10px; background-color:#C0C0C0; border: 1px solid #e1e1e1; padding: 4px ; color: black}")
             elif styleSheet == "coords": self.setStyleSheet("QLabel {  color : white; font-size: 11px; background-color: rgba(93, 93, 93, 125);}")
             """
             self.setAlignment(alignment)

     #NEW 3.0.0
     def setMyStyleSheet(self, style):
         styleSheet = {"special": "QLabel {display: block;  border: 2px solid gray;  border-radius: 10px; text-align: center;  line-height: 10px; background-color: #C0C0C0; border: 1px solid black; color: black}",
                       "infos": "QLabel {font-size:10px; background-color:#C0C0C0; border: 1px solid #e1e1e1; padding: 4px ; color: black}",
                       "normal": "QLabel {font-size:10px; background-color:#C0C0C0; border: 1px solid #e1e1e1; padding: 4px ; color: black}",
                       "coords": 'QLabel {color : white; font-size: 11px; background-color: rgba(93, 93, 93, 125)}',
                       "error" : "QLabel {display: block;  border: 2px solid gray;  border-radius: 10px; text-align: center;  line-height: 10px; background-color: #CC0000; border: 1px solid red; color: white}"}

         styleCss = styleSheet[style]
         self.setStyleSheet(styleCss)
      
         
         

class myPushButton(QPushButton):
     def __init__(self, *args): super(myPushButton, self).__init__(*args)
            
     def initPushButton(self, sizeWidth, sizeHeight, coordX, coordY, name, text, toolTip, isGeom, icon, iconWidth, iconHeight, isStyleSheeted):
            self.setMinimumSize(sizeWidth, sizeHeight)
            self.setMaximumSize(sizeWidth, sizeHeight)
            if isGeom : self.setGeometry(coordX, coordY, sizeWidth, sizeHeight)
            if icon != "" :
               self.setIcon(QIcon(icon))
               self.setIconSize(QSize(iconWidth, iconHeight))
            self.setToolTip(toolTip)
            if isStyleSheeted : self.setStyleSheet("QPushButton {border: none;}")
            self.setObjectName(name)
            if text != "" : self.setText(text)

class progressBar(QProgressBar):
    def __init__(self, * args):
        QProgressBar.__init__(self, * args)

    def initTextEdit(self, width, heigth, posX, posY, name, isGeom, initValue, viewText, alignement, isStyleSheet):
        self.setMinimumSize(width, heigth)
        self.setMaximumSize(width, heigth)
        if isGeom: self.setGeometry(posX, posY, width, heigth)
        self.setProperty("value", initValue)
        self.setAlignment(alignement)
        self.setTextVisible(viewText)
        self.setObjectName(name)
        self.setAccessibleName(name)
        if isStyleSheet == 0:
            self.setStyleSheet(
                               "progressBar {border: 2px solid grey; border-radius: 5px; text-align: center;}"
                               "progressBar::chunk {background-color: #6C96C6; width: 10px; margin: 0.5px;}"
                               )            


class printerDlg(object):
      def __init__(self, parent, obj, title, orientation):
                    
              printer = QPrinter() 
              printer.setPageSize(QPageSize.A4)
              printer.setOrientation(orientation)
              printer.setPageMargins(5, 10, 5, 10, QPrinter.Unit.Millimeter)
              printer.setOrientation(orientation)
              printer.setOutputFormat(QPrinter.OutputFormat.NativeFormat)
              self = QPrintPreviewDialog(printer)
              windowIcon(self, "print.png")
              self.setWindowTitle(title)
              self.setWindowFlags(Qt.WindowType.WindowMaximizeButtonHint | Qt.WindowType.WindowStaysOnTopHint | Qt.WindowType.WindowCloseButtonHint)
              self.paintRequested.connect(obj.print_)
              self.exec()


class saveDlg(QDialog):
    def __init__(self, parent, initDir):
        QDialog.__init__(self)
        self.parent = parent
        self.initDir = initDir
        self.setupUi()

    def setupUi(self):
        self.setObjectName("saveDlg")
        self.maxWidth, self.maxheight = 620, 200 
        self.setMinimumSize(self.maxWidth,self.maxheight)
        self.setMaximumSize(self.maxWidth,self.maxheight)        
        self.resize(self.maxWidth, self.maxheight) 
        windowIcon(self, "checkdoubleglasses.png")

        self.setWindowTitle("%s ..." % translateMsg("Save maps as images"))

        self.styleTextEdit = """
                    color: black; background-color: rgba(255, 255, 125, 50);
                    border-left-width: 1px;
                    border-left-color: black;
                    border-left-style: solid;
                    border-right-color: black;
                    border-right-width: 1px;
                    border-right-style: solid;
                    border-right-radius: 3px;"""

        self.styleTextEditDisaBled  = """
                    color: black; background-color: rgba(125, 125, 125, 50);
                    border-left-width: 1px;
                    border-left-color: black;
                    border-left-style: solid;
                    border-right-color: black;
                    border-right-width: 1px;
                    border-right-style: solid;
                    border-right-radius: 3px;"""

        self.authorizedMime = ["*.*"]


        icon = getThemeIcon("savefile.png")
        toolTip = translateMsg("Save maps as images")
        self.saveButton = funnyPushButton(self)
        self.saveButton.initPushButton(24, 24, self.width()-160, self.height()-30, "saveButton", "", toolTip, True, icon, 24, 24, True)
        self.saveButton.setShortcut(QKeySequence("Ctrl+S"))

        icon = getThemeIcon("close.png")
        toolTip = translateMsg("Close")
        self.closeButton = QPushButton(self)
        self.closeButton.setObjectName("closeButton")
        self.closeButton.setText("&%s" % translateMsg("Close"))
        self.closeButton.setGeometry(self.width()-120, self.height()-30, 100, 25)


        self.lblNameFirstImage = QLabel(self)
        self.lblNameFirstImage.setAlignment(Qt.AlignmentFlag.AlignRight)
        self.lblNameFirstImage.setText("%s %s 1 :" % (translateMsg("Name"), translateMsg("MapCanvas").lower()))
        self.lblNameFirstImage.setGeometry(20, 20, 120, 25)


        self.listExtension = QComboBox(self)
        self.listExtension.setGeometry(520, 20, 60, 25)
        self.listExtension.setEditable(False) 
        self.listExtension.setEnabled(True)
        self.listExtension.setObjectName("listExtension")
        #listExtensions = makeListFormatsExtensions(self, False, True, False)
        listExtensions = ["bmp", "jpg", "png", "tif"]
        self.listExtension.addItems(listExtensions)

        if "png" in listExtensions : self.listExtension.setCurrentIndex( listExtensions.index("png"))
                

        self.nameFirstImage = lineEditNameFile(self)
        self.nameFirstImage.initCtrlWidget(self.saveButton, self.listExtension)
        self.nameFirstImage.setGeometry(150, 20, 360, 25)
        self.nameFirstImage.setObjectName("nameFirstImage")
        self.nameFirstImage.setStyleSheet(self.styleTextEdit)
        self.nameFirstImage.setText("mapviewer1.png")
        self.nameFirstImage.textChanged.connect(self.nameFirstImage.checkRegExp)
        self.nameFirstImage.checkRegExp()

        self.lblNameSecondImage = QLabel(self)
        self.lblNameSecondImage.setAlignment(Qt.AlignmentFlag.AlignRight)
        self.lblNameSecondImage.setText("%s %s 2 :" % (translateMsg("Name"), translateMsg("MapCanvas").lower()))
        self.lblNameSecondImage.setGeometry(20, 50, 120, 25)


        self.nameSecondImage = lineEditNameFile(self)
        self.nameSecondImage.initCtrlWidget(self.saveButton, self.listExtension)
        self.nameSecondImage.setGeometry(150, 50, 360, 25)
        self.nameSecondImage.setObjectName("nameSecondImage")
        self.nameSecondImage.setStyleSheet(self.styleTextEdit)
        self.nameSecondImage.setText("mapviewer2.png")
        self.nameSecondImage.textChanged.connect(self.nameSecondImage.checkRegExp)
        self.nameSecondImage.checkRegExp()
        
        self.lblFolder = QLabel(self)
        self.lblFolder.setText("%s :" % translateMsg("Folder"))
        self.lblFolder.setAlignment(Qt.AlignmentFlag.AlignRight)
        self.lblFolder.setGeometry(20, 80, 120, 25)
 
        self.pathToSaveImage = textEditFolder(self)
        self.pathToSaveImage.initTextEdit(360, 40, 150, 80,  "pathToSaveImage", True, False, True, False, True)
        self.pathToSaveImage.setStyleSheet(self.styleTextEditDisaBled)
        self.pathToSaveImage.setText(self.initDir)

        self.getFolderButton = funnyPushButton(self)
        self.getFolderButton.initPushButton(28, 28, 520, 80, "getFolderButton", "", translateMsg("Select a folder"), True, getThemeIcon("folder.png"), 28, 28, True)

        self.getFolderButton.clicked.connect(self.getFolder)

        self.listExtension.currentTextChanged.connect(self.updateExtension)
        self.saveButton.clicked.connect(self.createFilesImages)
        self.closeButton.clicked.connect(self.close)

    def updateExtension(self):
        widgets = (self.nameFirstImage, self.nameSecondImage)
        for widget in widgets :
            if widget.text().find(".") == 0 : widget.setText("%s.%s" % (widget.text(), self.listExtension.currentText()))
            else :
               extension = widget.text().split(".")[-1]
               widget.setText("%s%s" % (widget.text()[:-3], self.listExtension.currentText()))
            widget.checkRegExp()              
        

    def closeEvent(self, event):
        self.parent.initDir = self.initDir

    def getFolder(self):
        if self.sender() == None: return
        if self.sender() == self.getFolderButton : obj = self.pathToSaveImage
        else: return

        windowIcon(self, "folder.png")

        self.initDir = dirname(__file__) if obj.toPlainText() == "" else dirname(obj.toPlainText())
        title = translateMsg("Select the folder")

        fileDialog = QFileDialog(self, title)
        fileDialog.setDirectory(self.initDir)
        fileDialog.setFileMode(QFileDialog.FileMode.Directory)
        fileDialog.setOption(QFileDialog.Option.ShowDirsOnly, False)
        #NEW 3.6.0
        #fileDialog.setOption(QFileDialog.Option.DontUseNativeDialog)
        fileDialog.setNameFilters(self.authorizedMime)
        fileDialog.selectNameFilter(self.authorizedMime[0])

        fixeLabelsFileDialog(self, fileDialog, 0, False) 

        if fileDialog.exec():
            inputDir = fileDialog.selectedFiles()[0]
            obj.setPlainText(cleanPath(inputDir.replace("\\", "/")))
            self.initDir = obj.toPlainText()

        
    def createFilesImages(self):

        folder = self.pathToSaveImage.toPlainText()
        fileName1 = self.nameFirstImage.text()
        fileName2 = self.nameSecondImage.text()
        extension = self.listExtension.currentText().upper()

        if folder == "" : return
        if fileName1 == fileName2 : return

        fileName = join(folder, fileName1) 
        self.parent.mapCanvas1.saveAsImage(fileName, None, extension)

        fileNameDouble = join(folder , fileName2) 
        self.parent.mapCanvas2.saveAsImage(fileNameDouble, None, extension)

        return fileName, fileNameDouble


class lineEditNameFile(QLineEdit):
      def __init__(self, *args):
          super(lineEditNameFile, self).__init__(*args)
          self.regex = QRegularExpression(r"^[\w\-\. ]+$", Qt.CaseSensitivity.CaseSensitive)
          
      def initCtrlWidget(self, widgetCtrl, extension):
          self.widgetCtrl = widgetCtrl
          self.extension = extension

      def checkRegExp(self):
          self.setStyleSheet("background-color:red;")
          self.widgetCtrl.setEnabled(False)
          cond = self.regex.exactMatch(self.text())
          if self.text().find(".") == 0 : return
          extension = self.text().split(".")[-1]
          if cond and extension.lower() == self.extension.currentText().lower() : #"png" :
             self.setStyleSheet("background-color:#AEEE00;")
             self.widgetCtrl.setEnabled(True)
             
          
#NEW 3.0.0
class myListWidget(QListWidget):
    def __init__(self, parent=None):
        QListWidget.__init__(self, parent)
        self.parent = parent.parent()

    def dropEvent(self, event):
        super(myListWidget, self).dropEvent(event)
        self.parent.updateListFiles()
        self.parent.updateMapViewerWithGrid()

class textEdit(QTextEdit):
      def __init__(self, parent, *args):
          super(textEdit, self).__init__(*args)
          self.parent = parent
          
      def focusOutEvent(self, event): self.parent.updateInfo(self)


class myMenuStyle(QProxyStyle):
    pass
    def pixelMetric(self, QStyle_PixelMetric, option=None, widget=None):
        if QStyle_PixelMetric == QStyle.PixelMetric.PM_SmallIconSize: return  20 
        else : return QProxyStyle.pixelMetric(self, QStyle_PixelMetric, option, widget)



class wizardData(QDialog):
    def __init__(self, parent):
        QDialog.__init__(self)
        self.parent = parent
        self.setupUi()
    
    def setupUi(self):
        self.setObjectName("ui_wizardData")
        self.maxWidth, self.maxheight = 400, 350 
        self.setMinimumSize(self.maxWidth, self.maxheight)
        self.setMaximumSize(self.maxWidth, self.maxheight)
        self.resize(self.maxWidth, self.maxheight) 
        windowIcon(self, "bggroup.png")

        self.setWindowTitle(translateMsg("Select group raster"))


        self.groupsViewer = None
        self.groupIconPath = getThemeIcon("group.png").replace("\\","/")
        self.groupIcon = QIcon(self.groupIconPath)

        self.applyButton = QPushButton(self)
        self.applyButton.setObjectName("applyButton")
        self.applyButton.setText(translateMsg("Validate"))

        self.closeButton = QPushButton(self)
        self.closeButton.setObjectName("closeButton")
        self.closeButton.setText(translateMsg("Close"))

        self.closeButton.clicked.connect(self.close)

        sender = self.parent.sender()        
        
        self.getGroupsViewer(True)
        if self.groupsViewer :
           self.identifyGroup(self.parent.groupRaster.toPlainText())  
           self.groupsViewer.itemDoubleClicked.connect(self.setValueForGroupRaster)
           self.applyButton.clicked.connect(self.setValueForGroupRaster)


    def getGroupsViewer(self, getGroups):
        self.groupsViewer = QTreeWidget(self)
        self.groupsViewer.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
        self.groupsViewer.setObjectName("groupsViewer")
        self.groupsViewer.headerItem().setText(0, "1")
        self.groupsViewer.header().setVisible(False)
        self.groupsViewer.header().setDefaultSectionSize(200)
        self.groupsViewer.setStyleSheet("QTreeWidget { font-size:12px; color: black; background-color: rgba(255, 255, 125, 50); border-left-width: 1px; border-left-color: black;  border-left-style: solid;  border-right-color: black; border-right-width: 1px; border-right-style: solid; border-right-radius: 3px;}"
                                       "QTreeWidget::item:selected {background-color: #5D5D5D; color: rgb(255,255,255); }")
        self.groupsViewer.setVisible(True)
        self.groupsViewer.setItemsExpandable(True)
        self.groupsViewer.setExpandsOnDoubleClick(True)
        self.groupsViewer.setContextMenuPolicy(Qt.ContextMenuPolicy.NoContextMenu)

        self.populateGroupsViewer(getGroups)


    def identifyGroup(self, txt) :
        if txt != "" :
           items = self.groupsViewer.findItems(txt, Qt.MatchContains|Qt.MatchRecursive, 0) 
           if items :
              if items[0] : self.groupsViewer.setCurrentItem(items[0])

    
    def setValueForGroupRaster(self):
        self.parent.groupRaster.setPlainText(self.groupsViewer.currentItem().text(0))
        self.parent.updateInfo(self.parent.groupRaster)
        self.close()

    def introspectNodes(self, group, nodes, parent, getGroups):
        for node in nodes:
                if QgsLayerTree.isGroup(node): 
                   legendItem = createTreeWidgetItem(self.groupIconPath, self.groupIcon, node.name())
                   conditionnalAddChild(self.groupsViewer, parent, legendItem)
                   legendItem.setExpanded(node.isExpanded())
                   self.introspectNodes(node, node.children(), legendItem, getGroups)        


    def populateGroupsViewer(self, getGroups):
        if not self.groupsViewer : return
        self.groupsViewer.clear()
        root = QgsProject.instance().layerTreeRoot() 
        if getGroups : self.introspectNodes(root, root.children(), None, getGroups)
        else :
            listLayers = QgsProject.instance().mapLayers()
            dictLayers = {}
            for key in listLayers :
                layer = listLayers[key]
                if not layer.name() in dictLayers.keys() : dictLayers[layer.name()]=key
            
            iconTypes = {0: (self.pointIcon, self.pointIconPath) , 1 : (self.lineIcon, self.lineIconPath),  2: (self.polygonIcon, self.polygonIconPath)}
        
            for layerName in sorted(dictLayers) :
                key = dictLayers[layerName]
                layer = listLayers[key]
                if layer.name()!= "tempo" and type(layer) == QgsVectorLayer :
                   
                   typeGeom = layer.geometryType()
                   if dicoHasKey(iconTypes, typeGeom) : icon, iconPath = iconTypes[typeGeom][0], iconTypes[typeGeom][1]
                   else  :  icon, iconPath = self.unknowIcon, self.unknowIconPath
                    
                   legendItem = createTreeWidgetItem(iconPath, icon, layer.name())
                   conditionnalAddChild(self.groupsViewer, None, legendItem)    

    def close(self): self.reject()

    def resizeEvent(self,ev):
        self.groupsViewer.setGeometry(10, 10, self.width()-20, self.height()-60)
        self.applyButton.setGeometry(self.width()-230, self.height()-35, 100, 25)
        self.closeButton.setGeometry(self.width()-120, self.height()-35, 100, 25)
        


        
class GroupDelegate(QStyledItemDelegate):
    def initStyleOption(self, option, index):
        super(GroupDelegate, self).initStyleOption(option, index)
        if not index.data(Qt.ItemDataRole.UserRole):
            option.text = "   %s" % option.text


class GroupItem(QStandardItem):
    def __init__(self, text):
        super(GroupItem, self).__init__(text)
        self.setData(True, Qt.ItemDataRole.UserRole)
        self._number_of_childrens = 0
        font = QFont('Times New Roman', 10)
        font.setBold(True)
        font.setUnderline(True)
        font.setOverline(True)
        self.setFont(font)
        self.setFlags(self.flags() & ~Qt.ItemFlag.ItemIsSelectable)

    def addChild(self, text):
        it = QStandardItem(text)        
        it.setData(False, Qt.ItemDataRole.UserRole)
        self._number_of_childrens += 1
        self.model().insertRow(self.row() + self._number_of_childrens, it)
        return it

class GroupComboBox(QComboBox):
    def __init__(self, parent=None):
        super(GroupComboBox, self).__init__(parent)
        self.setModel(QStandardItemModel(self))
        delegate = GroupDelegate(self)
        self.setItemDelegate(delegate)
        self.setIconSize(QSize(24, 24))
        self.iconGroup = QIcon(getThemeIcon("group.png"))
        self.iconChild = QIcon(getThemeIcon("raster.png"))

    def addGroup(self, model, text):
        it = GroupItem(text)
        it.setBackground(QBrush(QColor(25, 230, 240, 200)))
        model.appendRow(it)
        self.setItemIcon(self.count()-1, self.iconGroup)
        return it

    def addChild(self, text):
        it = QStandardItem(text)
        it.setData(True, Qt.ItemDataRole.UserRole)
        self.model().appendRow(it)
        self.setItemIcon(self.count()-1, self.iconChild)
        return it        

#NEW 3.3.0
def getGroupForTheLayer(mapCanvasLayer):      
    root = QgsProject.instance().layerTreeRoot()
    groupLayer = None
    if not mapCanvasLayer : return None
    if not isinstance(mapCanvasLayer, QgsRasterLayer) : return None
    for layer in root.findLayers() :
        if isinstance(layer.layer(), QgsRasterLayer) :                
           if layer.layer().id() == mapCanvasLayer.id() :
              if type(layer.parent()) == QgsLayerTreeGroup : groupLayer = layer.parent() ; break
    return groupLayer
    
class layersComboBox(QFrame):
    def __init__(self, parent, objectName, mapViewerLayer, scanVisibility):
        super(layersComboBox, self).__init__(parent)
        self.parent = parent
        self.objectName = objectName
        self.mapViewerLayer = mapViewerLayer
        self.scanVisibility = scanVisibility
        self.imgProj = join(dirname(__file__), "icons/projection.png")
        self.imgGroupe = join(dirname(__file__), "icons/group.png")
        
        self.layer = None
        self.layerGroup = None
        self.includeGroup = False
        
        self.setStyleSheet("QFrame{color: black; background-color: rgba(255, 255, 125, 50); border: 1px solid; border-radius: 5px; background-color: rgba(255, 255, 255, 210);")

        self.layerLabel = QTextEdit(self)
        self.layerLabel.setText("%s !" % translateMsg("Please select a layer"))
        self.layerLabel.setEnabled(False)
        self.layerLabel.setStyleSheet("QTextEdit{font-size:10px; color: black; background-color: rgba(255, 255, 125, 50); border-left-width: 1px; border-left-color: black;  border-left-style: solid;  border-right-color: black; border-right-width: 1px; border-right-style: solid; border-right-radius: 3px;}")
        self.installEventFilter(self)   
        
        self.buttonSelLayer = funnyPushButton(self) 
        self.buttonSelLayer.initPushButton(20, 20, 40, 90, "buttonSelLayer", "", "%s : %s" % (translateMsg("Select Master layer for Map"), self.mapViewerLayer.objectName()), True, getThemeIcon("layers-icon.png"), 18, 18, True)
        self.buttonSelLayer.clicked.connect(self.getLayerForCombo)

    def eventFilter(self, obj, event):
        if obj :
           if obj == self :
              #NEW 4.0.0 
              try :
                if event.type() == QEvent.Type.MouseButtonDblClick :self.buttonSelLayer.clicked.emit(); return True
              except : pass  
        return False

    def setLayer(self):
        if self.layer :           
           self.layerGroup = getGroupForTheLayer(self.layer)
           self.layerLabel.setHtml("<table><tr valign='middle'><td valign='middle'><b>%s</b> %s : %s, %s : %s</td></tr></table>" % (self.layer.name(),
                                                                     "<img src='file:///{}' width='{}' height='{}'>".format(self.imgProj, 13, 13),
                                                                     self.layer.crs().authid(),
                                                                     "<img src='file:///{}' width='{}' height='{}'>".format(self.imgGroupe, 20, 20),
                                                                     self.layerGroup.name() if self.layerGroup else "Aucun" ))
           self.layerLabel.setToolTip(self.layer.id())
           self.mapViewerLayer.refresh() 
          
    def setLayerGroup(self) :
        if self.layer : self.layerGroup = getGroupForTheLayer(self.layer)

    def getLayerName(self) : return self.layer.name()
    def getLayerSource(self) : return self.layer.source()
    def getLayerCrs(self) : return self.layer.crs()
    def getLayerGroup(self) : return self.layerGroup

    def resizeEvent(self, event):
        self.layerLabel.setGeometry(5, 2, self.width(), 25)
        self.buttonSelLayer.setGeometry(self.layerLabel.width()-35, 5, 25, 25)
        

    def getLayerForCombo(self) :
        self.dialogLayer = myLayerTreeView(self, self.parent, self.mapViewerLayer, self.scanVisibility, self.layer) 
        self.dialogLayer.exec()




class myLayerTreeView(QDialog):
      
      def __init__(self, parent, ihm, mapViewerLayer, scanVisibility, layer):
        super(myLayerTreeView, self).__init__()  
        self.setObjectName("uiMyLayerTreeView")
        self.parent = parent
        self.ihm = ihm
        self.mapViewerLayer = mapViewerLayer
        self.layer = layer
        
        self.resize(500, 500)
        self.setWindowFlags(Qt.WindowType.WindowMinimizeButtonHint | Qt.WindowType.WindowMaximizeButtonHint | Qt.WindowType.WindowCloseButtonHint)
        self.setWindowTitle(translateMsg("Double glass viewer"))
        windowIcon(self, "layers-icon.png")
        self.setWindowTitle(self.parent.sender().toolTip())


        self.groupIconPath = getThemeIcon("group.png").replace("\\","/")
        self.groupIcon = QIcon(self.groupIconPath)
        self.iconPathVectorLayer = getThemeIcon("polygon.png").replace("\\","/")
        self.iconVectorLayer = QIcon(self.iconPathVectorLayer)
        self.iconPathRasterLayer = getThemeIcon("raster.png").replace("\\","/")
        self.iconRasterLayer = QIcon(self.iconPathRasterLayer)        
        self.iconPathRasterLayerOut = getThemeIcon("raster_out.png").replace("\\","/")
        self.iconRasterLayerOut = QIcon(self.iconPathRasterLayerOut)

        self.styleSheetPushButton = "QPushButton::checked {icon-size: 48px, 48px; border-width: 5px; border-radius: 6px; border-color: black; background-color : #58a41c; border-right-color: black;}"
        self.styleSheetPushButton+= "QPushButton {icon-size: 48px, 48px; border-width: 0px; border-radius: 10px; }"

        self.mapTarget = QButtonGroup(self)
        self.mapTarget.setExclusive(True)
        self.butMap1 = QPushButton("%s 1" % translateMsg("MapCanvas"), self)
        self.butMap1.setCheckable(True)        
        self.butMap1.setStyleSheet(self.styleSheetPushButton)
        self.butMap2 = QPushButton("%s 2" % translateMsg("MapCanvas"), self)
        self.butMap2.setCheckable(True)
        self.butMap2.setStyleSheet(self.styleSheetPushButton)
        self.mapTarget.addButton(self.butMap1, 1)
        self.mapTarget.addButton(self.butMap2, 2)

        self.butMap1.setChecked(True) if self.parent == self.ihm.listLayers1 else self.butMap2.setChecked(True)

        icon = getThemeIcon("reload.png")
        toolTip = translateMsg("Reload layers from current project")
        self.reloadData = funnyPushButton(self)
        self.reloadData.initPushButton(24, 24, self.width()-160, self.height()-30, "reloadData", "", toolTip, True, icon, 24, 24, True)

        self.buttonExpandAllNodes = funnyPushButton(self) 
        self.buttonExpandAllNodes.initPushButton(24, 24, self.width()-80, 5, "buttonExpandAllNodes", "", translateMsg("Expand all nodes"), True, getThemeIcon("nodes_expand.png"), 15, 15, True)

        self.buttonReduceAllNodes = funnyPushButton(self) 
        self.buttonReduceAllNodes.initPushButton(24, 24, self.width()-60, 5, "buttonReduceAllNodes", "", translateMsg("Close all nodes"), True, getThemeIcon("nodes_reduce.png"), 15, 15, True)

        icon = getThemeIcon("add.png")
        toolTip = "%s" % translateMsg("Add layers")
        self.addPermanentLayers = funnyPushButton(self)
        self.addPermanentLayers.initPushButton(24, 24, self.width()-160, self.height()-30, "addPermanentLayers", "", toolTip, True, icon, 24, 24, True)   

        icon = getThemeIcon("del.png")
        toolTip = "%s" % translateMsg("Delete layers")
        self.delPermanentLayers = funnyPushButton(self)
        self.delPermanentLayers.initPushButton(24, 24, self.width()-160, self.height()-30, "delPermanentLayers", "", toolTip, True, icon, 24, 24, True)  

        self.viewer = QTreeWidget(self)
        self.viewer.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
        self.viewer.setObjectName("viewer")
        self.viewer.headerItem().setText(0, "1")
        self.viewer.header().setVisible(False)
        self.viewer.header().setDefaultSectionSize(200)
        self.viewer.setStyleSheet("QTreeWidget { font-size:12px; color: black; background-color: rgba(255, 255, 125, 50); border-left-width: 1px; border-left-color: black;  border-left-style: solid;  border-right-color: black; border-right-width: 1px; border-right-style: solid; border-right-radius: 3px;}"
                                  "QTreeWidget::item:selected {background-color: #5D5D5D; color: rgb(255,255,255); }")
        self.viewer.setVisible(True)
        self.viewer.setItemsExpandable(True)
        self.viewer.setExpandsOnDoubleClick(True)
        self.viewer.setContextMenuPolicy(Qt.ContextMenuPolicy.NoContextMenu)

        self.includeGroup = QCheckBox(self)
        self.includeGroup.setText(translateMsg("Extend the resource to the group"))
        self.includeGroup.setEnabled(False) if self.parent == self.ihm.listLayers1 else self.includeGroup.setEnabled(True)
        self.includeGroup.setCheckState(Qt.CheckState.Unchecked) #False NEW 4.0.0             

        self.applyButton = QPushButton(self)
        self.applyButton.setObjectName("applyButton")
        self.applyButton.setText(translateMsg("Validate"))

        self.closeButton = QPushButton(self)
        self.closeButton.setObjectName("closeButton")
        self.closeButton.setText(translateMsg("Close"))
        self.closeButton.clicked.connect(self.close)

        self.populateGroupsViewer(True)

        self.viewer.itemDoubleClicked.connect(self.setValueForGroupRaster)
        self.includeGroup.stateChanged[int].connect(self.setValueForGroupRaster)
        self.applyButton.clicked.connect(self.setValueForGroupRaster)
        #NEW 4.0.0
        self.mapTarget.buttonClicked.connect(self.updatePopulate) #[int]
        self.buttonExpandAllNodes.clicked.connect(self.expandTreeWidget)
        self.buttonReduceAllNodes.clicked.connect(self.expandTreeWidget)                
        self.addPermanentLayers.clicked.connect(self.ihm.addLayersToMapCanvas)
        self.delPermanentLayers.clicked.connect(self.ihm.delLayersFromMapCanvas)
        self.reloadData.clicked.connect(self.updatePopulate)


      def expandTreeWidget(self):
          if self.sender().objectName() == "buttonExpandAllNodes" : self.viewer.expandAll()
          else : self.viewer.collapseAll()
      

      def updatePopulate(self, idButton):
          self.populateGroupsViewer(True)
          self.includeGroup.setEnabled(False) if idButton == 1 else self.includeGroup.setEnabled(True)
          self.setWindowTitle("%s : %s" % (translateMsg("Select Master layer for Map"), idButton))

      def introspectNodes(self, group, nodes, parent, getGroups):
        transformer, geomTargetExtent = None, None
        scanVisibility = self.butMap2.isChecked()
        if scanVisibility :
           geomTargetExtent = self.ihm.mapCanvas1.extent()
           if self.ihm.mapCanvas1.layers() :
              refCrs = self.ihm.mapCanvas1.layers()[0].crs()
              transformer = QgsCoordinateTransform()
              transformer.setDestinationCrs(refCrs)  

           
        for node in nodes:
            if QgsLayerTree.isGroup(node):
               if node.children() : 
                   legendItem = createTreeWidgetItem(None, self.groupIcon, node.name())
                   conditionnalAddChild(self.viewer, parent, legendItem)
                   legendItem.setExpanded(False)
                   self.introspectNodes(node, node.children(), legendItem, getGroups) 
            elif QgsLayerTree.isLayer(node):
                   if type(node) == QgsLayerTreeLayer : layer = node.layer()
                   elif type(node) == QgsLayerTreeNode :
                        try : layer = node.checkedLayers()[0]
                        except : layer = None
                   else : layer = None
                   icon = self.iconRasterLayer
                   if layer :
                       
                      if type(layer) == QgsRasterLayer :
                         if scanVisibility :
                            if transformer :
                               extent = layer.extent()
                               if layer.crs() != refCrs :
                                   transformer.setSourceCrs(layer.crs())
                                   extent = transformer.transform(extent, QgsCoordinateTransform.ForwardTransform)
                                   
                            if self.ihm.mapCanvas1.layers() :      
                               if not extent.intersects(geomTargetExtent) : icon = self.iconRasterLayerOut                            
                    
                         legendItem = createTreeWidgetItem(layer.id(), icon, node.name())
                         conditionnalAddChild(self.viewer, parent, legendItem)

                         if self.layer :  
                            if layer == self.layer : self.viewer.setCurrentItem(legendItem)
      
        
      def populateGroupsViewer(self, getGroups):
        if not self.viewer : return
        self.viewer.clear()
        root = QgsProject.instance().layerTreeRoot() 
        self.introspectNodes(root, root.children(), None, getGroups)

      def close(self): self.reject()
      
      def resizeEvent(self, event):
          self.butMap1.setGeometry(10, 10, 120, 30)
          self.butMap2.setGeometry(130, 10, 120, 30)
          self.buttonExpandAllNodes.setGeometry(260, 10, 25, 25)
          self.buttonReduceAllNodes.setGeometry(300, 10, 25, 25)          
          self.addPermanentLayers.setGeometry(340, 10, 25, 25)
          self.delPermanentLayers.setGeometry(380, 10, 25, 25)
          self.reloadData.setGeometry(420, 10, 25, 25)
          self.viewer.setGeometry(10, 60, self.width()-10, self.height()-130)
          self.includeGroup.setGeometry(10, self.height()-65, self.width()-10, 25) 
          self.applyButton.setGeometry(self.width()-230, self.height()-35, 100, 25)
          self.closeButton.setGeometry(self.width()-120, self.height()-35, 100, 25)


      def setValueForGroupRaster(self):
          if not self.viewer : return
          if not self.viewer.currentItem() : return
          if self.viewer.currentItem().whatsThis(0) :
             layer = QgsProject.instance().mapLayer(self.viewer.currentItem().whatsThis(0))
             if layer :
                #NEW 3.3.0
                self.parent = self.ihm.listLayers1 if self.butMap1.isChecked() else self.ihm.listLayers2
                self.parent.layer = layer
                self.parent.includeGroup = (self.includeGroup.isChecked() and self.includeGroup.isEnabled())
                self.parent.setLayer()

                
                
