//--------------------------------------------------------------------------------//
// COPYRIGHT NOTICE //
//--------------------------------------------------------------------------------//
// Copyright (c) 2012, Instituto de Microelectronica de Sevilla (IMSE-CNM) //
// //
// All rights reserved. //
// //
// Redistribution and use in source and binary forms, with or without //
// modification, are permitted provided that the following conditions are met: //
// //
// * Redistributions of source code must retain the above copyright notice, //
// this list of conditions and the following disclaimer. //
// //
// * Redistributions in binary form must reproduce the above copyright //
// notice, this list of conditions and the following disclaimer in the //
// documentation and/or other materials provided with the distribution. //
// //
// * Neither the name of the IMSE-CNM nor the names of its contributors may //
// be used to endorse or promote products derived from this software //
// without specific prior written permission. //
// //
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" //
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE //
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE //
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE //
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL //
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER //
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, //
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE //
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //
//--------------------------------------------------------------------------------//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
//EDITOR DE LA ESTRUCTURA DE UN SISTEMA DIFUSO //
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
package xfuzzy.xfedit;
import xfuzzy.lang.*;
import xfuzzy.xfmt.Xfmt;
import xfuzzy.util.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
/**
* Panel de edici�n de la estructura de un sistema difuso
*
* @author Francisco Jos� Moreno Velo
*
*/
public class XfeditStructure extends JTextArea
implements MouseListener, MouseMotionListener, KeyListener {
/**
* C�digo asociado a la clase serializable
*/
private static final long serialVersionUID = 95505666603039L;
//----------------------------------------------------------------------------//
// MIEMBROS PRIVADOS //
//----------------------------------------------------------------------------//
/**
* Descripci�n de la estructura a mostrar
*/
private SystemModule system;
/**
* Men� desplegable asociado a los m�dulos de la estructura
*/
private JPopupMenu popup;
/**
* Referencia a la aplicaci�n Xfmt a la que pertenece el objeto
* (null si pertenece a una aplicaci�n Xfedit)
*/
private Xfmt xfmt;
/**
* Referencia a la aplicaci�n Xfedit a la que pertenece el objeto
* (null si pertenece a una aplicaci�n Xfmt)
*/
private Xfedit xfedit;
/**
* Componentes de llamadas a m�dulos que forman parte de la estructura
*/
private XfeditCallComponent call[];
/**
* Componente de llamada a m�dulo seleccionado
*/
private XfeditCallComponent selectedcall;
/**
* Puntos (cuadraditos) que representan variables de entrada del sistema
*/
private XfeditVariableDot inputdot[];
/**
* Puntos (cuadraditos) que representan variables de salida del sistema
*/
private XfeditVariableDot outputdot[];
/**
* Puntos (cuadraditos) que representan variables de internas del sistema
*/
private XfeditVariableDot dot[];
/**
* Punto seleccionado
*/
private XfeditVariableDot selecteddot;
/**
* Fuente de letra para etiquetar las variables
*/
private FontMetrics fm;
/**
* Niveles de encadenamiento de m�dulos en la estructura
*/
private int levels;
/**
* Anchura de las diferentes zonas a representar: etiquetas
* de las variables de entrada, de salida, internas y
* anchura de los componentes de llamada
*/
private int inputwidth, outputwidth, callwidth, varwidth;
/**
* Altura total de la representaci�n
*/
private int totalheight;
/**
* Posici�n en la que se pulsa el bot�n del rat�n (para dibujar
* el arrastre)
*/
private int mousex, mousey;
/**
* Contador de canales para las l�neas de conexi�n
*/
public int counter;
//----------------------------------------------------------------------------//
// CONSTRUCTOR //
//----------------------------------------------------------------------------//
/**
* Constructor para la aplicaci�n Xfedit
*/
public XfeditStructure(Xfedit xfedit, SystemModule system) {
super();
this.xfedit = xfedit;
this.system = system;
build();
}
/**
* Constructor para la aplicaci�n Xfmt
* @param xfmt
* @param system
*/
public XfeditStructure(Xfmt xfmt, SystemModule system) {
super();
this.xfmt = xfmt;
this.system = system;
build();
}
//----------------------------------------------------------------------------//
// M�TODOS P�BLICOS //
//----------------------------------------------------------------------------//
/**
* Calcula los componentes para representar la estructura
*/
public void setValues() {
Variable input[] = system.getInputs();
Variable output[] = system.getOutputs();
ModuleCall rcall[] = system.getModuleCalls();
this.fm = getFontMetrics(getFont());
this.counter = 0;
this.dot = new XfeditVariableDot[0];
this.inputdot = new XfeditVariableDot[input.length];
this.outputdot = new XfeditVariableDot[output.length];
this.call = new XfeditCallComponent[rcall.length];
for(int i=0; i<input.length; i++) {
inputdot[i] = new XfeditVariableDot(this,input[i], 0, true);
addDot(inputdot[i]);
}
for(int i=0; i<output.length; i++) {
outputdot[i] = new XfeditVariableDot(this,output[i], 0, false);
addDot(outputdot[i]);
}
for(int i=0; i<rcall.length; i++) {
call[i] = new XfeditCallComponent(this,rcall[i]);
}
this.inputwidth = 0;
this.outputwidth = 0;
this.callwidth = 80;
this.levels = 0;
for(int i=0; i<input.length; i++)
inputwidth = Math.max(inputwidth,fm.stringWidth(input[i].toString()));
for(int i=0; i<output.length; i++)
outputwidth = Math.max(outputwidth,fm.stringWidth(output[i].toString()));
for(int i=0; i<rcall.length; i++) {
int labelwidth = fm.stringWidth(rcall[i].getName())+10;
callwidth = Math.max(callwidth,labelwidth);
}
for(int i=0; i<call.length; i++)
if(call[i].level > levels) levels = call[i].level;
this.inputwidth += 20;
this.outputwidth += 20;
this.levels ++;
for(int i=0; i<outputdot.length; i++) outputdot[i].level = levels;
this.varwidth = 30 + 3*counter;
Dimension size = getSize();
int width = (size.width>350 ? size.width : 350);
int vwidth = (width-inputwidth-outputwidth-levels*callwidth)/(levels+1);
if(varwidth < vwidth) varwidth = vwidth;
int twidth = inputwidth+levels*callwidth+(levels+1)*varwidth+outputwidth;
if(twidth < width) twidth = width;
int theight = 70 + 3*counter;
for(int lv=0; lv<levels; lv++) {
int lvheight = 70 + 3*counter;
for(int i=0; i<call.length; i++)
if(call[i].level == lv) lvheight += call[i].height+20;
if(lvheight > theight) theight = lvheight;
}
setPreferredSize(new Dimension(twidth,theight));
}
/**
* Sobreescribe el m�todo que pinta el panel
*/
public void paint(Graphics g) {
super.paint(g);
paintBase(g);
}
/**
* Regenera el contenido del panel y vuelve a dibujarlo
*/
public void refresh() {
setValues();
revalidate();
repaint();
}
/**
* Selecciona una llamada a un m�dulo
*/
public void setSelection(XfeditCallComponent scall) {
if(selectedcall != null) selectedcall.setSelected(false);
selectedcall = scall;
if(scall != null) scall.setSelected(true);
if(xfedit != null) xfedit.listSelection(Xfedit.STRUCTURE);
}
/**
* Deselecciona cualquier llamada a un m�dulo
*/
public void clearSelection() {
if(selectedcall != null) selectedcall.setSelected(false);
selectedcall = null;
repaint();
}
/**
* Inserta una nueva llamada a una base de reglas
*/
public void insertCall() {
XfeditCallEditor dialog = new XfeditCallEditor(xfedit);
dialog.setVisible(true);
Object selection = dialog.getSelection();
if(selection == null) return;
if(selection instanceof Rulebase) {
Rulebase rb = (Rulebase) selection;
Variable nullvar = system.searchVariable("NULL");
Variable sysinputvar[] = new Variable[rb.getInputs().length];
Variable sysoutputvar[] = new Variable[rb.getOutputs().length];
for(int i=0; i<sysinputvar.length; i++) sysinputvar[i] = nullvar;
for(int i=0; i<sysoutputvar.length; i++) sysoutputvar[i] = nullvar;
system.addCall(rb,sysinputvar,sysoutputvar);
refresh();
}
else if(selection instanceof CrispBlock) {
CrispBlock cb = (CrispBlock) selection;
Variable nullvar = system.searchVariable("NULL");
Variable sysinputvar[] = new Variable[cb.inputs()];
for(int i=0; i<sysinputvar.length; i++) sysinputvar[i] = nullvar;
Variable sysoutputvar = nullvar;
system.addCall(cb,sysinputvar,sysoutputvar);
refresh();
}
}
/**
* Elimina la llamada seleccionada
*/
public void removeCall() {
if(selectedcall == null) return;
system.removeCall(selectedcall.call);
selectedcall = null;
refresh();
}
/**
* Busca el componente que representa a una llamada
*/
public XfeditCallComponent searchCallComponent(RulebaseCall rbc) {
for(int i=0; i<call.length; i++) if(call[i].call == rbc) return call[i];
return null;
}
/**
* Busca el punto correspondiente a una variable
*/
public XfeditVariableDot searchDot(Variable var) {
if(var == null) return null;
if(var.isInner()) for(int i=0; i<dot.length; i++)
{ if(dot[i].sysvar == var && dot[i].isOrigin()) return dot[i]; }
else for(int i=0; i<dot.length; i++)
{ if(dot[i].sysvar == var && dot[i].isGlobal()) return dot[i]; }
return null;
}
/**
* Busca el punto correspondiente a una variable
*/
public XfeditVariableDot searchOriginDot(Variable var) {
if(var == null) return null;
if(var.isInner() || var.isOutput()) for(int i=0; i<dot.length; i++) {
if(dot[i].sysvar == var &&
dot[i].isInternal() &&
dot[i].isOutput()) return dot[i];
}
else for(int i=0; i<dot.length; i++)
{ if(dot[i].sysvar == var && dot[i].isGlobal()) return dot[i]; }
return null;
}
/**
* A�adir un punto (variable) a la gr�fica
*/
public void addDot(XfeditVariableDot ndot) {
XfeditVariableDot adot[] = new XfeditVariableDot[this.dot.length+1];
System.arraycopy(this.dot,0,adot,0,this.dot.length);
adot[this.dot.length] = ndot;
this.dot = adot;
}
/**
* Interfaz KeyListener - Acci�n de soltar una tecla
*/
public void keyReleased(KeyEvent e) {
if(xfedit == null) return;
int code = e.getKeyCode();
if(code == KeyEvent.VK_BACK_SPACE) { removeCall(); removeLink(); }
if(code == KeyEvent.VK_DELETE) { removeCall(); removeLink(); }
if(code == KeyEvent.VK_CUT) { removeCall(); removeLink(); }
if(code == KeyEvent.VK_INSERT) insertCall();
e.consume();
}
/**
* Interfaz KeyListener - Acci�n de presionar una tecla
*/
public void keyPressed(KeyEvent e) {
}
/**
* Interfaz KeyListener - Acci�n de pulsar una tecla
*/
public void keyTyped(KeyEvent e) {
}
/**
* Interfaz MouseListener - Acci�n de presionar el bot�n del rat�n
*/
public void mousePressed(MouseEvent e) {
if(e.isPopupTrigger()) {
if(xfedit != null) popup.show((Component) e.getSource(), e.getX(), e.getY());
return;
}
mousex = -1; mousey = -1;
if(xfedit == null) return;
setSelection(searchCall(e.getX(),e.getY()));
selecteddot = searchDot(e.getX(),e.getY());
repaint();
}
/**
* Interfaz MouseListener - Acci�n de sltar el bot�n del rat�n
*/
public void mouseReleased(MouseEvent e) {
if(e.isPopupTrigger()) {
if(xfedit != null) popup.show((Component) e.getSource(), e.getX(), e.getY());
return;
}
mousex = -1; mousey = -1;
if(xfedit != null) insertLink(selecteddot,searchDot(e.getX(),e.getY()));
repaint();
}
/**
* Interfaz MouseListener - Acci�n de pulsar el bot�n del rat�n
*/
public void mouseClicked(MouseEvent e) {
if(xfmt == null) return;
XfeditCallComponent call = searchCall(e.getX(),e.getY());
if(call == null) return;
if(call.call instanceof RulebaseCall) {
xfmt.monitorize((RulebaseCall) call.call);
}
}
/**
* Interfaz MouseListener - Acci�n de entrar el rat�n en el componente
*/
public void mouseEntered(MouseEvent e) {
}
/**
* Interfaz MouseListener - Acci�n de salir el rat�n del componente
*/
public void mouseExited(MouseEvent e) {
}
/**
* Interfaz MouseMotionListener - Acci�n de mover el rat�n
*/
public void mouseMoved(MouseEvent e) {
}
/**
* Interfaz MouseMotionListener - Acci�n de arrastrar el rat�n
*/
public void mouseDragged(MouseEvent e) {
if(selecteddot == null) return;
mousex = e.getX();
mousey = e.getY();
repaint();
}
//----------------------------------------------------------------------------//
// M�TODOS PRIVADOS //
//----------------------------------------------------------------------------//
/**
* Genera el contenido del panel
*/
private void build() {
setBackground(XConstants.textbackground);
setBorder(BorderFactory.createLoweredBevelBorder());
setFont(XConstants.textfont);
addMouseListener(this);
addMouseMotionListener(this);
addKeyListener(this);
setEditable(false);
setValues();
if(xfedit != null) createPopupMenu();
}
/**
* Genera el men� desplegable
*/
private void createPopupMenu() {
String label[] = { "New module call","Remove module call" };
String command[] = { "NewCall", "DelCall" };
popup = new JPopupMenu();
JMenuItem item[] = new JMenuItem[command.length];
for(int i=0; i<command.length; i++) {
item[i] = new JMenuItem(label[i]);
item[i].setActionCommand(command[i]);
item[i].addActionListener(xfedit);
item[i].setFont(XConstants.font);
popup.add(item[i]);
}
}
/**
* Devuelve la llamada a m�dulo correspondiente a una determinada posici�n
*/
private XfeditCallComponent searchCall(int x, int y) {
for(int i=0; i<call.length; i++) if(call[i].include(x,y)) return call[i];
return null;
}
/**
* Vuelve a colocar los componentes de llamada
*/
private void reallocate() {
XfeditCallComponent aux;
for(int i=0; i<call.length; i++) call[i].clearPrevious();
for(int i=0; i<call.length; i++) call[i].setPrevious();
for(int i=0; i<call.length; i++)
for(int j=i+1; j<call.length; j++)
if(call[i].isPrevious(call[j])) {
aux=call[i]; call[i]=call[j]; call[j]=aux;
system.exchangeCall(i,j);
i--;
break;
}
}
/**
* Busca el punto (variable) correspondiente a una posici�n
*/
private XfeditVariableDot searchDot(int x, int y) {
for(int i=0; i<dot.length; i++) if(dot[i].include(x,y)) return dot[i];
return null;
}
/**
* Crea un enlace entre dos puntos
*/
private void insertLink(XfeditVariableDot a, XfeditVariableDot b) {
if(a == null || b == null || a == b) return;
XfeditVariableDot origdot = null;
XfeditVariableDot destdot = null;
if( a.isDestination() ) destdot = a;
if( b.isDestination() ) destdot = b;
if( a.isOrigin() ) origdot = a;
if( b.isOrigin() ) origdot = b;
if( origdot == null || destdot == null) return;
if( origdot.isGlobal() && destdot.isGlobal() ) return;
if(origdot.call != null && origdot.call == destdot.call) {
XDialog.showMessage(this,"Cannot make loops");
return;
}
if(origdot.call != null && origdot.call.isPrevious(destdot.call) ) {
XDialog.showMessage(this,"Cannot make loops");
return;
}
removeLink(destdot);
if(origdot.isGlobal()) {
destdot.setSystemVariable(origdot.sysvar);
} else if(origdot.isNull() && destdot.isGlobal()) {
origdot.setSystemVariable(destdot.sysvar);
} else if(origdot.isNull()) {
Variable inner = system.addInnerVariable();
origdot.setSystemVariable(inner);
destdot.setSystemVariable(inner);
} else if(destdot.isGlobal()) {
removeLink(origdot);
origdot.setSystemVariable(destdot.sysvar);
} else {
destdot.setSystemVariable(origdot.sysvar);
}
reallocate();
refresh();
}
/**
* Elimina el enlace seleccionado
*/
private void removeLink() {
removeLink(selecteddot);
refresh();
}
/**
* Elimina un enlace
*/
private void removeLink(XfeditVariableDot rmdot) {
if(rmdot == null) return;
Variable nullvar = system.searchVariable("NULL");
if(rmdot.isInternal())
if(rmdot.isDestination()) {
rmdot.setSystemVariable(nullvar);
} else {
rmdot.setSystemVariable(nullvar);
for(int i=0; i<dot.length; i++)
if(dot[i] != rmdot && dot[i].isInternal() && dot[i].sysvar == rmdot.sysvar)
dot[i].setSystemVariable(nullvar);
}
if(rmdot.isGlobal())
if(rmdot.isOrigin()) {
for(int i=0; i<dot.length; i++)
if(dot[i] != rmdot && dot[i].isInternal() && dot[i].sysvar == rmdot.sysvar)
dot[i].setSystemVariable(nullvar);
} else {
for(int i=0; i<dot.length; i++)
if(dot[i] != rmdot && dot[i].sysvar == rmdot.sysvar)
dot[i].setSystemVariable(nullvar);
}
}
/**
* Pinta la estructura
*/
private void paintBase(Graphics g) {
Dimension size = getSize();
this.totalheight = size.height;
Variable input[] = system.getInputs();
Variable output[] = system.getOutputs();
int ih = (size.height-35-3*counter)/(input.length+1);
int oh = (size.height-35-3*counter)/(output.length+1);
int sw = size.width-inputwidth-outputwidth;
g.setColor(XConstants.systembg);
g.fillRoundRect(inputwidth,10,sw,size.height-20,15,15);
g.setColor(Color.black);
g.drawRoundRect(inputwidth,10,sw,size.height-20,15,15);
for(int i=0; i<inputdot.length; i++) {
inputdot[i].setPoint(inputwidth,10+ih+ih*i);
inputdot[i].paintDot(g);
}
for(int i=0; i<outputdot.length; i++) {
outputdot[i].setPoint(size.width-outputwidth,10+oh+oh*i);
outputdot[i].paintDot(g);
}
varwidth = (size.width-inputwidth-outputwidth-levels*callwidth)/(levels+1);
for(int lv=0; lv<levels; lv++) {
int lvcounter = 0, lvheight = 0;
for(int i=0; i<call.length; i++) if(call[i].level == lv) {
lvcounter++;
lvheight += call[i].height;
}
int ch = (size.height-35-lvheight-3*counter)/(lvcounter+1);
int cpos = inputwidth + varwidth + lv*(varwidth + callwidth);
int ypos = 10 + ch;
for(int i=0; i<call.length; i++) if(call[i].level == lv) {
call[i].paintCall(g,cpos,ypos,callwidth);
ypos += ch+call[i].height;
}
}
for(int i=0; i<call.length; i++) call[i].paintLinks(g);
paintDrag(g);
}
/**
* Pinta la l�nea discont�nua si se est� creando un enlace
*/
private void paintDrag(Graphics g) {
if(selecteddot == null || mousex == -1 || mousey == -1) return;
float dash[] = {4.0f,4.0f};
Graphics2D gd = (Graphics2D) g.create();
gd.setStroke(new BasicStroke(1.0f,2,0,10.0f,dash,0.0f));
gd.setColor(Color.black);
gd.drawLine(selecteddot.x, selecteddot.y, mousex, mousey);
}
/**
* Pinta un enlace
*/
public void paintLink(Graphics g, XfeditVariableDot orig,
XfeditVariableDot dest) {
if(orig == null || dest == null) return;
if(orig.isNull()) return;
if(dest.isNull()) return;
Color channelcolor = Color.black;
switch(orig.channel%4) {
case 0: channelcolor = Color.black; break;
case 1: channelcolor = Color.red; break;
case 2: channelcolor = Color.blue; break;
case 3: channelcolor = Color.green; break;
}
g.setColor(channelcolor);
if(orig.level == dest.level) {
int channel = orig.x + varwidth/2 + (orig.channel-counter/2)*3;
g.drawLine(orig.x, orig.y, channel, orig.y);
g.drawLine(channel, orig.y, channel, dest.y);
g.drawLine(channel, dest.y, dest.x, dest.y);
if(selecteddot != dest && selecteddot != orig) return;
g.drawLine(orig.x, orig.y-1, channel+1, orig.y-1);
g.drawLine(channel+1, orig.y-1, channel+1, dest.y-1);
g.drawLine(channel+1, dest.y-1, dest.x, dest.y-1);
}
else {
int channel1 = orig.x + varwidth/2 + (orig.channel-counter/2)*3;
int channel2 = totalheight - 25 - 3*orig.channel;
int channel3 = dest.x - varwidth/2 + (orig.channel-counter/2)*3;
g.drawLine(orig.x, orig.y, channel1, orig.y);
g.drawLine(channel1, orig.y, channel1, channel2);
g.drawLine(channel1, channel2, channel3, channel2);
g.drawLine(channel3, channel2, channel3, dest.y);
g.drawLine(channel3, dest.y, dest.x, dest.y);
if(selecteddot != dest && selecteddot != orig) return;
channel1++; channel2--; channel3++;
g.drawLine(orig.x, orig.y-1, channel1, orig.y-1);
g.drawLine(channel1, orig.y-1, channel1, channel2);
g.drawLine(channel1, channel2, channel3, channel2);
g.drawLine(channel3, channel2, channel3, dest.y);
g.drawLine(channel3, dest.y-1, dest.x, dest.y-1);
}
g.setColor(Color.black);
}
}