View Javadoc

1   package fr.ove.openmath.jome.ctrlview.bidim;
2   
3   import java.awt.*;
4   import fr.ove.openmath.jome.ctrlview.bidim.*;
5   import fr.ove.openmath.jome.ctrlview.bidim.selection.events.SelectionEvent;
6   import fr.ove.openmath.jome.model.*;
7   import fr.ove.utils.FontInfo;
8   
9   /***
10  * A layout manager that lays the display of the nth-root.
11  *
12  * @author © 1999 DIRAT Laurent
13  * @version 2.0 22/07/1999
14  */
15  public class RootLayout extends HorizontalLayout {
16      /***
17      * According to the operator, the layout manager has to add some components (e.g. brackets, ...)
18      * or has to perform some "re-oganisation" before rendering.<BR>
19      * As soon as the layout manager is set to the display, this mehtod MUST be called with the display laid out
20      * as parameter. This method serves as well as a registering method. So all sub-classes of the instance MUST
21      * call super.initDisplay(displayToLay).
22      * @param displayToLay the display laid by the instance
23      */
24      public void initDisplay(Display displayToLay) {
25          super.initDisplay(displayToLay);
26  
27          FormulaTreeStructure fts = (FormulaTreeStructure) this.displayToLay.getListener();
28          
29          // On ajoute le tick de la racine dans la liste des display
30          SymbolDisplay tick = new SymbolDisplay(this.displayToLay.getGraphicContext(), new SqrtTick());
31          // Le display du tick de la racine est le display d'un opÈrateur (on peut le considÈrer comme tel)
32          tick.setIsSymbolOperatorDisplay(true);
33          this.displayToLay.add(tick);
34          
35          // On met un listener au tick.
36          // En fait, il n'y en a pas besoin, dans le sens o? il n'y a pas spÈcifiquement de fts qui
37          // Ècoute le comportement de ce display. NÈanmoins, il s'avËre nÈcessaire qu'il en ait
38          // un, par exemple lors de l'iconification, car c'est le display qui reÁoit la demande
39          // d'iconification qui envoie l'ÈvÈnement correspondant ? la FTS. Or si ce display n'a pas
40          // d'Ècouteur, alors pb. Par cohÈrence, l'Ècouteur du display d'opÈrateur, est le fts qui
41          // reprÈsente cette opÈration. Par contre, la fts en question, n'Ècoute pas le display
42          // d'opÈrateur.
43          tick.addControlListener(fts);
44  
45          // On ajoute la barre de la racine dans la liste des display
46          SymbolDisplay bar = new SymbolDisplay(this.displayToLay.getGraphicContext(), new Bar());
47          // Le display de la barre de la racine est le display d'un opÈrateur (on peut le considÈrer comme tel)
48          bar.setIsSymbolOperatorDisplay(true);
49          this.displayToLay.add(bar);
50  
51          // On met un listener ? la barre de la racine.
52          // Idem remarque ci-dessus.
53          bar.addControlListener(fts);
54          
55          this.displayToLay.computeAncestorsAttributes();
56      }
57      
58      /***
59      * Updates the level of the display that is layed out.<BR>
60      * @param level the level put to the display
61      */
62      public void updateLevel(int level) {
63          Display tmp;
64          
65          // Pour Èviter de refaire le calcul plusieurs fois....
66          if (displayToLay.getUpdateLevel()) {
67              // On met ? jour le niveau du display gÈrÈ par le LM.
68              displayToLay.setLevel(level);
69              displayToLay.setUpdateLevel(false);
70              
71              int i = 0;
72              int count = displayToLay.getComponentCount();
73              // Si le nombre de composant est 4, alors on a affaire au display d'une racine n-iËme.
74              // (sinon, c'et le display d'une racine carrÈe)
75              // On fait en sorte que la puissance de la racine soit en premiËre position dans display,
76              // sinon, ? l'affichage, elle va Ítre en partie masquÈe par le display du tick.
77              if (count == 4) {
78                  // On regarde si le premier display est celui d'un opÈrateur (celui du tick en fait)
79                  // Si c'est le cas, il faut dÈplacer le display de la puissance.
80                  if (((Display) displayToLay.getComponent(0)).isSymbolOperatorDisplay()) {
81                      tmp = (Display) displayToLay.getComponent(3);
82                      
83                      // De part l'implÈmentation du add(tmp, tmp, 0), tmp va Ítre enlevÈ de la liste
84                      // des listeners qu'il Ècoute. Chose qu'on ne veut pas.
85                      displayToLay.removeFromListListeners(false);
86                      
87                      displayToLay.add(tmp, tmp, 0);
88                      
89                      // On se remet dans la cas classique o? la suppression de display entraÓnera sa suppression
90                      // dans la liste des listeners qu'il Ècoute.
91                      displayToLay.removeFromListListeners(true);
92                      
93                      displayToLay.adjustRank();
94                  }
95                  
96                  // On rÈduit la taille de la puissance de 1
97                  tmp = (Display) displayToLay.getComponent(0);
98                  ((DisplayLayout) tmp.getLayout()).updateLevel(level+1);
99                  tmp.setUpdateLevel(false);
100                 
101                 // On incrÈmente l'indice i, pour que dans la boucle qui suit, on parte bien du display
102                 // du tick
103                 i++;
104             }
105             
106             // le tick, la barre de la racine, et l'ÈlÈment sous la racine
107             // ont le niveau du display qui les contient
108             for (; i < count; i++) {
109                 tmp = (Display) displayToLay.getComponent(i);
110                 ((DisplayLayout) tmp.getLayout()).updateLevel(level);
111                 tmp.setUpdateLevel(false);
112             }
113         }
114     }
115 
116     /***
117     * Checks the validity of the selection.
118     */
119     public void validateSelection() {
120         SelectionEvent selEvt = new SelectionEvent(displayToLay);
121 
122         // La validitÈ de la sÈlection est triviale.
123         // Si un des displays du dessin de la racine est sÈlectionnÈ alors on sÈlectionne tout.
124         // Si l'ÈlÈment sous la racine et le numÈro de la racine ont chacun des ÈlÈments sÈlectionnÈs
125         Display tick, bar;
126         int count = displayToLay.getComponentCount();
127         if (count == 4) {
128             tick = (Display) displayToLay.getComponent(1);
129             bar = (Display) displayToLay.getComponent(2);
130         }
131         else {
132             tick = (Display) displayToLay.getComponent(0);
133             bar = (Display) displayToLay.getComponent(1);
134         }
135         
136         if (tick.isSelected() || bar.isSelected()) {
137             // SÈlectionne le display.
138             displayToLay.select();
139             // On purge la liste des ÈlÈments sÈlectionnÈs.
140             selEvt.setAction(SelectionEvent.PURGE, null);
141             displayToLay.fireSelectionEvent(selEvt);
142             // On y ajoute os parenthËses
143             selEvt.setAction(SelectionEvent.ADD, displayToLay);
144             displayToLay.fireSelectionEvent(selEvt);
145         }
146         else {
147             if (count == 4) {
148                 Display nthRoot = (Display) displayToLay.getComponent(0);
149                 Display arg = (Display) displayToLay.getComponent(3);
150                 if (nthRoot.gotSelectedElements() && arg.gotSelectedElements()) {
151                     // SÈlectionne le display.
152                     displayToLay.select();
153                     // On purge la liste des ÈlÈments sÈlectionnÈs.
154                     selEvt.setAction(SelectionEvent.PURGE, null);
155                     displayToLay.fireSelectionEvent(selEvt);
156                     // On y ajoute os parenthËses
157                     selEvt.setAction(SelectionEvent.ADD, displayToLay);
158                     displayToLay.fireSelectionEvent(selEvt);
159                 }
160             }
161         }
162         
163         // On a vÈrifiÈ la validitÈ de la sÈlection de la racine. On doit maitenant
164         // la contrÙler au niveau supÈrieur, au niveau du pËre.
165         Display display = displayToLay;
166         if (display.getParent() instanceof Display) {
167             display = (Display) display.getParent();
168             FormulaTreeStructure fts = (FormulaTreeStructure) display.getListener();
169             if (fts.getFather() != null) // Si fts est la racine de la formule alors la sÈlection ? dÈj? ÈtÈ validÈe
170                 ((DisplayLayout) display.getLayout()).validateSelection();
171         }
172 
173         // On met ? jour l'affichage.
174         display.repaint();
175     }
176     
177     /***
178     * Checks the validity of the deselection.
179     * @param display the display to deselect.
180     */
181     public void validateDeselection(Display display) {
182         Display father = displayToLay;
183         SelectionEvent selEvt = new SelectionEvent(father);
184         
185         if (father.isSelected()) {
186             father.setNotSelected();
187             // On enlËve le display pËre de la liste des display sÈlectionnÈs.
188             selEvt.setAction(SelectionEvent.REMOVE, father);
189             father.fireSelectionEvent(selEvt);
190             
191             Display tick, bar, arg;
192             Display nthRoot = null;
193             
194             if (father.getComponentCount() == 4) {
195                 nthRoot = (Display) father.getComponent(0);
196                 tick = (Display) father.getComponent(1);
197                 bar = (Display) father.getComponent(2);
198                 arg = (Display) father.getComponent(3);
199             }
200             else {
201                 tick = (Display) father.getComponent(0);
202                 bar = (Display) father.getComponent(1);
203                 arg = (Display) father.getComponent(2);
204             }
205             
206             if ((display == tick) || (display == bar)) {
207                 // Comme on ne sait pas de qui on vient, on dÈsÈlectionne les 2
208                 tick.setNotSelected();
209                 bar.setNotSelected();
210                 // On considËre que le comportement par dÈfaut et de sÈlectionner l'argument de la racine.
211                 // MÍme si on est dans le cas d'une racine n-iËme (on dÈsÈlectionne donc le numÈro de la racine)
212                 selEvt.setAction(SelectionEvent.ADD, arg);
213                 father.fireSelectionEvent(selEvt);
214                 
215                 if (nthRoot != null)
216                     nthRoot.deselect();
217             }
218             else if (display == arg) {
219                 tick.setNotSelected();
220                 bar.setNotSelected();
221                 
222                 if (nthRoot != null) {
223                     selEvt.setAction(SelectionEvent.ADD, nthRoot);
224                     father.fireSelectionEvent(selEvt);
225                 }
226             }
227             else {  // ben forcÈment, display == nthRoot
228                 tick.setNotSelected();
229                 bar.setNotSelected();
230                 selEvt.setAction(SelectionEvent.ADD, arg);
231                 father.fireSelectionEvent(selEvt);
232             }
233             
234             
235             // Comme pour la sÈlection, on contrÙle la validitÈ de la dÈsÈlection.
236             if (father.getParent() instanceof Display) {
237                 father = (Display) father.getParent();
238                 FormulaTreeStructure fts = (FormulaTreeStructure) display.getListener();
239                 if (fts.getFather() != null) // Si fts est la racine de la formule alors la dÈsÈlection ? dÈj? ÈtÈ validÈe
240                     ((DisplayLayout) father.getLayout()).validateDeselection(displayToLay);
241             }
242             
243             // HÈ oui, on contrÙle la validitÈ de la sÈlection... dans une dÈsÈlection.
244             // Toujours le mÍme pb, est-ce que le nouvel Ètat de la sÈlection (aprËs
245             // dÈsÈlection donc) est syntaxiquement cohÈrent ?
246             validateSelection();
247             
248             // On met ? jour l'affichage.
249             father.repaint();
250         }        
251 
252     }
253 
254     /***
255     * Computes the size of the display according to its children size (if any),
256     * and its different attributes.
257     * @return the size of the display.
258     */
259     public Dimension computeAttributes() {
260         updateLevel(displayToLay.getLevel());
261             
262         int width = 0;
263         int height = 0;
264         
265         int thickness = FontInfo.getLineThickness(displayToLay, displayToLay.getFont());
266         
267         Display arg;
268         SymbolDisplay barDisplay, tickDisplay;
269         Bar bar;
270         SqrtTick tick;
271         
272         int count = displayToLay.getComponentCount();
273         
274         if (count == 4) {
275             tickDisplay = (SymbolDisplay) displayToLay.getComponent(1);
276             barDisplay = (SymbolDisplay) displayToLay.getComponent(2);
277             arg = (Display) displayToLay.getComponent(3);
278         }
279         else {
280             tickDisplay = (SymbolDisplay) displayToLay.getComponent(0);
281             barDisplay = (SymbolDisplay) displayToLay.getComponent(1);
282             arg = (Display) displayToLay.getComponent(2);
283         }
284         
285         // On calcule la taille du display de l'ÈlÈment sous la racine
286         arg.invalidate();
287         arg.setComputeAttributes(true);
288         ((DisplayLayout) arg.getLayout()).computeAttributes();
289         arg.setSize(arg.getPreferredSize());
290         
291         // On calcule la taille du display de la barre de la racine
292         bar = (Bar) barDisplay.getSymbol();
293         bar.setHeight(thickness);
294         bar.setWidth(arg.getWidth() + 4);
295         barDisplay.invalidate();
296         barDisplay.setComputeAttributes(true);
297         barDisplay.setSize(barDisplay.getPreferredSize());
298         
299         // On met un dÈcalage vertical au display de la barre pour qu'il se trouve au dessus du display
300         // de l'argument.
301         barDisplay.setShiftY(-arg.getAscent() - barDisplay.getDescent() );
302         // On met un dÈcalage horizontal au display de l'argument pour que son c^tÈ gauche soit alignÈ
303         // au display de la barre.
304         arg.setShiftX(-bar.getWidth());
305         
306         // On calcule la taille du display du "tick" de la racine
307         tick = (SqrtTick) tickDisplay.getSymbol();
308         tick.setThickness(thickness);
309         tick.setHeight(arg.getHeight() + thickness);
310         tick.setWidth(displayToLay.getFontMetrics(displayToLay.getFont()).stringWidth("A"));
311         tick.setAscent(arg.getAscent() + thickness);
312         tick.setDescent(arg.getDescent());
313         tickDisplay.invalidate();
314         tickDisplay.setComputeAttributes(true);
315         tickDisplay.setSize(tickDisplay.getPreferredSize());
316         
317         int ascent;
318         
319         if (count == 4) {
320             // On a le display d'une racine n-iËme.
321             // On s'occupe du display de la puissance de la racine.
322             Display nthRoot = (Display) displayToLay.getComponent(0);
323             nthRoot.setSize(nthRoot.getPreferredSize());
324             
325             // On fixe maintenant les dÈcalages nÈcessaires ? chacun des display pour leur bon
326             // positionnement.
327             nthRoot.setShiftY(-(int) Math.round(0.42f * (float) tickDisplay.getAscent()) - nthRoot.getDescent());
328             tickDisplay.setShiftX(-tickDisplay.getWidth() / 2);
329             
330             width += nthRoot.getWidth() + (tickDisplay.getWidth()/2) + barDisplay.getWidth();
331             ascent = Math.max(nthRoot.getAscent() - nthRoot.getShiftY(), tickDisplay.getAscent());
332         }
333         else {
334             width += tickDisplay.getWidth() + barDisplay.getWidth();
335             ascent = tickDisplay.getAscent();
336         }
337         
338         height +=  ascent + arg.getDescent();
339                 
340         displayToLay.setSize(width, height);
341         displayToLay.setAscent(ascent);
342         displayToLay.setDescent(arg.getDescent());
343         
344         displayToLay.setComputeAttributes(false);
345         
346         return new Dimension(width, height);
347     }
348     
349 
350     /***
351     * The display needs to be rebuilt. We do this.
352     */
353     public void rebuildDisplay() {        
354         // La taille des displays est probablement diffÈrente de ceux qui Ètaient
355         // prÈcÈdemment. On demande alors le recalcul des display ancÍtres.
356         displayToLay.computeAncestorsAttributes();
357     }
358 }