1 package com.example.furt.myapplication;
3 import android.graphics.Color;
4 import android.util.TypedValue;
5 import android.view.Gravity;
6 import android.view.View;
7 import android.view.ViewGroup;
8 import android.view.ViewTreeObserver;
9 import android.widget.RelativeLayout;
10 import android.widget.TextView;
12 import java.util.ArrayList;
13 import java.util.List;
16 public class Node implements Tree {
18 static int FIELD_UNSET = -1;
22 static int FAKE_CLOSED=3;
23 static int CANCELED=4;
24 static int FAKE_CANCELED=5;
26 Formula F; //formula legata al nodo
27 String ruleName; //nome della regola associata a questo nodo
28 List<Node> Children = new ArrayList<Node>(); //Nodi figli
29 List<Hypothesis> NodeHP = new ArrayList<Hypothesis>(); //ipotesi associate
30 Node Father; //puntatore al padre
31 View.OnClickListener handler; //touchHandler per far comparire il pop-up delle regole
32 View.OnLongClickListener longHandler; //touchHandler per far comparire il menu con tutte le regole
33 boolean hasFocus; //indica se questo nodo è quello selezionato
34 int status; //stato del nodo (chiuso, aperto,fake...)
35 RelativeLayout global; //layout in cui si trova l'albero
36 BorderedTextView view; //view contenente questo oggetto
37 TextView ruleView; //view contenente l'intestazione della regola
39 float baseLine; //baseLine del Node: corrisponde alla lunghezza dell'overline
40 float leftOffset; //leftOffset: corrisponde alla larghezza della porzione di albero a sinistra del nodo
41 float rightOffset; //rightOffset: porzione di albero a destra del nodo
42 float maxWidth; //larghezza del sottoalbero di questo nodo (comprensiva del nodo stesso)
54 baseLine = getBaseWidth();
58 /**************SetView**************+*****/
59 /*****************************************/
60 /*******Setta la TextView del node ******/
61 /** e il layout in cui deve comparire ***/
62 /*****************************************/
64 public void setView(BorderedTextView t,RelativeLayout r) {
66 ruleView =new TextView(t.getContext());
71 /**************Count****************+*****/
72 /*****************************************/
73 /*******Restituisce il numero di ******/
74 /*************nodi dell'albero ***********/
75 /*****************************************/
84 /**************addHP****************+*****/
85 /*****************************************/
86 /*******Metodi per aggiungere ipotesi*****/
87 /*******sotto forma di Hypothesis o di****/
88 /*************Formula*********************/
89 /*****************************************/
91 public void addHPFormula(List<Formula>List,boolean deleted) {
92 loop:for (Formula newHp:List) {
93 for (Hypothesis oldHp: NodeHP) {
94 if (oldHp.HP.toString().equals(newHp.toString())) { //ipotesi già presente: passo alla prossima
98 NodeHP.add(new Hypothesis(newHp, deleted));
102 public void addHPList(List<Hypothesis>List) {
103 loop:for (Hypothesis newHp:List) {
104 for (Hypothesis oldHp: NodeHP) {
105 if (oldHp.HP.toString().equals(newHp.HP.toString())) { //ipotesi già presente: passo alla prossima
109 Hypothesis copyHP=new Hypothesis(newHp.HP, newHp.isDeleted);
110 copyHP.fromNode=newHp.fromNode;
115 /*****************************************/
116 /******* Algoritmo di ricerca del ********/
117 /******* focus: restituisce false ********/
118 /******* se la dimostrazione è completa***/
120 public boolean searchFocus(Node caller)
122 if (Children.size()==0) //caso base: controllo se il focus può essere su di me
124 if (status!=OPEN && status!=FAKE) //nodo chiuso: il focus non può essere su di me
126 hasFocus=true; //posso assumere il focus
128 handler.onClick(view);
131 //passo induttivo: vado su tutti i miei figli, poi su mio padre. Termino se il padre è null.
132 for (Node n:Children) {
134 if (n == caller) //per ogni figlio diverso dal chiamante
138 if(n.searchFocus(null))
141 if (caller==null) //ero stato chiamato da mio padre: è inutile richiamarlo
143 else if (Father==null)
144 return false; //padre null: l'albero è dimostrato e non ci sono nodi a cui dare il focus.
146 return Father.searchFocus(this); //propaga il searchFocus sul padre
148 public void addChild(Node N) {
152 //c'è un nuovo nodo nell'albero: tutti i campi dell'algoritmo di disegno sono da ricalcolare per questo ramo
153 baseLine=FIELD_UNSET;
154 maxWidth=FIELD_UNSET;
155 leftOffset=FIELD_UNSET;
156 rightOffset=FIELD_UNSET;
159 tmp.baseLine=FIELD_UNSET;
160 tmp.leftOffset=FIELD_UNSET;
161 tmp.rightOffset=FIELD_UNSET;
162 tmp.maxWidth=FIELD_UNSET;
167 public float getBaseWidth() {
171 public float getMaxWidth() //ritorna la larghezza massima del sottoalbero
173 if (maxWidth!=FIELD_UNSET)
176 maxWidth=getLeftOffset()+getBaseWidth()+getRightOffset();
180 public float getLineWidth()
182 if (baseLine!=FIELD_UNSET)
186 baseLine=Math.max(getUpLine(),getBaseWidth()); //la baseline è il massimo valore tra la upLine e la baseWidth
190 public float getUpLine() //calcola la upLine, ovvero la dimensione prevista del sottoalbero assumendo il nodo attuale di lunghezza unitaria
192 float spaceSize= DrawActivity.spaceSize;
193 if (Children.size()==0)
194 return(getBaseWidth());
197 res+=(n.getMaxWidth())+spaceSize;
199 res-=Children.get(0).getLeftOffset(); //la linea deve fermarsi all'inizio della formula del primo figlio sinistro
200 res-=Children.get(Children.size()-1).getRightOffset(); //la linea deve finire alla fine della formula dell'ultimo figlio destro
204 /**Calcolo del leftOffset
205 * esso è uguale al leftOffset del primo figlio, a cui aggiungere la porzione di overline a sinistra della formula del nodo
206 * o rimuovere la mezza differenza tra la baseWidth e la upLine. Se il leftOffset risulterebbe negativo, diventa nullo.
209 public float getLeftOffset() {
210 if (leftOffset != FIELD_UNSET) {
214 if (Children.size() == 0) //foglia
217 leftOffset=Math.max(Children.get(0).getLeftOffset() + ((getUpLine() - getBaseWidth()) / 2),0);
222 public float getRightOffset()
224 if (rightOffset!=FIELD_UNSET)
227 if (Children.size() == 0) //foglia
230 rightOffset=Math.max(Children.get(Children.size() - 1).getRightOffset() + ((getUpLine() - getBaseWidth()) / 2),0);
235 public float getMaxHeight() //calcolo dell'altezza massima dell'albero attuale.
237 float baseRes=F.height();
239 for (Node n:Children)
241 float tempH=n.getMaxHeight()+baseRes;
248 void Refactor() //metodo di supporto: svuota i campi dell'intero sottoalbero che verranno ricalcolati dall'algoritmo alla prossima applicazione
250 baseLine=FIELD_UNSET;
251 leftOffset=FIELD_UNSET;
252 rightOffset=FIELD_UNSET;
253 maxWidth=FIELD_UNSET;
259 public void Clean() //metodo di supporto: rimuove dal layout legato al Node tutte le view presenti
261 global.removeAllViews();
264 public boolean isCorrect() //controlla per l'intero sottoalbero se esso è privo di nodi fake.
266 if (status==FAKE || status==FAKE_CANCELED || status==FAKE_CLOSED)
268 else for (Node n:Children)
277 float interval=0; //intervallo di spazio da sommare nella creazione dei figli
278 float spaceSize= DrawActivity.spaceSize; //dimensione della spaziatura tra i sottoalberi
279 int childNo=Children.size(); //numero di figli del nodo attuale
280 if (childNo==0) //foglia: possibile ramo dimostrato
283 view.setBorders(null); //le foglie non hanno overline
284 if (status!=OPEN && status!=FAKE) { //nodo chiuso
285 view.setTextColor(Color.GRAY);
286 if (hasFocus) //avevo il focus: provo a darlo ad uno dei miei fratelli
288 if (!(Father.searchFocus(this))) { //focus inapplicabile nell'albero: dimostrazione completata.
289 DrawActivity.finishedTree(view.getContext());
294 if (status==CANCELED || status==FAKE_CANCELED) { //nodo cancellato
295 view.setWidth((int) F.sizeDeleted());
296 view.setText(F.toStringDeleted());
297 view.setOnClickListener(null); //rimuove eventuali listener per evitare il proseguimento dell'albero
298 /**NOTA: è possibile permettere all'utente di proseguire la dimostrazione
299 * di un ramo chiuso commentando la riga precedente e aggiungendo nel touchnodeHandler
300 * un'controllo che, in caso di nodo chiuso cliccato, lo riapre e gli riaffida il focus.
305 if (hasFocus && DrawActivity.selectedNode!=this)
306 handler.onClick(view);
307 else if (hasFocus) //hasFocus: must be colored red anyway
308 view.setTextColor(Color.RED);
312 ruleView.setText(ruleName);
313 ruleView.setTextSize(TypedValue.COMPLEX_UNIT_PX, DrawActivity.textSize / 2);
314 RelativeLayout.LayoutParams intlp=new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
315 intlp.addRule(RelativeLayout.RIGHT_OF,view.getId());
316 intlp.addRule(RelativeLayout.ABOVE,view.getId());
317 ruleView.setLayoutParams(intlp);
318 global.addView(ruleView);
320 hasFocus=false; //se sono arrivato a questo punto il nodo non è una foglia e non può avere il focus di default
322 for (int i=0;i<childNo;i++) {
323 final Node newChild=Children.get(i); //recupero l'i-esimo figlio
324 RelativeLayout.LayoutParams lp=new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
325 final BorderedTextView childView=new BorderedTextView(view.getContext());
326 childView.setBorders(DrawActivity.b);
327 lp.addRule(RelativeLayout.ABOVE, view.getId());
328 if (i==0) //first child
330 float leftPos= view.getLeft()-Math.max((newChild.getLineWidth()-newChild.getBaseWidth())/2,0); //il primo figlio è posizionato a sinistra del padre, con un ulteriore offset
331 // sinistro pari allo scarto della sua overline
332 if (getLineWidth()>getUpLine()) //sono più grande dell'overline prevista: aggiungo un ulteriore offset al figlio
333 leftPos+=((getLineWidth()-getUpLine())/2);
334 lp.setMargins(Math.round(leftPos), 0, 0, 0); //leftPos è il margine sinistro del figlio
335 childView.setLeft(Math.round(leftPos));
338 lp.setMargins(Math.round(view.getLeft() + interval), 0, 0, 0); //figlio intermedio: basta aggiungere l'interval alla posizione sinistra del padre
339 childView.setLeft(Math.round(view.getLeft() + interval));
341 childView.setLayoutParams(lp);
342 childView.setId(DrawActivity.globalId++);
343 childView.setText(newChild.F.toString());
344 childView.setTextSize(TypedValue.COMPLEX_UNIT_PX, DrawActivity.textSize);
345 childView.setGravity(Gravity.CENTER); //formula al centro della sua overline
346 childView.setWidth(Math.round(newChild.getLineWidth()));
347 global.addView(childView);
348 childView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
350 public void onGlobalLayout() {
351 newChild.setView(childView,global);
352 if(newChild.handler==null)
353 newChild.handler=new touchnodeHandler(newChild);
354 newChild.longHandler=new longnodeHandler(newChild);
355 newChild.view.setOnClickListener(newChild.handler);
356 newChild.view.setOnLongClickListener(newChild.longHandler);
357 childView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
361 if (i!=childNo-1) //l'ultimo figlio non ha intervalli
363 float leftOverflow=Math.max((Children.get(i+1).getLineWidth()-Children.get(i+1).getBaseWidth())/2,0);
364 interval += (newChild.getBaseWidth()+newChild.getRightOffset()+Children.get(i+1).getLeftOffset()-(leftOverflow)); //devo aggiungere all'interval la basewidth del figlio attuale, il rightoffset del figlio attuale e il leftOffset del figlio successivo.
366 if (i!=0) //devo sommare anche il leftOverflow del nuovo figlio
367 interval+=newChild.getLeftOffset();
371 void Resize() //metodo per ridisegnare l'albero dopo un pinch zoom: l'algoritmo applicato è uguale a quello del metodo Draw()
374 float interval=0; //intervallo di spazio da sommare nella creazione dei figli
375 float spaceSize= DrawActivity.spaceSize;
376 int childNo=Children.size();
380 ruleView.setText(ruleName);
381 ruleView.setTextSize(TypedValue.COMPLEX_UNIT_PX, DrawActivity.textSize / 2);
382 RelativeLayout.LayoutParams intlp=new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
383 intlp.addRule(RelativeLayout.RIGHT_OF,view.getId());
392 intlp.setMargins(0,0,0,sum);
393 intlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
394 ruleView.setLayoutParams(intlp);
396 for (i=0;i<childNo;i++) {
397 Node newChild=Children.get(i);
398 RelativeLayout.LayoutParams lp=new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
399 TextView childView=Children.get(i).view;
400 lp.addRule(RelativeLayout.ABOVE,view.getId());
401 if (i==0) //first child
403 float leftPos= view.getLeft()-Math.max((newChild.getLineWidth()-newChild.getBaseWidth())/2,0);
404 if (getLineWidth()>getUpLine())
405 leftPos+=((getLineWidth()-getUpLine())/2);
406 lp.setMargins(Math.round(leftPos), 0, 0, 0);
407 childView.setLeft(Math.round(leftPos));
410 lp.setMargins(Math.round(view.getLeft() + interval), 0, 0, 0);
411 childView.setLeft(Math.round(view.getLeft() + interval));
413 childView.setLayoutParams(lp);
414 childView.setTextSize(TypedValue.COMPLEX_UNIT_PX, DrawActivity.textSize);
416 if(newChild.status==CANCELED || newChild.status==FAKE_CANCELED)
417 childView.setWidth((int) ((newChild.F.sizeDeleted())));
419 childView.setWidth(Math.round(newChild.getLineWidth()));
423 if (i!=childNo-1) //l'ultimo figlio non ha intervalli
425 float leftOverflow=(Children.get(i+1).getLineWidth()-Children.get(i+1).getBaseWidth())/2;
426 interval += (newChild.getBaseWidth()+newChild.getRightOffset()+Children.get(i+1).getLeftOffset()-(leftOverflow));
428 if (i!=0) //devo sommare anche il leftOverflow del nuovo figlio
429 interval+=newChild.getLeftOffset();