Salome HOME
CCAR: correction d'un probleme de mise a jour de contexte lors d'une insertion
[tools/eficas.git] / Editeur / treewidget.py
1 # -*- coding: utf-8 -*-
2 #            CONFIGURATION MANAGEMENT OF EDF VERSION
3 # ======================================================================
4 # COPYRIGHT (C) 1991 - 2002  EDF R&D                  WWW.CODE-ASTER.ORG
5 # THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
6 # IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
7 # THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
8 # (AT YOUR OPTION) ANY LATER VERSION.
9 #
10 # THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
11 # WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
12 # MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
13 # GENERAL PUBLIC LICENSE FOR MORE DETAILS.
14 #
15 # YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
16 # ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
17 #    1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
18 #
19 #
20 # ======================================================================
21 import os,sys,string,re,types,traceback
22 from Tkinter import *
23
24
25 import fontes
26 import images
27 from Ihm import CONNECTOR
28
29 #
30 __version__="$Name:  $"
31 __Id__="$Id: treewidget.py,v 1.22 2005/06/01 15:18:16 eficas Exp $"
32 #
33
34 Fonte_Standard = fontes.standard
35
36 class Tree :
37     def __init__(self,appli,jdc_item,scrolledcanvas,command = None,rmenu=None):
38         self.item = jdc_item
39         self.scrolledcanvas = scrolledcanvas
40         self.canvas = self.scrolledcanvas.component('canvas')
41         self.canvas.bind("<Key-Prior>", self.page_up)
42         self.canvas.bind("<Key-Next>", self.page_down)
43         self.canvas.bind("<Key-Up>", self.unit_up)
44         self.canvas.bind("<Key-Down>", self.unit_down)             
45         self.canvas.bind("<Key-Left>", self.mot_up)
46         self.canvas.bind("<Key-Right>", self.mot_down)
47         self.canvas.bind("<1>", self.canvas_select)             
48         self.tree = self
49         self.command = command
50         self.rmenu=rmenu
51         self.appli = appli
52         self.parent = None
53         self.racine = self
54         self.node_selected = None
55         self.build_children()
56
57     def canvas_select(self,event):
58         self.canvas.focus_set()
59
60     def page_up(self,event):
61         event.widget.yview_scroll(-1, "page")
62     def page_down(self,event):
63         event.widget.yview_scroll(1, "page")
64     def unit_up(self,event):
65         event.widget.yview_scroll(-1, "unit")
66     def unit_down(self,event):
67         event.widget.yview_scroll(1, "unit")              
68
69     def mot_down(self,event):
70         self.select_next(None)
71         self.canvas.focus_set()
72
73     def mot_up(self,event):
74         self.node_selected.select_mot_previous()
75         self.canvas.focus_set()
76
77     def build_children(self):
78         """ Construit la liste des enfants de self """
79         self.children = []
80         child = self.item.itemNode(self,self.item,self.command,self.rmenu)
81         self.children.append(child)
82         child.state='expanded'
83
84     def draw(self):
85         """ Dessine l'arbre """
86         lasty = 8
87         x = 5
88         for child in self.children:
89             child.draw(x,lasty)
90             lasty = child.lasty + 15
91         self.children[0].select()
92         self.resizescrollregion()
93
94     def deselectall(self):
95         """ déselectionne tous les éléments de l'arbre """
96         if self.node_selected :
97             self.node_selected.deselect()
98             
99     def update(self):
100         """ Update tous les éléments de l'arbre """
101         for child in self.children:
102             child.update()
103
104     def update_valid(self) :
105         """Cette methode a pour but de mettre a jour la validite du noeud
106            et de propager la demande de mise à jour à son parent
107         """
108         pass
109
110     def resizescrollregion(self):
111         x0,y0,x1,y1=self.canvas.bbox(ALL)
112         # On ajoute une marge approximativement de la moitié du canvas
113         y1=y1+self.canvas.winfo_height()/2
114         self.canvas.configure(scrollregion = (x0,y0,x1,y1))
115
116     def select_next(self,event):
117         self.node_selected.select_next()
118         self.canvas.focus_set()
119
120     def select_previous(self,event):
121         self.node_selected.select_previous()
122
123     def full_creation(self,name,index):
124         raise "OBSOLETE"
125         # A changer lorsqu'il y aura plusieurs jdc ouverts en même temps
126         self.children[0].full_creation(name,index)
127
128     def verif_all(self):
129         raise "OBSOLETE"
130         traceback.print_stack()
131         for child in self.children :
132             self.verif_all_children()
133
134     def see(self,items):
135         x1, y1, x2, y2=apply(self.canvas.bbox, items)
136         while x2 > self.canvas.canvasx(0)+self.canvas.winfo_width():
137             old=self.canvas.canvasx(0)
138             self.canvas.xview_scroll( 1, 'units')
139             # avoid endless loop if we can't scroll
140             if old == self.canvas.canvasx(0):
141                 break
142         while y2 > self.canvas.canvasy(0)+self.canvas.winfo_height():
143             old=self.canvas.canvasy(0)
144             self.canvas.yview_scroll( 1, 'units')
145             if old == self.canvas.canvasy(0):
146                 break
147         # done in this order to ensure upper-left of object is visible
148         while x1 < self.canvas.canvasx(0):
149             old=self.canvas.canvasx(0)
150             self.canvas.xview_scroll( -1, 'units')
151             if old == self.canvas.canvasx(0):
152                 break
153         while y1 < self.canvas.canvasy(0):
154             old=self.canvas.canvasy(0)
155             self.canvas.yview_scroll( -1, 'units')
156             if old == self.canvas.canvasy(0):
157                 break
158             
159 class Node :
160     def __init__(self,parent,item,command=None,rmenu=None):
161         self.parent = parent
162         self.item = item
163         self.connections=[]
164         self.connect()
165
166         self.command = command
167         self.rmenu=rmenu
168         self.tree = self.parent.tree
169         self.appli = self.parent.appli
170         self.canvas = self.parent.canvas
171         self.init()
172
173     def init(self):
174         self.state='collapsed'
175         self.displayed = 0
176         self.selected = 0
177         self.x = self.y  =None
178         self.lasty = 0
179         self.children = None
180         self.id = []
181         if self.parent is self.tree:
182            self.racine=self
183         else:
184            self.racine = self.parent.racine
185            
186         # etape = noeud d'étape auquel appartient self
187         # = self si c'est lui-même
188         #if isinstance(self.parent,Tree) :
189             # on est  sur un noeud de JDC
190             #self.etape=None
191         #elif isinstance(self.parent.parent,Tree) :
192             # on est sur un noeud d'étape
193             #self.etape=self
194         #else :
195             # on est sur un noeud de mot-clé
196             #self.etape=self.parent.etape
197
198     def reconnect(self):
199         self.disconnect()
200         self.connect()
201
202     def connect(self):
203         self.connections.append(self.item._object)
204         CONNECTOR.Connect(self.item._object,"add",self.onAdd,())
205         CONNECTOR.Connect(self.item._object,"supp",self.onSupp,())
206         CONNECTOR.Connect(self.item._object,"valid",self.onValid,())
207         if self.item.object is not self.item._object:
208            CONNECTOR.Connect(self.item.object,"add",self.onAdd,())
209            CONNECTOR.Connect(self.item.object,"supp",self.onSupp,())
210            CONNECTOR.Connect(self.item.object,"valid",self.onValid,())
211            self.connections.append(self.item.object)
212
213     def disconnect(self):
214         for c in self.connections:
215            CONNECTOR.Disconnect(c,"add",self.onAdd,())
216            CONNECTOR.Disconnect(c,"supp",self.onSupp,())
217            CONNECTOR.Disconnect(c,"valid",self.onValid,())
218         self.connections=[]
219
220     def __del__(self):
221         """ appele a la destruction du noeud """
222         #print "NOEUD DETRUIT",self,self.item.GetLabelText()[0]
223
224     def force_select(self):
225         if self.selected:
226            # le noeud est selectionné. On force la reconstruction du panel associé
227            if self.command:apply(self.command,(None,))
228            self.select()
229
230     def onValid(self):
231         #print "onValid : l'item a changé de validité ",self.item,self.item.object,self.item.object.isvalid()
232         self.update_node_valid()
233         self.update_node_label()
234         self.update_node_texte()
235
236     def onAdd(self,objet):
237         #print "onAdd : un objet a été ajouté aux fils de l'item ",self.item.object,objet
238         self.expand_node()
239         old_nodes=self.children
240         self.update_nodes()
241         self.redraw_children(old_nodes)
242         self.force_select()
243
244     def onSupp(self,objet):
245         #print "onSupp : un objet a été supprimé des fils de l'item ",self.item.object,objet
246         self.expand_node()
247         old_nodes=self.children
248         self.update_nodes()
249         self.redraw_children(old_nodes)
250         self.force_select()
251
252     def update_nodes(self):
253         #print "update_nodes",self
254         newnodes=[]
255         inodes=iter(self.children)
256         sublist=self.item._GetSubList()
257         iliste=iter(sublist)
258
259         while(1):
260            old_item=item=None
261            for node in inodes:
262               old_item=node.item
263               if old_item in sublist:break
264               #print "item supprime",old_item
265            for item in iliste:
266               if item is old_item:break
267               #print "item ajoute",item
268               child = item.itemNode(self,item,self.command,self.rmenu)
269               newnodes.append(child)
270
271            if old_item is None and item is None:break
272            if old_item is item:
273               #print "item conserve",item
274               newnodes.append(node)
275
276         self.children=newnodes
277         self.reconnect()
278
279     def supprime(self):
280         self.disconnect()
281         self.efface_node()
282
283         #self.label_id=None
284         #self.text_id=None
285         #self.label=None
286         #self.text=None
287         #self.image_id=None
288         #self.icone_id=None
289         #self.etape=None
290         ####self.parent=None
291         #self.command = None
292         #self.rmenu=None
293         #self.tree = None
294         #self.appli=None
295         #self.canvas = None
296
297         if not self.children : return
298         for child in self.children:
299             child.supprime()
300         self.children=None
301
302     def redraw_children(self,old_nodes):
303         #print "redraw_children",old_nodes
304         #print self.children
305         y = self.y + 20
306         x = self.x + 15
307         supp_nodes=[]
308
309         inodes=iter(old_nodes)
310         iliste=iter(self.children)
311         # on parcourt la liste des anciens noeuds (node)
312         # et la liste des nouveaux noeuds (new_node) en parallele (iterateurs)
313
314         while(1):
315            new_node=node=None
316            for node in inodes:
317               #print "ancien noeud",node
318               if node in self.children:break # ancien noeud toujours present
319               #print "noeud supprime",node,node.item.GetLabelText()[0]
320               dy=node.y-node.lasty -20
321               #print "deplacer noeuds",y,dy
322               node.move_nodes(y,dy)
323               node.supprime()
324               #supp_nodes.append(node)
325
326            for new_node in iliste:
327               #print "nouveau noeud",new_node
328               if new_node in old_nodes: break # nouveau noeud deja present
329               #print "noeud ajoute",new_node,new_node.item.GetLabelText()[0]
330               y=self.draw_node(new_node,x,y)
331
332            if node is None and new_node is None : break
333
334            if node is new_node: # ancien noeud
335               #print "noeud conserve",node
336               node.update_label_texte()
337               y=y+node.lasty-node.y +20
338
339         self.racine.update_coords()
340         self.canvas.delete('line')
341         self.racine.trace_ligne()
342         self.tree.resizescrollregion()
343         # Mettre à 1 pour verifier les cycles entre objets node
344         #withCyclops=0
345         #if withCyclops:
346            #from Misc import Cyclops
347            #z = Cyclops.CycleFinder()
348            #print supp_nodes
349            #for o in supp_nodes:
350              #z.register(o)
351            #del supp_nodes
352            #del o
353            #z.find_cycles()
354            #z.show_stats()
355            #z.show_cycles()
356
357     def tag_move_nodes(self,y):
358         """ Marque pour deplacement tous les noeuds au dela de l'ordonnée y """
359         self.canvas.dtag(ALL,'move')
360         # on marque tous les ids au dela de y
361         x0, y0, x1, y1 = self.canvas.bbox(ALL)
362         self.canvas.addtag_overlapping('move',x0,y,x1,y1)
363
364     def move_nodes(self,y,dy):
365         """ Déplace de l'incrément dy les noeuds au dela de l'ordonnée y """
366         self.tag_move_nodes(y)
367         # on déplace tous les items de dy
368         self.canvas.move('move',0,dy)
369
370     def draw_node(self,new_node,x,y):
371         """ Dessine le noeud new_node en x,y en deplacant les noeuds existants
372             en y et au dela
373             Retourne la position du premier des noeuds deplaces
374         """
375         self.tag_move_nodes(y)
376         #if new_node.item.isactif():
377            #new_node.state = 'expanded'
378         new_node.state = 'expanded'
379         new_node.draw(x,y)
380         dy=(new_node.get_nb_children()+1)*20
381         #print "deplacer noeuds",y,dy
382         self.canvas.move('move',0,dy)
383         return new_node.lasty+20
384
385     def build_children(self):
386         """ Construit la liste des enfants de self """
387         self.children = []
388         sublist = self.item._GetSubList()
389         if not sublist : return
390         for item in sublist :
391             child = item.itemNode(self,item,self.command,self.rmenu)
392             self.children.append(child)
393             
394     #-----------------------------------------------
395     # Méthodes de sélection/déselection d'un noeud
396     #-----------------------------------------------
397     
398     def select(self, event=None):
399         """
400         Rend le noeud courant (self) sélectionné et déselectionne
401         tous les autres
402         """
403         #print "SELECT",self
404         if not self.children : self.build_children()
405         self.tree.deselectall()
406         self.selected = 1
407         self.tree.node_selected = self
408         if self.command:apply(self.command,(self,))
409         self.highlight()
410         self.make_visible()
411
412     def deselect(self, event=None):
413         """ Déselectionne self """
414         self.selected = 0
415         if self.displayed == 1 : self.dehighlight()
416             
417     def make_visible(self):
418         """ Rend l'objet self visible cad déplace le scroll pour que self soit dans
419             la fenêtre de visu
420         """
421         lchild=self.last_child()
422         self.tree.see((self.image_id,lchild.image_id))
423         
424     def select_next(self,ind=0):
425         """ on doit chercher à sélectionner dans l'ordre:
426                 - son premier fils s'il est affiché
427                 - son frère cadet s'il existe
428                 - son oncle (benjamin de son père)
429                 - ... appel récursif ...
430         """
431         if self.state=='expanded' and len(self.children) > ind:
432             self.children[ind].select()
433         else :
434             index = self.parent.children.index(self) + 1
435             try :
436               if isinstance(self.parent,TREE) :
437                 try:
438                     self.children[ind].select()
439                 except:
440                     self.children[0].select()
441               else :                
442                 self.parent.select_next(index)
443             except :
444                 self.parent.select_next(index)
445
446     def select_mot_previous(self):
447         index = self.parent.children.index(self) - 1
448         try :
449             if index > 0  :
450                self.parent.children[index].select()
451             else :
452                self.parent.select()
453         except:
454             self.parent.select()
455
456     def select_previous(self):
457         """ on doit d'abord sélectionner(dans l'ordre) :
458              - son frère aîné
459              - son père
460         """
461         index = self.parent.children.index(self) + 1
462         try :
463             self.parent.children[index].select()
464         except:
465             self.parent.select()
466
467     def popup(self,event=None):
468         """
469             Declenche le traitement associé au clic droit de la souris
470             sur l'icone du Node
471         """
472         if not self.rmenu:return
473         apply(self.rmenu,(self,event))
474
475     #-----------------------------------------------
476     # Méthodes de recherche d'informations
477     #-----------------------------------------------
478     def geticonimage(self,name=None):
479         """
480         Retourne l'image qui doit être associée à self
481         """
482         if not name :
483             name = self.item.GetIconName()
484         if not name or name == 'aucune' :
485             return None
486         return images.get_image(name)
487
488     def get_nb_children(self):
489         """ Retourne le nombre d'enfants affichés de self """
490         nb = 0
491         if self.state =='collapsed' :  return nb
492         for child in self.children :
493             nb = nb + 1 + child.get_nb_children()
494         return nb
495
496     def get_liste_id(self):
497         """ Retourne la liste de tous les id (filiation comprise) de self """
498         liste = self.id
499         for child in self.children:
500             liste.extend(child.get_liste_id())
501         return liste
502
503     def get_node_fils(self,name) :
504         """ Retourne le fils de self de nom name s'il existe"""
505         for child in self.children:
506             if child.item.get_nom() == name: return child
507         return None
508
509     #-----------------------------------------------
510     # Méthodes d'affichage d'un noeud
511     #-----------------------------------------------
512     def draw(self,x,y):
513         """ Permet de tracer le noeud self """
514         # le début du noeud est en x,y
515         self.x = x
516         self.y = y
517         self.lasty = y
518         self.displayed = 1
519         self.id=[]
520         # choix de l'icone à afficher : + ou -
521         if self.item.IsExpandable():
522             if self.state == 'expanded':
523                 iconname = "minusnode"
524                 callback = self.collapse
525             else:
526                 iconname = "plusnode"
527                 callback = self.expand
528             image = self.geticonimage(name=iconname)
529             self.icone_id = self.canvas.create_image(self.x, self.y, image=image)
530             self.callback_id=self.canvas.tag_bind(self.icone_id, "<1>", callback)
531             self.id.append(self.icone_id)
532         # création de la ligne horizontale
533         self.ligne_id = self.canvas.create_line(self.x,self.y,self.x+10,self.y)
534         self.id.append(self.ligne_id)
535         self.canvas.tag_lower(self.ligne_id)
536         # affichage de l'icone (carre ,rond, ovale ...) de couleur
537         image = self.geticonimage()
538         if image != None :
539             self.image_id = self.canvas.create_image(self.x+15,self.y,image = image)
540             self.select_id2=self.canvas.tag_bind(self.image_id,"<1>",self.select)
541             self.popup_id2=self.canvas.tag_bind(self.image_id,"<3>",self.popup)
542             self.id.append(self.image_id)
543         else:
544             self.image_id = None
545         # affichage du texte : nom de l'objet (ETAPE ou MOT-CLE) et sa valeur
546         self.drawtext()
547         if self.state == 'expanded' :
548             if not self.children : self.build_children()
549             if len(self.children) > 0:
550                 self.drawchildren()
551                 self.lasty = self.children[-1].lasty
552    
553     def drawchildren(self):
554         """ Dessine les enfants de self """
555         y = self.y + 20
556         x = self.x + 15
557         for child in self.children:
558             child.draw(x,y)
559             nb = child.get_nb_children()
560             y = y + 20*(nb+1)
561         self.trace_ligne()
562
563     def drawtext(self):
564         """ Affiche les deux zones de texte après l'icône de couleur de l'objet """
565         if self.image_id != None :
566             textx = self.x + 30
567         else:
568             textx = self.x + 15
569         texty = self.y
570         # nom,fonte et couleur de l'objet du noeud à afficher
571         labeltext,fonte,couleur = self.item.GetLabelText()
572         if labeltext    == ''   : labeltext = '   '
573         if fonte        == None : fonte = Fonte_Standard
574         if couleur      == None : couleur = 'black'
575         # création du widget label
576         self.label = Label(self.canvas,
577                            text = labeltext,
578                            fg = couleur,
579                            bg = 'gray95',
580                            font=fonte)
581         self.label_id = self.canvas.create_window(textx,texty,window=self.label,anchor='w')
582         self.id.append(self.label_id)
583         # bindings sur le widget label
584         self.select_id=self.label.bind("<1>", self.select)
585         self.popup_id=self.label.bind("<3>", self.popup)
586         self.enter_id=self.label.bind("<Enter>",self.enter)
587         self.leave_id=self.label.bind("<Leave>",self.leave)
588         # valeur de cet objet à afficher
589         x0, y0, x1, y1 = self.canvas.bbox(self.label_id)
590         textx = max(x1, 200) + 10
591         text = self.item.GetText() or " "
592         self.text = Label(self.canvas, text=text,
593                             bd=0, padx=2, pady=2,background='gray95',
594                             font=fonte)
595         if self.selected:
596             self.highlight()
597         else:
598             self.dehighlight()
599         self.text_id = self.canvas.create_window(textx, texty,anchor="w", window=self.text)
600         self.id.append(self.text_id)
601         
602     def highlight(self,event=None):
603         """ Met en surbrillance self"""
604         if hasattr(self,'label'):
605             self.label.configure(fg='white',bg='#00008b')
606             
607     def dehighlight(self,event=None):
608         """ Rétablit l'affichage normal de self"""
609         if hasattr(self,'label'):
610             self.label.configure(fg='black',bg='gray95')
611
612     def enter(self,event=None):
613         """ Met en surbrillance self et affiche le fr de l'objet """
614         self.highlight()
615         fr = self.item.get_fr()
616         self.appli.affiche_infos(fr)
617         
618     def leave(self,event=None):
619         """ Rétablit l'affichage normal de self et efface le fr de l'objet """
620         if not self.selected :
621             self.dehighlight()
622         self.appli.affiche_infos('')
623
624     def collapse_children(self):
625         """ Collapse récursivement tous les descendants de self """
626         if not self.children : return
627         for child in self.children:
628             child.state='collapsed'
629             child.collapse_children()
630             
631     def collapse(self,event = None):
632         """ Collapse self et descendants et retrace self """
633         nb = self.get_nb_children()
634         self.state = 'collapsed'
635         self.collapse_children()
636         self.redraw(-nb)
637         self.select()
638    
639     def expand_node(self,event = None):
640         """ Expanse self et le retrace """
641         if self.state == 'expanded':return
642         #if not self.item.isactif() : return
643         if not self.children : self.build_children()
644         self.state = 'expanded'
645         nb = self.get_nb_children()
646         self.redraw(nb)
647
648     def expand(self,event = None):
649         """ Expanse self et le retrace """
650         self.expand_node()
651         self.select()
652
653     def redraw(self,nb):
654         """ Redessine self :  nb est le décalage à introduire
655             en dessous de self pour le redessiner """
656         # nb = nombre d'items de décalage
657         self.move(20*nb)
658         # on efface self et on le redessine
659         self.efface()
660         self.draw(self.x,self.y)
661         # Il n'est pas nécessaire d'appeler update
662         # il suffit d'updater les coordonnees et de retracer les lignes
663         self.racine.update_coords()
664         self.racine.trace_ligne()
665         self.update_valid()
666         self.tree.resizescrollregion()
667         
668     def update_coords(self):
669         """ Permet d'updater les coordonnes de self et de tous ses enfants"""
670         if self.displayed == 0 : return
671         if self.image_id != None :
672             coords = self.canvas.coords(self.image_id)
673             self.x = coords[0]-15
674         else:
675             coords = self.canvas.coords(self.label_id)
676             self.x = coords[0]-15
677         self.y = coords[1]
678         self.lasty = self.y
679         if self.state == 'expanded' :
680             for child in self.children:
681                 if child.displayed != 0:
682                     child.update_coords()
683                     self.lasty = child.lasty
684
685     def update_icone(self):
686         """ Met à jour les icônes de tous les noeuds : teste la validité de l'objet
687         Cette méthode est très lente, trop !!"""
688         if self.image_id != None :
689             image = self.geticonimage()
690             self.canvas.itemconfig(self.image_id,image=image)
691         if self.state == 'expanded':
692             for child in self.children:
693                 if child.displayed != 0:
694                     child.update_icone()
695
696     def update_label_texte(self):
697         """ Met a jour le label du noeud et celui de tous ses fils ouverts """
698         self.update_node_label()
699         if self.state == 'expanded' :
700             for child in self.children:
701                 if child.displayed != 0 : child.update_label_texte()
702
703     def update_texte(self):
704         """ Met à jour les noms des SD et valeurs des mots-clés """
705         self.update_node_texte()
706         if self.state == 'expanded' :
707             for child in self.children:
708                 if child.displayed != 0 : child.update_texte()
709         
710     def update_node_label(self):
711         """ Met a jour le label du noeud """
712         # nom,fonte et couleur de l'objet du noeud à afficher
713         labeltext,fonte,couleur = self.item.GetLabelText()
714         if labeltext    == ''   : labeltext = '   '
715         if fonte        == None : fonte = Fonte_Standard
716         if couleur      == None : couleur = 'black'
717         self.label.configure(text=labeltext,font=fonte)
718
719     def update_node_texte(self):
720         """ Met à jour les noms des SD et valeurs des mots-clés """
721         text = self.item.GetText()
722         if text == None : text = ''
723         self.text.configure(text=text)
724
725     def update_node_valid(self) :
726         """Cette methode remet a jour la validite du noeud (icone)
727            Elle appelle isvalid
728         """
729         if self.image_id != None :
730             image = self.geticonimage()
731             self.canvas.itemconfig(self.image_id,image=image)
732
733     def update_valid(self) :
734         """Cette methode a pour but de mettre a jour la validite du noeud
735            et de propager la demande de mise à jour à son parent
736         """
737         self.update_node_valid()
738         self.parent.update_valid()
739
740     def update(self,event=None) :
741         """ Classe Node :
742             Cette méthode est appelée pour demander l update d un noeud 
743             d'un jeu de commandes
744             Cette demande est transmise au noeud racine (le JDC) qui update
745             tout l arbre représentant le jeu de commandes
746             Pendant cette mise à jour, on appelle la méthode isvalid qui
747             fera l update de tous les objets déclarés modifiés lors des
748             actions précédentes
749             La métode isvalid est en général appelée par l intermédiaire de
750             update_icone -> geticonimage -> GetIconName
751         """
752         #print "update",self
753         #traceback.print_stack()
754         self.racine.update_coords()
755         self.racine.trace_ligne()
756         self.racine.update_icone()
757         self.racine.update_texte()
758         self.tree.resizescrollregion()
759
760     def efface_node(self):
761         if self.displayed != 0:
762            self.label.unbind("<1>", self.select_id)
763            self.label.unbind("<3>", self.popup_id)
764            self.label.unbind("<Enter>",self.enter_id)
765            self.label.unbind("<Leave>",self.leave_id)
766            self.canvas.tag_unbind(self.image_id,"<1>",self.select_id2)
767            self.canvas.tag_unbind(self.image_id,"<3>",self.popup_id2)
768            if self.item.IsExpandable():
769               self.canvas.tag_unbind(self.icone_id, "<1>", self.callback_id)
770            self.label.destroy()
771            self.text.destroy()
772
773         for id in self.id :
774             self.canvas.delete(id)
775         self.id=[]
776         self.label_id=None
777         self.text_id=None
778         self.image_id=None
779         self.icone_id=None
780         self.label=None
781         self.text=None
782         self.displayed=0
783
784     def efface(self):
785         """ Efface du canvas les id associés à self : cad les siens et ceux
786             de ses enfants """
787         self.efface_node()
788         if not self.children : return
789         for child in self.children:
790             child.efface()
791
792     def move(self,dy):
793         """ Déplace de l'incrément dy tous les id en dessous de self """
794         # il faut marquer tous les suivants de self
795         bbox1 = self.canvas.bbox(ALL)
796         self.canvas.dtag(ALL,'move')
797         self.canvas.delete('line')
798         try:
799             self.canvas.addtag_overlapping('move',bbox1[0],self.y +10,bbox1[2],bbox1[3])
800         except:
801             print "Erreur dans move :"
802             print self
803             print self.item
804             print self.item.getObject()
805             print self.item.getObject().definition.label
806             print 'y=',self.y
807             print 'dy=',dy
808         # on déplace tous les items de dy
809         self.canvas.move('move',0,dy)
810
811     def trace_ligne(self):
812         """ Dessine les lignes verticales entre frères et entre père et premier fils"""
813         if self.state=='collapsed' : return
814         if len(self.children)==0 : return
815         # on est bien dans le cas d'un noeud expansé avec enfants ...
816         # il faut rechercher l'ordonnée du dernier fils de self
817         y_end = self.children[-1].y
818         ligne = self.canvas.create_line(self.x+15,self.y,self.x+15,y_end,tags='line')
819         self.canvas.tag_lower(ligne)
820         for child in self.children :
821             try:
822                 child.trace_ligne()
823             except:
824                 print "Erreur dans trace_ligne :"
825                 print child
826                 print child.item.getObject()
827
828     def last_child(self):
829         lchild=self
830         if self.state == 'expanded' and self.children:
831            lchild= self.children[-1].last_child()
832         return lchild
833
834     #------------------------------------------------------------------
835     # Méthodes de création et destruction de noeuds
836     # Certaines de ces méthodes peuvent être appelées depuis l'externe
837     #------------------------------------------------------------------
838     def replace_node(self,node1,node2):
839         """ Remplace le noeud 1 par le noeud 2 dans la liste des enfants de self"""
840         raise "OBSOLETE"
841         index= self.children.index(node1)
842         self.delete_node_child(node1)
843         self.children.insert(index,node2)
844         
845     def replace_enfant(self,item):
846         """ Retourne le noeud fils à éventuellement remplacer """
847         raise "OBSOLETE"
848         return self.get_node_fils(item.get_nom())
849
850     def full_creation(self,name,pos=None):
851         """
852             Interface avec ACCAS : création de l'objet de nom name et
853             du noeud associé. Retourne le noeud fils ainsi créé
854         """
855         raise "OBSOLETE"
856         #print "full_creation",name,pos,self.item
857         item = self.item.additem(name,pos)
858         if item == None or item == 0:
859             # impossible d'ajouter le noeud de nom : name
860             return 0
861
862         enfant = self.replace_enfant(item)
863         if enfant :
864             # un fils de même nom existe déjà : on le remplace
865             child = item.itemNode(self,item,self.command,self.rmenu)
866             self.replace_node(enfant,child)
867         else :            
868             child = item.itemNode(self, item,self.command,self.rmenu)
869             if pos is None:
870                 self.children.append(child)
871             else :
872                 self.children.insert(pos,child)
873         return child
874
875     def append_brother_BAK(self,name,pos='after',retour='non'):
876         """
877         Permet d'ajouter un frère à self
878         par défaut on l'ajoute après self
879         Méthode externe
880         """
881         raise "OBSOLETE"
882         # on veut ajouter le frère de nom name directement avant ou après self
883         index = self.parent.children.index(self)
884         if pos == 'before':
885             index = index
886         elif pos == 'after':
887             index = index +1
888         else:
889             print str(pos)," n'est pas un index valide pour append_brother"
890             return
891         return self.parent.append_child(name,pos=index,retour=retour)
892     
893     def append_node_child(self,fils,pos=None,verif='oui'):
894         """
895         Fait appel à la création complète de fils et à la vérification
896         des conditions en fonction du contexte
897         Attention : fils peut être un nom ou déjà un object (cas d'une copie)
898         """
899         raise "OBSOLETE"
900         if not self.children : self.build_children()
901         if pos == None :
902             if type(fils) == types.InstanceType:
903                 pos = self.item.get_index_child(fils.nom)
904             else:
905                 pos = self.item.get_index_child(fils)
906         child = self.full_creation(fils,pos)
907         if child == 0 :
908             # on n'a pas pu créer le noeud fils
909             return 0
910         self.state = 'expanded'
911         child.displayed = 1
912         if child.item.isactif():
913            child.state = 'expanded'
914         if not child.children : child.build_children()
915         if verif == 'oui':
916            child.verif_condition()
917            self.verif_condition()
918         return child
919             
920     def append_brother(self,name,pos='after',retour='non'):
921         """
922         Permet d'ajouter un objet frère à l'objet associé au noeud self
923         par défaut on l'ajoute immédiatement après 
924         Méthode externe
925         """
926         # on veut ajouter le frère de nom name directement avant ou après self
927         index = self.parent.children.index(self)
928         if pos == 'before':
929             index = index
930         elif pos == 'after':
931             index = index +1
932         else:
933             print str(pos)," n'est pas un index valide pour append_brother"
934             return 0
935         return self.parent.append_child(name,pos=index)
936
937     def append_child(self,name,pos=None,verif='oui',retour='non'):
938         """
939            Methode pour ajouter un objet fils à l'objet associé au noeud self.
940            On peut l'ajouter en début de liste (pos='first'), en fin (pos='last')
941            ou en position intermédiaire.
942            Si pos vaut None, on le place à la position du catalogue.
943         """
944         #print "append_child",self,self.children
945         if pos == 'first':
946             index = 0
947         elif pos == 'last':
948             index = len(self.children)
949         elif type(pos) == types.IntType :
950             # position fixee
951             index = pos
952         elif type(pos) == types.InstanceType:
953             # pos est un item. Il faut inserer name apres pos
954             index = self.item.get_index(pos) +1
955         elif type(name) == types.InstanceType:
956             index = self.item.get_index_child(name.nom)
957         else:
958             index = self.item.get_index_child(name)
959         obj=self.item.additem(name,index)
960         #print obj
961         if obj is None:obj=0
962         if obj == 0:return 0
963         #print "append_child",index,self.children
964         child=self.children[index]
965         child.select()
966         return child
967
968     def append_child_BAK(self,name,pos=None,verif='oui',retour='non'):
969         """
970         Permet d'ajouter un fils à self
971         on peut l'ajouter en fin de liste (défaut) ou en début
972         Méthode externe
973         """
974         raise "OBSOLETE"
975         if pos == 'first':
976             index = 0
977         elif pos == 'last':
978             index = len(self.children)
979         elif pos != None and type(pos) == types.IntType :
980             # on donne la position depuis l'extérieur
981             # (appel de append_child par append_brother par exemple)
982             index = pos
983         elif type(pos) == types.InstanceType:
984             # pos est un item. Il faut inserer name apres pos
985             index = self.item.get_index(pos) +1
986         else :
987             if type(name) == types.InstanceType:
988                 index = self.item.get_index_child(name.nom)
989             else:
990                 index = self.item.get_index_child(name)
991         nbold = self.get_nb_children()
992         self.state='expanded'
993         child = self.append_node_child(name,pos=index)
994         if child == 0 :
995             # on n'a pas pu créer le fils
996             return 0
997         nbnew = self.get_nb_children()
998         self.redraw(nbnew-nbold)
999         child.select()
1000         if retour == 'oui': return child
1001
1002     def delete_node_child_BAK(self,child):
1003         """ Supprime child des enfants de self et les id associés """
1004         raise "OBSOLETE"
1005         child.efface()
1006         self.children.remove(child)
1007         self.canvas.update()
1008         
1009     def delete_child_BAK(self,child):
1010         """ 
1011             Supprime child des enfants de self, tous les id associés
1012             ET l'objet associé 
1013         """
1014         raise "OBSOLETE"
1015         if self.item.suppitem(child.item):
1016             self.delete_node_child(child)
1017             return 1
1018         else :
1019             return 0
1020                     
1021     def delete(self):
1022         """ 
1023             Méthode externe pour la destruction de l'objet associé au noeud
1024             La mise à jour des noeuds est faite par onSupp sur notification
1025         """
1026         index = self.parent.children.index(self) - 1 
1027         if index < 0 : index =0
1028
1029         ret=self.parent.item.suppitem(self.item)
1030         if ret == 0:return
1031
1032         brothers=self.parent.children
1033         if brothers:
1034            toselect=brothers[index]
1035         else:
1036            toselect=self.parent
1037         toselect.select()
1038
1039     def delete_BAK(self):
1040         """ Méthode externe pour la destruction du noeud ET de l'objet
1041             Gère l'update du canvas"""
1042         raise "OBSOLETE"
1043         pere = self.parent
1044         nbold = pere.get_nb_children()
1045
1046         if self.parent.children.index(self) > 0 :
1047             index = self.parent.children.index(self) - 1 
1048         else:
1049             index=0
1050         if self.parent.delete_child(self):
1051             if self.item.get_position() == 'global':
1052                 self.etape.verif_all()
1053             elif self.item.get_position() == 'global_jdc':
1054                 self.racine.verif_all()
1055             else:
1056                 self.parent.verif_condition()
1057         else:
1058             print 'Erreur dans la destruction de ',self.item.get_nom(),' dans delete'
1059
1060         nbnew = pere.get_nb_children()
1061         pere.redraw(nbnew-nbold)
1062
1063         # Le noeud n'est pas au 1er niveau
1064         if  pere.parent.parent != None:
1065             pere.select()
1066         else:
1067             enfants = self.parent.children
1068             try:
1069               enfants[index].select()
1070             except :
1071               try :
1072                 enfants[index+1].select()
1073               except :
1074                 # on est avant debut
1075                 pass
1076
1077     def doPaste(self,node_selected):
1078         self.appli.message="Vous ne pouvez copier que des commandes ou des mots-clés facteurs !"
1079         return 0
1080
1081     def doPaste_Commande(self,objet_a_copier):
1082         """
1083         Réalise la copie de l'objet passé en argument qui est nécessairement
1084         une commande
1085         """
1086         child = self.append_brother(objet_a_copier,retour='oui')
1087         return child
1088
1089     #--------------------------------------------------------------
1090     # Méthodes de vérification du contexte et de validité du noeud
1091     #--------------------------------------------------------------
1092     def verif_all(self):
1093         raise "OBSOLETE"
1094         traceback.print_stack()
1095         self.verif_all_children()
1096             
1097     def verif_all_children(self):
1098         raise "OBSOLETE"
1099         traceback.print_stack()
1100         if not self.children : self.build_children()
1101         self.verif()
1102         for child in self.children :
1103             child.verif_all_children()
1104
1105     def verif(self) :
1106         """ 
1107             Lance la vérification des conditions des blocs de self et le cas
1108             échéant redessine self 
1109         """
1110         raise "OBSOLETE"
1111         traceback.print_stack()
1112         nbold = self.get_nb_children()
1113         test = self.verif_condition()
1114         nbnew = self.get_nb_children()
1115         if test != 0 :
1116             self.redraw(nbnew-nbold)
1117
1118     def verif_condition(self):
1119         """
1120         on lance la vérification des conditions de chaque bloc de self
1121         on crée ou supprime les noeuds concernés
1122         (self est d'un niveau inférieur ou égal à l'ETAPE)
1123         """
1124         raise "OBSOLETE"
1125         traceback.print_stack()
1126         test = 0
1127         l_bloc_arajouter,l_bloc_aenlever = self.verif_condition_bloc()
1128         if len(l_bloc_arajouter) > 0:
1129             test = 1
1130             for mc in l_bloc_arajouter:
1131                 self.append_node_child(mc,verif='non')
1132         if len(l_bloc_aenlever) > 0:
1133             test = 1
1134             for mc in l_bloc_aenlever:
1135                 mocle = self.get_node_fils(mc)
1136                 self.delete_child(mocle)
1137         l_mc_presents = self.item.get_liste_mc_presents()
1138         l_mc_arajouter= self.verif_condition_regles(l_mc_presents)
1139         if len(l_mc_arajouter) > 0:
1140             test = 1
1141             for mc in l_mc_arajouter:
1142                 self.append_node_child(mc,verif='non')
1143         if len(l_mc_arajouter)+len(l_bloc_arajouter)+len(l_bloc_aenlever) != 0 :
1144             self.verif_condition()
1145         return test
1146         
1147     def verif_condition_bloc(self):
1148         raise "OBSOLETE"
1149         traceback.print_stack()
1150         return self.item.verif_condition_bloc()
1151
1152     def verif_condition_regles(self,l_mc_presents):
1153         raise "OBSOLETE"
1154         traceback.print_stack()
1155         return self.item.verif_condition_regles(l_mc_presents)
1156     
1157