from qgis.PyQt.QtCore import (QFileInfo, QPoint, QRectF, QSettings, QSizeF, Qt, pyqtSignal)
from qgis.PyQt.QtGui import (QBrush, QColor, QFont, QIcon, QImage, QPalette, QPixmap, QMovie, QStandardItem, QStandardItemModel, QTextDocument)
from qgis.PyQt.QtWidgets import (QAction, QApplication, QButtonGroup, QComboBox, QDialog, QFileDialog, QFrame, QGraphicsView, QGraphicsScene, QGraphicsPixmapItem,
                                 QGroupBox, QHBoxLayout, QInputDialog, QLabel, QLineEdit, QListWidget, QMenu,
                                 QMessageBox, QPushButton, QSizePolicy, QTableView, QTabWidget, QTextEdit, QTreeWidget, QTreeWidgetItem, QToolBar)
from qgis.PyQt.QtCore import QObject, QRegExp
from qgis.PyQt.QtWidgets import QAbstractItemView, QVBoxLayout, QTreeWidget, QListView, QTreeView
from qgis.PyQt.QtGui import QKeySequence
from qgis.PyQt.QtPrintSupport import QPrinter

from qgis.core import (Qgis, QgsLayerTree, QgsMapLayerProxyModel, QgsProject, QgsRasterLayer, QgsVectorLayer)
from qgis.gui import (QgsCollapsibleGroupBox, QgsMapLayerComboBox, QgsMessageBar)

from ast import literal_eval
from datetime import datetime as thedatetime
from os.path import (basename, dirname, exists, isfile, join, splitext)
from webbrowser import open as webbrowseropen
from xml.etree import ElementTree as etree
import re

from rdigc.objmaker import (funnyPushButton, labeller, myMsgDlgBox,  printerPreview, tableFieldsMapper, textEditFolder)
from rdigc.params import (APIMODELMETEOITEMS, LEGENDSTYLES, PARAMFILES, STYLECOMBOBOX, TOOLTIPPARAMFILES)
from rdigc.utils import (cleanPath, conditionnalAddChild, createTreeWidgetItem, dicoHasKey, fixeLabelsFileDialog, getComboLayer,
                         getThemeIcon, iff, myPushButton, prettifyXml, setObjSizes, sortDic, timeZones, visitTreeList, windowIcon)

from PyQt5.QtWebKit import QWebSettings
from PyQt5.QtWebKitWidgets import QWebView, QWebPage


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("Sauvegarder")
          self.parent.dlg.saveButton.setToolTip("Sauvegarder les informations")
          self.parent.dlg.saveButton.setObjectName("saveButton")
          self.parent.dlg.saveButton.setShortcut(QKeySequence("CTRL+S"))
          
          self.parent.dlg.closeButton = QPushButton(self)
          self.parent.dlg.closeButton.setText("Quitter")
          self.parent.dlg.closeButton.setToolTip("Annuler et fermer la boîte de dialogue")
          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.closeButton.clicked.connect(self.parent.dlg.reject)

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

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

      def initType(self, key, context):
          if key == "uri" :
                if context == 0 :
                   self.buttonWizard = funnyPushButton(self) 
                   self.buttonWizard.initPushButton(24, 24, 400, 0, "buttonWizard", "", QApplication.translate("rdigc", "Assistant"), True, getThemeIcon("files.png"), 24, 24, True, False)
                   self.buttonWizard.clicked.connect(self.getData)
                   self.setReadOnly(True)
                elif context == 1 :
                   self.buttonWizard = funnyPushButton(self) 
                   self.buttonWizard.initPushButton(24, 24, 400, 0, "buttonWizard", "", QApplication.translate("rdigc", "Assistant"), True, getThemeIcon("tag.png"), 24, 24, True, False)

                   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);}")
                   self.addTagDynamap = menuAddTags.addAction(icon, '{:<30}'.format(QApplication.translate("rdigc","Insérer une balise Dynamap")), self.addTag)
                   self.addTagLat = menuAddTags.addAction(icon, '{:<30}'.format(QApplication.translate("rdigc","Insérer une balise Latitude")), self.addTag)
                   self.addTagLong = menuAddTags.addAction(icon, '{:<30}'.format(QApplication.translate("rdigc","Insérer une balise Longitude")), self.addTag)
                   menuAddTags.addSeparator()
                   icon = QIcon(getThemeIcon("urllocalisator.png"))
                   self.testUrl = menuAddTags.addAction(icon, '{:<30}'.format(QApplication.translate("rdigc","Ouvrir l'URL dans un navigateur")), self.openWebUrl)
                   self.buttonWizard.setMenu(menuAddTags)
                   
          if key == "iconGroupData" :
             self.buttonWizard = funnyPushButton(self) 
             self.buttonWizard.initPushButton(24, 24, 400, 0, "buttonWizard", "", QApplication.translate("rdigc", "Assistant"), True, getThemeIcon("files.png"), 24, 24, True, False)
             self.buttonWizard.clicked.connect(self.getIcon)             
             self.setReadOnly(True)

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

      def openWebUrl(self):
          url = self.text()
          try : webbrowseropen(url)
          except : pass            

      def addTag(self):
          textToInsert = {self.addTagDynamap : "[DYNAMAP]", self.addTagLat : "[LAT]", self.addTagLong : "[LONG]"}  
          self.insert(textToInsert[self.sender()])
          return

      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, "Dossier images de l'extension")
            mimeTypeFilters = (("image/png", "image/icon", "image/jpeg"))
            fileDialog.setMimeTypeFilters(mimeTypeFilters)
            fileDialog.setViewMode(QFileDialog.Detail)
            fileDialog.setFileMode(QFileDialog.ExistingFiles)  
            fileDialog.setAcceptMode(QFileDialog.AcceptOpen)
            fileDialog.setDirectory(self.iconDir)
            fileDialog.setFileMode(QFileDialog.ExistingFiles)
            fileDialog.currentChanged.connect(self.verifiyPath)

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


      def getData(self) :
        if self.sender() == None: return

        dialogInput = QInputDialog(self.sender()) 
        dialogInput.setWindowTitle(QApplication.translate("rdigc","Choix de type :"))
        dialogInput.setLabelText("<h2>Type de ressource :</h2><br>")
        dialogInput.setOptions(QInputDialog.UseListViewForComboBoxItems)
        dialogInput.setComboBoxItems(["Dossier", "Fichier"])

        for child in dialogInput.children():
            if type(child) == QListView :
                    child.setMinimumHeight(60)
                    child.setMaximumHeight(60)
                    child.doubleClicked.connect(dialogInput.accept)
                    break

        if dialogInput.exec_() :                        

              selection = child.selectionModel()
              indexElementSelectionne = int(selection.currentIndex().row())
              initDir = dirname(__file__) if self.sender().parent().text() == "" else dirname(self.sender().parent().text())

              title = "Sélectionner un dossier ..." if indexElementSelectionne == 0 else  "Sélectionner un fichier ..."
              fileDialog = QFileDialog(self.sender(), title)
              windowIcon(fileDialog, "folderrdi.png") if indexElementSelectionne == 0 else  windowIcon(fileDialog, "files.png")
              fileDialog.setDirectory(initDir)
              fileDialog.setFileMode(QFileDialog.DirectoryOnly) if indexElementSelectionne == 0 else fileDialog.setFileMode(QFileDialog.ExistingFiles)
              fileDialog.setOption(QFileDialog.ShowDirsOnly, True) if indexElementSelectionne == 0 else fileDialog.setOption(QFileDialog.ShowDirsOnly, False)

              if indexElementSelectionne == 1 : fileDialog.setNameFilters(["*.*"])

              fixeLabelsFileDialog(self, fileDialog, 0, False) 

              if fileDialog.exec_():
                  inputDir = fileDialog.selectedFiles()[0]
                  #NEW 4.1.5
                  self.sender().parent().setText(cleanPath(inputDir.replace("\\", "/"), False))
                  if indexElementSelectionne == 1 : self.sender().parent().setText(self.sender().parent().text()[:-1])


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


      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("#") > 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):
        QTreeWidget.__init__(self, parent)

        self.vectors, self.rasters, self.alrRasters = None, None, None
        self.parent = parent
        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.InternalMove)
        self.header().setDefaultSectionSize(150)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setMinimumWidth(300)
        self.setGeometry(500, 200, 300, 400)
        self.settings = settings

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

        self.toolBarWidget = QToolBar(self) 
        self.toolBarWidget.setObjectName("toolBarWidget")
        self.toolBarWidget.setGeometry(300, 30, 180, 40)
        self.toolBarWidget.setAllowedAreas(Qt.TopToolBarArea | Qt.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.buttonExpandAllNodes = funnyPushButton(self.toolBarWidget) 
        self.buttonExpandAllNodes.initPushButton(24, 24, 10, 5, "buttonExpandAllNodes", "", QApplication.translate("rdigc", "Ouvrir toutes les branches"), True, getThemeIcon("nodes_expand.png"), 24, 24, True, False)

        self.buttonReduceAllNodes = funnyPushButton(self.toolBarWidget) 
        self.buttonReduceAllNodes.initPushButton(24, 24, 45, 5, "buttonReduceAllNodes", "", QApplication.translate("rdigc", "Fermer toutes les branches"), True, getThemeIcon("nodes_reduce.png"), 24, 24, True, False)

        self.buttonAddGroup = funnyPushButton(self.toolBarWidget) 
        self.buttonAddGroup.initPushButton(24, 24, 80, 5, "buttonAddGroup", "", QApplication.translate("rdigc", "Ajouter un groupe"), True, getThemeIcon("addgroup.png"), 24, 24, True, False)

        self.buttonEditNode = funnyPushButton(self.toolBarWidget) 
        self.buttonEditNode.initPushButton(24, 24, 115, 5, "buttonEditNode", "", QApplication.translate("rdigc", "Editer l'entrée"), True, getThemeIcon("edit.png"), 24, 24, True, False)

        self.buttonDelNode = funnyPushButton(self.toolBarWidget) 
        self.buttonDelNode.initPushButton(24, 24, 150, 5, "buttonDelNode", "", QApplication.translate("rdigc", "Supprimer l'entrée"), True, getThemeIcon("del.png"), 24, 24, True, False)

        
        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.WhatsThisRole,"root")
        self.setRootIsDecorated( False )
        self.setHeaderLabels([QApplication.translate("rdigc", "Valeurs :")])

        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.treeWidgetItemChanged(None, 0)

        self.parent.installEventFilter(self)

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

    def eventFilter(self, obj, event):
        if obj is self.parent:
           if event.type() == event.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, 60, self.parent.width()-30, self.parent.height()-70) 
        self.setColumnWidth(0, self.parent.width()-150)
        self.setColumnWidth(1, 80)              

    def addGroup(self):
        i = 0
        groupData = "Nouveau groupe %s" % i
        while not self.checkUnicityName(groupData) :
              i+= 1
              groupData = "Nouveau groupe %s" % 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.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.DisplayRole) == text : verifyUnicity = False ; break

        if not verifyUnicity : QMessageBox.information(mainCtrl, "Erreur", "La clef <b><u>%s</u></b> existe déjà !" % text )
        if self.dlg : self.dlg.saveButton.setEnabled(verifyUnicity)

        return verifyUnicity


    def verifyItem(self, item):
        role = Qt.WhatsThisRole
        values = item.data(0, role)
        dictValues = literal_eval(values)
        if dicoHasKey(dictValues, "labelGroupData"):
              if dictValues["labelGroupData"]=="API RDIGC" : return False
        if item.parent() :
           values = item.parent().data(0, role)
           dictValues = literal_eval(values)
           if dicoHasKey(dictValues, "labelGroupData"):
              if dictValues["labelGroupData"]=="API RDIGC" : return False
              
        return True  

    def treeWidgetItemChanged(self, item, column):
        if item :
           cond = self.verifyItem(item)           
           self.newNode = False
           self.buttonAddGroup.setEnabled(cond)
           self.buttonDelNode.setEnabled(cond)
           self.buttonEditNode.setEnabled(cond)
           if item.parent() :
              self.buttonEditNode.setIcon(QIcon(getThemeIcon("edit.png")))
              self.buttonEditNode.setToolTip("Editer l'entrée")
              self.buttonDelNode.setToolTip("Supprimer l'entrée")
           else :
              self.newNode = True   
              self.buttonEditNode.setIcon(QIcon(getThemeIcon("add.png")))
              self.buttonEditNode.setToolTip("Ajouter une entrée")
              self.buttonDelNode.setToolTip("Supprimer le groupe")
        else :
              self.buttonDelNode.setEnabled(False)
              self.buttonEditNode.setEnabled(False)
              self.buttonEditNode.setIcon(QIcon(getThemeIcon("noaction.png")))
              self.buttonEditNode.setToolTip("Sélectionner une entrée")
              self.buttonDelNode.setToolTip("Sélectionner une entrée")

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

        if itemParent and not self.newNode :
              self.dlg.setWindowTitle(QApplication.translate("rdigc", "Propriétés du groupe"))
              self.dlg.setGeometry(200, 200, 480, 180)
        else :
              self.dlg.setWindowTitle(QApplication.translate("rdigc", "Propriétés de l'entrée") if item else "%s%s" % (QApplication.translate("rdigc", "Propriétés d'une nouvelle entrée,  groupe "), itemParent.data(0, Qt.DisplayRole)))
              self.dlg.setGeometry(200, 200, 480, 320)

        self.dlg.setWindowIcon(self.currentItem().icon(0)) if not self.newNode else self.dlg.setWindowIcon(self.comboBox.itemIcon(self.comboBox.currentIndex())) 
        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.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)
            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)
               if itemParent and key == "groupe" : value.editingFinished.connect(self.verifyUnicity)

            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 = 0, 0
        if text.find("[") > -1 :
           baliseO, baliseF = text.count("["), text.count("]")
        elif text.find("{") > -1 :
           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, "Erreur", "Le nom de l'entrée ne peut être une chaîne vide !"); return    
        
        if not self.newNode :
           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.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"])], "entrée", self.currentItem().childCount(), iconItem, "entrée", 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("localraster.png") if self.comboBox.currentIndex() == 2 else getThemeIcon("urlweb.png")
        return iconItem      
      

    def editItem(self, item) :
        if not item : return
        if not self.verifyItem(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.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.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.UserRole , category)
        item.setData(0, Qt.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


class ui_Params(QDialog):
    def __init__(self, parent):
        QDialog.__init__(self)
        self.parent = parent
        self.setupUi()
    
    def setupUi(self):
        self.setObjectName("ui_Params")
        self.maxWidth, self.maxheight = 580, 740
        setObjSizes(self, self.maxWidth, self.maxheight)
        self.resize(self.maxWidth, self.maxheight) 
        windowIcon(self, "parametres_off.png")

        self.setWindowTitle("Paramètres de %s..." % self.parent.namePlugin)

        self.styleComboBox = STYLECOMBOBOX
        self.styleTextEdit = """QTextEdit {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  = """QTextEdit {color: black; background-color: rgba(75, 75, 75, 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;}
                                         QTextEdit QMenu::item {color: black; background-color: white;}
                                         QTextEdit QMenu::item::selected{ color: white; background-color: grey;}"""
        #NEW 4.1.5
        self.styleTextEditDisaBledOnError  = """QTextEdit {color: black; background-color: rgba(255, 0, 0, 100); 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.styleTextEditDisaBledWhite  = """QTextEdit {color: white; 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.styleTableFieldsMapper = """QHeaderView::section {color: #ffffff; background-color: #646464; padding: 4px; font-size: 8pt; border-style: none; border-bottom: 1px solid #fffff8;
                                                               border-right: 1px solid #fffff8;}
                                         QHeaderView::section:horizontal{ border-top: 1px solid #fffff8;}
                                         QHeaderView::section:vertical{border-left: 1px solid #fffff8;}
                                         """

        fontLabels = QFont("Arial", 8, QFont.Medium, False)
        fontLabels.setBold(True)
        self.imgChecked = getThemeIcon("checked.png").replace("\\","/")
        self.imgUnChecked = getThemeIcon("unchecked.png").replace("\\","/")
        
        self.styleCollapsibleGroupBox  = "QGroupBox{background-color:qlineargradient(x1: 0, y1: 0, x2: 0.5, y2: 0.5, stop: 0 #FFFFBF, stop: 1 #FEB164); border:1px;}"

        self.wizardData = None
        
        self.labelImage = QLabel(self)
        myImgPath = getThemeIcon("rdiparams.png")
        carIcon = QImage(myImgPath)
        self.labelImage.setPixmap(QPixmap.fromImage(carIcon))
        self.labelImage.setGeometry(10, 5, 550, 620)

        self.initDir = ""
        self.authorizedMime = ["*.*"]

        self.hLayout = QHBoxLayout(self.labelImage)
        self.hLayout.setObjectName("hLayout")

        self.tabs = QTabWidget(self.labelImage) 
        self.tabs.setTabsClosable(False)
        self.tabs.setDocumentMode(True)
        self.tabs.setTabShape(QTabWidget.Rounded)
        self.tabs.setStyleSheet("""QTabWidget::pane { border: 1px solid black;  background: white;}
                                   QTabWidget::tab-bar:top {top: 1px;}
                                   QTabWidget::tab-bar:bottom { bottom: 1px;}
                                   QTabWidget::tab-bar:left { right: 1px;}
                                   QTabWidget::tab-bar:right {left: 1px;}
                                   QTabBar::tab { border: 1px solid black;}
                                   QTabBar::tab:selected { background: white;}
                                   QTabBar::tab:!selected { background: silver;}
                                   QTabBar::tab:!selected:hover { background: #999;}
                                   QTabBar::tab:top:!selected { margin-top: 3px;}
                                   QTabBar::tab:bottom:!selected { margin-bottom: 3px;}
                                   QTabBar::tab:top, QTabBar::tab:bottom { min-width: 8ex; margin-right: -1px; padding: 5px 10px 5px 10px;}
                                   QTabBar::tab:top:selected {border-bottom-color: none;}
                                   QTabBar::tab:bottom:selected { border-top-color: none;}
                                   QTabBar::tab:top:last, QTabBar::tab:bottom:last,
                                   QTabBar::tab:top:only-one, QTabBar::tab:bottom:only-one { margin-right: 0;}
                                   QTabBar::tab:left:!selected {margin-right: 3px;}
                                   QTabBar::tab:right:!selected { margin-left: 3px;}
                                   QTabBar::tab:left, QTabBar::tab:right {min-height: 8ex; margin-bottom: -1px; padding: 10px 5px 10px 5px;}
                                   QTabBar::tab:left:selected { border-left-color: none;}
                                   QTabBar::tab:right:selected { border-right-color: none;}
                                   QTabBar::tab:left:last, QTabBar::tab:right:last,
                                   QTabBar::tab:left:only-one, QTabBar::tab:right:only-one { margin-bottom: 0;}""")

        self.hLayout.addWidget(self.tabs)

        self.groupBoxParamsGeneraux = QGroupBox(self.labelImage)  
        self.groupBoxParamsGeneraux.setObjectName("groupBoxParamsGeneraux")
        self.groupBoxParamsGeneraux.setGeometry(10, 20, 540 , self.maxheight - 60)
        self.groupBoxParamsGeneraux.setStyleSheet("QGroupBox { border: 0px solid black;}")

        #NEw 4.1.5
        self.dicoCtrls = {"layerTerritoire" : ("zoomall.png", "Territoire :", QgsMapLayerProxyModel.VectorLayer, "Zoomer sur la couche sélectionnée pour le territoire de référence",
                                               110, 80, QgsMapLayerComboBox, self.zoomToTerritoire, self.styleComboBox, "grpParamTerrain"), 
                          "layerTerrain" : (None, "Terrain MNT :", QgsMapLayerProxyModel.RasterLayer, "Sélectionner la couche du terrain MNT...",
                                            110, 120, QgsMapLayerComboBox, None, self.styleComboBox, "grpParamTerrain"),
                          
                          "layerCommunes" : (None, "Communes :", QgsMapLayerProxyModel.VectorLayer, "Sélectionner la couche des communes ...",
                                            110, 160, QgsMapLayerComboBox, self.updateFieldsCommunes, self.styleComboBox, "grpParamTerrain"),
                          "tableFieldsCommunes" : (None, "Attributs\ndes\nCommunes :", "administratif", "Sélectionner les attributs pour les information sur les communes",
                                             110, 160, tableFieldsMapper, None, None, "grpParamTerrain"),
                          
                          "layerStations" : (None, "Stations :", QgsMapLayerProxyModel.VectorLayer, "Sélectionner la couche des stations...",                                             
                                             110, 160, QgsMapLayerComboBox, self.updateFieldsStations, self.styleComboBox, "grpParamHydro"),
                          "tableFieldsStations" : (None, "Attributs\ndes\nStations :", "hydrometrie", "Sélectionner les attributs pour les information sur les stations",
                                             110, 160, tableFieldsMapper, None, None, "grpParamHydro"),

                          "layerWater" : (None, "Cours d'eau :", QgsMapLayerProxyModel.VectorLayer, "Sélectionner la couche des cours d'eau...",
                                          110, 160, QgsMapLayerComboBox, self.updateFieldsWater, self.styleComboBox, "grpParamHydro"),
                          "tableFieldsWater" : (None, "Attributs\ndes\nCours d'eau :", "cours_eau", "Sélectionner les attributs pour les information sur les cours d'eau",
                                             110, 160, tableFieldsMapper, None, None, "grpParamHydro"),                          
                          
                          "layerCrues" : (None, "Occurences\ncrues vecteur :", QgsMapLayerProxyModel.VectorLayer, "Sélectionner la couche des occurrences de crues vecteur...",
                                          110, 160, QgsMapLayerComboBox, None, self.styleComboBox, "grpParamHydro"),

                          "rasterCrues" : ("edit.png", "Occurences\ncrues raster :", None, "Renseigner la pattern pour le nom des occurrences de crues raster...",
                                          110, 160, textEditFolder, self.getWizardPattern, self.styleTextEditDisaBled, "grpParamHydro"),
                          
                          "pathImgStations" : ("folderrdi.png", "Dossier images\ndes stations :", None, "Sélectionner le dossier des images des stations...",
                                             110, 240, textEditFolder, self.getFolder, self.styleTextEditDisaBledWhite, "grpParamHydro"),
                          
                          "timezone" : (None, "fus. horaire :", None, "Sélectionner la zone temporelle...",
                                        110, 200, QComboBox, None, self.styleComboBox, "grpParamHoro"),
                          "apimodelmeteo" : (None, "API météo :", None, "Sélectionner le modèle météo pour le service ...",
                                        110, 200, QComboBox, None, self.styleComboBox, "grpParamHoro"),
                          
                          "pathLocalData" : ("folderrdi.png", "Dossier RDI :", None, "Sélectionner le dossier des ressources locales images...",
                                             110, 240, textEditFolder, self.getFolder, self.styleTextEditDisaBled, "grpFolders"),
                          "pathLocalFolderStations" : ("folderrdi.png", "Doc Stations :", None, "Sélectionner le dossier pour les documents des stations...",
                                                       110, 280, textEditFolder, self.getFolder, self.styleTextEditDisaBled, "grpFolders"),
                          "nameGroupRaster" : ("bggroup.png", "Groupe Fond :", None, "Sélectionner le groupe pour le fond de carte ...",
                                               110, 280, textEditFolder, self.getWizardGroupRaster, self.styleTextEditDisaBled, "grpFolders"),
  
                          "pathCompass" : ("filenortharrow.png", "Flèche Nord :", None, "Sélectionner le fichier pour la flèche Nord...",
                                             110, 320, textEditFolder, self.getFile, self.styleTextEditDisaBled, "grpRendering"),
                          "pathAlarm1" : ("filesound.png", "Alarme niv.1 :", None, "Sélectionner le fichier pour le son alarme de vigilance...",
                                             110, 360, textEditFolder, self.getFile, self.styleTextEditDisaBled, "grpRendering"),
                          "pathAlarm2" : ("filesound.png", "Alarme niv.2 :", None, "Sélectionner le fichier pour le son alarme de 1er débordement ...",
                                             110, 400, textEditFolder, self.getFile, self.styleTextEditDisaBled, "grpRendering"),
                          "pathAlarm3" : ("filesound.png", "Alarme niv.3 :", None, "Sélectionner Sélectionner le fichier pour le son alarme de dépassement critique ...",
                                             110, 440, textEditFolder, self.getFile, self.styleTextEditDisaBled, "grpRendering"),
                          "pathQml" : ("fileqml.png", "Fichier style :", None, "Sélectionner Sélectionner le fichier style QGIS pour le masque (filtre) ...",
                                             110, 440, textEditFolder, self.getFile, self.styleTextEditDisaBled, "grpRendering"),
                          }

        self.listEltsTree = {"mappingData": self.parent.mappingData, "groupsActions": self.parent.groupsActions,
                             "thematicInondations": self.parent.thematicInondations, "thematicSubmersions": self.parent.thematicSubmersions}
        #NEW 4.1.5    
        self.listElts = ["layerStations", "tableFieldsStations", "layerTerrain", "layerCommunes", "tableFieldsCommunes", "layerTerritoire", "timezone", "apimodelmeteo",
                         "pathImgStations", "layerWater", "tableFieldsWater", "layerCrues", "fieldCrues", "rasterCrues", "pathLocalData", "pathLocalFolderStations", "nameGroupRaster",
                         "pathCompass", "pathAlarm1", "pathAlarm2", "pathAlarm3", "pathQml"]

        posY = 10
        theGroup = None
        self.collapsibleGroupBox = []
        #NEW 4.1.5
        titleCollapsibleGroupBox = ["grpParamTerrain", "grpParamHydro", "grpParamHoro", "grpFolders", "grpMapping", "grpRendering"]
        #NEw 4.1.5
        lblCollapsibleGroupBox = ["Paramètres de terrain", "Paramètres hydrauliques", "Paramètres horaires et API", "Paramètres des ressources locales",
                                  "Paramètres d'affectation et de correspondance", "Paramètres de restitution"]
        styleCollapsibleGroupBox = [
                 "QGroupBox{background-color:qlineargradient(x1: 0, y1: 0, x2: 0.5, y2: 0.5, stop: 0 #FFFFBF, stop: 1 #FEB164); border:1px solid black;}",
                 "QGroupBox{background-color:qlineargradient(x1: 0, y1: 0, x2: 0.5, y2: 0.5, stop: 0 #47b8f0, stop: 1 #2c379a); border:1px solid black;}",
                 "QGroupBox{background-color:qlineargradient(x1: 0, y1: 0, x2: 0.5, y2: 0.5, stop: 0 #f0f0f0, stop: 1 #f1f122); border:1px solid black;}",
                 "QGroupBox{background-color:qlineargradient(x1: 0, y1: 0, x2: 0.5, y2: 0.5, stop: 0 #CCCCCC, stop: 1 #DDDDDD); border:1px solid black;}",
                 "QGroupBox{background-color:qlineargradient(x1: 0, y1: 0, x2: 0.5, y2: 0.5, stop: 0 #FFFFFF, stop: 1 #dbdfe1); border:1px solid white;}",
                 "QGroupBox{background-color:qlineargradient(x1: 0, y1: 0, x2: 0.5, y2: 0.5, stop: 0 #FFFFCC, stop: 1 #dbdfe1); border:1px solid white;}"
                 ]
        delta = 240 
        for i in range(len(lblCollapsibleGroupBox)):
              
              collapsibleGroupBox = QgsCollapsibleGroupBox(self.groupBoxParamsGeneraux)
              collapsibleGroupBox.setCollapsed(False) 
              collapsibleGroupBox.setScrollOnExpand(True)
              collapsibleGroupBox.setStyleSheet(styleCollapsibleGroupBox[i])
              collapsibleGroupBox.setObjectName(titleCollapsibleGroupBox[i]) 
              collapsibleGroupBox.setMaximumWidth(self.maxWidth - 65)
              collapsibleGroupBox.setMinimumWidth(self.maxWidth - 65)               
              collapsibleGroupBox.setGeometry(5,  posY+5, self.maxWidth - 65, self.maxheight - delta) 
              collapsibleGroupBox.setTitle(lblCollapsibleGroupBox[i])
     
              posY+= 15
  
              self.collapsibleGroupBox.append(collapsibleGroupBox)
     
              setattr(self, titleCollapsibleGroupBox[i], collapsibleGroupBox)
              if i == 0 : theGroup = collapsibleGroupBox

              collapsibleGroupBox.collapsedStateChanged.connect(self.changePosGroups)
        
        posY = 25
        conteneur = None
        for key in self.dicoCtrls :

            img, text, filterCtrl, toolTip = self.dicoCtrls[key][0], self.dicoCtrls[key][1], self.dicoCtrls[key][2], self.dicoCtrls[key][3]
            classCtrl, slotCtrl, styleCtrl = self.dicoCtrls[key][6], self.dicoCtrls[key][7],  self.dicoCtrls[key][8]
            nameCollapsibleGroupBox = self.dicoCtrls[key][9]
            collapsibleGroupBox = self.getCollapsibleGroupBoxByName(nameCollapsibleGroupBox)
            masterCtrl = None

            if conteneur != collapsibleGroupBox :               
               if conteneur :
                  conteneur.setMinimumHeight(posY+15)
                  conteneur.setMaximumHeight(posY+15)
               posY = 25
                  
            conteneur = self.groupBoxParamsGeneraux if not collapsibleGroupBox else collapsibleGroupBox            

            label = QLabel(conteneur) 
            label.setText(text)
            label.setAlignment(Qt.AlignRight)
            label.setGeometry(5, posY+5, 90, 25) if text.find("\n")==-1 else label.setGeometry(5, posY+5, 90, 50)
            label.setFont(fontLabels)

            ctrl = classCtrl(conteneur) 
            ctrl.setObjectName(key)
            if classCtrl in (QgsMapLayerComboBox, QComboBox) : ctrl.setEditable(False)
            elif classCtrl == textEditFolder : ctrl.setReadOnly(True)
            ctrl.setEnabled(True)

            if classCtrl in (QComboBox, QgsMapLayerComboBox) :
                  if classCtrl == QgsMapLayerComboBox :      
                     ctrl.setFilters(filterCtrl)
                     ctrl.model().sortOrder()
                  elif classCtrl == QComboBox :
                       if key == "apimodelmeteo" : ctrl.addItems(sorted(APIMODELMETEOITEMS.keys())); ctrl.currentIndexChanged.connect(self.setToolTipMeteo);
                       else : ctrl.addItems(timeZones())   
            ctrl.setStyleSheet(styleCtrl)
            
            if classCtrl == textEditFolder :
                  ctrl.setGeometry(100, posY, 380, 50) if key != "rasterCrues" else ctrl.setGeometry(100, posY, 380, 25)
            else :      
                  ctrl.setGeometry(100, posY, 380, 25) if not key == "layerCrues" else ctrl.setGeometry(100, posY, 190, 25)

            if classCtrl == tableFieldsMapper :
               posY = ctrl.setData(filterCtrl, posY);
               if masterCtrl : masterCtrl.layerChanged.emit(masterCtrl.currentLayer()); masterCtrl = None ;

            if key == "timezone" :                  
               icon = QIcon(getThemeIcon("montre.png")) 
               for i in range(ctrl.count()) : ctrl.setItemIcon(i, icon)

            if key == "layerCrues" :
               self.fieldCrues = QComboBox(conteneur) 
               self.fieldCrues.setObjectName("fieldCrues")
               self.fieldCrues.setStyleSheet(self.styleComboBox)
               self.fieldCrues.setGeometry(300, posY, 180, 25)
   
            if key in ("layerCrues", "layerCommunes", "layerWater", "layerStations") : ctrl.layerChanged.connect(self.setFields); masterCtrl = ctrl;
            setattr(self, key, ctrl)

            if img :
               imgCtrl = funnyPushButton(conteneur) 
               imgCtrl.initPushButton(28, 28, 485, posY, "img%s" % key, "", toolTip, True, getThemeIcon(img), 28, 28, True, False)
               if slotCtrl :
                     if type(slotCtrl)== str : pass
                     else :
                           imgCtrl.clicked.connect(slotCtrl)
                           if key in ("pathImgStations", "rasterCrues","pathLocalData", "pathLocalFolderStations", "nameGroupRaster") : ctrl.slotCtrl = imgCtrl 
                           
               setattr(self, "img%s" % key, imgCtrl)

            if classCtrl == textEditFolder :
               posY+= 40 if key == "rasterCrues" else 60
            else : posY+= 30 

        if conteneur :
           conteneur.setMinimumHeight(posY+15)
           conteneur.setMaximumHeight(posY+15)
        
        collapsibleGroupBox = self.getCollapsibleGroupBoxByName("grpMapping")
        posY = 25           

        self.lblDicos = QLabel(collapsibleGroupBox)
        #NEW 4.1.5
        self.lblDicos.setText("Groupes :")
        self.lblDicos.setAlignment(Qt.AlignRight)
        self.lblDicos.setGeometry(15, posY+5, 70, 25)
        self.lblDicos.setFont(fontLabels)

        self.printTreeWidget = QWebView(self.groupBoxParamsGeneraux)  
        self.printTreeWidget.setObjectName("printTreeWidget")
        self.printTreeWidget.page().settings().setAttribute(QWebSettings.AutoLoadImages, True)
        self.printTreeWidget.setVisible(False)
        self.imgpuce = getThemeIcon("imgpuce.png").replace("\\","/")
        self.printTreeWidget.setHtml("<body>><img src='file:///%s' width=16px height=16px></body>" % self.imgpuce)        

        self.dicosViewer = QTreeWidget(collapsibleGroupBox) 
        self.dicosViewer.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.dicosViewer.setObjectName("dicosViewer")
        self.dicosViewer.headerItem().setText(0, "1")
        self.dicosViewer.header().setVisible(False)
        self.dicosViewer.header().setDefaultSectionSize(200)
        self.dicosViewer.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.dicosViewer.setVisible(True)
        self.dicosViewer.setItemsExpandable(True)
        self.dicosViewer.setExpandsOnDoubleClick(False)

        size = 400
        
        self.dicosViewer.setContextMenuPolicy(Qt.CustomContextMenu)
        self.dicosViewer.customContextMenuRequested.connect(self.contextMnu)
        self.dicosViewer.setGeometry(95, posY, 380, size-25) 

        self.toolBarWidgetDicosViewer = QToolBar(collapsibleGroupBox)  
        self.toolBarWidgetDicosViewer.setObjectName("toolBarWidgetDicosViewer")
        sizeWidth = ctrlMap(self, self.toolBarWidgetDicosViewer, True, False, False, 5, 5, 40, 200, "toolBarWidgetDicosViewer")
        self.toolBarWidgetDicosViewer.setGeometry(480, posY, 40, sizeWidth + 30)
        self.toolBarWidgetDicosViewer.setAllowedAreas(Qt.TopToolBarArea | Qt.BottomToolBarArea)
            
        collapsibleGroupBox.setMinimumHeight(size+15) 
        collapsibleGroupBox.setMaximumHeight(size+15) 

        theGroup.collapsedStateChanged.emit(True)
        theGroup.setCollapsed(True)

        self.buttonAddNode.setEnabled(False)
        self.buttonEditNode.setEnabled(False)
        self.buttonDeleteNode.setEnabled(False)

        self.expandAllNodes.clicked.connect(self.expandTreeAllNodes)
        self.closeAllNodes.clicked.connect(self.expandTreeAllNodes)
        self.buttonAddNode.clicked.connect(self.addNode)
        self.buttonEditNode.clicked.connect(self.getWizardGroupRaster)
        self.buttonDeleteNode.clicked.connect(self.deleteNode)
        self.buttonRefreshTreeWidget.clicked.connect(self.refreshDataDicosViewer)
        self.buttonPrintTreeWidget.clicked.connect(self.getPrintTreeWidget)

        index = self.tabs.addTab(self.groupBoxParamsGeneraux, QIcon(getThemeIcon("parametres_off.png")), "Généraux")
        self.tabs.setTabToolTip(index,  "Paramètres généraux")

        self.groupBoxParamsPrint = QGroupBox(self)  
        self.groupBoxParamsPrint.setObjectName("groupBoxParamsPrint")
        self.groupBoxParamsPrint.setStyleSheet("QGroupBox { border: 0px solid black;}")
        
        self.fileLogoLeft = imageViewer(self.groupBoxParamsPrint, self, "1x1")
        self.fileLogoLeft.setObjectName("fileLogoLeft")
        self.toolBarImageViewerLeft = QToolBar(self.fileLogoLeft)

        self.fileLogoRight = imageViewer(self.groupBoxParamsPrint, self, "2x1")
        self.fileLogoRight.setObjectName("fileLogoRight")
        self.toolBarImageViewerRight = QToolBar(self.fileLogoRight)
        
        ctrlMapTemp(self, self.toolBarImageViewerLeft, True, False, False, 5, 5, 280, 35)
        self.toolBarImageViewerLeft.setAllowedAreas(Qt.TopToolBarArea | Qt.BottomToolBarArea)
        self.fileLogoLeft.setGeometry(10, 30, 250, 250)
        self.fileLogoLeft.setVisible(True)
        self.toolBarImageViewerLeft.setGeometry(int(self.fileLogoLeft.width()/2)-120, self.fileLogoLeft.height()-90, 240, 80)

        ctrlMapTemp(self, self.toolBarImageViewerRight, True, False, False, 5, 5, 280, 35)
        self.toolBarImageViewerRight.setAllowedAreas(Qt.TopToolBarArea | Qt.BottomToolBarArea)
        self.fileLogoRight.setGeometry(270, 30, 250, 250)
        self.fileLogoRight.setVisible(True)
        self.toolBarImageViewerRight.setGeometry(int(self.fileLogoRight.width()/2)-120, self.fileLogoRight.height()-90, 240, 80)

        self.lblTitleService = QLabel(self.groupBoxParamsPrint)
        self.lblTitleService.setGeometry(15, 280, 130, 30)
        self.lblTitleService.setAlignment(Qt.AlignRight)
        self.lblTitleService.setText("Texte entête :")
        self.lblTitleService.setStyleSheet("QLabel { font-size: 10px;  color : black; background-color: rgba(160, 160, 160, 125); border: 1px solid black;  border-radius: 4px;  padding: 8px;}")

        self.titleService = QTextEdit(self.groupBoxParamsPrint)
        self.titleService.setStyleSheet("QTextEdit { font-size: 12px;  text-align: center; color : black; background-color: white; border: 1px solid black;  border-radius: 4px;  padding: 2px;}")
        self.titleService.setAlignment(Qt.AlignCenter)
        self.titleService.setAcceptRichText(False)
        self.titleService.setObjectName("titleService")
        self.setAcceptDrops(False)        
        self.titleService.setGeometry(150, 280, 365, 30)
        
        self.setReporting()        
        self.titleService.textChanged.connect(self.setPreviewHtml)

        self.printPreviewHtml = QWebView(self.groupBoxParamsPrint)  
        self.printPreviewHtml.setObjectName("printPreviewHtml")
        self.printPreviewHtml.page().settings().setAttribute(QWebSettings.AutoLoadImages, True)
        self.printPreviewHtml.setVisible(True)
        self.printPreviewHtml.setGeometry(10, 340, 510, 160) 
        self.printPreviewHtml.setHtml(self.previewEnteteHtml())
        
        self.printMapview = QTextDocument()
         
        index = self.tabs.addTab(self.groupBoxParamsPrint, QIcon(getThemeIcon("printer_off.png")), "Impressions")
        self.tabs.setTabToolTip(index, "Paramètres d'impression")

        self.groupIcon = QIcon(getThemeIcon("group.png"))
        
        self.groupBoxParamsLinked = QGroupBox(self)  
        self.groupBoxParamsLinked.setObjectName("groupBoxParamsLinked")
        self.groupBoxParamsLinked.setStyleSheet("QGroupBox { border: 0px solid black;}")

        index = self.tabs.addTab(self.groupBoxParamsLinked, QIcon(getThemeIcon("linkedweb.png")), "Ressources Externes")
        self.tabs.setTabToolTip(index,  "Paramétrer les Liens Web & les Ressource locales")

        self.comboBoxParamsLinked = QComboBox(self.groupBoxParamsLinked)
        self.comboBoxParamsLinked.setObjectName("comboBoxParamsLinked")
        self.comboBoxParamsLinked.setToolTip("Type de ressources")
        self.comboBoxParamsLinked.setEditable(False)
        self.comboBoxParamsLinked.setEnabled(True)

        self.comboBoxParamsLinked.addItems(["Ressources locales", "Liens Web", "Serveurs Web (WMS, WMTS)"])
        self.comboBoxParamsLinked.setGeometry(10, 30, self.width()-120, 25)
        self.comboBoxParamsLinked.setCurrentIndex(0)
        self.comboBoxParamsLinked.setStyleSheet(self.styleComboBox)

        self.refreshLinkedDataButton = funnyPushButton(self.groupBoxParamsLinked)
        self.refreshLinkedDataButton.initPushButton(32, 32, self.width()-100,  25, "refreshLinkedDataButton", "", "Recharger les données", True, getThemeIcon("refresh.png"), 32, 32, True, False)

        self.pathIcons = ["dico.png", "urlweb.png", "localraster.png"]
        icons = [QIcon(getThemeIcon(path)) for path in self.pathIcons]
        for i in range(self.comboBoxParamsLinked.count()) : self.comboBoxParamsLinked.setItemIcon(i, icons[i])

        default = Qt.ItemIsSelectable| Qt.ItemIsEnabled |  Qt.ItemIsTristate | Qt.ItemIsUserCheckable  
        defaultalr = Qt.ItemIsSelectable | Qt.ItemIsTristate 
        checkebable = Qt.ItemIsUserCheckable
        drag = Qt.ItemIsDragEnabled
        drop = Qt.ItemIsDropEnabled

        settings = {"groupdata":(["root"], default|drop), "entrée":(["groupdata"], default|drag|drop)}

        index = self.comboBoxParamsLinked.currentIndex()    
        self.treeParamsLinked = legendTreeWidget(settings, self.groupBoxParamsLinked, index, self.pathIcons[index], self.comboBoxParamsLinked)

        self.groupBoxParamsInterface = QGroupBox(self.labelImage)  
        self.groupBoxParamsInterface.setObjectName("groupBoxParamsInterface")
        self.groupBoxParamsInterface.setGeometry(10, 20, 540 , self.maxheight -60)
        self.groupBoxParamsInterface.setStyleSheet("QGroupBox { border: 0px solid black;}")

        index = self.tabs.addTab(self.groupBoxParamsInterface, QIcon(getThemeIcon("user.png")), "Profil")
        self.tabs.setTabToolTip(index,  "Paramètres de sauvegarde") 

        label = QLabel(self.groupBoxParamsInterface) 
        label.setText("Dossier de sauvegarde du profil de paramétrage :")
        label.setAlignment(Qt.AlignLeft)
        label.setGeometry(10, 20, 400, 25) 
        label.setFont(fontLabels)

        self.lblDefaultContext = QLabel(self.groupBoxParamsInterface)
        self.lblDefaultContext.setObjectName("lblDefaultContext")
        self.lblDefaultContext.setGeometry(10, 40, 20, 25)
        self.lblDefaultContext.setAlignment(Qt.AlignCenter)

        dicoCtrls = {"pathFolderParams" : ("folderrdi.png", "", None, "Sélectionner le dossier pour la sauvegarde des paramètres...",
                                           110, 280, textEditFolder, self.getFolder, self.styleTextEditDisaBled, "grpFolders")}

        for key in dicoCtrls :      
            img, text, filterCtrl, toolTip = dicoCtrls[key][0], dicoCtrls[key][1], dicoCtrls[key][2], dicoCtrls[key][3]
            classCtrl, slotCtrl, styleCtrl = dicoCtrls[key][6], dicoCtrls[key][7],  dicoCtrls[key][8]

            ctrl = classCtrl(self.groupBoxParamsInterface) 
            ctrl.setObjectName(key)
            ctrl.setEnabled(True)
            ctrl.setReadOnly(True)
            ctrl.setStyleSheet(styleCtrl) 
            ctrl.setGeometry(32, 40, self.maxWidth-125, 50)
            ctrl.setText(self.parent.pathFolderParams)

            setattr(self, key, ctrl)

            if img :
               imgCtrl = funnyPushButton(self.groupBoxParamsInterface)
               imgCtrl.initPushButton(28, 28, self.maxWidth-90, 40, "img%s" % key, "", "Sélectionner le dossier pour la sauvegarde des paramètres", True, getThemeIcon(img), 28, 28, True, False)
               if slotCtrl :
                     if type(slotCtrl)== str : pass
                     else :
                           imgCtrl.clicked.connect(slotCtrl)
                           if key == "pathFolderParams" : ctrl.slotCtrl = imgCtrl   
                           
               setattr(self, "img%s" % key, imgCtrl)
 
        self.printRapportParams = QWebView(self.groupBoxParamsInterface)  
        self.printRapportParams.setObjectName("printRapportParams")
        self.printRapportParams.page().settings().setAttribute(QWebSettings.AutoLoadImages, True)
        self.printRapportParams.setVisible(True)
        self.printRapportParams.setGeometry(10, 100, self.maxWidth-75, 160)

        self.loadContextParams = funnyPushButton(self.groupBoxParamsInterface)
        self.loadContextParams.initPushButton(120, 30, 15, 270, "loadContextParams", "Charger", "Charger les paramètres du profil sélectionné", True, getThemeIcon("load.png"), 24, 24, False, True)
        self.loadContextParams.setShortcut(QKeySequence("CTRL+L"))
        self.loadContextParams.clicked.connect(self.loadContextRdigc)

        self.saveContextParams = funnyPushButton(self.groupBoxParamsInterface)
        self.saveContextParams.initPushButton(120, 30, 140, 270, "saveContextParams", "Par défaut", "Définir le profil comme profil par défaut pour les futures sessions %s" % self.parent.namePlugin, True, getThemeIcon("savedefault.png"), 24, 24, False, True)
        self.saveContextParams.setShortcut(QKeySequence("CTRL+A"))
        self.saveContextParams.clicked.connect(self.setContextRdigc)

        self.saveAllContextParams = funnyPushButton(self.groupBoxParamsInterface)
        self.saveAllContextParams.initPushButton(120, 30, 265, 270, "saveAllContextParams", "Sauvegarder", "Sauvegarder toutes les informations du profil", True, getThemeIcon("save.png"), 24, 24, False, True)
        self.saveAllContextParams.clicked.connect(self.saveParams)

        self.reloadContextParams = funnyPushButton(self.groupBoxParamsInterface)
        self.reloadContextParams.initPushButton(120, 30, 390, 270, "reloadContextParams", "Rétablir", "Recharger les informations pour le profil par déafut", True, getThemeIcon("reload.png"), 24, 24, False, True)
        self.reloadContextParams.clicked.connect(self.loadContextRdigc)         

        self.barInfo = QgsMessageBar(self)
        self.barInfo.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)

        self.comboBoxParamsLinked.currentIndexChanged.connect(self.getInfosInFile)
        self.refreshLinkedDataButton.clicked.connect(self.getInfosInFile) 

        self.saveButton = funnyPushButton(self)
        self.saveButton.initPushButton(128, 128, self.width()-280,  self.height()-140, "saveButton", "", "Sauvegarder le paramétrage", True, getThemeIcon("saveparams.png"), 128, 128, True, False)

        self.closeButton = funnyPushButton(self)
        self.closeButton.initPushButton(128, 128, self.width()-140,  self.height()-140, "closeButton", "", "Fermer", True, getThemeIcon("closeabout.png"), 128, 128, True, False)

        #CONNECTEURS
        self.tabs.currentChanged.connect(self.enabledCtrl)
        
        self.buttonGetImageL.clicked.connect(self.fileLogoLeft.loadImage) 
        self.buttonZoomAllL.clicked.connect(self.fileLogoLeft.viewAllImage) 
        self.buttonZoominL.clicked.connect(self.fileLogoLeft.zoomIn) 
        self.buttonZoomOutL.clicked.connect(self.fileLogoLeft.zoomOut)
        self.buttonPrintImageL.clicked.connect(self.printImage)

        self.buttonGetImageR.clicked.connect(self.fileLogoRight.loadImage) 
        self.buttonZoomAllR.clicked.connect(self.fileLogoRight.viewAllImage) 
        self.buttonZoominR.clicked.connect(self.fileLogoRight.zoomIn) 
        self.buttonZoomOutR.clicked.connect(self.fileLogoRight.zoomOut)
        self.buttonPrintImageR.clicked.connect(self.printImage)
       
        self.dicosViewer.itemClicked.connect(self.updateToolBarWidgetDicosViewer)
        self.dicosViewer.itemDoubleClicked.connect(self.dicosViewerDoubleClickedItem)
        self.saveButton.clicked.connect(self.saveParams)
        self.closeButton.clicked.connect(self.reject)

        self.buttonZoomAllL.clicked.emit()
        self.buttonZoomAllR.clicked.emit()

        self.initContextRdigc()


    def getWizardPattern(self):
        nameObj = self.sender()
        obj =  self.findChild(textEditFolder, nameObj.objectName().replace("img", ""))
        if obj :
              text, ok = QInputDialog.getText(self, "Défition de la pattern ...", "Saisissez la chaîne de caractères pour la recherche", QLineEdit.Normal, obj.toPlainText())
              if ok : obj.setPlainText("%s" % text)
        
    def initContextRdigc(self):
        self.getInfosInFile()

        self.listEltsTree["mappingData"] = self.parent.mappingData
        self.listEltsTree["groupsActions"] = self.parent.groupsActions
        self.listEltsTree["thematicInondations"] = self.parent.thematicInondations
        self.listEltsTree["thematicSubmersions"] = self.parent.thematicSubmersions

        self.updatePrintTreeWidget()
        self.updateFieldsStations()
        self.updateFieldsCommunes()
        self.updateFieldsWater()
        
        self.isValidContext()

        if self.parent.memoryLayer :
           if exists(self.parent.styleQml) :
              self.parent.memoryLayer.loadNamedStyle(self.parent.styleQml, True)
              self.parent.canvas.redrawAllLayers()

        self.refreshDataDicosViewer()      

    def updateImgDefaultContext(self, cond):
        self.lblDefaultContext.setText("X") if cond else self.lblDefaultContext.setText(" ")
        self.lblDefaultContext.setToolTip("Profil par défaut") if cond else self.lblDefaultContext.setToolTip("Profil autre que celui défini par défaut,\nvalable uniquement pour la session de travail.")
        self.lblDefaultContext.setStyleSheet("background-color: #00FF00;font-weight: bold; border: 2px solid grey; border-radius: 5px;") if cond else self.lblDefaultContext.setStyleSheet("background-color: #FF0000; border: 2px solid grey; border-radius: 5px;}")
          
    def setContextRdigc(self) :
        s = QSettings()
        s.setValue("%spathFolderParams" % self.parent.settings, self.pathFolderParams.toPlainText())
        self.updateImgDefaultContext(True)
        self.barInfo.pushMessage("Information :", "Le profil a été enregistré comme profil par défaut !", Qgis.Info, 3)
          
    def enabledCtrl(self, index): self.saveButton.setVisible(index != 3)

    def isValidContext(self) :
        html, error = self.getRapportParams()
        self.printRapportParams.setHtml(html)

        s = QSettings() 
        pathFolderParams = s.value("%spathFolderParams"  % self.parent.settings, "" )
        cond = (pathFolderParams == self.pathFolderParams.toPlainText())

        self.updateImgDefaultContext(cond)
        
        self.loadContextParams.setEnabled(error==[] and not cond)
        self.saveContextParams.setEnabled(error==[] and not cond)
        text = "Sauvegarder" if error==[] else "Copier"
        self.saveAllContextParams.setText(" %s" % text)
        self.saveAllContextParams.setToolTip("%s toutes les informations du profil" % text)
        
    def getRapportParams(self) :       
        html = "<style>\n"
        html+= "img {width: 24px; height: 24px;}\n"
        html+= "#infos {border: medium solid #6495ed; border-collapse: collapse; width: 100%%; }\n"
        html+= "#infos tr.title {padding: 8px; font-weight: bold; font-family:  Montserrat, Arial, Helvetica, sans-serif; border: thin solid #6495ed; width: 33%%;padding: 5px; text-align: center ; background-color: #D0E3FA; padding-top: 12px;  padding-bottom: 12px;}\n"
        html+= "#infos tr.checked {font-family:  Montserrat, Arial, Helvetica, sans-serif; border: thin solid #6495ed; width: 33%%;padding: 5px;text-align: left ; color: #000000; background-color: #00ff00;}\n"
        html+= "#infos tr.unchecked {font-family:  Montserrat, Arial, Helvetica, sans-serif; border: thin solid #6495ed; width: 33%%;padding: 5px;text-align: left ; color: #FFFFFF; background-color: #ff0000;}\n"
        html+= "</style>\n"
        html+= "<table id='infos'>\n"
        html+= "<tr class='title'><td colspan='2'>Rapport de présence</td></tr>\n"

        error = []

        i = 0
        for paramFile in PARAMFILES :
            pathParamFile = "%s%s.xml" % (self.pathFolderParams.toPlainText(), paramFile)
            if exists(pathParamFile):  html+= "<tr class='checked'><td><img src='file:///%s'></td><td title='%s'>%s.xml</td></tr>\n" % (self.imgChecked, TOOLTIPPARAMFILES[i], paramFile)
            else : html+= "<tr class='unchecked'><td><img src='file:///%s'></td><td title='%s'>%s.xml</td></tr>\n" % (self.imgUnChecked, TOOLTIPPARAMFILES[i], paramFile); error.append(paramFile) ;
            i+= 1      
        html+= "</table>\n"
        return html, error

    def setToolTipMeteo(self, index):
        key = self.apimodelmeteo.currentText()
        self.apimodelmeteo.setToolTip(APIMODELMETEOITEMS[key]["legend"]) 
        
    def updateFieldsCommunes(self):
        layerCommunes = self.layerCommunes.layer(self.layerCommunes.currentIndex())            
        if not layerCommunes : return          
        self.tableFieldsCommunes.setFields(layerCommunes)
      
    def updateFieldsStations(self):
        layerStations = self.layerStations.layer(self.layerStations.currentIndex())            
        if not layerStations : return          
        self.tableFieldsStations.setFields(layerStations)
          
    def updateFieldsWater(self):
        layerWater = self.layerWater.layer(self.layerWater.currentIndex())            
        if not layerWater : return          
        self.tableFieldsWater.setFields(layerWater)
        
    def getCollapsibleGroupBoxByName(self, name):
        refCollapsibleGroupBox = None
        if name != "" :
           for collapsibleGroupBox in self.collapsibleGroupBox:
               if collapsibleGroupBox.objectName() == name :
                  refCollapsibleGroupBox = collapsibleGroupBox   
                  break
        return refCollapsibleGroupBox    
          
    def changePosGroups(self):
         posY, newHeight, i = 10, 0, 0
         refCollapsibleGroupBox = self.sender()
         for collapsibleGroupBox in self.collapsibleGroupBox:
              collapsibleGroupBox.blockSignals(True)
              if collapsibleGroupBox != refCollapsibleGroupBox : collapsibleGroupBox.setCollapsed(True)
              else : collapsibleGroupBox.setCollapsed(False)
              collapsibleGroupBox.blockSignals(False)
              collapsibleGroupBox.setGeometry(5, posY, self.maxWidth - 65, collapsibleGroupBox.height())

              posY+= collapsibleGroupBox.height() if not collapsibleGroupBox.isCollapsed() else 25
              newHeight+= 25 + collapsibleGroupBox.height() if not collapsibleGroupBox.isCollapsed() else 25
              i+= 1

    def parseFile(self, fileName):
        with open(fileName, 'r') as unknown_file:
             data = re.sub(r'\s+', '', unknown_file.read())
             if (re.match(r'^<.+>$', data)): return True
             else : return False

    def setReporting(self):
        self.fileLogoLeft.loadImageFromFile(self.parent.fileLogoLeft)
        self.fileLogoLeft.setToolTip(self.parent.fileLogoLeft)

        self.fileLogoRight.loadImageFromFile(self.parent.fileLogoRight)
        self.fileLogoRight.setToolTip(self.parent.fileLogoRight)
        
        self.titleService.setPlainText(self.parent.titleService)

    def setMenus(self, fileName, index):
        if index == 0 :
           self.parent.menuLocalInfos = self.parent.getInfosInFile(fileName, None)
           self.parent.localInfos.setMenu(self.parent.menuLocalInfos)
        elif index == 1 :
           self.parent.menuWebInfos = self.parent.getInfosInFile(fileName, None)
           self.parent.webInfos.setMenu(self.parent.menuWebInfos)
        else :
           self.parent.updatelistLayersRaster()           


    def loadContextRdigc(self):          
        if self.sender() == self.reloadContextParams : title, msg = "Confirmer le choix", "<h3>Confirmez-vous le chargement<br>des paramètres du profil par défaut ?</h3>"
        else : title, msg = "Confirmer le choix", "<h3>Confirmez-vous le chargement<br>des paramètres du profil sélectionné ?</h3>"
        
        msgDlgBox = myMsgDlgBox(self, QMessageBox.Question, "%s ..." % title, msg, "rdistop.png", "inondation.png", 0, None, self)              
        msgDlgBox.setWindowIcon(self.tabs.tabIcon(self.tabs.currentIndex()))
        ret = msgDlgBox.exec_()    

        if ret == 1 :
              index = 0
              orIndex = self.tabs.currentIndex()

              if self.sender() == self.reloadContextParams :
                    s = QSettings() 
                    pathFolderParams = s.value("%spathFolderParams"  % self.parent.settings, "" )
                    if pathFolderParams == "" : pathFolderParams = join(dirname(__file__),"data/").replace("\\", "/")
                    self.pathFolderParams.setText(pathFolderParams)
                    self.parent.pathFolderParams = pathFolderParams
              else :
                    self.parent.pathFolderParams = self.pathFolderParams.toPlainText()
              
              
              for paramFile in PARAMFILES :
                  fileName = "%s%s.xml" % (self.parent.pathFolderParams, paramFile)
                  if index == 0 :
                     self.parent.loadParamsRdi()
                     items = [elt for elt in self.parent.mappingData.keys()] 
                     self.parent.listForDataSearch.clear()
                     self.parent.listForDataSearch.addItems(items) 
                     self.parent.listForDataSearch.setCurrentIndex(1)
                     self.parent.updateCompleter()
                     self.setReporting()
                     self.previewEnteteHtml()
                  else :
                     self.tabs.setCurrentIndex(2)   
                     self.comboBoxParamsLinked.setCurrentIndex(index-1)
                     self.setMenus(fileName, index-1)
                  index+= 1

              self.tabs.setCurrentIndex(orIndex)
              self.initContextRdigc()
              self.parent.comboActiveLayer.layerChanged.emit(self.parent.comboActiveLayer.currentLayer())
              if self.parent.dialogReportingStations : self.parent.dialogReportingStations.updateListStations()


    def getInfosInFile(self):          
          fileName = PARAMFILES[self.comboBoxParamsLinked.currentIndex()+1]
          fileName = join(self.parent.pathFolderParams, "%s.xml" % fileName)

          contextMenu = QMenu()  
          
          if exists(fileName):

               index = self.comboBoxParamsLinked.currentIndex()   
               self.treeParamsLinked.setContext(index, self.pathIcons[index])
               self.treeParamsLinked.clear()
               self.treeParamsLinked.treeWidgetItemChanged(None, 0)

               if not self.parseFile(fileName) :
                   msg = "Fichier XML invalide"
                   msgErr = "Racine RDI introuvable"
                   self.barInfo.pushMessage("Attention :", "%s :<br>%s." % (msg, msgErr), Qgis.Warning, 3)
                   return

               error = 0
               try:
                   doc = etree.parse(fileName).getroot()
                   if not doc.tag in ('RDI', 'rdiPlugin', 'rdigcPlugin') :
                      error, msg, msgErr = 1, "Fichier XML invalide", "Racine RDIGC introuvable"
               except (etree.ParseError, error):
                   error, msg = 1, "Impossible de parser le fichier XML"
               except (IOError, error):
                   error, msg = 1, "Impossible d'ouvrir le fichier XML"

               if error == 1:
                   self.barInfo.pushMessage("Attention :", "%s :<br>%s." % (msg, msgErr), Qgis.Warning, 3)
                   return

               isLocal = doc.attrib.get('local')
               if isLocal : isLocal = eval(isLocal)
               
               iconRessource = getThemeIcon("localraster.png").replace("\\", "/") if index == 2 else getThemeIcon("urlweb.png").replace("\\", "/")
               iconItem = getThemeIcon("urlweb.png").replace("\\", "/")               

               i = 0
               groupData, itemGroupData = "", None
               keys = ("labeluri", "uri", "paramuri")

               version = int(doc.attrib.get('version').split('.')[0])
                  
               if version >= 3 :
                  for gridraster in doc.findall('groupData'):
                      labelGroupData = gridraster.attrib.get('labelGroupData')
                      iconGroupDataPath = join(dirname(__file__), "icons/%s" % gridraster.attrib.get('iconGroupData'))
                      iconGroupData = QIcon(iconGroupDataPath)

                      dictValuesGroup = {}  
                      dictValuesGroup['labelGroupData'] = labelGroupData
                      dictValuesGroup['iconGroupData'] = gridraster.attrib.get('iconGroupData')

                      if labelGroupData != "" : itemGroupData = self.treeParamsLinked.addItem([labelGroupData], "groupdata", -1, iconGroupData, "groupdata", None, dictValuesGroup) 

                      data = []
                      try : data = gridraster.getchildren()
                      except : data = gridraster.findall('data')
                      
                      for child in data : 
                          dictValues = {} 
                          for key in keys : dictValues[key] = child.attrib.get(key)
                            
                          text = child.attrib.get("labeluri")
                          self.treeParamsLinked.addItem(['{:<100}'.format(text)], "entrée", i, iconRessource, "entrée", itemGroupData, dictValues) 

               else :

                     dictValuesGroup = {}
                     
                     for gridraster in doc.findall('data'):
                         value = gridraster.attrib.get('uri')

                         
                         dictValues = {} 
                         for key in keys : dictValues[key] = gridraster.attrib.get(key)
                         
                         if groupData != gridraster.attrib.get('groupData') :
                            groupData = gridraster.attrib.get('groupData')                              
                            dictValuesGroup['labelGroupData'] = groupData
                            dictValuesGroup['iconGroupData'] = "group.png"
                            
                            if groupData != "" : itemGroupData = self.treeParamsLinked.addItem([groupData], "groupdata", -1, self.groupIcon, "groupdata", None, dictValuesGroup)
                                                     
                         test = exists(value) if isLocal else True
                         if test :
                            if isLocal :
                               sourceFile = QFileInfo(value)
                               if sourceFile.isFile():
                                  iconMime = getThemeIcon("%s.png" % sourceFile.suffix().lower())
                                  if exists(iconMime) : iconRessource = iconMime
                                  else :   iconRessource = getThemeIcon("unknow.png")  
                               else : iconRessource = getThemeIcon("folder.png")

                            text = gridraster.attrib.get("labeluri")
                            self.treeParamsLinked.addItem(['{:<100}'.format(text)], "entrée", i, iconRessource, "entrée", itemGroupData, dictValues)

                            i+= 1


          return contextMenu        


    def setFields(self) : self.getFields(self.sender())
    
    def getFields(self, qgsMapLayerComboBox):

        lstObj = (self.layerCrues, self.layerWater, self.layerCommunes, self.layerStations)
        lstTableDefinition = (None, self.parent.tableFieldsWater, self.parent.tableFieldsCommunes, self.parent.tableFieldsStations)
        if not qgsMapLayerComboBox in lstObj : return
        else :    
 
           indexObj = lstObj.index(qgsMapLayerComboBox)
           obj = lstObj[indexObj]
           if not obj :  return

           if obj != self.layerCrues :
              
                 layer = obj.layer(qgsMapLayerComboBox.currentIndex())

                 if not layer : return

                 tableDefinition = lstTableDefinition[indexObj]

                 if tableDefinition == None : return       
                 if tableDefinition == {} : return

                 listAttributs =[] 
                 for key in tableDefinition.keys() :
                     if key != "FILTERWITH" : listAttributs.append(tableDefinition[key])

                 fieldsNames = layer.fields().names()
                 mapfieldsNames = map(lambda x:x.upper(), fieldsNames)            
                 
                 notInListAttributs = []
                 fieldsNames = layer.fields().names()
                 for attribut in listAttributs :
                     if attribut not in fieldsNames : notInListAttributs.append(attribut)   

                 cond = ( notInListAttributs == [])
                 obj.setStyleSheet("background-color:red;") if not cond else obj.setStyleSheet("background-color:#AEEE00;")
                 obj.setToolTip("Attributs :\n%s\n%s dans la couche %s" % (listAttributs, "trouvés" if cond else "non trouvés", qgsMapLayerComboBox.currentText()))
                 
                 if obj == self.layerStations : self.updateFieldsStations()
                 elif obj == self.layerCommunes : self.updateFieldsCommunes()
                 elif obj == self.layerWater : self.updateFieldsWater()
                 
           else : 
                    comboField, index = None, -1
                    index = obj.currentIndex()
                    comboField = self.fieldCrues

                    if comboField == None : return
                    comboField.clear()

                    if index > -1 :
                       
                       layer = qgsMapLayerComboBox.layer(index)
                       if not layer : return

                       provider = layer.dataProvider()
                       fields = provider.fields()

                       nameFields = [field.name() for field in fields]
                       comboField.addItems(nameFields)    


    def printImage(self, fileName):
        fileName = self.fileLogoLeft.toolTip() if self.sender() == self.buttonPrintImageL else self.fileLogoRight.toolTip()
        if fileName == "" : return
        if not exists(fileName): self.fileLogoLeft.clearImage(); return
        
        orientation = QPrinter.Landscape 
        title = "%s : %s" % ("Imprimer le fichier", fileName)
        printer = QPrinter(); printer.setPageSize(QPrinter.A4); printer.setOrientation(QPrinter.Landscape);
        printer.setPageMargins(5, 5, 5, 5, QPrinter.Millimeter); printer.setOutputFormat(QPrinter.NativeFormat); orientation = self.getImage(printer, fileName);       
        dlgPrint = printerPreview(self, self.printMapview, title, orientation, "printer.png")

    def getImage(self, printer, fileName):
        self.printMapview.clear()
        self.printMapview.setPageSize(QSizeF(printer.pageRect().size()))

        try:
            from osgeo.gdal import Open as gdalopen
            from osgeo.gdal import GA_ReadOnly
        except:
            from gdal import Open as gdalopen
            from gdal import GA_ReadOnly

        sourceWidth, sourceHeight = printer.pageRect().width(), printer.pageRect().height()
        dataset = gdalopen( fileName, GA_ReadOnly )
        orientation = QPrinter.Landscape
        if dataset :
            sourceWidth = dataset.RasterXSize
            sourceHeight = dataset.RasterYSize
            if sourceHeight > sourceWidth : orientation = QPrinter.Portrait
              
        xscale = (printer.pageRect().width())/ float(sourceWidth) 
        yscale = (printer.pageRect().height()-100) / float(sourceHeight)
        scale = min(xscale, yscale)
        
        width = int(sourceWidth*scale) if orientation == QPrinter.Landscape else printer.pageRect().width()-50
        height = int(sourceHeight*scale)
        title = "%s : %s" % ("Fichier", fileName)

        html = """
        <head><title>Edition</title></head>
        <body>
            <table width="100%">
                <tr><td><h3>{} {}</h3></td></tr>
                <tr><td><hr></td></tr>
                <tr><td><center><img src="{}" width="{}" height="{}"></center></td></tr>
                <tr><td><hr></td></tr>
                <tr><td align=right>RDI, le {}</td></tr>
            </table>    
        </body>
        """.format(title, "", fileName, width, height, thedatetime.now().strftime("%d/%m/%Y à %Hh%Mm%Ss"))
        self.printMapview.setHtml(html)

        return orientation
        

    def refreshDataDicosViewer(self): 

        layerCrues = None
        
        for elt in self.listElts :
            ctrlValue = getattr(self.parent, elt)

            ctrl, ctrlType = None,  None
            ctrl = self.findChild(QgsMapLayerComboBox, elt)
            
            if ctrl :  ctrlType = QgsMapLayerComboBox
            else :
                ctrl = self.findChild(textEditFolder, elt)
                if ctrl : ctrlType = textEditFolder
                else :
                    ctrl = self.findChild(QComboBox, elt)
                    if ctrl : ctrlType = QComboBox
                    else :                          
                          ctrl = self.findChild(tableFieldsMapper, elt)
                          if ctrl : ctrlType = tableFieldsMapper
                          else :
                                ctrl = None

            if ctrl :
               if ctrlType == textEditFolder :
                  #NEW 4.1.5
                  if not ctrl.objectName() in  ("rasterCrues", "nameGroupRaster") :
                     ctrl.setStyleSheet(self.styleTextEditDisaBled) if exists(ctrlValue) else ctrl.setStyleSheet(self.styleTextEditDisaBledOnError)   
                  ctrl.setPlainText(ctrlValue)
                  
               elif ctrlType == QComboBox :
                    idx = -1
                    if elt == "fieldCrues" and layerCrues : idx = layerCrues.fields().indexOf(ctrlValue)
                    elif elt == "apimodelmeteo" : idx, ctrlValue = ctrlValue[0], ctrlValue[1]
                    else : idx = ctrl.findText(ctrlValue) 
                    if idx > -1 :
                          ctrl.setCurrentIndex(idx)
                          ctrl.setCurrentText(ctrlValue) 
                    
               elif ctrlType == QgsMapLayerComboBox :
                    layers = QgsProject.instance().mapLayersByName(ctrlValue)
                    if layers :
                        layer = layers[0]
                        ctrl.setLayer(layer)
                        if elt == "layerCrues" : layerCrues = layer

               elif ctrlType == tableFieldsMapper :
                    if ctrlValue : 
                       for key in ctrlValue.keys() : ctrl.getItemPosByKey(key, ctrlValue[key])
        
        puce = getThemeIcon("dico.png").replace("\\","/")
        instance = QgsProject.instance()

        self.dicosViewer.clear()
        
        for key in self.listEltsTree.keys() :

            obj = self.listEltsTree[key]
            typeObj = type(obj)

            itemBase = QTreeWidgetItem()
            itemBase.setIcon(0, QIcon(puce))
            itemBase.setText(0, "%s" % key)

            if typeObj == dict :
               itemBase.setStatusTip(0, "") if key != "mappingData" else itemBase.setStatusTip(0, "A")
            else : itemBase.setStatusTip(0, "A")
            
            itemBase.setWhatsThis(0, puce)
            self.dicosViewer.addTopLevelItem(itemBase)

            eltIconPath = getThemeIcon("elt.png").replace("\\","/")
            eltIcon = QIcon(eltIconPath)

            if typeObj == list :
               for elt in obj :
                   text  ="%s" % elt
                   itemSchema = QTreeWidgetItem()
                   itemSchema.setIcon(0, eltIcon)
                   itemSchema.setText(0, text)
                   itemSchema.setStatusTip(0, "ED")
                   itemSchema.setWhatsThis(0, eltIconPath)
                   itemSchema.setBackground(0, iff(not instance.mapLayersByName(text), QBrush(QColor(255, 0, 0, 125)), QBrush(QColor(0, 255, 0, 125))))
                   itemBase.addChild(itemSchema)

            elif typeObj == dict :
                 for elt in sorted(obj.keys()) :
                     itemSchema = QTreeWidgetItem()
                     itemSchema.setIcon(0, eltIcon)
                     itemSchema.setText(0, "%s" % elt)
                     itemSchema.setWhatsThis(0, eltIconPath)
                     itemSchema.setStatusTip(0, "ED")  if not (elt.startswith("action") and itemBase.text(0) == "groupsActions" ) else itemSchema.setStatusTip(0, "") 
                     itemBase.addChild(itemSchema)

                     i = 0
                     for item in obj[elt] :
                         text = "%s" % item
                         itemSchemaItem = QTreeWidgetItem()
                         icon, iconPath = eltIcon, eltIconPath
                         if key == "groupsActions" :
                              rac = elt.lower().replace("action", "").replace("enjeux", "").replace("risques", "")  
                              path = getThemeIcon("%s_off.png" % rac).replace("\\","/")
                              if exists(path) : icon, iconPath = QIcon(path), path                              
                               
                         itemSchemaItem.setIcon(0, icon)
                         itemSchemaItem.setText(0, text)
                         itemSchemaItem.setStatusTip(0, "E")
                         itemSchemaItem.setWhatsThis(0, iconPath)
                         itemSchema.addChild(itemSchemaItem)

                         if i == 0 :
                            if type(item) == list :
                               cond = False 
                               for subitem in item :                                
                                   if instance.mapLayersByName(subitem) : cond = True ; break ;
                               itemSchema.setBackground(0, iff(not cond, QBrush(QColor(255, 0, 0, 125)), QBrush(QColor(0, 255, 0, 125))))
                                      
                            elif type(item) == str :
                               itemSchemaItem.setBackground(0, iff(not instance.mapLayersByName(text) and not instance.layerTreeRoot().findGroup(text), QBrush(QColor(255, 0, 0, 125)), QBrush(QColor(0, 255, 0, 125))))
       
                         i+= 1
                         

    def dicosViewerDoubleClickedItem(self, item, col) :
        if self.buttonEditNode.isEnabled() : self.getWizardGroupRaster()

    def getWizardGroupRaster(self):
        self.wizardData = wizardData(self)
        self.wizardData.exec_()
        self.wizardData = None

    def updateToolBarWidgetDicosViewer(self, item, col) :
        self.buttonAddNode.setEnabled(False)
        self.buttonEditNode.setEnabled(False)
        self.buttonDeleteNode.setEnabled(False)

        if item.statusTip(0).find("A") != -1 : self.buttonAddNode.setEnabled(True)
        if item.statusTip(0).find("E") != -1 : self.buttonEditNode.setEnabled(True)
        if item.statusTip(0).find("D") != -1 : self.buttonDeleteNode.setEnabled(True)

        
    def contextMnu(self, point):
        if self.dicosViewer.currentItem() == None: return      
        item = self.dicosViewer.currentItem()
        if not item : return

        actions = {"act_editNode" : ("edit.png", "Edit node", "" , self.getWizardGroupRaster, item.statusTip(0).find("E") != -1),
                   "act_addNode" : ("add.png", "Add node" , "", self.addNode, item.statusTip(0).find("A") != -1),
                   "act_deleteNode" : ("del.png", "Delete node" , "", self.deleteNode, item.statusTip(0).find("D") != -1),
                   }
        self.builderContextMenu(self.sender(), actions, point)

    def builderContextMenu(self, obj, actions, point):
        contextMnu = QMenu()
        
        for key in actions :
            if key.startswith("~") : contextMnu.addSeparator()
            else :   
                icon = getThemeIcon(actions[key][0])
                action = QAction(QIcon(icon), actions[key][1], self)
                if actions[key][2] != "" : action.setShortcut(QKeySequence(actions[key][2]))
                setattr(contextMnu, key, action)
                action.setEnabled(actions[key][4])
                action.setObjectName(key)
                contextMnu.addAction(action)
                action.triggered.connect(actions[key][3])

        contextMnu.exec_(obj.mapToGlobal(point))

    def addNode(self):
        parent = self.dicosViewer.currentItem()
        eltIconPath = getThemeIcon("elt.png").replace("\\","/")
        eltIcon = QIcon(eltIconPath)

        if parent.text(0) == "mappingData" :
           keys = [parent.child(i).text(0) for i in range(parent.childCount()) ]
           name, ok = QInputDialog.getText(None, "Nouvelle clef", "", QLineEdit.Normal)
           if not ok: return
           name = name.strip()
           if name == "" : QMessageBox.information(None, "Erreur", "La clef ne peut être une chaîne vide !"); return
           if name in keys : QMessageBox.information(None, "Erreur", "La clef <b><u>%s</u></b> existe déjà !" % name ); return
           keys.append(name)
           keys = sorted(keys)
           index = keys.index(name)
        else : name = "Nouveau paramètre"
        
        item = QTreeWidgetItem()
        item.setIcon(0, eltIcon)
        item.setText(0, name) 
        item.setWhatsThis(0, eltIconPath)
        item.setStatusTip(0, "ED") 
        item.setBackground(0, QBrush(QColor(255, 0, 0, 125)))
        parent.insertChild(index, item) if parent.text(0) == "mappingData" else parent.addChild(item)

        if parent.text(0) == "mappingData" :
           iconPath = getThemeIcon("elt.png").replace("\\","/")
           icon = QIcon(eltIconPath)
            
           itemSchemaItem = QTreeWidgetItem()
           itemSchemaItem.setIcon(0, icon)
           itemSchemaItem.setText(0, "[]")
           itemSchemaItem.setStatusTip(0, "E")
           itemSchemaItem.setWhatsThis(0, iconPath)
           item.addChild(itemSchemaItem)

           itemSchemaItem = QTreeWidgetItem()
           itemSchemaItem.setIcon(0, icon)
           itemSchemaItem.setText(0, "[]")
           itemSchemaItem.setStatusTip(0, "E")
           itemSchemaItem.setWhatsThis(0, iconPath)
           item.addChild(itemSchemaItem)

        if parent.text(0) != "mappingData" :
           self.dicosViewer.setCurrentItem(item) 
           self.updateToolBarWidgetDicosViewer(item, 0)
        else  :
           self.dicosViewer.setCurrentItem(itemSchemaItem) 
           self.updateToolBarWidgetDicosViewer(itemSchemaItem, 0)
            
        self.getWizardGroupRaster()

        
    def deleteNode(self):
        item = self.dicosViewer.currentItem()
        if not item : return

        if not item.childCount() in (0, 2) : return
        
        root = self.dicosViewer.invisibleRootItem()
        self.dicosViewer.setCurrentItem(item)

        title, msg = "Suppression d'un élément des dictionnaires", "Confirmez la suppression de l'élément :<br> <b>%s</b> ?" % item.text(0)
        msgDlgBox = myMsgDlgBox(self, QMessageBox.Question, "%s ..." % title, msg, "rdistop.png", "inondation.png", 0, None, self)
        
        windowIcon(msgDlgBox , "elt.png")
        ret = msgDlgBox.exec_()    

        if ret == 1 :           
           for item in self.dicosViewer.selectedItems():
               (item.parent() or root).removeChild(item)

               self.updateToolBarWidgetDicosViewer(self.dicosViewer.currentItem(), 0)
 

    def expandTreeAllNodes(self):
        treeWidget = self.dicosViewer
        if self.sender().objectName().startswith("expandAll") : treeWidget.expandAll()
        else : treeWidget.collapseAll()

    def setPreviewHtml(self): self.printPreviewHtml.setHtml(self.previewEnteteHtml())

    def previewEnteteHtml(self):
        fileLogoLeft = self.fileLogoLeft.toolTip().replace("\\", "/")
        fileLogoRight = self.fileLogoRight.toolTip().replace("\\", "/")

        html = "<HTML><BODY><head><title>Rapport</title></head>%s" % LEGENDSTYLES
        html+= "<table width='100%%'><tr><th width='10%%'><img src='file:///%s' width='90px' height='90px'></th><th width='85%%'><p style='font-size: 10px;'>%s</p></th><th width='5%%'><img src='file:///%s' width='170px' height='80px'></th></tr><tr><th colspan='3'><hr></th></tr>" % (fileLogoLeft, "%s, paramètres extension" % self.titleService.toPlainText(), fileLogoRight)
        html+= "<tr><td colspan='3'>"
        html+= "<table>"

        return html
 
    def updatePrintTreeWidget(self):
      
        results, maxlevel = self.visitTreeList(self.dicosViewer)

        fileLogoLeft = self.fileLogoLeft.toolTip().replace("\\", "/")
        fileLogoRight = self.fileLogoRight.toolTip().replace("\\", "/")
    
        html = "<HTML><BODY><head><title>Rapport</title></head>%s" % LEGENDSTYLES
        html+= "<table width='100%%'><tr><th><img src='file:///%s' width='90px' height='90px'></th><th><h2>%s</h2></th><th><img src='file:///%s' width='170px' height='80px'></th></tr><tr><th colspan='3'><hr></th></tr>" % (fileLogoLeft, "%s, paramètres extension" % self.titleService.toPlainText(), fileLogoRight)
        html+= "<tr><td colspan='3'>"
        html+= "<table>"
        nbInfos = maxlevel + 2
        
        for elt in results :
            html+= "<tr>"   
            for i in range(elt[0]): html+= "<td>&nbsp;</td>"
            if elt[4] == "#ff0000" : color = "rgba(255, 0, 0, 0.5)"
            elif elt[4] == "#00ff00" : color = "rgba(0, 255, 0, 0.5)"
            else : color = "rgba(255, 255, 255, 255)" 
            html+= "<td style='background-color: %s;' width=20px height=20px><img src='file:///%s' width=20px height=20px></td><td style='background-color: %s;' colspan=%s>%s</td>\n" % (color, elt[2], color, nbInfos-elt[0], elt[1])
            html+= "</tr>" 

        html+= "</table>"
        html+= "</td></tr><tr><td colspan='3'><hr></td></tr><tr><td align=right colspan='3'><i>%s, édition le %s</i></td></tr></table>" %  (self.parent.namePlugin, thedatetime.now().strftime("%d/%m/%Y à %Hh%Mm%Ss"))
        html+= "</BODY>\n</HTML>"

        self.printTreeWidget.setHtml(html) 

    def visitTreeElt(self, listItems, item, level, maxlevel):
        listItems.append([level, item.text(0), item.whatsThis(0), item.text(1), item.background(0).color().name()])
        for i in range(item.childCount()):
            if maxlevel < level+1 : maxlevel = level+1  
            maxlevel = self.visitTreeElt(listItems, item.child(i), level+1, maxlevel)
        return maxlevel    
     
    def visitTreeList(self, tree) :
        listItems = []
        maxlevel = 0
        for i in range(tree.topLevelItemCount()) :
            maxlevel = self.visitTreeElt(listItems, tree.topLevelItem(i), 0, 1)
        return listItems, maxlevel

    def getPrintTreeWidget(self):
        self.updatePrintTreeWidget()
        printer = QPrinter(); printer.setPageSize(QPrinter.A4); printer.setOrientation(QPrinter.Portrait);
        printer.setPageMargins(5, 5, 5, 5, QPrinter.Millimeter); printer.setOutputFormat(QPrinter.NativeFormat);
        orientation = QPrinter.Portrait
        dlgPrint = printerPreview(self, self.printTreeWidget, "%s :" % "Imprimer les dictionnaires", orientation, "printer.png")        
 
    def saveParams(self):
 
        if self.tabs.currentIndex() == 2 :

              try : fileName = PARAMFILES[self.comboBoxParamsLinked.currentIndex()+1]
              except : return

              fileName = join(self.parent.pathFolderParams, "%s.xml" % fileName)
              title, msg = "Confirmer la sauvegarde", "<h3>Confirmez-vous l'enregistrement<br>des paramètres actuellement saisis ?<br>dans : %s</h3>" % basename(fileName)
              msgDlgBox = myMsgDlgBox(self, QMessageBox.Question, "%s ..." % title, msg, "rdistop.png", "inondation.png", 0, None, self)              
              msgDlgBox.setWindowIcon(self.tabs.tabIcon(self.tabs.currentIndex()))
              ret = msgDlgBox.exec_()    

              if ret == 1 : self.saveLinkedParams(fileName, self.comboBoxParamsLinked.currentIndex(), 3)
                          

        elif self.tabs.currentIndex() == 3 :

             orIndex = self.tabs.currentIndex()

             title = self.saveAllContextParams.text().lstrip()

             title, msg = "Confirmer l'opération", "<h3>%s de tous les paramètres ?<br>sous :</h3><font style='font-family:  Montserrat, Arial, Helvetica, sans-serif; font-size: 0.7em;'>%s</font>" % (title, self.pathFolderParams.toPlainText())
             msgDlgBox = myMsgDlgBox(self, QMessageBox.Question, "%s ..." % title, msg, "rdistop.png", "inondation.png", 0, None, self)
             msgDlgBox.setWindowIcon(self.tabs.tabIcon(self.tabs.currentIndex()))
             ret = msgDlgBox.exec_()
              
             if ret == 1 :
                   self.parent.pathFolderParams = self.pathFolderParams.toPlainText()

                   index = 0
                   for paramFile in PARAMFILES :
                       fileName = "%s%s.xml" % (self.pathFolderParams.toPlainText(), paramFile)
                       if index == 0 : self.saveGeneralParams(fileName)
                       else :
                          self.tabs.setCurrentIndex(2)   
                          self.comboBoxParamsLinked.setCurrentIndex(index-1)
                          self.saveLinkedParams(fileName, index-1, 1)
                       index+= 1
                       
                   self.tabs.setCurrentIndex(orIndex)     
                   self.isValidContext()
                     

        else :

              try : fileName = PARAMFILES[0]
              except : return
              
              fileName = join(self.parent.pathFolderParams, "%s.xml" % fileName)                            
              title, msg = "Confirmer la sauvegarde", "<h3>Confirmez-vous l'enregistrement<br>des paramètres actuellement saisis ?<br>dans : %s</h3>" % basename(fileName)
              msgDlgBox = myMsgDlgBox(self, QMessageBox.Question, "%s ..." % title, msg, "rdistop.png", "inondation.png", 0, None, self)              
              msgDlgBox.setWindowIcon(self.tabs.tabIcon(self.tabs.currentIndex()))
              ret = msgDlgBox.exec_()    

              if ret == 1 : self.saveGeneralParams(fileName)

    def saveGeneralParams(self, fileName) :
        doc = etree.Element('rdigcPlugin')
        doc.attrib['version'] = self.parent.version
        for children in self.groupBoxParamsGeneraux.children() :
           if type(children) == QgsCollapsibleGroupBox :
                 for child in children.children() :
                       if type(child) in (textEditFolder, QgsMapLayerComboBox, QComboBox, QTreeWidget, tableFieldsMapper) :
                          if type(child) in (textEditFolder, QgsMapLayerComboBox, QComboBox) :
                             if child.objectName() == "apimodelmeteo" :
                                   value = APIMODELMETEOITEMS[child.currentText()]["model"]
                             else :      
                                   value = child.currentText() if type(child) in (QComboBox, QgsMapLayerComboBox) else child.toPlainText()  
                             connection = etree.SubElement(doc, child.objectName())
                             connection.attrib['value'] = value
                             setattr(self.parent, child.objectName(), value)
                          elif type(child) == tableFieldsMapper :
                              connection = etree.SubElement(doc, child.objectName())                            
                              self.exportTableWidget(child, connection)                            
                          else :
                             connection = etree.SubElement(doc, child.objectName())
                             self.exportTreeWidget(child, connection)

        for child in self.groupBoxParamsPrint.children() :
           if type(child) in (QTextEdit, imageViewer) :
              value = child.toolTip().replace("\\", "/") if type(child) == imageViewer else child.toPlainText().replace("\\", "/") 
              connection = etree.SubElement(doc, child.objectName())
              connection.attrib['value'] = value
              setattr(self.parent, child.objectName(), value) 

        try : xml = prettifyXml(etree.tostring(doc))
        except :
           self.barInfo.pushMessage("Avertissement :", "Impossible de sauvegarder les paramètres (défaut xml) !", Qgis.Warning, 3) 
           return    

        with open(fileName, 'w', encoding="utf-8", errors="surrogateescape") as fileobj: fileobj.write(xml)
        self.barInfo.pushMessage("Information :", "Fichier <b>%s</b> sauvegardé !" % basename(fileName), Qgis.Info, 3)

        self.parent.loadParamsRdi()
        items = [elt for elt in self.parent.mappingData.keys()]
        self.parent.listForDataSearch.clear()
        self.parent.listForDataSearch.addItems(items) 
        self.parent.listForDataSearch.setCurrentIndex(1)
        self.parent.updateCompleter()
        self.parent.updateComboCrues()
        #NEW 4.1.5
        self.parent.initNorthArrow()

                 
    def saveLinkedParams(self, fileName, index, duration):
          
        doc = etree.Element('rdigcPlugin')
        doc.attrib['local'] = "True" if index == 0 else "False"
        doc.attrib['version'] = self.parent.version
        
        root = self.treeParamsLinked.invisibleRootItem()
        for i in range(root.childCount()):
            group = root.child(i)
            groupData = group.text(0)
            values = group.data(0, Qt.WhatsThisRole) 
            dictValues = literal_eval(values)                           
            connection = etree.SubElement(doc, "groupData")
            for key in dictValues.keys() : connection.attrib[key] = dictValues[key]

            for j in range(group.childCount()) :
                values = group.child(j).data(0, Qt.WhatsThisRole) 
                dictValues = literal_eval(values)
                connectionItem = etree.SubElement(connection, "data") 
                for key in dictValues.keys() : connectionItem.attrib[key] = dictValues[key]

        try : xml = prettifyXml(etree.tostring(doc))
        except :
             self.barInfo.pushMessage("Avertissement :", "Impossible de sauvegarder les paramètres (défaut xml) !", Qgis.Warning, duration) 
             return 
            
        with open(fileName, 'w', encoding="utf-8", errors="surrogateescape") as fileobj: fileobj.write(xml)
        self.barInfo.pushMessage("Information :", "Fichier <b>%s</b> sauvegardé !" % basename(fileName), Qgis.Info, duration)

        self.setMenus(fileName, index)

                 
    def exportTableWidget(self, tableWidget, xmlRoot):
          connection = etree.SubElement(xmlRoot, "mappingData")
          ctrlValue = {}
          
          for i in range(tableWidget.rowCount()):
            eltXml = etree.SubElement(connection, "ITEM%s" % i)
            key = tableWidget.item(i, 0).text()
            try : value = tableWidget.cellWidget(i, 1).currentText() #QComboBox Widget
            except : value = tableWidget.cellWidget(i, 1).text() #QLabel Widget
            eltXml.attrib['name'] = key
            eltXml.attrib['value'] = value
            ctrlValue[key] = value
          
          setattr(self.parent, tableWidget.objectName(), ctrlValue)  
                 

     
    def exportTreeWidget(self, tree, xmlRoot) :
        for i in range(tree.topLevelItemCount()) :
            key = tree.topLevelItem(i).text(0)
            connection = etree.SubElement(xmlRoot, key)
            
            obj = self.listEltsTree[key]
            typeObj = type(obj)

            ctrlValue = {} if typeObj == dict else [] 

            if typeObj == list :
               for j in range(tree.topLevelItem(i).childCount()):
                   eltXml = etree.SubElement(connection, "VALUE%s" % j)
                   value = tree.topLevelItem(i).child(j).text(0)
                   eltXml.attrib['value'] = value
                   ctrlValue.append(value)
               setattr(self.parent, key, ctrlValue)
               
            elif typeObj == dict :       
               for j in range(tree.topLevelItem(i).childCount()):
                   eltXml = etree.SubElement(connection, "ITEM%s" % j)
                   keyDico =  tree.topLevelItem(i).child(j).text(0)
                   eltXml.attrib['value'] = keyDico
                   ctrlValue[keyDico] = []
                   
                   for k in range(tree.topLevelItem(i).child(j).childCount()):
                       subEltXml = etree.SubElement(eltXml, "VALUE%s" % k)
                       value = tree.topLevelItem(i).child(j).child(k).text(0)
                       subEltXml.attrib['value'] = value
                       try : value = eval(value)
                       except : pass
                       ctrlValue[keyDico].append(value)
                       
               setattr(self.parent, key, ctrlValue)


    def zoomToTerritoire(self):
        layer = self.layerTerritoire.layer(self.layerTerritoire.currentIndex())
        if layer :
           self.parent.canvas.setExtent(layer.extent())
           self.parent.canvas.refresh()

    #NEW 4.1.5 
    def getFile(self):
        if self.sender() == None: return
        obj = self.findChild(textEditFolder, self.sender().objectName()[3:])
        if not obj : return

        windowIcon(self, "files.png")

        self.initDir = dirname(__file__) if obj.toPlainText() == "" else dirname(obj.toPlainText())
        title = self.sender().toolTip()

        fileDialog = QFileDialog(self, title)

        if self.sender().objectName().find("pathCompass") > -1 :
              extension = "svg"
              fileDialog.setNameFilters(("Fichiers svg (*.svg *.SVG)",))
        elif self.sender().objectName().find("pathQml") > -1 :
              extension ="qml"
              fileDialog.setNameFilters(("Fichiers styles QGIS (*.qml *.QML)",))
        else :
              extension = "wav"
              fileDialog.setNameFilters(("Fichiers son (*.wav *.WAV)",))

        fileDialog.setDefaultSuffix(extension)
        fileDialog.setViewMode(QFileDialog.Detail)
        fileDialog.setDirectory(self.initDir)
        fileDialog.setFileMode(QFileDialog.ExistingFile)
        fileDialog.setAcceptMode(QFileDialog.AcceptOpen)

        fixeLabelsFileDialog(self, fileDialog, 0, False) 

        if fileDialog.exec_():
            inputFile = fileDialog.selectedFiles()[0]
            obj.setStyleSheet(self.styleTextEditDisaBled) if exists(inputFile) else obj.setStyleSheet(self.styleTextEditDisaBledOnError) 
            obj.setPlainText(cleanPath(inputFile.replace("\\", "/"), True))            
        
        windowIcon(self, "parametres_off.png")
        

    def getFolder(self):
        if self.sender() == None: return
        obj = self.findChild(textEditFolder, self.sender().objectName()[3:])
        if not obj : return
        windowIcon(self, "folderrdi.png")

        self.initDir = dirname(__file__) if obj.toPlainText() == "" else dirname(obj.toPlainText())
        title = self.sender().toolTip() 

        fileDialog = QFileDialog(self, title)
        fileDialog.setDirectory(self.initDir)
        fileDialog.setFileMode(QFileDialog.Directory)
        fileDialog.setOption(QFileDialog.ShowDirsOnly, False)
        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("\\", "/"), False))
            if obj == self.pathFolderParams : self.isValidContext()
        
        windowIcon(self, "parametres_off.png")


    def resizeEvent(self,ev): self.barInfo.setGeometry(0, 0, self.width(), 60)


def ctrlMap(self, conteneur, complete, getPointsForExtent, simply, iX, iY, iW, iH, name):
      
         self.groupMap = QGroupBox(conteneur)
         self.groupMap.setStyleSheet("QGroupBox {font-size: 12px; 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;}")
         
         if name == "toolBarWidgetMappingData" :
            dicoCtrls = {"lineadd" : ("add.png", "Ajouter le couple comme élément", 7, 10),
                         "linemoveup" : ("moveup.png", "Déplacer l'élément vers le haut",7, 30),
                         "linemovedown" : ("movedown.png", "Déplacer l'élément vers le bas", 7, 50),
                         "linedelete" : ("del.png", "Supprimer l'élément", 7, 70),
                         "linedeleteall" : ("deleteall.png", "Supprimer tous les éléments", 7, 90)
                         } 
         else :    
            dicoCtrls = {"expandAllNodes" : ("nodes_expand.png", "Ourvir tous les noeuds", 7, 10),
                         "closeAllNodes" : ("nodes_reduce.png", "Fermer tous les noeuds",7, 30),
                         "buttonAddNode" : ("add.png", "Ajouter un élément", 7, 50),
                         "buttonEditNode" : ("edit.png", "Editer l'élément", 7, 70),
                         "buttonDeleteNode" : ("del.png", "Supprimer un élément", 7, 90),
                         "buttonRefreshTreeWidget" : ("refresh.png", "Réinitialiser les dictionnaires", 7, 110),
                         "buttonPrintTreeWidget" : ("printer.png", "Imprimer les dictionnaires", 7, 130)
                         } 

         pos =  20
         sizeIcon = 24
       
         for key in dicoCtrls :
      
                   icon = getThemeIcon(dicoCtrls[key][0])
                   toolTip = QApplication.translate("rdigc", dicoCtrls[key][1])

                   button = myPushButton(self.groupMap) 
                   button.initPushButton(sizeIcon, sizeIcon, dicoCtrls[key][2], pos, key, "", toolTip, True, icon, sizeIcon, sizeIcon, True)
                   styleSheetPushButton = "QPushButton::checked {icon-size:  %spx, %spx; border-width: 1px; border-radius: 5px; border-color: black; background-color: #BBBBBB;}" % (sizeIcon, sizeIcon)
                   styleSheetPushButton+= "QPushButton {icon-size:  %spx, %spx; border-width: 0px; border-radius: 10px; background: transparent; }" % (sizeIcon, sizeIcon)
                   button.setStyleSheet(styleSheetPushButton)
                      
                   setattr(self, key, button)
                   pos+= 30

         sizeWidth = len(self.groupMap.children())*30
         self.groupMap.setGeometry(0, 0, 55, sizeWidth+20)
          
         return sizeWidth

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 = 570, 410
        setObjSizes(self, self.maxWidth, self.maxheight)
        self.resize(self.maxWidth, self.maxheight) 
        windowIcon(self, "parametres_off.png")

        title = ""
        if type(self.sender()) in (textEditFolder, funnyPushButton) : title = self.sender().toolTip()
        elif type(self.sender()) == QTreeWidget : title  = self.sender().currentItem().parent().text(0).upper()
        elif type(self.sender()) == myPushButton : title = self.parent.dicosViewer.currentItem().parent().text(0).upper()
        self.setWindowTitle("Assistant sélection de paramètres%s%s" % (" : " if title != "" else "...", title))


        self.polygonIconPath = getThemeIcon("polygon.png").replace("\\","/")
        self.polygonIcon = QIcon(self.polygonIconPath)
        self.lineIconPath = getThemeIcon("linestring.png").replace("\\","/")
        self.lineIcon = QIcon(self.lineIconPath)
        self.pointIconPath = getThemeIcon("point.png").replace("\\","/")
        self.pointIcon = QIcon(self.pointIconPath)
        self.unknowIconPath = getThemeIcon("unknown.png").replace("\\","/")
        self.unknowIcon = QIcon(self.unknowIconPath)


        self.groupsViewer = None
        self.comboLayers = None
        self.lineEditValue = None 
        self.groupIconPath = getThemeIcon("group.png").replace("\\","/")
        self.groupIcon = QIcon(self.groupIconPath)
        self.iconPathLayer = getThemeIcon("layers-icon.png").replace("\\","/")
        self.iconLayer = QIcon(self.iconPathLayer)

        self.applyButton = QPushButton(self)
        self.applyButton.setObjectName("applyButton")
        self.applyButton.setText("&Appliquer")

        self.closeButton = QPushButton(self)
        self.closeButton.setObjectName("closeButton")
        self.closeButton.setText("&Fermer")

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

        sender = self.parent.sender()

        if sender == self.parent.imgnameGroupRaster :        

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

        elif sender in (self.parent.dicosViewer, self.parent.buttonEditNode, self.parent.buttonAddNode) or type(sender) == QAction :
           if sender in (self.parent.buttonEditNode, self.parent.buttonAddNode) or type(sender) == QAction : sender = self.parent.dicosViewer
           currentItemParent = sender.currentItem().parent() 
           if currentItemParent.text(0).startswith("action") :
              self.getGroupsViewer(True)
              if self.groupsViewer :
                 self.identifyGroup(sender.currentItem().text(0)) 
                 self.groupsViewer.itemDoubleClicked.connect(self.setValueForGroupActions)
                 self.applyButton.clicked.connect(self.setValueForGroupActions)
           elif currentItemParent.text(0).startswith("thematic") :
              self.getGroupsViewer(False)
              if self.groupsViewer :
                 self.identifyGroup(sender.currentItem().text(0)) 
                 self.groupsViewer.itemDoubleClicked.connect(self.setValueForGroupActions)
                 self.applyButton.clicked.connect(self.setValueForGroupActions)
           elif currentItemParent.text(0).startswith("mapping") :
                oldName = sender.currentItem().text(0)
                self.getLineEditValue(oldName)
                self.applyButton.clicked.connect(self.setValueForKey)
           else :
              self.getLayersAndFields()
              self.applyButton.clicked.connect(self.setValueForMapping)


    def getLayersAndFields(self):

        self.lblComboLayers = labeller("lblComboLayers", "Couches :", "special", Qt.AlignRight, self)
        
        self.comboLayers = QgsMapLayerComboBox(self)
        self.comboLayers.setEditable(False)
        self.comboLayers.setEnabled(True)
        self.comboLayers.model().sortOrder()
        self.comboLayers.setFilters(QgsMapLayerProxyModel.VectorLayer)

        self.lblComboFields = labeller("lblComboFields", "Attributs :", "special", Qt.AlignRight, self)
        
        self.comboFields = QComboBox(self)
        self.comboFields.setObjectName("comboFields")
        self.comboFields.setStyleSheet(self.parent.styleComboBox)

        self.comboLayers.layerChanged.connect(self.setFields)

        self.lblMappingData = QLabel(self)
        self.lblMappingData.setText("Tableau de Mapping :")
        self.lblMappingData.setAlignment(Qt.AlignCenter)

        self.listLayers = QListWidget(self)
        self.listFields = QListWidget(self)
        

        self.toolBarWidgetMappingData = QToolBar(self) 
        self.toolBarWidgetMappingData.setObjectName("toolBarWidgetMappingData")
        sizeWidth = ctrlMap(self, self.toolBarWidgetMappingData, True, False, False, 5, 5, 40, 200, "toolBarWidgetMappingData")
        self.toolBarWidgetMappingData.setAllowedAreas(Qt.TopToolBarArea | Qt.BottomToolBarArea)
        self.toolBarWidgetMappingData.setGeometry(520, 130, 40, sizeWidth + 30)

        
        self.lineadd.clicked.connect(self.addItem)
        self.linemoveup.clicked.connect(self.moveUpItem)
        self.linemovedown.clicked.connect(self.moveDownItem)
        self.linedelete.clicked.connect(self.deleteItem)
        self.linedeleteall.clicked.connect(self.deleteAllItems)
        
        parent = self.parent.dicosViewer.currentItem().parent()

        if parent.childCount() != 2 : return

        listLayers = eval(parent.child(0).text(0))
        listFields = eval(parent.child(1).text(0)) 
        self.listLayers.addItems(listLayers)
        self.listFields.addItems(listFields)
        self.listFields.setEnabled(False)

        self.listLayers.clicked.connect(self.synchroListes)
        #NEW 4.1.5   
        iRow, idx, findElt = 0, 0, False
        for elt in listLayers :
            for i in  range(self.comboLayers.model().rowCount()) :
                if elt == self.comboLayers.itemText(i) :
                   idx, findElt = i, True   
                   break
            if findElt : break
            iRow+= 1

        self.comboLayers.setCurrentIndex(idx)
        self.getFields(self.comboLayers)        
        self.synchroListes()

        if findElt :
           self.listLayers.setCurrentRow(iRow)
           self.listLayers.itemPressed.emit(self.listLayers.currentItem())
           self.synchroListes()
           self.listFields.setFocus()

           fieldName = self.listFields.currentItem().text() 

           for i in  range(self.comboFields.model().rowCount()) :   
                 if self.comboFields.itemText(i) == fieldName :
                    self.comboFields.setCurrentIndex(i)
                    break


    def synchroListes(self):
        if self.listLayers.count() > 0 : self.listFields.setCurrentRow(self.listLayers.currentRow())
        self.linedeleteall.setEnabled(self.listLayers.count()>0)
        self.applyButton.setEnabled(self.listLayers.count()>0)
        self.linedelete.setEnabled(self.listLayers.currentRow()>-1)
        self.linemoveup.setEnabled(self.listLayers.currentRow()>0)
        self.linemovedown.setEnabled(self.listLayers.currentRow()<self.listLayers.count()-1 and self.listLayers.currentRow()>-1)


    def addItem(self):
        self.listLayers.blockSignals(True)
        #NEW 4.15  MatchContaints   
        if self.listLayers.findItems(self.comboLayers.currentText(), Qt.MatchExactly|Qt.MatchRecursive) and self.listFields.findItems(self.comboFields.currentText(), Qt.MatchExactly|Qt.MatchRecursive) :
           QMessageBox.information(None, "Information", "Le couple :<h4>[%s - %s]</h4> est déjà présent dans le tableau de Mapping !" % (self.comboLayers.currentText(), self.comboFields.currentText())) 
           return
        
        self.listLayers.insertItem(self.listLayers.count(), self.comboLayers.currentText())
        self.listFields.insertItem(self.listFields.count(), self.comboFields.currentText())
        self.listLayers.blockSignals(False)
        self.synchroListes()
        
    def deleteItem(self):

        if self.listLayers.currentRow() == -1: return

        title, msg = "Suppression d'un élément du mapping", "Confirmez la suppression de l'élément couplé :<br> <b>%s - %s</b> ?" % (self.listLayers.currentItem().text(), self.listFields.currentItem().text())
        msgDlgBox = myMsgDlgBox(self, QMessageBox.Question, "%s ..." % title, msg, "rdistop.png", "inondation.png", 0, None, self)
        windowIcon(msgDlgBox , "elt.png")
        ret = msgDlgBox.exec_()    

        if ret == 1 :
           currentRow = self.listLayers.currentRow()
           self.listLayers.blockSignals(True)
           self.listLayers.takeItem(currentRow)
           self.listFields.takeItem(currentRow)
           self.listLayers.blockSignals(False)

           self.synchroListes()

    def deleteAllItems(self):
        title, msg = "Suppression de tous les éléments du mapping", "Confirmez la suppression ?" 
        msgDlgBox = myMsgDlgBox(self, QMessageBox.Question, "%s ..." % title, msg, "rdistop.png", "inondation.png", 0, None, self)
        windowIcon(msgDlgBox , "elt.png")
        ret = msgDlgBox.exec_()    

        if ret == 1 :
           currentRow = self.listLayers.currentRow()
           self.listLayers.blockSignals(True)
           self.listLayers.clear()
           self.listFields.clear()
           self.listLayers.blockSignals(False)

           self.synchroListes()

    def moveUpItem(self):
        currentRow = self.listLayers.currentRow()
        currentItemListLayers = self.listLayers.takeItem(currentRow)
        currentItemListFields = self.listFields.takeItem(currentRow)
        self.listLayers.insertItem(currentRow - 1, currentItemListLayers)
        self.listFields.insertItem(currentRow - 1, currentItemListFields)
        self.listLayers.setCurrentRow(currentRow - 1)
        self.synchroListes()

    def moveDownItem(self):
        currentRow = self.listLayers.currentRow()
        currentItemListLayers = self.listLayers.takeItem(currentRow)
        currentItemListFields = self.listFields.takeItem(currentRow)
        self.listLayers.insertItem(currentRow + 1, currentItemListLayers)
        self.listFields.insertItem(currentRow + 1, currentItemListFields)
        self.listLayers.setCurrentRow(currentRow + 1)
        self.synchroListes()
        
       
    def setFields(self) : self.getFields(self.sender())
    def getFields(self, qgsMapLayerComboBox):
        if not self.comboLayers : return
        self.comboFields.clear()

        if self.comboLayers.currentIndex() > -1 :
           layer = self.comboLayers.layer(qgsMapLayerComboBox.currentIndex())            
           if not layer : return

           provider = layer.dataProvider()
           fields = provider.fields()

           nameFields = [field.name() for field in fields]
           self.comboFields.addItems(nameFields)

    
    def getLineEditValue(self, oldName):
        self.lineEditValue = QLineEdit(self)
        self.lineEditValue.setObjectName("lineEditValue")
        self.lineEditValue.setText(oldName)

    def getGroupsViewer(self, getGroups):
        self.groupsViewer = QTreeWidget(self)
        self.groupsViewer.setHorizontalScrollBarPolicy(Qt.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.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 setValueForKey(self) :
        newName = self.lineEditValue.text()
        if newName == "" : QMessageBox.information(None, "Erreur", "La clef ne peut être une chaîne vide !"); return
        if self.parent.dicosViewer.currentItem().text(0) == newName : return 
        parent = self.parent.dicosViewer.currentItem().parent()
        keys = [parent.child(i).text(0) for i in range(parent.childCount()) ]
        if newName in keys : QMessageBox.information(None, "Erreur", "La clef <b><u>%s</u></b> existe déjà !" % newName ); return
        self.parent.dicosViewer.currentItem().setText(0, newName)

    def setValueForMapping(self):
        parent = self.parent.dicosViewer.currentItem().parent()

        if parent.childCount() != 2 : return

        listLayers = [self.listLayers.item(i).text() for i in range(self.listLayers.count())]
        listFields = [self.listFields.item(i).text() for i in range(self.listFields.count())]
        if listLayers == [] : return
        parent.child(0).setText(0, "%s" % listLayers)
        parent.child(1).setText(0, "%s" % listFields)

        instance = QgsProject.instance()
        cond = False 
        for subitem in eval(parent.child(0).text(0)) :                                
            if instance.mapLayersByName(subitem) : cond = True ; break ;
        parent.setBackground(0, iff(not cond, QBrush(QColor(255, 0, 0, 125)), QBrush(QColor(0, 255, 0, 125))))
        self.close()

           
    def setValueForGroupActions(self):
        instance = QgsProject.instance()
        item = self.parent.dicosViewer.currentItem()
        text = self.groupsViewer.currentItem().text(0)
        itemExist = False
        if item.parent().text(0) in ("thematicInondations", "thematicSubmersions") :
           for i in range(item.parent().childCount()) :
               if item.parent().child(i).text(0) == text : itemExist = True ; break
        if itemExist :
           QMessageBox.information(None, "Information", "L'élément :<h4>[%s]</h4> est déjà présent pour la thématique <h4>[%s]</h4> !" % (text, item.parent().text(0))) 
           return
            
        item.setText(0, text)
        item.setBackground(0, iff(not instance.mapLayersByName(text) and not instance.layerTreeRoot().findGroup(text), QBrush(QColor(255, 0, 0, 125)), QBrush(QColor(0, 255, 0, 125))))
        self.close()
        
    def setValueForGroupRaster(self):
        self.parent.nameGroupRaster.setPlainText(self.groupsViewer.currentItem().text(0))
        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):
        if self.lineEditValue : self.lineEditValue.setGeometry(10, 10, self.width()-20, 25) 
        if self.groupsViewer : self.groupsViewer.setGeometry(10, 10, self.width()-20, self.height()-60)
        if self.comboLayers :
           self.lblComboLayers.setGeometry(20, 25, 140, 20)
           self.comboLayers.setGeometry(170, 20, 250, 25)
           self.lblComboFields.setGeometry(20, 65, 140, 20)
           self.comboFields.setGeometry(170, 60, 250, 25)
           self.lblMappingData.setGeometry(130, 100, 160, 25)
           self.listLayers.setGeometry(20, 130, 240, 230)
           self.listFields.setGeometry(270, 130, 240, 230)           
        self.applyButton.setGeometry(self.width()-230, self.height()-35, 100, 25)
        self.closeButton.setGeometry(self.width()-120, self.height()-35, 100, 25)


class imageViewer(QGraphicsView):
    photoClicked = pyqtSignal(QPoint)

    def __init__(self, parent, widget, ratio):
        super(imageViewer, self).__init__(parent)
        self.widget = widget
        self.ratio = ratio
        self._zoom = 0
        self._empty = True
        self._scene = QGraphicsScene(self)
        self._photo = QGraphicsPixmapItem()
        self._scene.addItem(self._photo)
        self.setScene(self._scene)
        self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
        self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setBackgroundBrush(QBrush(QColor(255, 255, 255)))
        self.setFrameShape(QFrame.NoFrame)

        self.styleSheetTextEdit = "QLineEdit { font-size: 10px;  text-align: center; color : black; background-color: rgba(160, 160, 160, 125); border: 1px solid black;  border-radius: 4px;  padding: 2px;}"

        self.sizeRatio = QLineEdit(self)
        self.sizeRatio.setAlignment(Qt.AlignCenter)
        self.sizeRatio.setStyleSheet(self.styleSheetTextEdit)
        self.sizeRatio.setGeometry(5, 5, 240, 15)
        self.sizeRatio.setText("ratio attendu : %s" % self.ratio)
        self.sizeRatio.setEnabled(False)

        self.sizeInfos = QLineEdit(self)
        self.sizeInfos.setAlignment(Qt.AlignCenter)
        self.sizeInfos.setStyleSheet(self.styleSheetTextEdit)
        self.sizeInfos.setGeometry(5, 230, 240, 15)
        self.sizeInfos.setText("dimension de l'image :")
        self.sizeInfos.setEnabled(False)
        

    def hasPhoto(self): return not self._empty

    def fitInView(self, scale=True):
        rect = QRectF(self._photo.pixmap().rect())
        if not rect.isNull():
            self.setSceneRect(rect)
            if self.hasPhoto():
                unity = self.transform().mapRect(QRectF(0, 0, 1, 1))
                self.scale(1 / unity.width(), 1 / unity.height())
                viewrect = self.viewport().rect()
                scenerect = self.transform().mapRect(rect)
                factor = min(viewrect.width() / scenerect.width(),
                             viewrect.height() / scenerect.height())
                self.scale(factor, factor)
            self._zoom = 0

    def clearImage(self):
        self._scene.removeItem(self._photo)
        self._photo = QGraphicsPixmapItem()
        self._scene.addItem(self._photo)
        self.setToolTip("")
        
    def zoomIn(self):
        factor = 1.25
        self._zoom += 1
        if self._zoom > 0: self.scale(factor, factor)
        
    def zoomOut(self):
        factor = 0.8
        self._zoom -= 1
        if self._zoom > -10: self.scale(factor, factor)
        
    def viewAllImage(self) : self.fitInView()     

    def loadImage(self) :
        mimeTypeFilters = (("application/octet-stream", "image/jpeg", "image/png"))
        dlgFile = QFileDialog(self, "Charger une image ...")
        dlgFile.setMimeTypeFilters(sorted(mimeTypeFilters))
        dlgFile.setViewMode(QFileDialog.Detail)
        dlgFile.setDirectory(dirname(self.toolTip()))
        dlgFile.setFileMode(QFileDialog.ExistingFiles)  
        dlgFile.setAcceptMode(QFileDialog.AcceptOpen)

        fixeLabelsFileDialog(self, dlgFile, 0, True)
        
        if dlgFile.exec_():
           if  dlgFile.selectedFiles() != [] :
               fileName = "%s" % (dlgFile.selectedFiles()[0])
               inputFile = QFileInfo(fileName)
               self.loadImageFromFile(fileName)
               self.setToolTip(fileName)
               self.widget.setPreviewHtml()
    
    def loadImageFromFile(self, fileName=""):
        if len(fileName) and isfile(fileName):
            image = QImage(fileName)
            self.setImage(image)
    
    def setImage(self, image):
        if type(image) is QPixmap :  pixmap = image
        elif type(image) is QImage :  pixmap = QPixmap.fromImage(image)
        else: raise RuntimeError("ImageViewer.setImage: le fichier en entrée doit être de type QImage ou QPixmap.")
        
        self._zoom = 0
        if pixmap and not pixmap.isNull():
            self._empty = False
            self.setDragMode(QGraphicsView.ScrollHandDrag)
            self._photo.setPixmap(pixmap)

            cond = False
            if self.ratio == "1x1" :
               cond = True if (pixmap.width()<=pixmap.height()*1.15) and (pixmap.height()<=pixmap.width()*1.15) else False
            else :
               cond = True if (pixmap.width()>=pixmap.height()*1.5) and (pixmap.height()<=pixmap.width()*0.65) else False                
            self.sizeInfos.setText("dimension de l'image : %s , %s pixels" % (pixmap.width(), pixmap.height()))

            if cond :
               self.sizeInfos.setStyleSheet("QLineEdit { font-size: 10px;  text-align: center; color : black; background-color: rgba(0, 255, 0, 125); border: 1px solid black;  border-radius: 4px;  padding: 2px;}")
            else :
               self.sizeInfos.setStyleSheet("QLineEdit { font-size: 10px;  text-align: center; color : black; background-color: rgba(255, 0, 0, 125); border: 1px solid black;  border-radius: 4px;  padding: 2px;}") 
              
        else:
            self._empty = True
            self.setDragMode(QGraphicsView.NoDrag)
            self._photo.setPixmap(QPixmap())
            self.sizeInfos.setText("dimension de l'image :")
        self.fitInView()

    def wheelEvent(self, event):
        if self.hasPhoto():
            if event.angleDelta().y() > 0:
                factor = 1.25
                self._zoom += 1
            else:
                factor = 0.8
                self._zoom -= 1
            
            if self._zoom> -10 :  self.scale(factor, factor)
            elif self._zoom > 0:  self.scale(factor, factor)
            else: self._zoom = 0

    def toggleDragMode(self):
        if self.dragMode() == QGraphicsView.ScrollHandDrag: self.setDragMode(QGraphicsView.NoDrag)
        elif not self._photo.pixmap().isNull(): self.setDragMode(QGraphicsView.ScrollHandDrag)

    def mousePressEvent(self, event):
        if self._photo.isUnderMouse(): self.photoClicked.emit(QPoint(event.pos()))
        super(imageViewer, self).mousePressEvent(event)


def ctrlMapTemp(self, conteneur, complete, getPointsForExtent, simply, iX, iY, iW, iH):
        
         self.groupMap = QGroupBox(conteneur)
         self.groupMap.setStyleSheet("QGroupBox {font-size: 12px; 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.actionsMapCanvas = QButtonGroup(conteneur) 
         self.actionsMapCanvas.setExclusive(True)
         sizeWidth = 0
         
         if conteneur  == self.toolBarImageViewerLeft :
             dicoCtrls = {"buttonGetImageL" : ("folderrdi.png", "Sélectionner l'image ...", 10, 2),
                          "buttonZoomAllL" : ("zoomall.png", "Afficher toute l'image", 40, 2),
                          "buttonZoominL" : ("zoomin.png", "Zoomer", 70, 2),
                          "buttonZoomOutL" : ("zoomout.png", "DéZoomer", 100, 2),
                          "buttonPrintImageL" : ("printer_off.png", "Imprimer l'image", 130, 2)
                          }

         elif conteneur  == self.toolBarImageViewerRight :    
             dicoCtrls = {"buttonGetImageR" : ("folderrdi.png", "Sélectionner l'image ...", 10, 2),
                          "buttonZoomAllR" : ("zoomall.png", "Afficher toute l'image", 40, 2),
                          "buttonZoominR" : ("zoomin.png", "Zoomer", 70, 2),
                          "buttonZoomOutR" : ("zoomout.png", "DéZoomer", 100, 2),
                          "buttonPrintImageR" : ("printer_off.png", "Imprimer l'image", 130, 2)
                          }
         pos = 10       
         for key in dicoCtrls :
      
                   icon = getThemeIcon(dicoCtrls[key][0])
                   toolTip = QApplication.translate("rdigc", dicoCtrls[key][1])

                   button = myPushButton(self.groupMap)
                   if key in ("buttonListSelectionActions", "buttonListFilesActions") : button.initPushButton(50, 25, pos, dicoCtrls[key][3], key, "", toolTip, True, icon, 50, 25, True) ; sizeWidth+= 50;                                               
                   else : button.initPushButton(25, 25, pos, dicoCtrls[key][3], key, "", toolTip, True, icon, 25, 25, True); sizeWidth+= 30;
                   
                   setattr(self, key, button)
                   pos+= 50 if key in ("buttonListSelectionActions", "buttonListFilesActions") else 30

                   if key in ("buttonZoominL", "buttonZoominR", "buttonZoomOutL", "buttonZoomOutR") : button.setAutoRepeat(True)
                   if key in ("buttonListSelectionActions", "buttonListFilesActions") : self.contextMnuObj(button)

         self.groupMap.setGeometry(20, 30, sizeWidth+15, 35) 

         return sizeWidth
