Back to index

wims  3.65+svn20090927
RootLayout.java
Go to the documentation of this file.
00001 /*
00002 $Id: RootLayout.java,v 1.3 2003/02/18 11:48:47 sander Exp $
00003 */
00004 
00005 
00006 /*
00007 Copyright (C) 2001-2002 Mainline Project (I3S - ESSI - CNRS -UNSA)
00008 
00009 This library is free software; you can redistribute it and/or
00010 modify it under the terms of the GNU Lesser General Public
00011 License as published by the Free Software Foundation; either
00012 version 2.1 of the License, or (at your option) any later version.
00013 
00014 This library is distributed in the hope that it will be useful,
00015 but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017 Lesser General Public License for more details.
00018 
00019 You should have received a copy of the GNU Lesser General Public
00020 License along with this library; if not, write to the Free Software
00021 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 
00023 For further information on the GNU Lesser General Public License,
00024 see: http://www.gnu.org/copyleft/lesser.html
00025 For further information on this library, contact: mainline@essi.fr
00026 */
00027 
00028 
00029 package fr.ove.openmath.jome.ctrlview.bidim;
00030 
00031 import java.awt.*;
00032 import fr.ove.openmath.jome.ctrlview.bidim.*;
00033 import fr.ove.openmath.jome.ctrlview.bidim.selection.events.SelectionEvent;
00034 import fr.ove.openmath.jome.model.*;
00035 import fr.ove.utils.FontInfo;
00036 
00043 public class RootLayout extends HorizontalLayout {
00052     public void initDisplay(Display displayToLay) {
00053         super.initDisplay(displayToLay);
00054 
00055         FormulaTreeStructure fts = (FormulaTreeStructure) this.displayToLay.getListener();
00056         
00057         // On ajoute le tick de la racine dans la liste des display
00058         SymbolDisplay tick = new SymbolDisplay(this.displayToLay.getGraphicContext(), new SqrtTick());
00059         // Le display du tick de la racine est le display d'un opérateur (on peut le considérer comme tel)
00060         tick.setIsSymbolOperatorDisplay(true);
00061         this.displayToLay.add(tick);
00062         
00063         // On met un listener au tick.
00064         // En fait, il n'y en a pas besoin, dans le sens où il n'y a pas spécifiquement de fts qui
00065         // écoute le comportement de ce display. Néanmoins, il s'avère nécessaire qu'il en ait
00066         // un, par exemple lors de l'iconification, car c'est le display qui reçoit la demande
00067         // d'iconification qui envoie l'événement correspondant à la FTS. Or si ce display n'a pas
00068         // d'écouteur, alors pb. Par cohérence, l'écouteur du display d'opérateur, est le fts qui
00069         // représente cette opération. Par contre, la fts en question, n'écoute pas le display
00070         // d'opérateur.
00071         tick.addControlListener(fts);
00072 
00073         // On ajoute la barre de la racine dans la liste des display
00074         SymbolDisplay bar = new SymbolDisplay(this.displayToLay.getGraphicContext(), new Bar());
00075         // Le display de la barre de la racine est le display d'un opérateur (on peut le considérer comme tel)
00076         bar.setIsSymbolOperatorDisplay(true);
00077         this.displayToLay.add(bar);
00078 
00079         // On met un listener à la barre de la racine.
00080         // Idem remarque ci-dessus.
00081         bar.addControlListener(fts);
00082         
00083         this.displayToLay.computeAncestorsAttributes();
00084     }
00085     
00090     public void updateLevel(int level) {
00091         Display tmp;
00092         
00093         // Pour éviter de refaire le calcul plusieurs fois....
00094         if (displayToLay.getUpdateLevel()) {
00095             // On met à jour le niveau du display géré par le LM.
00096             displayToLay.setLevel(level);
00097             displayToLay.setUpdateLevel(false);
00098             
00099             int i = 0;
00100             int count = displayToLay.getComponentCount();
00101             // Si le nombre de composant est 4, alors on a affaire au display d'une racine n-ième.
00102             // (sinon, c'et le display d'une racine carrée)
00103             // On fait en sorte que la puissance de la racine soit en première position dans display,
00104             // sinon, à l'affichage, elle va être en partie masquée par le display du tick.
00105             if (count == 4) {
00106                 // On regarde si le premier display est celui d'un opérateur (celui du tick en fait)
00107                 // Si c'est le cas, il faut déplacer le display de la puissance.
00108                 if (((Display) displayToLay.getComponent(0)).isSymbolOperatorDisplay()) {
00109                     tmp = (Display) displayToLay.getComponent(3);
00110                     
00111                     // De part l'implémentation du add(tmp, tmp, 0), tmp va être enlevé de la liste
00112                     // des listeners qu'il écoute. Chose qu'on ne veut pas.
00113                     displayToLay.removeFromListListeners(false);
00114                     
00115                     displayToLay.add(tmp, tmp, 0);
00116                     
00117                     // On se remet dans la cas classique où la suppression de display entraînera sa suppression
00118                     // dans la liste des listeners qu'il écoute.
00119                     displayToLay.removeFromListListeners(true);
00120                     
00121                     displayToLay.adjustRank();
00122                 }
00123                 
00124                 // On réduit la taille de la puissance de 1
00125                 tmp = (Display) displayToLay.getComponent(0);
00126                 ((DisplayLayout) tmp.getLayout()).updateLevel(level+1);
00127                 tmp.setUpdateLevel(false);
00128                 
00129                 // On incrémente l'indice i, pour que dans la boucle qui suit, on parte bien du display
00130                 // du tick
00131                 i++;
00132             }
00133             
00134             // le tick, la barre de la racine, et l'élément sous la racine
00135             // ont le niveau du display qui les contient
00136             for (; i < count; i++) {
00137                 tmp = (Display) displayToLay.getComponent(i);
00138                 ((DisplayLayout) tmp.getLayout()).updateLevel(level);
00139                 tmp.setUpdateLevel(false);
00140             }
00141         }
00142     }
00143 
00147     public void validateSelection() {
00148         SelectionEvent selEvt = new SelectionEvent(displayToLay);
00149 
00150         // La validité de la sélection est triviale.
00151         // Si un des displays du dessin de la racine est sélectionné alors on sélectionne tout.
00152         // Si l'élément sous la racine et le numéro de la racine ont chacun des éléments sélectionnés
00153         Display tick, bar;
00154         int count = displayToLay.getComponentCount();
00155         if (count == 4) {
00156             tick = (Display) displayToLay.getComponent(1);
00157             bar = (Display) displayToLay.getComponent(2);
00158         }
00159         else {
00160             tick = (Display) displayToLay.getComponent(0);
00161             bar = (Display) displayToLay.getComponent(1);
00162         }
00163         
00164         if (tick.isSelected() || bar.isSelected()) {
00165             // Sélectionne le display.
00166             displayToLay.select();
00167             // On purge la liste des éléments sélectionnés.
00168             selEvt.setAction(SelectionEvent.PURGE, null);
00169             displayToLay.fireSelectionEvent(selEvt);
00170             // On y ajoute os parenthèses
00171             selEvt.setAction(SelectionEvent.ADD, displayToLay);
00172             displayToLay.fireSelectionEvent(selEvt);
00173         }
00174         else {
00175             if (count == 4) {
00176                 Display nthRoot = (Display) displayToLay.getComponent(0);
00177                 Display arg = (Display) displayToLay.getComponent(3);
00178                 if (nthRoot.gotSelectedElements() && arg.gotSelectedElements()) {
00179                     // Sélectionne le display.
00180                     displayToLay.select();
00181                     // On purge la liste des éléments sélectionnés.
00182                     selEvt.setAction(SelectionEvent.PURGE, null);
00183                     displayToLay.fireSelectionEvent(selEvt);
00184                     // On y ajoute os parenthèses
00185                     selEvt.setAction(SelectionEvent.ADD, displayToLay);
00186                     displayToLay.fireSelectionEvent(selEvt);
00187                 }
00188             }
00189         }
00190         
00191         // On a vérifié la validité de la sélection de la racine. On doit maitenant
00192         // la contrôler au niveau supérieur, au niveau du père.
00193         Display display = displayToLay;
00194         if (display.getParent() instanceof Display) {
00195             display = (Display) display.getParent();
00196             FormulaTreeStructure fts = (FormulaTreeStructure) display.getListener();
00197             if (fts.getFather() != null) // Si fts est la racine de la formule alors la sélection à déjà été validée
00198                 ((DisplayLayout) display.getLayout()).validateSelection();
00199         }
00200 
00201         // On met à jour l'affichage.
00202         display.repaint();
00203     }
00204     
00209     public void validateDeselection(Display display) {
00210         Display father = displayToLay;
00211         SelectionEvent selEvt = new SelectionEvent(father);
00212         
00213         if (father.isSelected()) {
00214             father.setNotSelected();
00215             // On enlève le display père de la liste des display sélectionnés.
00216             selEvt.setAction(SelectionEvent.REMOVE, father);
00217             father.fireSelectionEvent(selEvt);
00218             
00219             Display tick, bar, arg;
00220             Display nthRoot = null;
00221             
00222             if (father.getComponentCount() == 4) {
00223                 nthRoot = (Display) father.getComponent(0);
00224                 tick = (Display) father.getComponent(1);
00225                 bar = (Display) father.getComponent(2);
00226                 arg = (Display) father.getComponent(3);
00227             }
00228             else {
00229                 tick = (Display) father.getComponent(0);
00230                 bar = (Display) father.getComponent(1);
00231                 arg = (Display) father.getComponent(2);
00232             }
00233             
00234             if ((display == tick) || (display == bar)) {
00235                 // Comme on ne sait pas de qui on vient, on désélectionne les 2
00236                 tick.setNotSelected();
00237                 bar.setNotSelected();
00238                 // On considère que le comportement par défaut et de sélectionner l'argument de la racine.
00239                 // 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)
00240                 selEvt.setAction(SelectionEvent.ADD, arg);
00241                 father.fireSelectionEvent(selEvt);
00242                 
00243                 if (nthRoot != null)
00244                     nthRoot.deselect();
00245             }
00246             else if (display == arg) {
00247                 tick.setNotSelected();
00248                 bar.setNotSelected();
00249                 
00250                 if (nthRoot != null) {
00251                     selEvt.setAction(SelectionEvent.ADD, nthRoot);
00252                     father.fireSelectionEvent(selEvt);
00253                 }
00254             }
00255             else {  // ben forcément, display == nthRoot
00256                 tick.setNotSelected();
00257                 bar.setNotSelected();
00258                 selEvt.setAction(SelectionEvent.ADD, arg);
00259                 father.fireSelectionEvent(selEvt);
00260             }
00261             
00262             
00263             // Comme pour la sélection, on contrôle la validité de la désélection.
00264             if (father.getParent() instanceof Display) {
00265                 father = (Display) father.getParent();
00266                 FormulaTreeStructure fts = (FormulaTreeStructure) display.getListener();
00267                 if (fts.getFather() != null) // Si fts est la racine de la formule alors la désélection à déjà été validée
00268                     ((DisplayLayout) father.getLayout()).validateDeselection(displayToLay);
00269             }
00270             
00271             // Hé oui, on contrôle la validité de la sélection... dans une désélection.
00272             // Toujours le même pb, est-ce que le nouvel état de la sélection (après
00273             // désélection donc) est syntaxiquement cohérent ?
00274             validateSelection();
00275             
00276             // On met à jour l'affichage.
00277             father.repaint();
00278         }        
00279 
00280     }
00281 
00287     public Dimension computeAttributes() {
00288         updateLevel(displayToLay.getLevel());
00289             
00290         int width = 0;
00291         int height = 0;
00292         
00293         int thickness = FontInfo.getLineThickness(displayToLay, displayToLay.getFont());
00294         
00295         Display arg;
00296         SymbolDisplay barDisplay, tickDisplay;
00297         Bar bar;
00298         SqrtTick tick;
00299         
00300         int count = displayToLay.getComponentCount();
00301         
00302         if (count == 4) {
00303             tickDisplay = (SymbolDisplay) displayToLay.getComponent(1);
00304             barDisplay = (SymbolDisplay) displayToLay.getComponent(2);
00305             arg = (Display) displayToLay.getComponent(3);
00306         }
00307         else {
00308             tickDisplay = (SymbolDisplay) displayToLay.getComponent(0);
00309             barDisplay = (SymbolDisplay) displayToLay.getComponent(1);
00310             arg = (Display) displayToLay.getComponent(2);
00311         }
00312         
00313         // On calcule la taille du display de l'élément sous la racine
00314         arg.invalidate();
00315         arg.setComputeAttributes(true);
00316         ((DisplayLayout) arg.getLayout()).computeAttributes();
00317         arg.setSize(arg.getPreferredSize());
00318         
00319         // On calcule la taille du display de la barre de la racine
00320         bar = (Bar) barDisplay.getSymbol();
00321         bar.setHeight(thickness);
00322         bar.setWidth(arg.getWidth() + 4);
00323         barDisplay.invalidate();
00324         barDisplay.setComputeAttributes(true);
00325         barDisplay.setSize(barDisplay.getPreferredSize());
00326         
00327         // On met un décalage vertical au display de la barre pour qu'il se trouve au dessus du display
00328         // de l'argument.
00329         barDisplay.setShiftY(-arg.getAscent() - barDisplay.getDescent() );
00330         // On met un décalage horizontal au display de l'argument pour que son c^té gauche soit aligné
00331         // au display de la barre.
00332         arg.setShiftX(-bar.getWidth());
00333         
00334         // On calcule la taille du display du "tick" de la racine
00335         tick = (SqrtTick) tickDisplay.getSymbol();
00336         tick.setThickness(thickness);
00337         tick.setHeight(arg.getHeight() + thickness);
00338         tick.setWidth(displayToLay.getFontMetrics(displayToLay.getFont()).stringWidth("A"));
00339         tick.setAscent(arg.getAscent() + thickness);
00340         tick.setDescent(arg.getDescent());
00341         tickDisplay.invalidate();
00342         tickDisplay.setComputeAttributes(true);
00343         tickDisplay.setSize(tickDisplay.getPreferredSize());
00344         
00345         int ascent;
00346         
00347         if (count == 4) {
00348             // On a le display d'une racine n-ième.
00349             // On s'occupe du display de la puissance de la racine.
00350             Display nthRoot = (Display) displayToLay.getComponent(0);
00351             nthRoot.setSize(nthRoot.getPreferredSize());
00352             
00353             // On fixe maintenant les décalages nécessaires à chacun des display pour leur bon
00354             // positionnement.
00355             nthRoot.setShiftY(-(int) Math.round(0.42f * (float) tickDisplay.getAscent()) - nthRoot.getDescent());
00356             tickDisplay.setShiftX(-tickDisplay.getWidth() / 2);
00357             
00358             width += nthRoot.getWidth() + (tickDisplay.getWidth()/2) + barDisplay.getWidth();
00359             ascent = Math.max(nthRoot.getAscent() - nthRoot.getShiftY(), tickDisplay.getAscent());
00360         }
00361         else {
00362             width += tickDisplay.getWidth() + barDisplay.getWidth();
00363             ascent = tickDisplay.getAscent();
00364         }
00365         
00366         height +=  ascent + arg.getDescent();
00367                 
00368         displayToLay.setSize(width, height);
00369         displayToLay.setAscent(ascent);
00370         displayToLay.setDescent(arg.getDescent());
00371         
00372         displayToLay.setComputeAttributes(false);
00373         
00374         return new Dimension(width, height);
00375     }
00376     
00377 
00381     public void rebuildDisplay() {        
00382         // La taille des displays est probablement différente de ceux qui étaient
00383         // précédemment. On demande alors le recalcul des display ancêtres.
00384         displayToLay.computeAncestorsAttributes();
00385     }
00386 }