PortaleOrdiniGruppo/PortalStudio/visualquery/treedrawfile/graphicNode.js
2025-03-24 15:28:26 +01:00

842 lines
33 KiB
JavaScript
Raw Permalink Blame History

/*
Dipende da:
NodoLogico
RaphaelAdapter
Descrizione:
Si occupa di gestire un nodo dal punto di vista grafico, al suo interno contiene un oggetto di tipo NodoLogico.
Ogni metodo o campo e' descritto nel codice, qui solo elenco con categorizzazione
Metodi disponibili sono:
init, costruttore
----- metodi get -----
getRadice,
getFirstAncientRedraw,
getPadre,
getFiglio,
getHowManyChild,
getPuntoX,
getLarghezza,
getPuntoY,
getAltezza,
getLarghezzaSubTree,
getAltezzaSubTree,
hasHiddenChild,
getMinWidthSubTree,
getMinHeightSubTree,
----- fine metodi get -----
----- spostamento nodo e figli. Non sono il drag dei figli o del nodo, possono essere utilizzati dalla funzionalita' drag-----
setX,
setY,
spostaFigli,
aggiornaConnettore,
associaDrag <---- non ha this.
----- fine spostamento -----
addFiglio, <----- per aggiungere Figli al nodo
----- cancellazione -----
deleteTree,
deleteSubTree,
----- fine cancellazione -----
----- nascondi -----
hideShowFigli,
hideShow,
----- fine nascondi -----
----- calcolo del connettore -----
controllaSpazio,
- calcoloCoordinate, <---- non e' this.
ottieniCoordinateCurva
----- fine calcolo del connettore -----
----- per redraw albero -----
- reDrawFunction <---- non e' this.
- riMostraSottoAlberoPrecedente <---- non e' this.
Membri:
_contenitore,
_figli,
_padre,
_connettore,
_infoLogiche,
_hideButton,
_hasHideSubTree,
_canvas,
_iconReDraw,
_isReDraw,
percorsoImmagini,
dimIcone
*/
/* exported NodoGrafico */
function NodoGrafico() {
//var per indicare dove si trovano le immagini
var percorsoImmagini = "treedrawfile/images/";
//var per indicare le dimensioni delle icone e del riquadro con il colore.
var dimIcone = 20;
var _contenitore, //rettangolo esterno che contiene tutto il nodo
_figli,
_padre,
_connettore, //rif al connettore al padre se presente
_infoLogiche, //rif alla parte logica
_hideButton, //rif al buttone per nascondere il sottoalbero
_hasHideSubTree, //valore per sapere se il nodo ha il proprio sottoalbero nascosto
_canvas, //rif a RaphaelAdapter
_iconReDraw, //icona per ridisegnare solo il sottoalbero
_isReDraw, //segnala se il nodo e' stato spostato in seguito ad un re-draw
_index;
/*costruttore e inizializzatore. Si occupa di associare i vari eventi del mouse ai singoli elementi.
come parametri vi sono il nome, il colore, le informazioni aggiuntive, i nomi delle informazioni aggiuntive, le coordinate dove disegnare, le dimensioni,
il riferimento al RaphaelAdapter e al padre (null se non ce l'ha, ovvero la radice)*/
this.init = function (nome, colore, infoaggiuntive, nomiInfoAggiuntive, origX, origY, larghezza, altezza, canvas, father,index) {
_index=index;
//controllo sui tipi dei parametri
if( typeof origX!= "number" || typeof origY!= "number" || !(canvas instanceof RaphaelAdapter) || !(father == null || father instanceof NodoGrafico) || typeof larghezza!= "number" || typeof altezza!= "number") {
alert("uno dei parametri di creazione del nodo grafico \350 errato");
return;
}
//inizializzo la componente logica il contenitore esterno
_infoLogiche = new NodoLogico();
_infoLogiche.init(nome, colore, infoaggiuntive, nomiInfoAggiuntive);
if(!_infoLogiche) { //qualcosa nella creazione della parte logica non ha funzionato
return;
}
_canvas = canvas;
var tuttiGliElementi = []; //questo array contiene TUTTI gli elementi del nodo, esclusi il connettore, i figli e il padre. Serve per il drag
//contiene tutti gli elementi possibili
if(document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1"))
_contenitore = canvas.createRect(origX, origY, larghezza, altezza, 0).attr({"fill": _infoLogiche.getColore(),"fill-opacity":0.05,"stroke": _infoLogiche.getColore(),"stroke-width":"3px"}); //lo sfondo e' bianco
else _contenitore = canvas.createRect(origX, origY, larghezza, altezza, 0).attr({"fill":"ffffff","stroke": _infoLogiche.getColore(),"stroke-width":"3px"}); //lo sfondo e' bianco
tuttiGliElementi.push(_contenitore);
//qui viene definito un colore, se si vuole immagine utilizzare la funzione canvas.createImage(path, x, y, larghezza, altezza). eseguire il push sull'array!
// var indicatore = canvas.createRect(origX + larghezza - dimIcone - 5, origY + 5, dimIcone, dimIcone, 0).attr({"title" : "Operation " +colore,"fill": "#ffffff","stroke":"#ffffff"});
// tuttiGliElementi.push(indicatore);
_contenitore.pair = tuttiGliElementi;
_hasHideSubTree = false;
_isReDraw = false;
//scrivo il testo, se e' pi<70> grande di tot caratteri scrivo i primi tot - 2 caratteri, poi 3 puntini. Nome completo visibile andando sul bottone i in ogni caso. tot dipende da quanto e' grande il rettangolo
var testuale;
//un carattere 8 pixel, sottraggo alla larghezza un valore pari a quanto occupato dai bottoni e ottengo il numero di caratteri ammesso
if(_infoLogiche.getNome().length > Math.floor((larghezza - 10)/8)) {
// testuale = canvas.createText(origX+10, origY+15, _infoLogiche.getNome().substr(0,Math.floor((larghezza - 40)/8 - 2))+"...").attr({"text-anchor": "start", "font-size": 16, "font-family": "Arial, Helvetica, sans-serif" });
var nomeOpArr=_infoLogiche.getNome().split(" ");
var nomeOperazione="";
var nomeOpTmp="";
var sep, i;
if (nomeOpArr.length>2){ //ho + di 2 parole
nomeOpTmp=nomeOpArr[0]+nomeOpArr[1];
if (nomeOpTmp.length>Math.floor((larghezza - 10)/8)) { // devo mettere \n sulle prime 2 parole
if (nomeOpArr[0].length>Math.floor((larghezza - 10)/8))
nomeOpArr[0]=nomeOpArr[0].substr(0,Math.floor((larghezza - 10)/8 - 2))+"."
nomeOperazione+=nomeOpArr[0]+'\n';
nomeOpTmp="";
sep="";
for (i=1; i<nomeOpArr.length; i++){
nomeOpTmp+=sep+nomeOpArr[i];
sep=" ";
}
if (nomeOpTmp.length>Math.floor((larghezza - 10)/8))
nomeOperazione+=nomeOpTmp.substr(0,Math.floor((larghezza - 10)/8 - 2))+"...";
else
nomeOperazione+=nomeOpTmp;
}
else{ //nella prima riga ci stanno 2 parole
nomeOperazione+=nomeOpArr[0]+" "+nomeOpArr[1]+"\n";
sep="";
nomeOpTmp="";
for (i=2; i<nomeOpArr.length; i++){
nomeOpTmp+=sep+nomeOpArr[i];
sep=" ";
}
if (nomeOpTmp.length>Math.floor((larghezza - 10)/8))
nomeOperazione+=nomeOpTmp.substr(0,Math.floor((larghezza - 10)/8 - 2))+"...";
else
nomeOperazione+=nomeOpTmp;
}
}
else if (nomeOpArr.length>1){
if (nomeOpArr[0].length>Math.floor((larghezza - 10)/8))
nomeOpArr[0]=nomeOpArr[0].substr(0,Math.floor((larghezza - 10)/8 - 2))+"."
nomeOperazione+=nomeOpArr[0]+"\n"; //ho solo 2 parole
if (nomeOpArr[1].length>Math.floor((larghezza - 10)/8))
nomeOperazione+=nomeOpArr[1].substr(0,Math.floor((larghezza - 10)/8 - 2))+"...";
else
nomeOperazione+=nomeOpArr[1];
}
else
nomeOperazione=nomeOpArr[0].substr(0,Math.floor((larghezza - 10)/8 - 2))+"...";
testuale = canvas.createText(origX+10, origY+25, nomeOperazione).attr({"text-anchor": "start", "font-size": 16, "font-family": "Arial, Helvetica, sans-serif" });
}
else {
testuale = canvas.createText(origX+10, origY+15, _infoLogiche.getNome()).attr({"text-anchor": "start", "font-size": 16, "font-family": "Arial, Helvetica, sans-serif" });
}
tuttiGliElementi.push(testuale);
// scrivo anche il nome della tabella (se c'<27>)
var indexTable=nomiInfoAggiuntive.indexOf("table");
if (indexTable!=-1){
var table="["+infoaggiuntive[indexTable]
if (table.length>Math.floor((larghezza - 10)/8))
table=table.substr(0,Math.floor((larghezza - 10)/8 - 2))+"...";
var tableName = canvas.createText(origX+10, origY+54, table+"]").attr({"text-anchor": "start", "font-size": 14, "font-family": "Arial, Helvetica, sans-serif" });
tuttiGliElementi.push(tableName);
}
//disegna connettore se c'e' padre
if(father && father instanceof NodoGrafico) {
_padre = father;
var pathCoordinate = this.ottieniCoordinateCurva();
// /* vedere documentazione SVG per sintassi path, qui M indica il punto di partenza, C i due punti dove deve passare la curva e poi il punto di fine.
// alla fine setto la grandezza del tratto (stroke-width calcolato in base al logaritmo in base 10 del parametro row del nodo + 1 (non ho trovato
// una funzione nativa per cui logaritmo naturale di row diviso logaritmo naturale di 10)*/
_connettore = canvas.createPath([["M", pathCoordinate[0][0], pathCoordinate[0][1]], ["C", pathCoordinate[1][0], pathCoordinate[1][1], pathCoordinate[1][2], pathCoordinate[1][3], pathCoordinate[0][2], pathCoordinate[0][3]]]).attr({stroke: "black", "stroke-width": Math.round(Math.log(_infoLogiche.getValRow())/Math.log(10)+ 1)});
_connettore.toBack(); //per evitare che si sovrapponga ad altro
}
//disegna rettangolo che contiene le informazioni
var infoDisegnate = new MostraInfo();
// var iconInfoDisegnate = canvas.createImage(percorsoImmagini + "info.png", origX + larghezza - (dimIcone + 5), origY + dimIcone + 10, dimIcone, dimIcone, 0);
var iconInfoDisegnate = canvas.createImage(percorsoImmagini + "info.png", origX + larghezza - (dimIcone + 5), origY + altezza - (dimIcone + 5), dimIcone, dimIcone, 0);
tuttiGliElementi.push(iconInfoDisegnate);
//mouseover fornito da Raphael non funziona molto bene, si utilizza la funzionalita' standard onmouseover
var a=function() {
if(!(infoDisegnate.isDrawed())) { //disegno le informazioni solo se infoDisegnate non e' disegnato e le mostro non oscurando l'informazione sul colore
infoDisegnate.crea(_infoLogiche.getNomiProprieta(), _infoLogiche.getProprieta(), _canvas, Math.round(_contenitore.attr("x") + 0.75*_contenitore.attr("width")), _contenitore.attr("y") + dimIcone + 10);
}
}
iconInfoDisegnate.mouseover(a)
if(_padre) {// la radice non puo' essere ridisegnata n<> drag (sarebbe ridondante con il pan
// icona per il drag
var iconDrag = canvas.createImage(percorsoImmagini + "dragOff.png", origX + 10, origY + altezza - (dimIcone + 5), dimIcone, dimIcone).attr({'title':'Move'});
tuttiGliElementi.push(iconDrag);
iconDrag.pair = tuttiGliElementi;
var outerThis = this; // all'interno delle funzioni lo scope cambia
associaDrag(iconDrag, outerThis);
}
}
//fine init
//metodi get
this.getRadice = function () {
if(!_padre) {
return this;
}
else {
return _padre.getRadice();
}
}
//ritorna il primo antenato su cui e' stata invocata la funzione reDrawFunction, se no la radice
this.getFirstAncientRedraw = function () {
if(!_padre) {
return this;
}
else if(_isReDraw) {
return this;
}
else {
return _padre.getFirstAncientRedraw();
}
}
this.getPadre = function() {
return _padre;
}
//ritorna il nodo nella posizione n, se n > _figli.length o non e' un numero ritorna l'ultimo, se non ci sono figli null
this.getFiglio = function(n) {
if(_figli && typeof n == "number" && n >= 0 && n < _figli.length) {
return _figli[n];
}
else {
if(_figli) {
return _figli[_figli.length - 1];
}
return null;
}
}
//ritorna il numero dei figli del nodo
this.getHowManyChild = function() {
if(_figli) {
return _figli.length;
}
else {
return 0;
}
}
this.getPuntoX = function () { // ritorna il punto x di _contenitore
return _contenitore.attr("x");
}
this.getLarghezza = function () {
return _contenitore.attr("width");
}
this.getPuntoY = function () { // ritorna il punto y di _contenitore
return _contenitore.attr("y");
}
this.getAltezza = function () {
return _contenitore.attr("height");
}
this.hasHiddenChild = function() {
return _hasHideSubTree;
}
//ritorna la larghezza massima del sottoalbero N.B. Non e' la sola larghezza del sottoalbero, ma la larghezza di quest'ultimo pii<69> la distanza da (0,0)
this.getLarghezzaSubTree = function () {
var maxWidth = this.getPuntoX() + this.getLarghezza();
if(_figli) { //chiamata ricorsiva per ottenere la larghezza massima dei figli, non posso assuemere che i figli siano pi<70> a a destra del padre, la struttura puo' essere stata manipolata
for(var i = 0; i < _figli.length; ++i) {
var widthFromChild = _figli[i].getLarghezzaSubTree();
if(widthFromChild > maxWidth) {
maxWidth = widthFromChild;
}
}
}
return maxWidth;
}
//ritorna l'altezza massima del sottoalbero. N.B. Non e' la sola altezza del sottoalbero, ma l'altezza di quest'ultimo pi<70> la distanza da (0,0)
this.getAltezzaSubTree = function () {
var maxHeight = this.getPuntoY() + this.getAltezza();
if(_figli) { //chiamata ricorsiva per ottenere l'altezza massima dei figli, non posso assuemere che i figli siano pi<70> in basso del padre, la struttura puo' essere stata manipolata
for(var i = 0; i < _figli.length; ++i) {
var heightFromChild = _figli[i].getAltezzaSubTree();
if(heightFromChild > maxHeight) {
maxHeight = heightFromChild;
}
}
}
return maxHeight;
}
//ritorna la coordinata x pi<70> piccola del sottoalbero
this.getMinWidthSubTree = function () {
var minWidth = this.getPuntoX();
if(_figli) { //chiamata ricorsiva per ottenere l'altezza massima dei figli, non posso assuemere che i figli siano pi<70> in basso del padre, la struttura puo' essere stata manipolata
for(var i = 0; i < _figli.length; ++i) {
var widthFromChild = _figli[i].getMinWidthSubTree();
if(widthFromChild < minWidth) {
minWidth = widthFromChild;
}
}
}
return minWidth;
}
//ritorna la coordinata y pi<70> piccola
this.getMinHeightSubTree = function () {
var minHeight = this.getPuntoY();
if(_figli) { //chiamata ricorsiva per ottenere l'altezza massima dei figli, non posso assuemere che i figli siano pi<70> in basso del padre, la struttura puo' essere stata manipolata
for(var i = 0; i < _figli.length; ++i) {
var heightFromChild = _figli[i].getMinHeightSubTree();
if(heightFromChild < minHeight) {
minHeight = heightFromChild;
}
}
}
return minHeight;
}
//fine metodi get
//Metodi di spostamento del nodo, divisi in spostamento coordinata x e y. Ogni metodo si occupa di aggiornare i connettori del nodo stesso e dei figli se presenti
this.setX = function (newX) {
if(typeof newX == "number") {
if(_contenitore) {
var differenzaOldNewPosition = newX - _contenitore.attr("x"); //differenza in quanto i singoli elementi del nodo hanno coordinate diverse, ma vanno spostati della stessa quantita'
if(differenzaOldNewPosition != 0) {
var i;
for(i = 0; i < _contenitore.pair.length; ++i) {
_contenitore.pair[i].attr({x: _contenitore.pair[i].attr("x") + differenzaOldNewPosition});
}
if(_padre) {
this.aggiornaConnettore();
}
if(_figli) {
for(i = 0; i < _figli.length; ++i) {
_figli[i].aggiornaConnettore();
}
}
}
}
}
}
this.setY = function(newY) {
if(typeof newY == "number") {
if(_contenitore) {
var differenzaOldNewPosition = newY - _contenitore.attr("y");
if(differenzaOldNewPosition != 0) {
var i;
for(i = 0; i < _contenitore.pair.length; ++i) {
_contenitore.pair[i].attr({y: _contenitore.pair[i].attr("y") + differenzaOldNewPosition});
}
if(_padre) {
this.aggiornaConnettore();
}
if(_figli) {
for(i = 0; i < _figli.length; ++i) {
_figli[i].aggiornaConnettore();
}
}
}
}
}
}
//sposta i figli di una certa quantita', richiamando sui figli setX e setY
this.spostaFigli = function(dx, dy) {
if(_figli) {
for(var i = 0; i < _figli.length; ++i) {
_figli[i].setX(_figli[i].getPuntoX() + dx);
_figli[i].setY(_figli[i].getPuntoY() + dy);
if(_figli[i].getFiglio(0)) {
_figli[i].spostaFigli(dx, dy);
}
}
}
}
//aggiorna il connettore
this.aggiornaConnettore = function() {
if(_padre != null) {
var pathCoordinate = this.ottieniCoordinateCurva();
_connettore.attr({path: [["M", pathCoordinate[0][0], pathCoordinate[0][1]], ["C", pathCoordinate[1][0], pathCoordinate[1][1], pathCoordinate[1][2], pathCoordinate[1][3], pathCoordinate[0][2], pathCoordinate[0][3]]]});
}
}
//associa le funzioni di drag. outerThis serve all'interno delle singole funzioni
var associaDrag = function (iconDrag, outerThis) {
iconDrag.node.canPan = false;
var dxIterazionePrecedente = 0,
dyIterazionePrecedente = 0,
start = function () {
//N.B. attr("x") non esiste per le figure come ellissi o cerchi. Recuperare cx aggiungendo un controllo sul tipo dell'oggetto
for(var i = 0; i < iconDrag.pair.length; ++i) {
iconDrag.pair[i].ox = iconDrag.pair[i].attr("x");
iconDrag.pair[i].oy = iconDrag.pair[i].attr("y");
}
//cambio per mostrare che il drag e' in funzione
// iconDrag.attr({ "src": percorsoImmagini + "dragOn.png"});
},
move = function (dx, dy) {
for(var i = 0; i < iconDrag.pair.length; ++i) {
iconDrag.pair[i].attr({x: iconDrag.pair[i].ox + dx, y: iconDrag.pair[i].oy + dy});
}
outerThis.aggiornaConnettore();
if(_figli) { //se ci sono figli sposto anche loro
/* ai figli passo sempre la differenza tra lo spostamento precedente e quello attuale, all'interno di un drag completo.
non posso passare direttamente dx o dy, perch<63> fino al termine del drag continua ad aumentare */
outerThis.spostaFigli(dx - dxIterazionePrecedente, dy - dyIterazionePrecedente);
dxIterazionePrecedente = dx;
dyIterazionePrecedente = dy;
}
},
end = function() {
//riporto a zero questi valori, al prossimo drag continueranno a memorizzare lo scarto corretto
dxIterazionePrecedente = 0;
dyIterazionePrecedente = 0;
//ripristino l'immagine del drag corretta
iconDrag.attr({ "src": percorsoImmagini + "dragOff.png"});
};
iconDrag.drag(move, start, end);
}
//fine parte spostamento
//aggiungi figli, se e' il primo figlio aggiungo anche la funzionalita' per nascondere/mostrare i figli
this.addFiglio = function (figlio) {
if(figlio instanceof NodoGrafico) {
if(!_figli) { // parte eseguita se e' il primo figlio aggiunto
_figli=[];
var outerThis = this; //memorizzo il riferimento this, all'interno delle funzioni this = window e non l'oggetto
if (_padre){
// hide degli antenati
_iconReDraw = _canvas.createImage(percorsoImmagini + "hideAncestors.png", this.getPuntoX() + dimIcone + 40, this.getPuntoY() + this.getAltezza() - (dimIcone + 5), dimIcone, dimIcone).attr({'title':'Hide ancestors'});
_iconReDraw.node.canPan = false; //il bottone per nascondere i figli non abilita il pan
_contenitore.pair.push(_iconReDraw);
_iconReDraw.node.onclick = function () {
if(document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1"))
_drawTree.hideAncestors(_index)
else reDrawFunction(outerThis,true); // def funzione in fondo
}
//hide del sottoalbero
_hideButton = _canvas.createImage(percorsoImmagini + "hideChild.png", this.getPuntoX() + dimIcone + 15, this.getPuntoY() + this.getAltezza() - (dimIcone + 5), dimIcone, dimIcone);
_hideButton.node.canPan = false; //il bottone per nascondere i figli non abilita il pan
_hideButton.insertAfter(_contenitore); // in questo modo il bottone e' esattamente dopo il "livello" del _contenitore. Se ci sposto sopra un figlio rimane oscurato dal figlio
_hideButton.attr({'title':'Hide child'})
_contenitore.pair.push(_hideButton);
_hideButton.node.onclick = function() {
// outerThis.hideShowFigli(_hasHideSubTree);
_hasHideSubTree = !_hasHideSubTree;
if(document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1")) {
_drawTree.hideSubTree(_index)
}
else{ outerThis.hideShowFigli(_hasHideSubTree);
_hasHideSubTree = !_hasHideSubTree;
if(!_hasHideSubTree) {
if (_hideButton)
_hideButton.attr({'title':'Hide child', "src": percorsoImmagini + "hideChild.png"});
}
else {
if (_hideButton)
_hideButton.attr({'title':'Show Child', "src": percorsoImmagini + "showChild.png"});
}
}
}
}
}
//aggiungo il figlio
_figli.push(figlio);
}
}
this.addIconShowChild=function(){
_hideButton = _canvas.createImage(percorsoImmagini + "showChild.png", this.getPuntoX() + dimIcone + 15, this.getPuntoY() + this.getAltezza() - (dimIcone + 5), dimIcone, dimIcone);
_hideButton.node.canPan = false; //il bottone per nascondere i figli non abilita il pan
_hideButton.insertAfter(_contenitore); // in questo modo il bottone e' esattamente dopo il "livello" del _contenitore. Se ci sposto sopra un figlio rimane oscurato dal figlio
_hideButton.attr({'title':'Show child'})
_hasHideSubTree=true;
_contenitore.pair.push(_hideButton);
_hideButton.node.onclick = function() {
_hasHideSubTree = !_hasHideSubTree;
if(document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1"))
_drawTree.hideSubTree(_index)
else {
if(!_hasHideSubTree) {
_hideButton.attr({'title':'Hide child', "src": percorsoImmagini + "hideChild.png"});
}
else {
_hideButton.attr({'title':'Show Child', "src": percorsoImmagini + "showChild.png"});
}
}
}
}
this.addIconShowAncestors=function(){
_iconReDraw = _canvas.createImage(percorsoImmagini + "showAncestors.png", this.getPuntoX() + dimIcone + 40, this.getPuntoY() + this.getAltezza() - (dimIcone + 5), dimIcone, dimIcone).attr({'title':'Show ancestors'});
_iconReDraw.node.canPan = false; //il bottone per nascondere i figli non abilita il pan
_contenitore.pair.push(_iconReDraw);
_iconReDraw.node.onclick = function () {
if(document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1"))
_drawTree.hideAncestors(_index)
}
}
/*funzioni delete. Si cancella solo la parte logica, la parte grafica viene eliminata da invocazione di clear sul canvas
cancellazione albero */
this.deleteTree = function () {
var radice;
if(_padre) {
radice = this.getRadice();
}
else {
radice = this;
}
radice.deleteSubTree();
}
//cancellazione sottoAlbero
this.deleteSubTree = function () {
while (_figli && _figli.length > 0) {
_figli[_figli.length - 1].deleteSubTree();
_figli.pop();
}
while(_contenitore.pair.length > 1) {
_contenitore.pair[_contenitore.pair.length - 1].remove();
_contenitore.pair.pop();
}
_contenitore.pair[0].remove(); //pair[0] e' contenitore, di conseguenza non devo fare il pop.
if(_padre) {
_connettore.remove();
}
_padre = null;
_infoLogiche = null;
}
/* Inizio parte per hide
Nasconde o mostra i figli sottostanti
show = false nascondi i figli, altrimenti mostra (true) */
this.hideShowFigli = function (show) {
if(_figli) {
for(var i = 0; i < _figli.length; ++i) {
if(!(show && _figli[i].hasHiddenChild())) {
_figli[i].hideShowFigli(show);
}
_figli[i].hideShow(show, show);
}
}
}
//nasconde o mostra se stesso, false nasconde, true mostra. showConnector e' per il connettore
this.hideShow = function (show, showConnector) {
var i;
if(!show) {
if(_padre && !showConnector) {
_padre.childHidden=true;
_connettore.hide();
}
for(i = 0; i < _contenitore.pair.length; ++i) {
_contenitore.pair[i].hide();
}
}
else {
if(_padre && showConnector) {
_padre.childHidden=false;
_connettore.show();
}
for(i = 0; i < _contenitore.pair.length; ++i) {
_contenitore.pair[i].show();
}
}
}
//fine parte della gestione di hide
/* funzione per controllare se un fratello sinistro passa sopra il connettore di un certo nodo.
figlio e' il nodo da controllare
posto e' il posto dove controllare. Per ora implementato solo up
*/
this.controllaSpazio = function (figlio, posto) {
if(!_figli || _figli.length == 0) {
return false;
}
var i = 0;
while(_figli[i] !== figlio && _figli.length > i) {
++i;
}
if(i == 0) {
//non ha fratelli sinistri
return false;
}
//ho l'indice del nodo oppure sono arrivato alla fine dell'array, ovvero il nodo figlio non e' ancora stato aggiunto perch<63> sto disegnando l'albero e non facendo drag
--i; //indice del fratello
switch(posto) {
case "up":
//se non e' sopra al nodo ritorno
if(! (_figli[i].getPuntoY() < figlio.getPuntoY())) {
return false;
}
// il fratello sinistro e' tra i punti dove parte e finisce il connettore del nodo al padre
if(_figli[i].getPuntoX() < figlio.getPuntoX() + figlio.getLarghezza()/2 && _figli[i].getPuntoX() > this.getPuntoX() + this.getLarghezza()/2) {
return true;
}
return false;
break;
default:
return false;
}
}
/* Parte per calcolo coordinate connettore, solo i punti di aggancio. NON altri punti del path.
x punto orgine del padre sull'asse x, y punto origine padre sull'asse y, a punto orgine del figlio sull'asse x, b punto origine padre sull'asse y,
Questa funzione assegna le coordinate xy e ab in base alle loro posizioni, all'altezza e alla larghezza delle figure. Ci sono 6 casi */
var calcoloCoordinate = function(x, y, a, b, altezzaXY, altezzaAB, larghezzaXY, larghezzaAB) {
var coordToReturn = [];
//entro quando il figlio e' in un "corridoio" alto come due nodi e sette decimi figli. Scelta arbitraria in base all'output grafico dell'albero
if ((y - b <= altezzaAB*1.2 && y >= b) || (y <=b && b - y < altezzaAB*1.5)){
//ramo if: collego sotto-sopra e non destra - sinistra, in quanto il punto di collegamento a sinistra/destra del padre sarebbe prima del punto di collegamento destro/sinistro del figlio
if((x >= a && a + larghezzaAB >= x) || (a > x && x + larghezzaXY > a)) { // N.B. per risparmiare righe di codice si puo' fare un'unica condizione if con if padre ma sarebbe poco chiaro
if(b > y) { //il padre e' sopra
coordToReturn.push(y + altezzaXY);
coordToReturn.push(x + larghezzaXY/2);
coordToReturn.push(b);
coordToReturn.push(a + larghezzaAB/2);
}
else {
coordToReturn.push(y);
coordToReturn.push(x + larghezzaXY/2);
coordToReturn.push(b + altezzaAB);
coordToReturn.push(a + larghezzaAB/2);
}
}
else {
if(x > a) { //il padre e' a destra
coordToReturn.push(y + altezzaXY/2);
coordToReturn.push(x);
coordToReturn.push(b + altezzaAB/2);
coordToReturn.push(a + larghezzaAB);
}
else {
coordToReturn.push(y + altezzaXY/2);
coordToReturn.push(x + larghezzaXY);
coordToReturn.push(b + altezzaAB/2);
coordToReturn.push(a);
}
}
}
else {
if(b > y) { //il padre e' sopra
coordToReturn.push(y + altezzaXY);
coordToReturn.push(x + larghezzaXY/2);
coordToReturn.push(b);
coordToReturn.push(a + larghezzaAB/2);
}
else {
coordToReturn.push(y);
coordToReturn.push(x + larghezzaXY/2);
coordToReturn.push(b + altezzaAB);
coordToReturn.push(a + larghezzaAB/2);
}
}
return coordToReturn;
}
//ottengo tutte le coordinate che mi servono per disegnare la curva. qui scelta arbitraria, se non c'e' spazio sopra si sposta il punto di collegamento al figlio a sinistra
this.ottieniCoordinateCurva = function () {
var puntoAggancioPadreX, //da dove parte il connettore punto x
puntoAggancioPadreY, //da dove parte il connettore punto y
baseAggancioFiglioX = _contenitore.attr("x"),
baseAggancioFiglioY = _contenitore.attr("y"),
puntoAggancioFiglioX, //dove si connette, punto x
puntoAggancioFiglioY, //dove si connette, punto y
yPadre = _padre.getPuntoY(),
xPadre = _padre.getPuntoX(),
altezzaPadre = _padre.getAltezza(),
larghezzaPadre = _padre.getLarghezza(),
altezza = _contenitore.attr("height"),
larghezza = _contenitore.attr("width"),
ax, // punto x della prima curva del connettore
bx, // punto x della seconda curva del connettore
ay, // punto y della prima curva del connettore
by; // punto y della seconda cuva del connettore
var coordinate = [];
coordinate = calcoloCoordinate(xPadre, yPadre, baseAggancioFiglioX, baseAggancioFiglioY, altezzaPadre, altezza, larghezzaPadre, larghezza);
puntoAggancioPadreY = coordinate[0];
puntoAggancioPadreX = coordinate[1];
puntoAggancioFiglioY = coordinate[2];
puntoAggancioFiglioX = coordinate[3];
//e' collegato sopra, e controllo che abbia spazio. Se controllaSpazio restituisce true sposto il punto di collegamento a sinistra
if(puntoAggancioFiglioX == baseAggancioFiglioX + larghezza/2 && puntoAggancioFiglioY == baseAggancioFiglioY && _padre.controllaSpazio(this, "up")) {
puntoAggancioFiglioY = baseAggancioFiglioY + altezza/2;
puntoAggancioFiglioX = baseAggancioFiglioX;
}
//distanza tra punto x dove arriva al figlio e punto di aggancio al padre
var differenzaXPadreFiglio = puntoAggancioFiglioX - puntoAggancioPadreX;
//distanza tra punto y dove arriva al figlio e punto di aggancio al padre
var differenzaYPadreFiglio = puntoAggancioFiglioY - puntoAggancioPadreY;
//calcolo punti coordinate delle x delle curve del connettore. N.B. non essendo valore assoluto differenzaXPadreFiglio non devo capire se e' a destra o a sinistra
if(differenzaXPadreFiglio == 0) { //il figlio e' sulla stessa linea del padre
ax = puntoAggancioPadreX;
bx = puntoAggancioFiglioX;
}
else { //sommo un ottavo della differenza al padre e sottraggo un ottavo della differenza al figlio.
ax = Math.round(puntoAggancioPadreX + differenzaXPadreFiglio/8);
bx = Math.round(puntoAggancioFiglioX - differenzaXPadreFiglio/8);
}
//calcolo punti coordinate delle y delle curve del connettore. N.B. non essendo valore assolute differenzaYPadreFiglio non devo capire se e' sopra o sotto
if (differenzaYPadreFiglio == 0) { //il figlio e' sulla stessa linea del padre
ay = puntoAggancioPadreY;
by = puntoAggancioFiglioY;
}
else { //sommo un terzo della differenza al padre e sottraggo un terzo della differenza al figlio.
ay = Math.round(puntoAggancioPadreY + differenzaYPadreFiglio/3);
by = Math.round(puntoAggancioFiglioY - differenzaYPadreFiglio/3);
}
// memorizzo in un array e restituisco
var coordinatePerPath = new Array();
for (var i=0; i <2; i++) {
coordinatePerPath[i]=new Array();
}
coordinatePerPath[0][0] = puntoAggancioPadreX;
coordinatePerPath[0][1] = puntoAggancioPadreY;
coordinatePerPath[0][2] = puntoAggancioFiglioX;
coordinatePerPath[0][3] = puntoAggancioFiglioY;
coordinatePerPath[1][0] = ax;
coordinatePerPath[1][1] = ay;
coordinatePerPath[1][2] = bx;
coordinatePerPath[1][3] = by;
return coordinatePerPath;
}
//fine parte ottenimento coordinate curve
// funzione per nascondere l'albero, spostare il sottoalbero in cima e poi mostrare solo quest'ultimo
var reDrawFunction = function(outerThis,nascondiSpostamento) {
if(!_isReDraw) {
//trovo il primo nodo ridisegnato (se non ce ne sono e' la radice) e nascondo il sottoalbero di quest'ultimo.
var padreReDraw = outerThis.getFirstAncientRedraw();
padreReDraw.hideShowFigli(false);
padreReDraw.hideShow(false, false);
if(!_hasHideSubTree) {
outerThis.hideShowFigli(true); //mostro i figli
}
outerThis.hideShow(true, false); //mostro se stesso
//ridimensiono il canvas in modo che sia grande come il sottoalbero
// _canvas.resizeCanvas(outerThis.getLarghezzaSubTree() + 10, outerThis.getAltezzaSubTree() + 10);
_iconReDraw.attr({'title':'Show ancestors', "src": percorsoImmagini + "showAncestors.png"});
_iconReDraw.node.onclick = function () {
riMostraSottoAlberoPrecedente(outerThis,true); // def funzione in fondo
}
_isReDraw = true;
}
if (nascondiSpostamento)
_contenitore.pair[3].hide(); // nascondo il bottone dello spostamento
}
//questa funzione mostra il sottoalbero del primo padre "ridisegnato"
var riMostraSottoAlberoPrecedente = function (outerThis,mostraSpostamento) {
if (_isReDraw && _padre) { //il nodo dev'essere ridisegnato e non dev'essere la radice
outerThis.hideShowFigli(false); //nascondo i figli
outerThis.hideShow(false, false); //nascondo se stesso
var padreReDraw;
padreReDraw = _padre.getFirstAncientRedraw(); //cerco il primo padre che e' stato ri-disegnato oppure arrivo alla radice
_iconReDraw.attr({'title':'Hide ancestors', "src": percorsoImmagini + "hideAncestors.png"});
_iconReDraw.node.onclick = function () {
reDrawFunction(outerThis,true); // def funzione in fondo
}
padreReDraw.hideShowFigli(true);
padreReDraw.hideShow(true, false); //del nodo non mostro l'eventuale connettore al padre
//ridimensiono il canvas in modo che sia grande come il sottoalbero del primo padre ri-disegnato
// _canvas.resizeCanvas(padreReDraw.getLarghezzaSubTree() + 10, padreReDraw.getAltezzaSubTree() + 10);
_isReDraw = false;
if (mostraSpostamento)
_contenitore.pair[3].show(); // mostro il bottone dello spostamento
}
}
}