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