JGraphX - Want Entire Graph to be Uneditable - java

I couldn't find an answer in the manual, or on SO. I want to make a graph with JGraphX that displays some verices and edges, but I don't want the user to be able to move anything around, nor for those green edit boxes to appear on the vortexes or edges. It's just for display only.
I tried this modification of the "Hello World" example to no avail. Any suggestions?
package com.mxgraph.examples.swing;
import java.util.Hashtable;
import javax.swing.JFrame;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxConstants;
import com.mxgraph.view.mxGraph;
import com.mxgraph.view.mxStylesheet;
public class HelloWorld extends JFrame
{
/**
*
*/
private static final long serialVersionUID = -2707712944901661771L;
public HelloWorld()
{
super("Hello, World!");
mxGraph graph = new mxGraph();
Object parent = graph.getDefaultParent();
//my addition of a stylesheet, I used it on the first node to see if it mad
//a difference, it didn't regarding dragability//////////////////////////////
mxStylesheet stylesheet = graph.getStylesheet();
Hashtable<String, Object> style = new Hashtable<String, Object>();
style.put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE);
style.put(mxConstants.STYLE_OPACITY, 50);
style.put(mxConstants.STYLE_FONTCOLOR, "#774400");
style.put(mxConstants.STYLE_EDITABLE, false);
stylesheet.putCellStyle("ROUNDED", style);
//tried this too///////////////////////////////////////////////
graph.setCellsEditable(false);
graph.getModel().beginUpdate();
try
{
Object v1 = graph.insertVertex(parent, null, "Hello", 20, 20, 80,
30, "ROUNDED");
Object v2 = graph.insertVertex(parent, null, "World!", 240, 150,
80, 30);
graph.insertEdge(parent, null, "Edge", v1, v2);
}
finally
{
graph.getModel().endUpdate();
}
mxGraphComponent graphComponent = new mxGraphComponent(graph);
getContentPane().add(graphComponent);
}
public static void main(String[] args)
{
HelloWorld frame = new HelloWorld();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 320);
frame.setVisible(true);
}
}

You can disable the whole graph component mxGraphComponent:
graphComponent.setEnabled(false);

You could override isCellSelectable to prevent cell selection
mxGraph graph = new mxGraph() {
#Override
public boolean isCellSelectable(Object cell) {
if (cell != null) {
if (cell instanceof mxCell) {
mxCell myCell = (mxCell) cell;
if (myCell.isEdge())
return false;
}
}
return super.isCellSelectable(cell);
}
};

Related

Dialog box prevents background events

I'm trying to implement an Excel like functionality where a cell may have a comment, so on hover I want a pop up to appear containing the desired text. All is well until the pop up appears. The problem I'm facing is that when the pop up appears all the background events are disabled. When hovering at any other part of the grid, nothing happens if the pop up is on display (e.g. the header is unreachable). Although I specifically state dialog.setGlassEnabled(false);. As I've seen by chrome dev tools, the glass div that acts as a background is indeed not present. So why is this strange behavior? Is there something else? Below is the whole test case (in order to reproduce easily) and on separate parts is the code that I believe is of importance.
I'm using GWT 2.6 and GXT 3.1, but the component I'm using is the native component DialogBox of GWT, so I believe GXT is irrelevant here.
mainGrid.addHandler(new GridHoverPopUpHandler()
{
#Override
public void onHover(Element element)
{
dialog.hide();
if(element == null) return;
Element cell = mainGrid.getView().findCell(element);
if(cell != null && cell.hasClassName("comment-indicator"))
{
Label label = new Label("mpazingka " + (String)cell.getInnerText());
dialog.setText("Title of comment");
dialog.setWidget(label);
dialog.setPopupPosition(absoluteX, absoluteY);
dialog.show();
}
}
}, MouseMoveEvent.getType());
abstract class GridHoverPopUpHandler implements MouseOutHandler, MouseMoveHandler
{
protected int absoluteX;
protected int absoluteY;
private Element lastHoveredElement = null;
protected DialogBox dialog;
public abstract void onHover(Element element);
GridHoverPopUpHandler()
{
dialog = new DialogBox();
dialog.setGlassEnabled(false);
}
#Override
public void onMouseMove(MouseMoveEvent event)
{
Element curHoveredElement = null;
NativeEvent natev = event.getNativeEvent();
if (Element.is(natev.getEventTarget()))
{
curHoveredElement = Element.as(natev.getEventTarget());
}
if(lastHoveredElement == curHoveredElement)
{
return;
}
absoluteX = event.getClientX();
absoluteY = event.getClientY();
lastHoveredElement = curHoveredElement;
onHover(curHoveredElement);
}
#Override
public void onMouseOut(MouseOutEvent event)
{
//TODO: Pendig implementation
}
}
package com.test.client;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.google.gwt.cell.client.DateCell;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseMoveHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sencha.gxt.core.client.ValueProvider;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.widget.core.client.ContentPanel;
import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
import com.sencha.gxt.widget.core.client.grid.ColumnModel;
import com.sencha.gxt.widget.core.client.grid.Grid;
import com.sencha.gxt.widget.core.client.grid.GridView;
import com.sencha.gxt.widget.core.client.grid.GridViewConfig;
import com.sencha.gxt.widget.core.client.grid.GroupSummaryView;
import com.sencha.gxt.widget.core.client.grid.SummaryColumnConfig;
public class GridHoverExample implements IsWidget, EntryPoint {
private static final StockProperties props = GWT.create(StockProperties.class);
private ContentPanel root;
private ArrayList<String> comments;
private void rootInit() {
root = new ContentPanel();
root.setHeadingText("Locked Grid Sample");
root.setPixelSize(400, 300);
comments = new ArrayList();
comments.add("Stack_2");
}
#Override
public Widget asWidget() {
if (root == null) {
rootInit();
ColumnConfig<Stock, String> nameCol = new SummaryColumnConfig<Stock, String>(props.name(), 100, SafeHtmlUtils.fromTrustedString("<b>Company</b>"));
ColumnConfig<Stock, String> symbolCol = new SummaryColumnConfig<Stock, String>(props.symbol(), 100, "Symbol");
ColumnConfig<Stock, Double> changeCol = new SummaryColumnConfig<Stock, Double>(props.change(), 100, "Change");
ColumnConfig<Stock, String> industryCol = new SummaryColumnConfig<Stock, String>(props.industry(), 100, "Industry");
ColumnConfig<Stock, Date> dateCol = new SummaryColumnConfig<Stock, Date>(props.date(), 100, "Date");
dateCol.setCell(new DateCell(DateTimeFormat.getFormat("MM/dd/yyyy")));
List<ColumnConfig<Stock, ?>> ccFree = new ArrayList<ColumnConfig<Stock, ?>>();
ccFree.add(nameCol);
ccFree.add(symbolCol);
ccFree.add(changeCol);
ccFree.add(dateCol);
ccFree.add(industryCol);
ColumnModel<Stock> cm = new ColumnModel<Stock>(ccFree);
ListStore<Stock> store = new ListStore<Stock>(props.key());
for (int i = 1; i <= 100; i++)
store.add(new Stock("Stack_"+i, "S_"+i, 2, 2, new Date()));
final Grid<Stock> mainGrid = new Grid<Stock>(store, cm);
mainGrid.addHandler(new GridHoverPopUpHandler()
{
#Override
public void onHover(Element element)
{
dialog.hide();
if(element == null) return;
Element cell = mainGrid.getView().findCell(element);
if(cell != null && cell.hasClassName("comment-indicator"))
{
Label label = new Label("mpazingka " + (String)cell.getInnerText());
dialog.setText("Title of comment");
dialog.setWidget(label);
dialog.setPopupPosition(absoluteX, absoluteY);
dialog.show();
}
}
}, MouseMoveEvent.getType());
mainGrid.setView(createGridView());
root.setWidget(mainGrid);
}
return root;
}
#Override
public void onModuleLoad() {
RootPanel.get("nameFieldContainer").add(asWidget());
}
private GridView<Stock> createGridView()
{
final GroupSummaryView<Stock> view = new GroupSummaryView<Stock>();
view.setShowGroupedColumn(false);
view.setStripeRows(true);
view.setColumnLines(true);
view.setSortingEnabled(false);
view.setShowDirtyCells(false);
view.setViewConfig(new GridViewConfig<Stock>()
{
#Override
public String getRowStyle(Stock model, int rowIndex)
{
return "";
}
#Override
public String getColStyle(Stock model, ValueProvider<? super Stock, ?> valueProvider, int rowIndex, int colIndex)
{
String style = "";
if(model.getName().equals(comments.get(0)) && colIndex == 0)
{
style += "comment-indicator ";
}
style += "columnWidth";
return style;
}
});
return view;
}
}
abstract class GridHoverPopUpHandler implements MouseOutHandler, MouseMoveHandler
{
protected int absoluteX;
protected int absoluteY;
private Element lastHoveredElement = null;
protected DialogBox dialog;
public abstract void onHover(Element element);
GridHoverPopUpHandler()
{
dialog = new DialogBox();
dialog.setGlassEnabled(false);
}
#Override
public void onMouseMove(MouseMoveEvent event)
{
Element curHoveredElement = null;
NativeEvent natev = event.getNativeEvent();
if (Element.is(natev.getEventTarget()))
{
curHoveredElement = Element.as(natev.getEventTarget());
}
if(lastHoveredElement == curHoveredElement)
{
return;
}
absoluteX = event.getClientX();
absoluteY = event.getClientY();
lastHoveredElement = curHoveredElement;
onHover(curHoveredElement);
}
#Override
public void onMouseOut(MouseOutEvent event)
{
//TODO: Pendig implementation
}
}
Update
Ok I found it. This widget in order to manipulate the DOM for making the component dragable captures some events (mouse events). I didn't follow through because of time restrictions, so I used DecoratedPopupPanel which is the parent class of DialogBox and doesn't capture all those events that I need. I'll be waiting for a day or two, for an answer about the part that does this event capturing, and how I can disable it. If not provided I'm posting this as an answer to close the topic.
I think you may have done it overkill by using the dialog to done the functionality of tool tip. IMHO, What you should do was add a tooltip to the cell that you want to have the "excel comment" functionality. So whenever, user hover to that cell, it will show the tooltip loaded with data from your comment. Take a look at the Basic Grid example at here: http://www.sencha.com/examples/#ExamplePlace:basicgrid
Try to hover your mouse to Change cell, it will pop you a tool tip. The key point was to create your own grid cell implementation, like this :
ColumnConfig<Stock, Double> changeCol = new ColumnConfig<Stock, Double>(props.change(), 100, "Change");
changeCol.setCell(new AbstractCell<Double>() {
#Override
public void render(Context context, Double value, SafeHtmlBuilder sb) {
String style = "style='color: " + (value < 0 ? "red" : "green") + "'";
String v = number.format(value);
sb.appendHtmlConstant("<span " + style + " qtitle='Change' qtip='" + v + "'>" + v + "</span>");
}
});
If you need to use different type of tooltip, take a look at the tooltips example at here: http://www.sencha.com/examples/#ExamplePlace:tooltips
Hope this could help you.

GXT 3.x : Dynamic freeze/lock columns. Autosize the grids

I took the sample code from here How to implement freeze column in GXT 3.x? and I came up with the code below. I've managed to make the columns dynamically move from locked grid to unlocked grid and vice versa. My problem is with the sizing of the grids. The original code used a fixed width. I can't have that. I need the grids (locked and unlocked) to fill as much space as the children columns need. The columns have a fixed width (let's say 50px;).
The part that interests me is here
HorizontalLayoutContainer gridWrapper = new HorizontalLayoutContainer();
root.setWidget(gridWrapper);
// add locked column, only 300px wide (in this example, use layouts
// to change how this works
HorizontalLayoutData lockedColumnLayoutData = new HorizontalLayoutData(300, 1.0);
// this is optional - without this, you get a little offset issue at
// the very bottom of the non-locked grid
lockedColumnLayoutData.setMargins(new Margins(0, 0, XDOM.getScrollBarWidth(), 0));
gridWrapper.add(lockedGrid, lockedColumnLayoutData);
// add non-locked section, taking up all remaining width
gridWrapper.add(mainGrid, new HorizontalLayoutData(1.0, 1.0));
and maybe here
final Grid<Stock> lockedGrid = new Grid<Stock>(store, lockedCm) {
#Override
protected Size adjustSize(Size size) {
// this is a tricky part - convince the grid to draw just
// slightly too wide
// and so push the scrollbar out of sight
Window.alert("" + (size.getWidth() + XDOM.getScrollBarWidth() - 1));
return new Size(size.getWidth() + XDOM.getScrollBarWidth() - 1, size.getHeight());
}
};
I am a newbie in GXT and in GWT in general so I used the same components as the original
answer. If what I need to accomplish can be solved easier with something other than HorizontalLayoutContainer, then feel free to change it.
package com.test.client;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import com.google.gwt.cell.client.DateCell;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sencha.gxt.core.client.Style.ScrollDirection;
import com.sencha.gxt.core.client.ValueProvider;
import com.sencha.gxt.core.client.dom.XDOM;
import com.sencha.gxt.core.client.util.Margins;
import com.sencha.gxt.core.client.util.Size;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.widget.core.client.ContentPanel;
import com.sencha.gxt.widget.core.client.Resizable;
import com.sencha.gxt.widget.core.client.Resizable.Dir;
import com.sencha.gxt.widget.core.client.container.HorizontalLayoutContainer;
import com.sencha.gxt.widget.core.client.container.HorizontalLayoutContainer.HorizontalLayoutData;
import com.sencha.gxt.widget.core.client.event.BodyScrollEvent;
import com.sencha.gxt.widget.core.client.event.BodyScrollEvent.BodyScrollHandler;
import com.sencha.gxt.widget.core.client.event.CollapseEvent;
import com.sencha.gxt.widget.core.client.event.CollapseEvent.CollapseHandler;
import com.sencha.gxt.widget.core.client.event.ExpandEvent;
import com.sencha.gxt.widget.core.client.event.ExpandEvent.ExpandHandler;
import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
import com.sencha.gxt.widget.core.client.grid.ColumnModel;
import com.sencha.gxt.widget.core.client.grid.Grid;
import com.sencha.gxt.widget.core.client.grid.GridView;
import com.sencha.gxt.widget.core.client.grid.GridViewConfig;
import com.sencha.gxt.widget.core.client.grid.GroupSummaryView;
import com.sencha.gxt.widget.core.client.grid.SummaryColumnConfig;
import com.sencha.gxt.widget.core.client.grid.filters.GridFilters;
import com.sencha.gxt.widget.core.client.grid.filters.StringFilter;
import com.sencha.gxt.widget.core.client.menu.Item;
import com.sencha.gxt.widget.core.client.menu.Menu;
import com.sencha.gxt.widget.core.client.menu.MenuItem;
public class GridExample implements IsWidget, EntryPoint {
private static final StockProperties props = GWT.create(StockProperties.class);
private ContentPanel root;
private void rootInit() {
root = new ContentPanel();
root.setHeadingText("Locked Grid Sample");
root.setPixelSize(600, 300);
final Resizable resizable = new Resizable(root, Dir.E, Dir.SE, Dir.S);
root.addExpandHandler(new ExpandHandler() {
#Override
public void onExpand(ExpandEvent event) {
resizable.setEnabled(true);
}
});
root.addCollapseHandler(new CollapseHandler() {
#Override
public void onCollapse(CollapseEvent event) {
resizable.setEnabled(false);
}
});
}
#Override
public Widget asWidget() {
if (root == null) {
rootInit();
ColumnConfig<Stock, String> nameCol = new SummaryColumnConfig<Stock, String>(props.name(), 50, SafeHtmlUtils.fromTrustedString("<b>Company</b>"));
ColumnConfig<Stock, String> symbolCol = new SummaryColumnConfig<Stock, String>(props.symbol(), 100, "Symbol");
ColumnConfig<Stock, Double> lastCol = new SummaryColumnConfig<Stock, Double>(props.last(), 75, "Last");
ColumnConfig<Stock, Double> changeCol = new SummaryColumnConfig<Stock, Double>(props.change(), 100, "Change");
ColumnConfig<Stock, Date> lastTransCol = new SummaryColumnConfig<Stock, Date>(props.lastTrans(), 100, "Last Updated");
lastTransCol.setCell(new DateCell(DateTimeFormat.getFormat("MM/dd/yyyy")));
List<ColumnConfig<Stock, ?>> l = new ArrayList<ColumnConfig<Stock, ?>>();
//l.add(nameCol);
l.add(symbolCol);
l.add(lastCol);
l.add(changeCol);
l.add(lastTransCol);
// create two column models, one for the locked section
ColumnModel<Stock> lockedCm = new ColumnModel<Stock>(Collections.<ColumnConfig<Stock, ?>> singletonList(nameCol));
ColumnModel<Stock> cm = new ColumnModel<Stock>(l);
ListStore<Stock> store = new ListStore<Stock>(props.key());
for (int i = 0; i < 30; i++)
store.add(new Stock("Stackoverflow" + i, "StackoverflowPosts"+i, 0, 2, new Date()));
// locked grid
final Grid<Stock> mainGrid = new Grid<Stock>(store, cm);
final Grid<Stock> lockedGrid = new Grid<Stock>(store, lockedCm) {
#Override
protected Size adjustSize(Size size) {
// this is a tricky part - convince the grid to draw just
// slightly too wide
// and so push the scrollbar out of sight
return new Size(size.getWidth() + XDOM.getScrollBarWidth() - 1, size.getHeight());
}
};
GridFilters<Stock> filters = new GridFilters<Stock>();
filters.setLocal(true);
filters.initPlugin(mainGrid);
filters.initPlugin(lockedGrid);
StringFilter<Stock> nameFilter = new StringFilter<Stock>(props.name());
filters.addFilter(nameFilter);
lockedGrid.setView(createGridView(mainGrid, "Unfreeze", true));
mainGrid.setView(createGridView(lockedGrid, "Freeze", false));
// link scrolling
lockedGrid.addBodyScrollHandler(new BodyScrollHandler() {
#Override
public void onBodyScroll(BodyScrollEvent event) {
mainGrid.getView()
.getScroller()
.scrollTo(ScrollDirection.TOP, event.getScrollTop());
}
});
mainGrid.addBodyScrollHandler(new BodyScrollHandler() {
#Override
public void onBodyScroll(BodyScrollEvent event) {
lockedGrid
.getView()
.getScroller()
.scrollTo(ScrollDirection.TOP, event.getScrollTop());
}
});
HorizontalLayoutContainer gridWrapper = new HorizontalLayoutContainer();
root.setWidget(gridWrapper);
// add locked column, only 300px wide (in this example, use layouts
// to change how this works
HorizontalLayoutData lockedColumnLayoutData = new HorizontalLayoutData();
// this is optional - without this, you get a little offset issue at
// the very bottom of the non-locked grid
lockedColumnLayoutData.setMargins(new Margins(0, 0, XDOM.getScrollBarWidth(), 0));
gridWrapper.add(lockedGrid, lockedColumnLayoutData);
// add non-locked section, taking up all remaining width
gridWrapper.add(mainGrid, new HorizontalLayoutData(1.0, 1.0));
}
return root;
}
#Override
public void onModuleLoad() {
RootPanel.get().add(asWidget());
}
private GridView<Stock> createGridView(final Grid<Stock> targetGrid, final String menuText, final boolean isLocked)
{
final GroupSummaryView<Stock> view = new GroupSummaryView<Stock>()
{
{
if(isLocked)
scrollOffset = 0;
}
protected Menu createContextMenu(final int colIndex)
{
final Menu createContextMenu = super.createContextMenu(colIndex);
MenuItem lockItem = new MenuItem();
lockItem.setText(menuText);
lockItem.addSelectionHandler(new SelectionHandler<Item>()
{
#Override
public void onSelection(SelectionEvent<Item> event) {
//I'm making new column models since getColumns() can't be modified
ColumnConfig<Stock, ?> column = grid.getColumnModel().getColumn(colIndex);
List<ColumnConfig<Stock, ?>> newCm = new ArrayList<>(cm.getColumns());
newCm.remove(colIndex);
grid.reconfigure(grid.getStore(), new ColumnModel<>(newCm));
List<ColumnConfig<Stock, ?>> newTargetCm = new ArrayList<>(targetGrid.getColumnModel().getColumns());
newTargetCm.add(column);
targetGrid.reconfigure(targetGrid.getStore(), new ColumnModel<>(newTargetCm));
grid.getView().refresh(true);
targetGrid.getView().refresh(true);
}
});
createContextMenu.add(lockItem);
return createContextMenu;
}
};
view.setShowGroupedColumn(false);
view.setForceFit(false);
view.setStripeRows(true);
view.setColumnLines(true);
view.setViewConfig(new GridViewConfig<Stock>()
{
#Override
public String getRowStyle(Stock model, int rowIndex)
{
return "";
}
#Override
public String getColStyle(Stock model, ValueProvider<? super Stock, ?> valueProvider, int rowIndex, int colIndex)
{
return "";
}
});
return view;
}
}
I think you should do something along these lines:
Calculate the width of the locked table according the columns, then set it to lockedColumnLayoutData
and force gridWrapper to layout.
#Override
public void onSelection(SelectionEvent<Item> event)
{
....
double lockedTableWidth = 0;//calculate
lockedColumnLayoutData.setWidth(lockedTableWidth);
gridWrapper.forceLayout();
}

JXTreeTable, row striping and proper repainting

I'm using a look and feel which does row striping of tables by default. When I put in a JXTreeTable, I noticed that for some reason it didn't get the row striping automatically.
So I put in a workaround using Highlighter, but it looks like I get a repainting glitch:
It seems like JXTreeTable is specifically repainting only the boundaries of the text instead of the whole cell. I have been trying to catch this in the debugger to figure out why, but every time I switch between programs, the whole window repaints, so it's nearly impossible to catch this kind of thing.
JTable and JTree both behave sanely. This look and feel is one which paints the whole row of a JTree (like Quaqua and Synth), so maybe that has something to do with it too. Perhaps JXTreeTable has some kind of assumption that the look and feel won't paint the rows of the tree? If so, is there a way to work around that? It won't just be this look and feel which has the issue.
The code:
import org.jdesktop.swingx.JXTreeTable;
import org.jdesktop.swingx.decorator.AbstractHighlighter;
import org.jdesktop.swingx.decorator.ComponentAdapter;
import org.jdesktop.swingx.decorator.Highlighter;
import org.jdesktop.swingx.treetable.DefaultMutableTreeTableNode;
import org.jdesktop.swingx.treetable.DefaultTreeTableModel;
import org.jdesktop.swingx.treetable.TreeTableModel;
import org.trypticon.haqua.HaquaLookAndFeel;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
import java.awt.BorderLayout;
import java.awt.Component;
import java.util.Arrays;
public class TreeTableDemo2 implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new TreeTableDemo2());
}
#Override
public void run() {
try {
UIManager.setLookAndFeel(new HaquaLookAndFeel());
} catch (Exception e) {
throw new RuntimeException(e);
}
JFrame frame = new JFrame("Tree Table Demo");
frame.setLayout(new BorderLayout());
frame.add(createPanel(), BorderLayout.CENTER);
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public JPanel createPanel() {
JPanel panel = new JPanel(new BorderLayout());
TreeTableModel treeTableModel = new DummyTreeTableModel();
JXTreeTable treeTable = new FixedTreeTable(treeTableModel);
JScrollPane treeTableScroll = new JScrollPane(treeTable);
panel.add(treeTableScroll, BorderLayout.CENTER);
return panel;
}
private static class FixedTreeTable extends JXTreeTable {
private static final Highlighter oddRowHighlighter = new AbstractHighlighter() {
#Override
protected Component doHighlight(Component component, ComponentAdapter componentAdapter) {
if (componentAdapter.row % 2 != 0 &&
!componentAdapter.isSelected()) {
component.setBackground(UIManager.getColor("Table.alternateRowColor"));
}
return component;
}
};
public FixedTreeTable(TreeTableModel treeModel) {
super(treeModel);
// This hack makes it paint correctly after releasing the mouse, which is not quite good enough.
// getSelectionModel().addListSelectionListener(new ListSelectionListener() {
// #Override
// public void valueChanged(ListSelectionEvent e) {
// Rectangle repaintRange = getCellRect(e.getFirstIndex(), 0, true);
// repaintRange.add(getCellRect(e.getLastIndex(), 0, true));
// repaint(repaintRange);
// }
// });
}
#Override
public void updateUI() {
removeHighlighter(oddRowHighlighter);
super.updateUI();
// JTable does this striping automatically but JXTable's default renderer
// seems to ignore it, so JXTreeTable inherits this broken behaviour.
if (UIManager.get("Table.alternateRowColor") != null) {
addHighlighter(oddRowHighlighter);
}
}
}
private static class DummyTreeTableNode extends DefaultMutableTreeTableNode {
private final Object[] values;
private DummyTreeTableNode(String name) {
super(name);
values = new Object[5];
values[0] = name;
}
private DummyTreeTableNode(Object... values) {
super(values[0]);
this.values = values;
}
#Override
public Object getValueAt(int column) {
return values[column];
}
}
private static class DummyTreeTableModel extends DefaultTreeTableModel {
private static DefaultMutableTreeTableNode rootNode = new DefaultMutableTreeTableNode();
static {
DefaultMutableTreeTableNode blue = new DefaultMutableTreeTableNode("Blue");
blue.add(new DummyTreeTableNode("Orionis C", 33000, 30000.0, 18.0, 5.90));
rootNode.add(blue);
DefaultMutableTreeTableNode bluish = new DefaultMutableTreeTableNode("Bluish");
bluish.add(new DummyTreeTableNode("Becrux", 30000, 16000.0, 16.0, 5.70));
bluish.add(new DummyTreeTableNode("Spica", 22000, 8300.0, 10.5, 5.10));
bluish.add(new DummyTreeTableNode("Achernar", 15000, 750.0, 5.40, 3.70));
bluish.add(new DummyTreeTableNode("Rigel", 12500, 130.0, 3.50, 2.70));
rootNode.add(bluish);
DefaultMutableTreeTableNode blueWhite = new DefaultMutableTreeTableNode("Blue-White");
blueWhite.add(new DummyTreeTableNode("Sirius A", 9500, 63.0, 2.60, 2.30));
blueWhite.add(new DummyTreeTableNode("Fomalhaut", 9000, 40.0, 2.20, 2.00));
blueWhite.add(new DummyTreeTableNode("Altair", 8700, 24.0, 1.90, 1.80));
rootNode.add(blueWhite);
DefaultMutableTreeTableNode white = new DefaultMutableTreeTableNode("White");
white.add(new DummyTreeTableNode("Polaris A", 7400, 9.0, 1.60, 1.50));
white.add(new DummyTreeTableNode("Eta Scorpii", 7100, 6.3, 1.50, 1.30));
white.add(new DummyTreeTableNode("Procyon A", 6400, 4.0, 1.35, 1.20));
rootNode.add(white);
DefaultMutableTreeTableNode yellowWhite = new DefaultMutableTreeTableNode("Yellow-White");
yellowWhite.add(new DummyTreeTableNode("Alpha Centauri A", 5900, 1.45, 1.08, 1.05));
yellowWhite.add(new DummyTreeTableNode("The Sun", 5800, 100.0, 1.00, 1.00));
yellowWhite.add(new DummyTreeTableNode("Mu Cassiopeiae", 5600, 0.70, 0.95, 0.91));
yellowWhite.add(new DummyTreeTableNode("Tau Ceti", 5300, 0.44, 0.85, 0.87));
rootNode.add(yellowWhite);
DefaultMutableTreeTableNode orange = new DefaultMutableTreeTableNode("Orange");
orange.add(new DummyTreeTableNode("Pollux", 5100, 0.36, 0.83, 0.83));
orange.add(new DummyTreeTableNode("Epsilon Eridani", 4830, 0.28, 0.78, 0.79));
orange.add(new DummyTreeTableNode("Alpha Centauri B", 4370, 0.18, 0.68, 0.74));
rootNode.add(orange);
DefaultMutableTreeTableNode red = new DefaultMutableTreeTableNode("Red");
red.add(new DummyTreeTableNode("Lalande 21185", 3400, 0.03, 0.33, 0.36));
red.add(new DummyTreeTableNode("Ross 128", 3200, 0.0005, 0.20, 0.21));
red.add(new DummyTreeTableNode("Wolf 359", 3000, 0.0002, 0.10, 0.12));
rootNode.add(red);
}
private static final Object[] columnNames = {
"Star", "Temperature (K)", "Luminosity", "Mass", "Radius"
};
public DummyTreeTableModel() {
super(rootNode, Arrays.asList(columnNames));
}
#Override
public Class<?> getColumnClass(int columnIndex) {
if (columnIndex == 0) {
return String.class;
} else {
return Double.class;
}
}
#Override
public boolean isCellEditable(Object node, int column) {
return false;
}
}
}
Further investigation round 1:
I finally managed to trap the condition in the debugger by watching a paint method in JXTreeTable. What I see is that it has something called ClippedTreeCellRenderer which has some fields which look like they correspond to the suspicious behaviour:
iconRect = {java.awt.Rectangle#2946}"java.awt.Rectangle[x=20,y=92,width=16,height=16]"
textRect = {java.awt.Rectangle#2947}"java.awt.Rectangle[x=20,y=-17,width=62,height=15]"
itemRect = {java.awt.Rectangle#2948}"java.awt.Rectangle[x=20,y=36,width=103,height=18]"
I haven't yet confirmed that it is definitely using this value to paint the rectangle, but textRect is exactly the size of the small window it does repaint. So now the question is, where on earth is JXTreeTable pulling these values from and why is it using them?
My instinct tells me that JXTreeTable's renderer is somehow using the tree cell renderer to render the cell directly instead of just telling the tree itself to paint. The logic for painting the row backgrounds and the expand/collapse icons is in the tree, not in the cells, so if it's doing that, it would make sense that it isn't consistently painting the tree.
Further investigation round 2:
I think I was on the wrong path entirely. It looks like the whole row is being painted, but tree.isPathSelected(path) returns false for the newly-selected blue rows and returns true for the just-deselected row.
I can confirm by breakpoints in DefaultTreeSelectionModel that the tree selection is only updated after you let go of the mouse, which is why it eventually returns to being rendered correctly.
I'll have to dig further into JXTreeTable to see how it's keeping those in sync.
I found the bug. It looks like some well-meaning code in JXTreeTable.java which is trying to reduce updates:
/**
* Class responsible for calling updateSelectedPathsFromSelectedRows
* when the selection of the list changse.
*/
class ListSelectionHandler implements ListSelectionListener {
#Override
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
updateSelectedPathsFromSelectedRows();
}
}
}
If you remove that if check, everything works correctly. I'll just have to make some local changes to SwingX I guess, since the project is essentially dead. :(

How to use multiple layers in Piccolo2D?

I want t have some picture above another one and want to utilize PCamera's addLayer() method.
Is this possible?
The following code throws NullPointerException. What's wrong with it?
package test.piccolo;
import java.awt.Color;
import edu.umd.cs.piccolo.PCamera;
import edu.umd.cs.piccolo.PLayer;
import edu.umd.cs.piccolo.nodes.PPath;
import edu.umd.cs.piccolox.PFrame;
public class Try_Cameras_01 {
#SuppressWarnings("serial")
public static void main(String[] args) {
new PFrame() {
private PLayer layer1 = new PLayer();
private PLayer layer2 = new PLayer();
private PLayer layer3 = new PLayer();
private PCamera camera = new PCamera();
{
camera.addLayer(layer1);
camera.addLayer(layer2);
camera.addLayer(layer3);
}
#Override
public void initialize() {
getCanvas().setCamera(camera);
PPath redRectangle = PPath.createRectangle(0, 0, 100, 100);
redRectangle.setStrokePaint(Color.black);
redRectangle.setPaint(Color.red);
PPath greenRectangle = PPath.createRectangle(20, 20, 100, 100);
greenRectangle.setStrokePaint(Color.black);
greenRectangle.setPaint(Color.green);
PPath blueRectangle = PPath.createRectangle(40, 40, 100, 100);
blueRectangle.setStrokePaint(Color.black);
blueRectangle.setPaint(Color.blue);
layer1.addChild(redRectangle);
layer2.addChild(greenRectangle);
layer3.addChild(blueRectangle);
}
};
}
}
The problem is that when you set up a new camera it has no associated root. As a result, PCanvas.getRoot() returns null and there an NPE in one of the painting methods. Here is a basic Piccolo2D runtime structure:
Read more in Piccolo2D Patterns.
In your case you're missing a link to PRoot from a PCamera. Here is a simple fix:
private PCamera camera = new PCamera(); {
PRoot root = new PRoot();
root.addChild(camera);
camera.addLayer(layer1);
camera.addLayer(layer2);
camera.addLayer(layer3);
}
That results in:
For reference here is a copy from PUtil.createBasicScenegraph() that creates a basic camera.
public static PCamera createBasicScenegraph() {
final PRoot root = new PRoot();
final PLayer layer = new PLayer();
final PCamera camera = new PCamera();
root.addChild(camera);
root.addChild(layer);
camera.addLayer(layer);
return camera;
}

How to send one variable from one class to another for gui purpose?

I am using Eclipse/Java 7. I have a class, which gets some values from queries and saves them in the 2 dimensional array path. Then I have made another class which designs 2 linked blocks. What I want to do is get the values of path1[0] and path[2][0] etc from the first class and automatically place them in the designed blocks. Any ideas how to do it? Thanks in advance
Example
1st class (results from queries):
public class OntoQ extends JFrame {
public static void main(String[] args) {
String[][] path = new String[20][2];
int pathi = 0;
int pathj = 0;
....
2nd class (design):
import javax.swing.JFrame;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.view.mxGraph;
public class Design extends JFrame {
public Design() {
super("Test");
mxGraph graph = new mxGraph();
Object parent = graph.getDefaultParent();
graph.getModel().beginUpdate();
try
{
Object v1 = graph.insertVertex(parent, null, "path[1][0]", 20, 20, 80,
30);
Object v2 = graph.insertVertex(parent, null, "path[2][0]" , 240, 150,
80, 30);
graph.insertEdge(parent, null, "Edge", v1, v2);
}
finally
{
graph.getModel().endUpdate();
}
mxGraphComponent graphComponent = new mxGraphComponent(graph);
add(graphComponent);
}
public static void main(String[] args)
{
Design frame = new Design();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 320);
frame.setVisible(true);
}
}

Categories