The GraphControlPanel requires intimate knowledge about the GraphPanel class. This would be true if only to access and modify the Graph. But also note that when the GraphControlPanel changes properties of the Graph, Edge or Vertex objects, the GraphControlPanel must tell the GraphPanel to redraw the Graph. This dependency is complicated by the fact that the GraphPanel class is responsible for allowing the user to select particular Vertex and Edge objects. When a user selects a Vertex or Edge, the GraphPanel must inform the GraphControlPanel about the selection so that the controls may be modified to display the appropriate values. Java allows this type of interdependency, and for this situation it is unavoidable; however, it certainly makes the logic more difficult.
In general, whenever a control on the GraphControlPanel issues an event (except for the mainB button), the GraphControlPanel modifies the Graph accordingly and invokes the GraphPanel repaint() or fullPaint() method to redraw the Graph. Of course certain modifications only make sense if the user has selected an Edge or a Vertex, so for these events the GraphControlPanel checks whether an Edge or Vertex has been selected.
The GraphControlPanel methods data() and selected() allow the GraphPanel to maintain syncronization between the two panels. The data() method writes values from the GraphControlPanel controls to a particular Edge or Vertex. The GraphPanel uses the data() method whenever the user creates a new Vertex or Edge. The selected() method writes values from a Vertex or Edge to the controls on the GraphControlPanel. So whenever the user selects a Vertex or Edge, the GraphPanel uses the selected() method to update the GraphControlPanel.
One complication to this interdependency is that both classes have to be compiled together. Writing the classes at the same time would have made debugging difficult, but the GraphPanel class was written and debugged first without any references to the GraphControlPanel class.
Note: The setDirB() method handles a completely unrelated problem. Depending on the platform, the dirB which allows switching between the directed and undirected graph was sometimes labeled incorrectly. Apparently applets are initialized differently on various platforms. To handle this problem, the GraphPanel sets the label on the dirB button.
package GraphStuff;
import MyAwt.*;
import MyUtil.*;
import java.awt.*;
public class GraphControlPanel extends Panel {
private GraphPanel gP;
Panel buttonP = new InsetPanel(new Insets(8,0,0,4)),
centerP = new Panel(),
rightP = new Panel();
Button defaultB = new Button("defaults"),
dirB = new Button("directed"),
clearB = new Button("clear");
TextField labelTxF = new TextField(3);
Scrollbar gS = new Scrollbar(Scrollbar.HORIZONTAL,
General.defaultWeight,1,-50,50);
NumberLabelGroup NLgp = new NumberLabelGroup();
NumberLabel
weightNL = new NumberLabel(General.defaultWeight,-50,50,NLgp),
flowNL = new NumberLabel(General.defaultFlow,
0,General.defaultCapacity-1,NLgp),
capacityNL = new NumberLabel(General.defaultCapacity,
1,General.defaultCapacity,NLgp),
minimumNL = new NumberLabel(General.defaultMinimum,
0,General.defaultCapacity-1,NLgp),
demandNL = new NumberLabel(General.defaultDemand,
-General.defaultCapacity,
General.defaultCapacity,NLgp),
radiiNL = new NumberLabel(General.vor(),8,30,NLgp),
fontNL = new NumberLabel(General.fontSize(),
General.minFont,General.maxFont,NLgp),
colorNL = new NumberLabel(0,0,General.maxColor,NLgp);
Checkbox weightC = new Checkbox("weight",null,General.showWeights),
flowC = new Checkbox("flow",null,General.showFlows),
capacityC = new Checkbox("cap",null,General.showCapacities),
minimumC = new Checkbox("min",null,General.showMinimums),
labelC = new Checkbox("label",null,General.showLabels),
demandC = new Checkbox("demand",null,General.showDemands);
public GraphControlPanel(GraphPanel gp) {
this(gp,null);
}
public GraphControlPanel(GraphPanel gp ,Button mainB) {
gP = gp;
gP.addControl(this);
labelTxF.setEditable(true);
centerP.setLayout(new GridLayout(2,6,4,0));
centerP.add(weightC);
centerP.add(flowC);
centerP.add(capacityC);
centerP.add(minimumC);
centerP.add(demandC);
centerP.add(labelC);
centerP.add(weightNL);
centerP.add(flowNL);
centerP.add(capacityNL);
centerP.add(minimumNL);
centerP.add(demandNL);
centerP.add(labelTxF);
rightP.setLayout(new GridLayout(2,3));
rightP.add(new Label("color",Label.LEFT));
rightP.add(new Label("radii",Label.LEFT));
rightP.add(new Label("font",Label.LEFT));
rightP.add(colorNL);
rightP.add(radiiNL);
rightP.add(fontNL);
if (mainB!=null) {
buttonP.setLayout(new GridLayout(2,2,4,4));
buttonP.add(mainB);
} else {
buttonP.setLayout(new GridLayout(3,1,4,4));
}
buttonP.add(dirB);
buttonP.add(defaultB);
buttonP.add(clearB);
setLayout(new BorderLayout());
add("West",buttonP);
add("Center",centerP);
add("East",rightP);
add("South",gS);
CheckboxUpdate();
}
void displayItems(boolean weights,
boolean flows,
boolean capacities,
boolean minimums,
boolean labels,
boolean demands) {
weightC.setState(weights);
flowC.setState(flows);
capacityC.setState(capacities);
minimumC.setState(minimums);
labelC.setState(labels);
demandC.setState(demands);
CheckboxUpdate();
}
void updateVorRadii() {
if (General.vor()<8)
General.vor(8);
else if (General.vor()>30)
General.vor(30);
fontNL.value(General.fontSize());
radiiNL.value(General.vor());
}
void setDirB(String gT) {
dirB.setLabel(gT);
}
void CheckboxUpdate() {
General.showWeights=weightC.getState();
General.showFlows=flowC.getState();
General.showCapacities=capacityC.getState();
General.showMinimums=minimumC.getState();
General.showLabels=labelC.getState();
General.showDemands=demandC.getState();
if (weightC.getState()) {
weightNL.show();
} else {
weightNL.hide();
if (weightNL.selected()) {
weightNL.selected(false);
}
}
if (flowC.getState()) {
flowNL.show();
} else {
flowNL.hide();
if (flowNL.selected()) {
flowNL.selected(false);
}
}
if (capacityC.getState()) {
capacityNL.show();
} else {
capacityNL.hide();
if (capacityNL.selected()) {
capacityNL.selected(false);
}
}
if (minimumC.getState()) {
minimumNL.show();
} else {
minimumNL.hide();
if (minimumNL.selected()) {
minimumNL.selected(false);
}
}
if (labelC.getState()) {
labelTxF.show();
} else {
labelTxF.hide();
}
if (demandC.getState()) {
demandNL.show();
} else {
demandNL.hide();
if (demandNL.selected()) {
demandNL.selected(false);
}
}
}
public boolean handleEvent(Event evt) {
if (evt.target instanceof NumberLabel) {
if (evt instanceof SEvent) {
if ( ((SEvent)evt).select() ) {
gS.setValues(NLgp.curNL().value(),gS.getVisible(),
NLgp.curNL().min(),NLgp.curNL().max());
return true;
} else {
return true;
}
}
return false;
} else if (evt.target instanceof Scrollbar) {
int svalue = ((Scrollbar)(evt.target)).getValue();
if (NLgp.curNL()==null)
return false;
if (NLgp.curNL().value()==svalue)
return true;
NLgp.curNL().value(svalue);
if (NLgp.curNL()==radiiNL) {
General.vor(svalue);
gP.fullPaint();
return true;
} else if (NLgp.curNL()==fontNL) {
General.fontSize(svalue);
gP.fullPaint();
return true;
} else {
if ( gP.graph.beenSelected()
&& (gP.graph.selectedPart() instanceof Edge) ) {
Edge E = (Edge)(gP.graph.selectedPart());
if (NLgp.curNL()==weightNL) {
E.weight(svalue);
} else if(NLgp.curNL()==flowNL) {
E.flow(svalue);
} else if(NLgp.curNL()==minimumNL) {
E.minimum(svalue);
} else if(NLgp.curNL()==colorNL) {
E.colorNum(svalue);
} else if(NLgp.curNL()==capacityNL) {
E.capacity(svalue);
}
gP.modified(true);
gP.repaint();
return true;
} else if ( gP.graph.beenSelected()
&&(gP.graph.selectedPart() instanceof Vertex) ) {
Vertex V = (Vertex)(gP.graph.selectedPart());
if (NLgp.curNL()==demandNL) {
V.demand(svalue);
} else if(NLgp.curNL()==colorNL) {
V.colorNum(svalue);
}
gP.modified(true);
gP.repaint();
return true;
}
}
return false;
} else if (evt.target instanceof Button) {
if ( evt.target==dirB && evt.id==Event.ACTION_EVENT) {
if (dirB.getLabel().equals("directed"))
gP.currentGraph(gP.SimpleUndirected);
else gP.currentGraph(gP.Simple);
return true;
}else if ( evt.target==clearB && evt.id==Event.ACTION_EVENT) {
gP.deleteAll();
return true;
} else if (evt.target==defaultB && evt.id==Event.ACTION_EVENT) {
weightNL.value(General.defaultWeight);
capacityNL.value(General.defaultCapacity);
flowNL.value(General.defaultFlow);
minimumNL.value(General.defaultMinimum);
colorNL.value(General.defaultColorNum);
demandNL.value(General.defaultDemand);
if (gP.graph.beenSelected()) {
data(gP.graph.selectedPart());
gP.repaint();
}
return true;
}
return false;
} else if (evt.target instanceof Checkbox
&& evt.id==Event.ACTION_EVENT) {
CheckboxUpdate();
gP.fullPaint();
return true;
} else if (evt.target instanceof TextField
&& (evt.id==Event.ACTION_EVENT
|| evt.id==Event.KEY_RELEASE ) ){
if (gP.graph.beenSelected()
&&(gP.graph.selectedPart() instanceof Vertex) ) {
Vertex V = (Vertex)(gP.graph.selectedPart());
V.vName(((TextField)(evt.target)).getText());
gP.modified(true);
gP.repaint();
labelTxF.show();
return true;
}
return false;
}
return super.handleEvent(evt);
}
void data(Object x) {
if (x instanceof Edge) {
Edge E = (Edge)x;
E.weight(weightNL.value());
E.flow(flowNL.value());
E.capacity(capacityNL.value());
E.minimum(minimumNL.value());
E.colorNum(colorNL.value());
} else if (x instanceof Vertex) {
Vertex V = (Vertex)x;
V.demand(demandNL.value());
V.colorNum(colorNL.value());
}
if (NLgp.curNL()!=null) {
gS.setValues(NLgp.curNL().value(),gS.getVisible(),
NLgp.curNL().min(),NLgp.curNL().max());
}
}
void selected(Object x) {
if (x instanceof Edge) {
Edge E = (Edge)x;
weightNL.value(E.weight());
capacityNL.value(E.capacity());
flowNL.value(E.flow());
minimumNL.value(E.minimum());
colorNL.value(E.colorNum());
labelTxF.setText("");
} else if(x instanceof Vertex) {
Vertex V = (Vertex)x;
labelTxF.setText(V.vName());
demandNL.value(V.demand());
colorNL.value(V.colorNum());
}
if (NLgp.curNL()!=null) {
gS.setValues(NLgp.curNL().value(),gS.getVisible(),
NLgp.curNL().min(),NLgp.curNL().max());
}
}
}