Salome HOME
PN : les listes sont maintenant "repeat" par defaut
[tools/eficas.git] / Editeur / treewidget.py
index c80538f9a01a9541d3bb739e7b897062562df5c6..311189fe75d0fa6baf9935d65a903ffd127ba09e 100644 (file)
@@ -1,11 +1,23 @@
-#@ MODIF treewidget Editeur  DATE 02/07/2001   AUTEUR D6BHHJP J.P.LEFEBVRE 
 #            CONFIGURATION MANAGEMENT OF EDF VERSION
 # ======================================================================
-# COPYRIGHT (C) 1991 - 2001  EDF R&D                  WWW.CODE-ASTER.ORG
-#              SEE THE FILE "LICENSE.TERMS" FOR INFORMATION ON USAGE AND
-#              REDISTRIBUTION OF THIS FILE.
+# COPYRIGHT (C) 1991 - 2002  EDF R&D                  WWW.CODE-ASTER.ORG
+# THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
+# IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
+# THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
+# (AT YOUR OPTION) ANY LATER VERSION.
+#
+# THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
+# WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
+# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
+# GENERAL PUBLIC LICENSE FOR MORE DETAILS.
+#
+# YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
+# ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
+#    1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
+#
+#
 # ======================================================================
-import os,sys,string,re,types
+import os,sys,string,re,types,traceback
 from Tkinter import *
 
 
@@ -14,13 +26,13 @@ import images
 
 #
 __version__="$Name:  $"
-__Id__="$Id: treewidget.py,v 1.1.1.1 2002/03/26 09:08:45 eficas Exp $"
+__Id__="$Id: treewidget.py,v 1.11 2003/03/07 16:17:12 eficas Exp $"
 #
 
 Fonte_Standard = fontes.standard
 
 class Tree :
-    def __init__(self,appli,jdc_item,scrolledcanvas,command = None):
+    def __init__(self,appli,jdc_item,scrolledcanvas,command = None,rmenu=None):
         self.item = jdc_item
         self.scrolledcanvas = scrolledcanvas
         self.canvas = self.scrolledcanvas.component('canvas')
@@ -28,14 +40,19 @@ class Tree :
         self.canvas.bind("<Key-Next>", self.page_down)
         self.canvas.bind("<Key-Up>", self.unit_up)
         self.canvas.bind("<Key-Down>", self.unit_down)             
+        self.canvas.bind("<1>", self.canvas_select)             
         self.tree = self
         self.command = command
+        self.rmenu=rmenu
         self.appli = appli
         self.parent = None
         self.racine = self
         self.node_selected = None
         self.build_children()
 
+    def canvas_select(self,event):
+        self.canvas.focus_set()
+
     def page_up(self,event):
         event.widget.yview_scroll(-1, "page")
     def page_down(self,event):
@@ -48,7 +65,7 @@ class Tree :
     def build_children(self):
         """ Construit la liste des enfants de self """
         self.children = []
-        child = Node(self,self.item,self.command)
+        child = Node(self,self.item,self.command,self.rmenu)
         self.children.append(child)
         child.state='expanded'
 
@@ -59,16 +76,9 @@ class Tree :
         for child in self.children:
             child.draw(x,lasty)
             lasty = child.lasty + 15
-            child.trace_ligne()
-        #self.update()
         self.children[0].select()
         self.resizescrollregion()
 
-    def deselectall_old(self):
-        """ déselectionne tous les éléments de l'arbre """
-        for child in self.children:
-            child.deselect()
-
     def deselectall(self):
         """ déselectionne tous les éléments de l'arbre """
         if self.node_selected :
@@ -79,8 +89,17 @@ class Tree :
         for child in self.children:
             child.update()
 
+    def update_valid(self) :
+        """Cette methode a pour but de mettre a jour la validite du noeud
+           et de propager la demande de mise à jour à son parent
+        """
+        pass
+
     def resizescrollregion(self):
-        self.scrolledcanvas.resizescrollregion()
+        x0,y0,x1,y1=self.canvas.bbox(ALL)
+        # On ajoute une marge approximativement de la moitié du canvas
+        y1=y1+self.canvas.winfo_height()/2
+        self.canvas.configure(scrollregion = (x0,y0,x1,y1))
 
     def select_next(self,event):
         self.node_selected.select_next()
@@ -95,17 +114,42 @@ class Tree :
     def verif_all(self):
         for child in self.children :
             self.verif_all_children()
+
+    def see(self,items):
+        x1, y1, x2, y2=apply(self.canvas.bbox, items)
+        while x2 > self.canvas.canvasx(0)+self.canvas.winfo_width():
+            old=self.canvas.canvasx(0)
+            self.canvas.xview_scroll( 1, 'units')
+            # avoid endless loop if we can't scroll
+            if old == self.canvas.canvasx(0):
+                break
+        while y2 > self.canvas.canvasy(0)+self.canvas.winfo_height():
+            old=self.canvas.canvasy(0)
+            self.canvas.yview_scroll( 1, 'units')
+            if old == self.canvas.canvasy(0):
+                break
+        # done in this order to ensure upper-left of object is visible
+        while x1 < self.canvas.canvasx(0):
+            old=self.canvas.canvasx(0)
+            self.canvas.xview_scroll( -1, 'units')
+            if old == self.canvas.canvasx(0):
+                break
+        while y1 < self.canvas.canvasy(0):
+            old=self.canvas.canvasy(0)
+            self.canvas.yview_scroll( -1, 'units')
+            if old == self.canvas.canvasy(0):
+                break
             
 class Node :
-    def __init__(self,parent,item,command=None):
+    def __init__(self,parent,item,command=None,rmenu=None):
         self.parent = parent
         self.item = item
         self.command = command
+        self.rmenu=rmenu
         self.tree = self.parent.tree
         self.appli = self.parent.appli
         self.canvas = self.parent.canvas
         self.init()
-        #self.build_children()
 
     def init(self):
         self.state='collapsed'
@@ -139,7 +183,7 @@ class Node :
         sublist = self.item._GetSubList()
         if not sublist : return
         for item in sublist :
-            child = Node(self,item,self.command)
+            child = Node(self,item,self.command,self.rmenu)
             self.children.append(child)
             
     #-----------------------------------------------
@@ -157,16 +201,7 @@ class Node :
         self.tree.node_selected = self
         if self.command:apply(self.command,(self,))
         self.highlight()
-        self.canvas.focus_force()
-        #self.make_visible()
-
-    def deselect_old(self, event=None):
-        """ Déselectionne self """
-        self.selected = 0
-        if self.displayed == 1:
-            self.dehighlight()
-        for child in self.children:
-            child.deselect()
+        self.make_visible()
 
     def deselect(self, event=None):
         """ Déselectionne self """
@@ -175,9 +210,10 @@ class Node :
             
     def make_visible(self):
         """ Rend l'objet self visible cad déplace le scroll pour que self soit dans
-        la fenêtre de visu"""
-        x0,y0,x1,y1 = self.canvas.bbox(ALL)
-        self.canvas.yview("moveto",self.y/y1)
+            la fenêtre de visu
+        """
+        lchild=self.last_child()
+        self.tree.see((self.image_id,lchild.image_id))
         
     def select_next(self,ind=0):
         """ on doit chercher à sélectionner dans l'ordre:
@@ -208,6 +244,15 @@ class Node :
             self.parent.children[index].select()
         except:
             self.parent.select()
+
+    def popup(self,event=None):
+        """
+            Declenche le traitement associé au clic droit de la souris
+            sur l'icone du Node
+        """
+        if not self.rmenu:return
+        apply(self.rmenu,(self,event))
+
     #-----------------------------------------------
     # Méthodes de recherche d'informations
     #-----------------------------------------------
@@ -273,6 +318,7 @@ class Node :
         if image != None :
             self.image_id = self.canvas.create_image(self.x+15,self.y,image = image)
             self.canvas.tag_bind(self.image_id,"<1>",self.select)
+            self.canvas.tag_bind(self.image_id,"<3>",self.popup)
             self.id.append(self.image_id)
         else:
             self.image_id = None
@@ -292,6 +338,7 @@ class Node :
             child.draw(x,y)
             nb = child.get_nb_children()
             y = y + 20*(nb+1)
+        self.trace_ligne()
 
     def drawtext(self):
         """ Affiche les deux zones de texte après l'icône de couleur de l'objet """
@@ -315,6 +362,7 @@ class Node :
         self.id.append(self.label_id)
         # bindings sur le widget label
         self.label.bind("<1>", self.select)
+        self.label.bind("<3>", self.popup)
         self.label.bind("<Enter>",self.enter)
         self.label.bind("<Leave>",self.leave)
         # valeur de cet objet à afficher
@@ -366,26 +414,17 @@ class Node :
         nb = self.get_nb_children()
         self.state = 'collapsed'
         self.collapse_children()
-        self.efface()
-        try:
-            self.move(-20*nb)
-        except:
-            pass
-        self.draw(self.x,self.y)
+        self.redraw(-nb)
         self.select()
-        self.update()
-
+   
     def expand(self,event = None):
         """ Expanse self et le retrace """
         if not self.item.isactif() : return
         if not self.children : self.build_children()
         self.state = 'expanded'
         nb = self.get_nb_children()
-        self.move(20*nb)
-        self.efface()
-        self.draw(self.x,self.y)
+        self.redraw(nb)
         self.select()
-        self.update()
 
     def redraw(self,nb):
         """ Redessine self :  nb est le décalage à introduire
@@ -395,7 +434,12 @@ class Node :
         # on efface self et on le redessine
         self.efface()
         self.draw(self.x,self.y)
-        self.update()
+        # Il n'est pas nécessaire d'appeler update
+        # il suffit d'updater les coordonnees et de retracer les lignes
+        self.racine.update_coords()
+        self.racine.trace_ligne()
+        self.update_valid()
+        self.tree.resizescrollregion()
         
     def update_coords(self):
         """ Permet d'updater les coordonnes de self et de tous ses enfants"""
@@ -432,6 +476,15 @@ class Node :
             for child in self.children:
                 if child.displayed != 0 : child.update_texte()
         
+    def update_valid(self) :
+        """Cette methode a pour but de mettre a jour la validite du noeud
+           et de propager la demande de mise à jour à son parent
+        """
+        if self.image_id != None :
+            image = self.geticonimage()
+            self.canvas.itemconfig(self.image_id,image=image)
+        self.parent.update_valid()
+
     def update(self,event=None) :
         """ Classe Node :
             Cette méthode est appelée pour demander l update d un noeud 
@@ -477,8 +530,6 @@ class Node :
             print 'dy=',dy
         # on déplace tous les items de dy
         self.canvas.move('move',0,dy)
-        # il faut réactualiser la zone de scroll
-        self.tree.resizescrollregion()
 
     def trace_ligne(self):
         """ Dessine les lignes verticales entre frères et entre père et premier fils"""
@@ -498,19 +549,12 @@ class Node :
                 print child
                 print child.item.object
 
-    def make_visible_OBSOLETE(self,nb):
-        """ Cette méthode a pour but de rendre le noeud self (avec tous ses descendants
-        affichés) visible dans le canvas """
-        x = self.canvas.canvasx(self.canvas.cget('width'))
-        y = self.canvas.canvasy(self.canvas.cget('height'))
-        #print 'x,y =',x,y
-        x0,y0,x1,y1 = self.canvas.bbox(ALL)
-        #print 'x0,y1=',x0,y1
-        y_deb = self.y
-        nb = self.get_nb_children()
-        y_fin = y_deb + 20*nb
-        #print 'y_deb,y_fin=',y_deb,y_fin
-        
+    def last_child(self):
+        lchild=self
+        if self.state == 'expanded' and self.children:
+           lchild= self.children[-1].last_child()
+        return lchild
+
     #------------------------------------------------------------------
     # Méthodes de création et destruction de noeuds
     # Certaines de ces méthodes peuvent être appelées depuis l'externe
@@ -523,30 +567,32 @@ class Node :
         
     def full_creation(self,name,pos=None):
         """
-        Interface avec ACCAS : création de l'objet de nom name et
-        du noeud associé. Retourne le noeud fils ainsi créé
+            Interface avec ACCAS : création de l'objet de nom name et
+            du noeud associé. Retourne le noeud fils ainsi créé
         """
         item = self.item.additem(name,pos)
         if item == None or item == 0:
             # impossible d'ajouter le noeud de nom : name
             return 0
         nature = item.get_nature()
-        #if nature =="COMMANDE" or nature == "OPERATEUR" or nature == "PROCEDURE":
         if nature in ("COMMANDE","OPERATEUR","PROCEDURE","COMMENTAIRE",
                       "PARAMETRE","COMMANDE_COMMENTARISEE","PARAMETRE_EVAL"):
             # on veut ajouter une commande ou un commentaire ou un paramètre
             # il ne faut pas rechercher un même objet déjà existant
             # à modifier : il faut tester l'attribut 'repetable' 
             enfant = None
+        elif self.item.object.isMCList():
+            # Dans ce cas on ne fait pas de remplacement. On ne cherche pas un objet de meme nom
+            enfant=None
         else :
             enfant = self.get_node_fils(item.get_nom())
         if enfant :
             # un fils de même nom existe déjà : on remplace
             # un MCFACT (ou une MCList) par une (autre) MCList
-            child = Node(self,item,self.command)
+            child = Node(self,item,self.command,self.rmenu)
             self.replace_node(enfant,child)
         else :            
-            child = Node(self, item,self.command)
+            child = Node(self, item,self.command,self.rmenu)
             if pos is None:
                 self.children.append(child)
             else :
@@ -578,7 +624,6 @@ class Node :
         """
         if not self.children : self.build_children()
         if pos == None :
-            #pos = len(self.children)
             if type(fils) == types.InstanceType:
                 pos = self.item.get_index_child(fils.nom)
             else:
@@ -587,10 +632,12 @@ class Node :
         if child == 0 :
             # on n'a pas pu créer le noeud fils
             return 0
-        child.displayed = 1
         self.state = 'expanded'
+        child.displayed = 1
+        if child.item.isactif():
+           child.state = 'expanded'
+        if not child.children : child.build_children()
         if verif == 'oui':
-            if not child.children : child.build_children()
             test = child.item.isMCList()
             if test :
                 child.children[-1].verif_condition()
@@ -613,6 +660,9 @@ class Node :
             # on donne la position depuis l'extérieur
             # (appel de append_child par append_brother par exemple)
             index = pos
+        elif type(pos) == types.InstanceType:
+            # pos est un item. Il faut inserer name apres pos
+            index = self.item.get_index(pos) +1
         else :
             if type(name) == types.InstanceType:
                 index = self.item.get_index_child(name.nom)
@@ -627,8 +677,6 @@ class Node :
         nbnew = self.get_nb_children()
         self.redraw(nbnew-nbold)
         child.select()
-        child.expand()
-        #child.make_visible()
         if retour == 'oui': return child
 
     def delete_node_child(self,child):
@@ -678,6 +726,7 @@ class Node :
                 print 'Erreur dans la destruction de ',self.item.get_nom(),' dans delete'
             nbnew = pere.get_nb_children()
         pere.redraw(nbnew-nbold)
+       pere.select()
 
     def copynode(self,node,pos) :
         """ node est le noeud à copier à la position pos de self ( = parent de node) """
@@ -692,15 +741,17 @@ class Node :
             try :
                 child.item.object.mc_liste = objet_copie.mc_liste
             except:
-                pass
+                traceback.print_exc()
+
     #--------------------------------------------------------------
     # Méthodes de vérification du contexte et de validité du noeud
     #--------------------------------------------------------------
-    def traite_mclist(self):
+    def traite_mclist_OLD(self):
         """ Dans le cas d'une MCList il faut vérifier qu'elle n'est pas vide
             ou réduite à un seul élément suite à une destruction
         """
         # self représente une MCList
+       print "on passe par traite_mclist ",len(self.item)
         if len(self.item) == 0 :
             # la liste est vide : il faut la supprimer
             self.delete()
@@ -711,6 +762,8 @@ class Node :
             noeud = self.children[0]
             if self.parent.delete_child(self):
                 self.parent.append_node_child(noeud.item,pos=index,verif='non')
+            else:
+               print "destruction de self impossible !"
             #if self.parent.delete_child(self):
             #    self.parent.copynode(self.children[0],index)
             #else :
@@ -718,6 +771,26 @@ class Node :
         else :
             return
 
+    def traite_mclist(self):
+        """ Dans le cas d'une MCList il faut vérifier qu'elle n'est pas vide
+            ou réduite à un seul élément suite à une destruction
+        """
+        # self représente une MCList
+        if len(self.item) == 0 :
+            # la liste est vide : il faut la supprimer
+            self.delete()
+        elif len(self.item) == 1:
+            # il ne reste plus qu'un élément dans la liste
+            # il faut supprimer la liste et créer directement l'objet
+            index = self.parent.children.index(self)
+            noeud = self.children[0]
+           noeud.parent = self.parent
+           self.parent.delete_node_child(self)
+            self.parent.item.replace_child(self.item,noeud.item)
+           self.parent.children.insert(index,noeud)
+        else :
+            return
+           
     def verif_all(self):
         self.verif_all_children()