Is it possible add multiple edges using TreeLayout? - java

I've to display a graph like a tree level in hierarchy so I use the TreeLayout included in Jung, but I don't know if it's possible use this layout to add multiple edges between nodes. And if it's not possible, how you recommend me to do that?
Thanks!
public class Visualizacion extends JApplet {
/**
* the graph
*/
Graph<String,String> graph;
Forest<String,String> tree;
Funciones f=new Funciones();
/**
* the visual component and renderer for the graph
*/
VisualizationViewer<String,String> vv;
String root;
Layout<String,String> layout;
Layout<String,String> layout2; ;
public Visualizacion(org.graphstream.graph.Graph grafito) {
graph= new DirectedSparseMultigraph<String, String>();
createTree(grafito);
MinimumSpanningForest2<String,String> prim =
new MinimumSpanningForest2<String,String>(graph,
new DelegateForest<String,String>(), DelegateTree.<String,String>getFactory(),
new ConstantTransformer(1.0));
tree = prim.getForest();
layout = new TreeLayout<String,String>(tree,100,100);
layout2 = new StaticLayout<String,String>(graph, layout);
Transformer<String,Paint>vertexPaint= new Transformer<String,Paint>(){
public Paint transform(String i){
Node node=grafito.getNode(i);
if(node.hasAttribute("ExcedeCaudal")){
return Color.RED;}
else{
return Color.lightGray;}
}
};
vv = new VisualizationViewer<String,String>(layout2, new Dimension(800,600));
vv.addGraphMouseListener(new TestGraphMouseListener<String>());
vv.setBackground(Color.white);
vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Line());
vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller());
vv.getRenderContext().setEdgeLabelTransformer(new ToStringLabeller());
vv.getRenderContext().setVertexShapeTransformer(new ClusterVertexShapeFunction());
vv.getRenderContext().setVertexFillPaintTransformer(vertexPaint);
// add a listener for ToolTips
vv.setVertexToolTipTransformer(new ToStringLabeller());
vv.getRenderContext().setArrowFillPaintTransformer(new ConstantTransformer(Color.lightGray));
vv.getRenderContext().setEdgeLabelTransformer(new EdgeLabelTransformer<String>());
Container content = getContentPane();
final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv);
content.add(panel);
final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse();
vv.setGraphMouse(graphMouse);
//ver cual nodo se selecciona
final PickedState<String> pickedState=vv.getPickedVertexState();
pickedState.addItemListener(new ItemListener(){
#Override
public void itemStateChanged(ItemEvent e){
Object subject=e.getItem();
if(subject instanceof String){
String vertice=(String)subject;
if(pickedState.isPicked(vertice)){
System.out.println("Vertice "+vertice+" está seleccionado");
}
else{
System.out.println("Vertice "+vertice+"no está seleccionado");
}
}
}
});
}private void createTree(org.graphstream.graph.Graph grafito) {
for(Node node:grafito){
graph.addVertex(node.getId());
}
int count=0;
for (Edge edge: grafito.getEachEdge()){
String padre=edge.getNode0().getId();
String hijo=edge.getNode1().getId();
String caudal=(edge.getAttribute("Caudal"));
graph.addEdge(caudal+"-"+count, padre,hijo,EdgeType.DIRECTED);
System.out.println("intento agregar "+edge.getAttribute("Caudal")+" cuyo padre es "+padre+" e hijo "+hijo);
count++;
}}private class EdgeLabelTransformer<V>implements Transformer<V,String>{
public String transform(V v){
return v.toString().split("-")[0];
}
}
class ClusterVertexShapeFunction<V> extends EllipseVertexShapeTransformer<V> {
ClusterVertexShapeFunction() {
setSizeTransformer(new ClusterVertexSizeFunction<V>(20));
}
#SuppressWarnings("unchecked")
#Override
public Shape transform(V v) {
if(v instanceof Graph) {
int size = ((Graph)v).getVertexCount();
if (size < 8) {
int sides = Math.max(size, 3);
return factory.getRegularPolygon(v, sides);
}
else {
return factory.getRegularStar(v, size);
}
}
return super.transform(v);
}
}
class ClusterVertexSizeFunction<V> implements Transformer<V,Integer> {
int size;
public ClusterVertexSizeFunction(Integer size) {
this.size = size;
}
public Integer transform(V v) {
if(v instanceof Graph) {
return 30;
}
return size;
}
}
static class TestGraphMouseListener<V> implements GraphMouseListener<V> {
public void graphClicked(V v, MouseEvent me) {
if(me.getClickCount()==2){
System.err.println("Vertex "+v+" fui doble click");
}
System.err.println("Vertex "+v+" was clicked at ("+me.getX()+","+me.getY()+")");
}
public void graphPressed(V v, MouseEvent me) {
System.err.println("Vertex "+v+" was pressed at ("+me.getX()+","+me.getY()+")");
}
public void graphReleased(V v, MouseEvent me) {
System.err.println("Vertex "+v+" was released at ("+me.getX()+","+me.getY()+")");
}
}
public void execute(org.graphstream.graph.Graph grafito){
JFrame frame = new JFrame();
Container content = frame.getContentPane();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
content.add(new Visualizacion(grafito));
frame.pack();
frame.setVisible(true);}}

JUNG Layout instances determine vertex positions, not edge positions; edge rendering is determined automatically based on the edge shape that you use and the number of connecting edges.
JUNG Tree objects, however, can only have one edge connecting any pair of vertices (otherwise it's not a tree).
So if you want a Graph whose vertices are laid out using TreeLayout, but which is not directly representable as a Tree, this is what you can do:
(1) Construct a Tree which encodes the structural relationships that you want (from your original Graph), but doesn't have the parallel edges you want. You can do this either directly yourself (by constructing a copy that removing parallel edges where they exist, or just not adding them initially if that's feasible), or by using the MinimumWeightSpanningTree algorithm to extract a Tree from your original Graph.
(2) Generate a TreeLayout for this Tree.
(3) Create a StaticLayout which copies the positions used by the TreeLayout.
(4) Use this StaticLayout as a layout algorithm for your original Graph.
You can see this process demonstrated in the JUNG MinimumSpanningTreeDemo.java, in the provided sample code that's part of the JUNG source distribution. If you just want to see the code, it's here: http://jung.sourceforge.net/site/jung-samples/xref/edu/uci/ics/jung/samples/MinimumSpanningTreeDemo.html

Related

Placeholder for TreeView?

I have a TreeView which is empty at the start and I want to set a placeholder until it is empty. Like the one available for ListView (setPlaceholder())
My first thought was to wrap the TreeView into a BorderPane and just change the center based on the number of elements in the TreeView. The problem is though that I add elements to the TreeView through drag and drop and if I set a label to the center for substituting a placeholder, I won't be able to drag n drop my items in the TreeView anymore. Any suggestions would be much appreciated.
TreeView has no placeholder support - not entirely certain, why not, but could be that an empty tree (whatever that means: null root? root without children? root not showing?) is a rare species.
It's rather simple to implement, though, by following the implementation for a virtualized control that supports it, f.i. TableView. All we need is a custom TreeViewSkin that
manages (creates and adds to the tree's hierarchy, layouts as needed) the placeholder
listens to relevant state of the tree and updates the placeholder's visibilty as appropriate
An example, toggling the emptyness by toggling the tree's root between null/not null via a button:
public class TreeViewWithPlaceholder extends Application {
private static class TreeViewPlaceholderSkin<T> extends TreeViewSkin<T> {
private StackPane placeholderRegion;
private Label placeholderLabel;
public TreeViewPlaceholderSkin(TreeView<T> control) {
super(control);
installPlaceholderSupport();
}
private void installPlaceholderSupport() {
registerChangeListener(getSkinnable().rootProperty(), e -> updatePlaceholderSupport());
updatePlaceholderSupport();
}
/**
* Updating placeholder/flow visibilty depending on whether or not the tree
* is considered empty.
*
* Basically copied from TableViewSkinBase.
*/
private void updatePlaceholderSupport() {
if (isTreeEmpty()) {
if (placeholderRegion == null) {
placeholderRegion = new StackPane();
placeholderRegion.getStyleClass().setAll("placeholder");
getChildren().add(placeholderRegion);
placeholderLabel = new Label("No treeItems");
placeholderRegion.getChildren().setAll(placeholderLabel);
}
}
getVirtualFlow().setVisible(!isTreeEmpty());
if (placeholderRegion != null)
placeholderRegion.setVisible(isTreeEmpty());
}
#Override
protected void layoutChildren(double x, double y, double w, double h) {
super.layoutChildren(x, y, w, h);
if (placeholderRegion != null && placeholderRegion.isVisible()) {
placeholderRegion.resizeRelocate(x, y, w, h);
}
}
private boolean isTreeEmpty() {
return getSkinnable().getRoot() == null;
}
}
private Parent createContent() {
TreeView<String> tree = new TreeView<>() {
#Override
protected Skin<?> createDefaultSkin() {
return new TreeViewPlaceholderSkin<>(this);
}
};
Button toggle = new Button("toggleRoot");
toggle.setOnAction(e -> {
TreeItem<String> root = tree.getRoot();
tree.setRoot(root == null ? new TreeItem<>("root") : null);
});
BorderPane content = new BorderPane(tree);
content.setBottom(toggle);
return content;
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
//stage.setTitle(FXUtils.version());
stage.show();
}
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(TreeViewWithPlaceholder.class.getName());
}

JGraphX new initialization of graph/graphComponent does not work as expected and causes artefacts

Hi I am using JGraphX to build some kind of Java swing based graph editor application. The application in general works fine and as intended under normal
circumstances. In general I have an class called Editor which contains all essential declarations for the graph, as seen in the code example below.
initializing the graph and overriding some of its methods
public class Editor extends JFrame implements Serializable {
Handler handler;
JTabbedPane tabPane;
mxGraphComponent graphComponent;
EntityDataTable dataTable;
protected static mxGraph graph = new mxGraph() {
// Overrides method to disallow edge label editing
public boolean isCellEditable(Object cell) {
if (cell instanceof mxCell) {
mxCell c = (mxCell) cell;
if (c.isEdge()) {
return false;
} else {
return false;
}
}
return false;
}
// Overrides method to disallow edge selection
public boolean isCellSelectable(Object cell)
{
if (model.isEdge(cell))
{
return false;
}
return super.isCellSelectable(cell);
}
// Overrides method to provide a cell label in the display
public String convertValueToString(Object cell) {
if (cell instanceof mxCell) {
Object value = ((mxCell) cell).getValue();
if (value instanceof Element) {
Element elt = (Element) value;
// String tag = elt.getTagName();
String tag = elt.getAttribute("name");
return tag;
}
}
return super.convertValueToString(cell);
}
public String getToolTipForCell(Object cell){
return "Double Click to Edit";
}
};
...
restricts certain undoEvents
protected mxEventSource.mxIEventListener undoHandler = new mxEventSource.mxIEventListener(){
public void invoke(Object source, mxEventObject evt)
{
mxUndoableEdit evt1 = (mxUndoableEdit) evt.getProperty("edit");
List<mxUndoableEdit.mxUndoableChange> changes = evt1.getChanges();
Object[] temp = graph.getSelectionCellsForChanges(changes);
boolean islegal = true;
for (int i = 0; i < temp.length; i++)
{
mxCell cell = (mxCell)temp[i];
String value = cell.getValue().toString();
if (value.equals("subprocess")||value.equals("optional")||value.equals("parallel")||value.equals("synchronous")||value.equals("activating")||value.equals("deactivating")){
//System.out.println("is not legal");
islegal = false;
}
}
for (int i = 0; i < changes.size(); i++){
if (changes.get(i).toString().contains("mxValueChange")){
islegal = false;
}
}
graph.setSelectionCells(graph.getSelectionCellsForChanges(changes));
if (islegal == true){
undoManager.undoableEditHappened((mxUndoableEdit) evt
.getProperty("edit"));
}else{
// System.out.println("illegal undo");
}
}};
...
protected boolean modified = false;
protected mxGraphOutline graphOutline;
protected JPanel actionPane;
mxUndoManager undoManager;
public Editor() {
handler = new Handler(this);
dataTable = new EntityDataTable(handler);
initGUI();
initGraphSettings();
}
public Editor(SaveData saveData) {
handler = new Handler(this);
dataTable = new EntityDataTable(handler);
initGUI();
initGraphSettings();
//erst alle entities erstellen und submitten, dann alle verbindungselemente zu den entities hinzufügen und nochmal submit
//Load entities
ArrayList<DataSaveElement> saveDataList = saveData.getSaveData(); for (int i = 0; i < saveDataList.size(); i++){
System.out.println("Loaded "+saveDataList.get(i).getType()+" "+saveDataList.get(i).getName());
if (saveDataList.get(i).getType().equals("Process")){
ProcessPopUp temp = new ProcessPopUp(handler, this);
temp.setGlobalID(saveDataList.get(i).getGlobalID());
temp.setName(saveDataList.get(i).getName());
temp.setDesc(saveDataList.get(i).getDescription());
temp.setType(saveDataList.get(i).getType());
... a lot of code for rebuilding, some graph settings, the gui etc.
Inside of the initGui() my mxGraphComponent is initialized
graphComponent = new mxGraphComponent(graph);
Since the graph visualization is only one part of the application and other data exists in the background, while saving all data values are stored including the postions for vertices etc. So when loading a save file a fresh application is build from the ground up simply adding all saved data values step by step. When I close the entire java application, start it again and load my saved file there is no problem at all. The problem occures when loading a saved file while the application is still running like e. g.
menuItem = new JMenuItem("Open...",
new ImageIcon("images/middle.gif"));
menuItem.addActionListener(new ActionListener() {
#java.lang.Override
public void actionPerformed(ActionEvent e) {
LoadAndSaveManager manager = new LoadAndSaveManager();
try {
Object o = manager.load(new FileChooser(0).getSelectedFile().getAbsolutePath());
SaveData saveData =(SaveData) o;
Editor editorNew = new Editor(saveData);
new MenuBar(editorNew);
editorNew.setVisible(true);
editor.dispose();
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
menu.add(menuItem);
My menubar is quite basic an gets the editor als parameter. Since a new Editor is created which creates a new mxGraph, as well as a new mxGraphComponent and finally disposes the old editor there should be no interference...at least as far as I know. However, despite having a new Editor instance which has its own new Graph and GraphComponent the old one is still used somehow. As shown in the example images below.
This will be saved and the application is closed completely.
Two nodes and a link for saving:
When starting the application and loading the saved data nothing is wrong.
Next I start a new one and add for example three nodes and two links.
Three nodes and two links:
Now I load the previously saved data. I would expect that the the window closes and a new one with the prevois data pops up. This is not the case. The data is loaded, but the old graph seems still active somehow and all nodes and links are on the graph.
Mixed up data:
If this would be the only problem I could simply clear the graph and add all "loading data" afterwards, however somehow the graphComponent seems to be broken as well. When dragging nodes the links sometimes are disrupted.
Disrupted links:
From my observations so far this seems to fix itself when selecting an area (I think this forces the graphComponent to refresh())
Selecting:
Unfortunately posting the entire code is not that much of an option, so I posted some code I think might be of importance for the problem. If further code is required I will specifically post it afterwards.
I am not sure why this happens and after hours of research I somehow hit a wall and and I am not sure what I am doing wrong. I would really appreciate some advice.
Here is an minimal full code example of the problem regarding the graphComponents interference problem when declaring a new one.
public class Main {
Editor editor;
public Main() {
editor = new Editor();
new MenuBar(editor);
editor.setVisible(true);
}
public static void main(String args[]) {
new Main();
}}
public class Editor extends JFrame {
mxGraphComponent graphComponent;
protected static mxGraph graph = new mxGraph() {
// Overrides method to disallow edge label editing
public boolean isCellEditable(Object cell) {
if (cell instanceof mxCell) {
mxCell c = (mxCell) cell;
if (c.isEdge()) {
return false;
} else {
return false;
}
}
return false;
}
// Overrides method to disallow edge selection
public boolean isCellSelectable(Object cell)
{
if (model.isEdge(cell))
{
return false;
}
return super.isCellSelectable(cell);
}
// Overrides method to provide a cell label in the display
public String convertValueToString(Object cell) {
if (cell instanceof mxCell) {
Object value = ((mxCell) cell).getValue();
if (value instanceof Element) {
Element elt = (Element) value;
// String tag = elt.getTagName();
String tag = elt.getAttribute("name");
return tag;
}
}
return super.convertValueToString(cell);
}
public String getToolTipForCell(Object cell){
return "Double Click to Edit";
}
};
public Editor() {
initGUI();
initGraphSettings();
}
public Editor(ArrayList<SaveDataElement> saveData) {
initGUI();
initGraphSettings();
//Load data
addToGraph(saveData);
}
public void initGUI(){
setExtendedState(JFrame.MAXIMIZED_BOTH);
setSize(new Dimension(1200, 900));
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
graphComponent = new mxGraphComponent(graph);
JPanel graphPanel = new JPanel(new BorderLayout());
graphPanel.add(graphComponent);
add(graphPanel);
}
public void initGraphSettings(){
Map<String, Object> style = graph.getStylesheet().getDefaultEdgeStyle();
style.put(mxConstants.STYLE_ALIGN, true);
style.put(mxConstants.STYLE_EDGE, mxConstants.EDGESTYLE_TOPTOBOTTOM);
graph.setCellsCloneable(false);
graphComponent.setConnectable(false);
graphComponent.getViewport().setBackground(Color.WHITE);
new mxRubberband(graphComponent);
}
public mxGraph getGraph(){
return graph;
}
public void addToGraph(ArrayList<SaveDataElement> saveData){
for (int i = 0; i < saveData.size(); i++) {
String name = saveData.get(i).getName();
int vertPosX = saveData.get(i).getPosX();
int vertPosY = saveData.get(i).getPosY();
new AddGraphNode("node", name, "rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;", vertPosX, vertPosY);
}
Object[] cells = graph.getChildVertices(graph.getDefaultParent());
Object startCell = null;
Object endCell = null;
for (int i = 0; i < saveData.size(); i++){
for (int j = 0; j < cells.length; j++){
if (((mxCell)cells[j]).getAttribute("name").equals(saveData.get(i).getName()))
startCell = cells[j];
for (int k = 0; k < saveData.get(i).getTargets().size(); k++){
if (((mxCell)cells[j]).getAttribute("name").equals(saveData.get(i).getTargets().get(k))){
endCell = cells[j];
new AddGraphLink(startCell, endCell,"Link", "endArrow=classic;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;verticalAlign=top;verticalLabelPosition=bottom'");
}
}
}
}
}}
public class MenuBar extends JMenuBar {
MenuBar(Editor editor){
JMenuBar menuBar = new JMenuBar();
JMenuItem menuItem;
JMenu menu = new JMenu("File");
menuBar.add(menu);
menuItem = new JMenuItem("Add");
menuItem.addActionListener(new ActionListener() {
#java.lang.Override
public void actionPerformed(ActionEvent e) {
//Setting up some data to create nodes and links
ArrayList<SaveDataElement> saveData = new ArrayList<SaveDataElement>();
ArrayList<String> targetsForTestX = new ArrayList<String>();
targetsForTestX.add("Test Y");
targetsForTestX.add("Test Z");
saveData.add(new SaveDataElement("Test X", 200, 300, targetsForTestX));
ArrayList<String> targetsForTestY = new ArrayList<String>();
saveData.add(new SaveDataElement("Test Y", 300, 420, targetsForTestY));
ArrayList<String> targetsForTestZ = new ArrayList<String>();
saveData.add(new SaveDataElement("Test Z", 100, 420, targetsForTestZ));
editor.addToGraph(saveData);
}
});
menu.add(menuItem);
menuItem = new JMenuItem("Load 1");
menuItem.addActionListener(new ActionListener() {
#java.lang.Override
public void actionPerformed(ActionEvent e) {
//Setting up some data to create nodes and links
ArrayList<SaveDataElement> saveData = new ArrayList<SaveDataElement>();
ArrayList<String> targetsForTest1 = new ArrayList<String>();
targetsForTest1.add("Test 2");
saveData.add(new SaveDataElement("Test 1", 40, 40, targetsForTest1));
ArrayList<String> targetsForTest2 = new ArrayList<String>();
saveData.add(new SaveDataElement("Test 2", 200, 40, targetsForTest2));
Editor editorNew = new Editor(saveData);
new MenuBar(editorNew);
editorNew.setVisible(true);
editor.dispose();
}
});
menu.add(menuItem);
editor.setJMenuBar(menuBar);
}}
public class SaveDataElement {
String name;
int posX, posY;
ArrayList<String> targets;
public SaveDataElement(String name, int posX, int posY, ArrayList<String> targets){
this.name = name;
this.posX = posX;
this.posY = posY;
this.targets = targets;
}
public String getName() {
return name;
}
public int getPosX() {
return posX;
}
public int getPosY() {
return posY;
}
public ArrayList<String> getTargets() {
return targets;
}}
public class AddGraphNode extends Editor {
public AddGraphNode(String tag, String name, String style, int vertPosX, int vertPoxY){
this.getGraph().getModel().beginUpdate();
Object parent = this.getGraph().getDefaultParent();
Document doc = mxDomUtils.createDocument();
Element entity = doc.createElement(tag);
entity.setAttribute("name", name);
try
{
Object v1 = this.getGraph().insertVertex(parent, "1",entity, vertPosX, vertPoxY, (int)(name.length()*8) ,
40, style);
}
finally
{
this.getGraph().getModel().endUpdate();
}
}}
public class AddGraphLink extends Editor{
public AddGraphLink(Object v1, Object v2, String relation, String style){
this.getGraph().getModel().beginUpdate();
Object parent = this.getGraph().getDefaultParent();
try
{
this.getGraph().insertEdge(parent, null, relation, v1, v2,style);
}
finally
{
this.getGraph().getModel().endUpdate();
}
}}
When using the the add menu item some nodes and links are added to the graph and with the load 1 menu item a new Editor will be created (creating a new graph and graphComponent). However, the added nodes and links are still present on the new component.
Regarding the visually broken links I mentioned above, this does not occur...I will investigate this further. Despite that this might also be connected with the graphComponent problem.
I see 3 major problems in your code:
Inappropriate use of a static field: protected static mxGraph graph = new mxGraph() {
Inappropriate inheritance: public class AddGraphLink extends Editor {
And again, inappropriate inheritance: class AddGraphNode extends Editor {
By making the graph field static, changes made to one variable will be felt in all variables, and this is the likely cause for your so-called "artifacts". And the reason that you feel that you must make the field static is because since the two classes above are inheriting from Editor (again inappropriately). The solution is obvious:
Make the graph field an instance field, not a static one, and
Don't use inheritance where it doesn't belong. Instead your AddGraphXxxx classes should not extend Editor but rather should have Editor fields within them, one that you can set via the constructor, and one whose methods you can call, something like this, for example:
// protected static mxGraph graph = new mxGraph() { //!! **** NO ****
private mxGraph graph = new mxGraph() { //!! **** YES ****
.....
.....
public class AddGraphNode {
public AddGraphNode(Editor editor, String tag, String name, String style, int vertPosX, int vertPoxY) {
// **** note use of the editor parameter below ****
editor.getGraph().getModel().beginUpdate();
Object parent = editor.getGraph().getDefaultParent();
Document doc = mxDomUtils.createDocument();
Element entity = doc.createElement(tag);
entity.setAttribute("name", name);
try {
// **** same here ****
Object v1 = editor.getGraph().insertVertex(parent, "1", entity, vertPosX, vertPoxY,
(int) (name.length() * 8), 40, style);
} finally {
// **** and the same here ****
editor.getGraph().getModel().endUpdate();
}
}
}
and you would create this instance within Editor by passing in the this parameter:
new AddGraphNode2(this, "node", name,
"rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;",
vertPosX, vertPosY);
You would make simiar changes to the AddGraphLink class.
Side note, please check out the The Use of Multiple JFrames, Good/Bad Practice? to see why swapping JFrames is not the best program design, and how you can change the code to improve its structure and user experience.

Override method that toggles 'isSelected' in getListCellRendererComponent

I would like to make my own method that controles when a component is 'isSelected'.
I have a JList containing multiple JPanel. The constructing class of the JPanel extends ListCellRenderer<>.
To show that one of the JList-component (the JPanels) is selected i use;
#Override
public Component getListCellRendererComponent(..., boolean isSelected, ...) {
if(isSelected){
setBackground(list.getSelectionBackground());
setForeground(list.getSelectionForeground());
} else {
setBackground(list.getBackground());
setForeground(list.getForeground());
}
return this;
}
I would like a method that keeps a selected item 'selected' eventhough I choose to select another. I understand this can be done by holding down CTRL, but .setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); does not quite do the trick. I would rather like to select multiple by clicking on them, and deselect by clicking on them.
For this i have worked with the ListSelectionMode, but i cant find a way.
When done the above I would like to implement a method that only selects a component in the list when clicked in a certain area (instead of the whole component which is preset). I have made this method, which returns true if the correct area is clicked, else false. But since I cant figure out how to override the mouseevent that makes the components 'isSelected' this has been tricky.
Here is the code for the method I would like to override the 'isSelected' method;
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent evt) {
if(ActionHandler.mouseClickedPrebuild(evt.getPoint())){
//This code runs if that special place is clicked!
//So now the component should be 'isSelected' or
//deselected if it already was 'isSelected'.
}
}
});
This code is in the constructor of my JList
And the mouseClickedPrebuild method;
public static boolean mouseClickedPrebuild(Point point) {
int index = theJList.locationToIndex(point);
Rectangle bounds = theJList.getCellBounds(index,index);
Point p = bounds.getLocation();
return ( ... long list of greater than & less than ...);
//This gives the certain area which is accepted to return true
I solved the issue!
So I get my view showing by running this line;
// UI Class JScrollPane Custom JList
UIConstructor.listview.setViewportView(new ListView( -insert ArrayList here- ));
Here is my ListView. The custom DefaultListSelectionModel I used to solve my problem was posted by #FuryComptuers right here;
JList - deselect when clicking an already selected item
I had to make a few changes to the code, since the two methods in the selectionModel will run before my mouseevent. I saved the variabels staticly, so instead of running the code in setSelectionInterval I did it inside my mousePressed.
I then could add the boolean isSelected which returns true, if a curtain area within a specific list element is clicked.
public class ListViewd extends JList {
static boolean isSelected;
static Point point;
static boolean gS = false;
static int in0;
static int in1;
#Override
public Dimension getPreferredScrollableViewportSize() {
Dimension size = super.getPreferredScrollableViewportSize();
size.setSize(new Dimension(0,0));
return size;
}
public ListView(ArrayList<System> items) {
DefaultListModel<System> list = new DefaultListModel<System>();
for (System item : items) {
list.addElement(item);
}
this.setSelectionModel(new DefaultListSelectionModel() {
boolean gestureStarted = false;
#Override
public void setSelectionInterval(int index0, int index1) {
gS = gestureStarted;
in0 = index0;
in1 = index1;
gestureStarted = true;
}
#Override
public void setValueIsAdjusting(boolean isAdjusting) {
if (!isAdjusting) {
gestureStarted = false;
}
}
});
ListSelectionModel selectionModel = this.getSelectionModel();
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
point = e.getPoint();
isSelected = ActionHandler.mouseClickedPrebuild(point);
if(!gS && isSelected){
if (isSelectedIndex(in0)) {
selectionModel.removeSelectionInterval(in0, in1);
} else {
selectionModel.addSelectionInterval(in0, in1);
}
}
}
});
setModel(list);
setCellRenderer(new ListModelPrebuild());
}

JUNG : Changing the vertex colour

I have used JUNG library to visualize one network consisted of servers and links through graph.
So I initialize my graph like this, Server and Link are classes in my project:
Graph<Server, Link> g;
g = new SparseMultigraph<Server, Link>();
Now I need to change the color of specific vertices..
so I used the code below:
// Setup up a new vertex to paint transformer
Transformer<Integer,Paint> vertexPaint = new Transformer<Integer,Paint>() {
public Paint transform(Integer i) {
return Color.GREEN;
}
};
vv.getRenderContext().setVertexFillPaintTransformer(vertexPaint);
I got java.lang.ClassCastException exception as following!!
Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: network.Server cannot be cast to java.lang.Integer
at GUI.GUI$9.transform(GUI.java:1)
at edu.uci.ics.jung.visualization.renderers.BasicVertexRenderer.paintShapeForVertex(BasicVertexRenderer.java:98)
at edu.uci.ics.jung.visualization.renderers.BasicVertexRenderer.paintIconForVertex(BasicVertexRenderer.java:74)
at edu.uci.ics.jung.visualization.renderers.BasicVertexRenderer.paintVertex(BasicVertexRenderer.java:37)
at edu.uci.ics.jung.visualization.renderers.BasicRenderer.renderVertex(BasicRenderer.java:70)
at edu.uci.ics.jung.visualization.renderers.BasicRenderer.render(BasicRenderer.java:55)
at edu.uci.ics.jung.visualization.BasicVisualizationServer.renderGraph(BasicVisualizationServer.java:367)
at edu.uci.ics.jung.visualization.BasicVisualizationServer.paintComponent(BasicVisualizationServer.java:321)
at javax.swing.JComponent.paint(Unknown Source)
My complete code to update the graph is here:
private static BasicVisualizationServer updateGraph(Network network) {
Set<Server> servers = network.getServers();
Set<Link> links = network.getLinks();
Graph<Server, Link> g;
// Graph<V, E> where V is the type of the vertices and E is the type of
// the edges
g = new SparseMultigraph<Server, Link>();
// Add some vertices and edges
for (Server server : servers) {
g.addVertex(server);
}
int i = 0;
for (Link link : links) {
g.addEdge(link, link.getSource(), link.getDest(), EdgeType.DIRECTED);
i++;
}
// Layout<V, E>, VisualizationViewer<V,E>
Layout<Integer, String> layout = new CircleLayout(g);
layout.setSize(new Dimension(300, 300));
VisualizationViewer<Integer, String> vv = new VisualizationViewer<Integer, String>(
layout);
vv.setPreferredSize(new Dimension(350, 350));
// Show vertex and edge labels
vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller());
vv.getRenderContext().setEdgeLabelTransformer(new ToStringLabeller());
// Setup up a new vertex to paint transformer...
Transformer<Integer,Paint> vertexPaint = new Transformer<Integer,Paint>() {
public Paint transform(Integer i) {
return Color.GREEN;
}
};
vv.getRenderContext().setVertexFillPaintTransformer(vertexPaint);
// Create a graph mouse and add it to the visualization component
DefaultModalGraphMouse gm = new DefaultModalGraphMouse();
gm.setMode(ModalGraphMouse.Mode.TRANSFORMING);
vv.setGraphMouse(gm);
rightPanel.setTopComponent(vv);
//Detecting selection of vertices
final PickedState<Integer> pickedState = vv.getPickedVertexState();
// Attach the listener that will print when the vertices selection changes.
pickedState.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent e) {
Object subject = e.getItem();
// The graph uses Integers for vertices.
if (subject instanceof Integer) {
Integer vertex = (Integer) subject;
if (pickedState.isPicked(vertex)) {
System.out.println("Vertex " + vertex
+ " is now selected");
} else {
System.out.println("Vertex " + vertex
+ " no longer selected");
}
}
}
});
return vv;
}
Your graph vertex type is Server, but your Transformer is expecting an Integer as input. You need to alter your code so that those match.

Java SWT - listen if new children are added to the Composite?

I have a Composite.
In SWT the children are added, by children, no by parents:
Composite parent = new Composite(shell, SWT.none);
Composite child = new Composite(parent, SWT.none);
That means, that inside of the parent I do not know, in which way and when the children are added to their parents.
QUESTION:
Inside of the parent I need to know, when new children are added to the parent.
Is there any way to register an onChildAddListener or any add() method to override?
The only way to add custom MyComposite and create custom ChildEvent as well as ChildEventListener interface. MyComposite is capable of registering listeners of ChildEvent and fire this event when a child Composite is created.
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.TypedEvent;
import org.eclipse.swt.internal.SWTEventListener;
import org.eclipse.swt.widgets.*;
/*
* This example demonstrates the minimum amount of code required
* to open an SWT Shell and process the events.
*/
public class HelloWorld1 {
public static void main (String [] args) {
Display display = new Display ();
Shell shell = new HelloWorld1 ().open (display);
while (!shell.isDisposed ()) {
if (!display.readAndDispatch ()) display.sleep ();
}
display.dispose ();
}
public Shell open (Display display) {
Shell shell = new Shell (display);
MyComposite parent = new MyComposite(shell, SWT.NONE);
parent.addChildEventListener(new ChildEventListener() {
#Override
public void add(ChildEvent e) {
System.out.println("Child added.");
}
});
MyComposite child = new MyComposite(parent, SWT.NONE);
// shell.open ();
return shell;
}
}
class MyComposite extends Composite {
ChildEventListener[] childEventListeners = new ChildEventListener[0];
public MyComposite(Composite parent, int style) {
super(parent, style);
if (parent instanceof MyComposite){
((MyComposite)parent).fireChildEvent(new ChildEvent(this));
}
}
public void addChildEventListener (ChildEventListener listener) {
checkWidget();
if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
ChildEventListener[] newChildEventListeners = new ChildEventListener[childEventListeners.length + 1];
System.arraycopy(childEventListeners, 0, newChildEventListeners, 0, childEventListeners.length);
childEventListeners = newChildEventListeners;
childEventListeners[childEventListeners.length - 1] = listener;
}
public void removeChildEventListener (ChildEventListener listener) {
if (childEventListeners.length == 0) return;
int index = -1;
for (int i = 0; i < childEventListeners.length; i++) {
if (listener == childEventListeners[i]){
index = i;
break;
}
}
if (index == -1) return;
if (childEventListeners.length == 1) {
childEventListeners = new ChildEventListener[0];
return;
}
ChildEventListener[] newChildEventListeners = new ChildEventListener[childEventListeners.length - 1];
System.arraycopy (childEventListeners, 0, newChildEventListeners, 0, index);
System.arraycopy (childEventListeners, index + 1, newChildEventListeners, index, childEventListeners.length - index - 1);
childEventListeners = newChildEventListeners;
}
public void fireChildEvent(ChildEvent event){
for (int i = 0; i < childEventListeners.length; i++) {
childEventListeners[i].add (event);
}
}
}
interface ChildEventListener extends SWTEventListener {
public void add(ChildEvent e);
}
class ChildEvent extends TypedEvent {
public ChildEvent(Widget widget) {
super(widget);
// TODO Auto-generated constructor stub
}
}
The purpose for which I wanted to know about child addition - is for layoing out the children
(adding LayoutData e.g. GridData) when the child is added to a child, which is layed out with a particular Layout (e.g. GridLayout).
Since it is not possible to layout children on addition - check their layout on every widget resize:
/*
* To make the forms fill the group - laying out the children is necessary.
* Unfortunately it is not possible to listen for children addition, in order to layout the children addition automatically.
* This means that the user will have to remember to layout the children, which is a nogo.
*
* Solution:
* istead of adding right layout on child addition - check if there are new children every time the widget is resized.
* if a widget without layoutData is found (new child not layed out) - add a layout
*/
this.addControlListener(new ControlAdapter() {
#Override
public void controlResized(ControlEvent e) {
layoutChildren();
}
});
}
private void layoutChildren(){
for(Control child:getChildren()){
if(child.getLayoutData() == null){
child.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
}
}
}
public void addToGroup(Composite child){
child.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
}

Categories

Resources