Eclipse RCP - Combining table rows by drag and drop the arrows - eclipse-rcp

I am new be in RCP development.
I want to create two tables with, each table contains different data.
Data from two tables have either 1 to 1 , 1 to many or many to 1 relationship.
And that can be done by drawing arrows between two tables.
For example,
**Row 1** **Row 2**
R1 V1 R2 V1
R1 V2 R2 V2
R1 V3 R2 V3
I want to draw arrows from R1V1 to ( R2V1 and R2V3 ) or vice a versa.
How can I show it graphically.
How can I find that which rows are combined by arrows.
Any help is appreciated.
--- Mandar

Here is the code that is based on the idea proposed by Nick. It is just to give an idea for someone who might wonder where to start to implement something like this shown below
This would let you click on any column on the left hand side table, then draws a line as your mouse moves on towards the right table, and anchors the line as soon as a column on the right hand side table is selected. It keeps a mapping between the left table row and right table row in a linked list as the mapping data model.
package sample;
import java.util.LinkedList;
import org.eclipse.draw2d.AutomaticRouter;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.FreeformLayeredPane;
import org.eclipse.draw2d.FreeformLayout;
import org.eclipse.draw2d.LightweightSystem;
import org.eclipse.draw2d.MarginBorder;
import org.eclipse.draw2d.PolylineConnection;
import org.eclipse.draw2d.PolylineDecoration;
import org.eclipse.draw2d.XYAnchor;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
public class GraphicTableMapper {
private static Point sourcePosition;
private static PathFigure currentPath;
private static Figure bf;
private static Canvas canvas;
private static int sourceRow;
private static int targetRow;
private static LinkedList<RowMapper> rowmapList = new LinkedList<RowMapper>();
public static void main(String[] args) {
Display display = Display.getDefault();
final Shell shell = new Shell(display);
shell.setSize(550, 500);
shell.setLayout(new GridLayout(3, false));
final Table table = new Table(shell, SWT.MULTI | SWT.BORDER
| SWT.FULL_SELECTION);
table.setLinesVisible(true);
table.setHeaderVisible(true);
final String[] titles = { "Serial Number", "Whatever" };
for (int i = 0; i < titles.length; i++) {
TableColumn column = new TableColumn(table, SWT.NONE);
column.setText(titles[i]);
}
int count = 100;// create 100 rows in table
for (int i = 0; i < count; i++) {
TableItem item = new TableItem(table, SWT.NONE);
item.setText(0, "x");
item.setText(1, "y");
item.setText(2, "!");
item.setText(3, "this stuff behaves the way I expect");
item.setText(4, "almost everywhere");
item.setText(5, "some.folder");
item.setText(6, "line " + i + " in nowhere");
}
for (int i = 0; i < titles.length; i++) {
table.getColumn(i).pack();
}
table.addListener(SWT.MouseDown, new Listener() {
public void handleEvent(Event event) {
Point pt = new Point(event.x, event.y);
TableItem item = table.getItem(pt);
if (item == null)
return;
for (int i = 0; i < titles.length; i++) {
Rectangle rect = item.getBounds(i);
if (rect.contains(pt)) {
int index = table.indexOf(item);
System.out.println("Item " + index + "-" + i);
sourcePosition = pt;
sourceRow = index;
currentPath = new PathFigure();
currentPath.setSourceAnchor(new XYAnchor(
new org.eclipse.draw2d.geometry.Point(-10,
event.y)));
currentPath
.setTargetAnchor(new XYAnchor(
new org.eclipse.draw2d.geometry.Point(
0, pt.y)));
bf.add(currentPath);
}
}
}
});
table.addMouseMoveListener(new MouseMoveListener() {
public void mouseMove(MouseEvent arg0) {
if (currentPath != null) {
((XYAnchor) (currentPath.getTargetAnchor()))
.setLocation(new org.eclipse.draw2d.geometry.Point(
0, arg0.y));
}
}
});
canvas = new Canvas(shell, SWT.None);
canvas.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_GREEN));
LightweightSystem lws = new LightweightSystem(canvas);
bf = new BaseFigure();
lws.setContents(bf);
canvas.addMouseMoveListener(new MouseMoveListener() {
public void mouseMove(MouseEvent arg0) {
if (currentPath != null) {
((XYAnchor) (currentPath.getTargetAnchor()))
.setLocation(new org.eclipse.draw2d.geometry.Point(
arg0.x > canvas.getSize().x - 5 ? canvas
.getSize().x - 5 : arg0.x, arg0.y));
}
}
});
GridData data2 = new GridData();
data2.verticalAlignment = SWT.TOP;
data2.grabExcessHorizontalSpace = false;
data2.grabExcessVerticalSpace = true;
data2.horizontalIndent = -10;
data2.widthHint = 200;
data2.heightHint = 1000;
canvas.setLayoutData(data2);
final Table table2 = new Table(shell, SWT.MULTI | SWT.BORDER
| SWT.FULL_SELECTION);
table2.setLinesVisible(true);
table2.setHeaderVisible(true);
data2 = new GridData();
data2.grabExcessHorizontalSpace = false;
data2.horizontalIndent = -10;
table2.setLayoutData(data2);
final String[] titles2 = { "Serial Number", "Whatever" };
for (int i = 0; i < titles.length; i++) {
TableColumn column = new TableColumn(table2, SWT.NONE);
column.setText(titles[i]);
canvas.redraw();
}
table2.addMouseMoveListener(new MouseMoveListener() {
public void mouseMove(MouseEvent event) {
if (currentPath != null) {
Point pt = new Point(event.x, event.y);
TableItem item = table2.getItem(pt);
if (item == null)
return;
for (int i = 0; i < titles2.length; i++) {
Rectangle rect = item.getBounds(i);
if (rect.contains(pt)) {
((XYAnchor) (currentPath.getTargetAnchor()))
.setLocation(new org.eclipse.draw2d.geometry.Point(
canvas.getSize().x - 5, event.y));
}
}
}
}
});
int count2 = 100;// create 100 rows in table 2
for (int i = 0; i < count2; i++) {
TableItem item = new TableItem(table2, SWT.NONE);
item.setText(0, "x");
item.setText(1, "y");
item.setText(2, "!");
item.setText(3, "this stuff behaves the way I expect");
item.setText(4, "almost everywhere");
item.setText(5, "some.folder");
item.setText(6, "line " + i + " in nowhere");
}
for (int i = 0; i < titles.length; i++) {
table2.getColumn(i).pack();
}
table2.addListener(SWT.MouseDown, new Listener() {
public void handleEvent(Event event) {
try {
Point pt = new Point(event.x, event.y);
TableItem item = table2.getItem(pt);
if (item == null)
return;
for (int i = 0; i < titles2.length; i++) {
Rectangle rect = item.getBounds(i);
if (rect.contains(pt)) {
int index = table2.indexOf(item);
targetRow = index;
System.out.println("Item " + index + "-" + i);
if (sourcePosition != null) {
add(event);
}
}
}
} finally {
sourcePosition = null;
sourceRow = -1;
targetRow = -1;
}
}
});
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
}
public static void add(Event event) {
bf.remove(currentPath);
PathFigure figure = new PathFigure();
figure.setSourceAnchor(currentPath.getSourceAnchor());
figure.setTargetAnchor(currentPath.getTargetAnchor());
bf.add(figure);
currentPath = null;
RowMapper mapper = new RowMapper();
mapper.sourceRow = sourceRow;
mapper.targetRow = targetRow;
if (!rowmapList.contains(mapper)) {
rowmapList.add(mapper);
}
}
class BaseFigure extends FreeformLayeredPane {
public BaseFigure() {
setLayoutManager(new FreeformLayout());
setBorder(new MarginBorder(5));
setBackgroundColor(ColorConstants.white);
setOpaque(true);
}
}
class PathFigure extends PolylineConnection {
public PathFigure() {
setTargetDecoration(new PolylineDecoration());
setConnectionRouter(new AutomaticRouter() {
#Override
protected void handleCollision(PointList list, int index) {
}
});
}
}
class RowMapper {
int sourceRow;
int targetRow;
#Override
public boolean equals(Object obj) {
if (obj instanceof RowMapper) {
RowMapper mapper = (RowMapper) obj;
return (sourceRow == mapper.sourceRow && targetRow == mapper.targetRow);
}
return false;
}
}

This is quite a difficult component to implement, I did one of these for Tibco Business Studio some time ago.
You'll need to place a Canvas between your two tables to draw the links on. You presumably have data models for your two tables, you'll also need a third model for storing the links and ensure that any modifications to this model trigger a refresh of the Canvas.
Next add drag and drop support to the two tables, dropping an item from table 1 onto table 2 should create a new item in your link model (thus triggering a Canvas refresh to draw the link).
Actually drawing the links in the right locations you'll have to work out yourself, but hopefully this gives you some ideas to start with.

Am I right in thinking that this implementation uses the mouse location to draw the arrows? So if you wanted to save / load a relationship you would have to save the x,y positions of the arrows and you'd have to make sure your components always stayed the same size?

Related

Item in ScrollView is not seen in Scene, but it shows in hierarchy

I am trying to display a list, generated dinamically. I created a prefab with the things I need in it (a TextView, 3 TMP_InputFields and 2 Buttons.)
To manage the different list items, I created a script (SkillManager, since the items represents skill the player can choose), which I attached to the prefab.
Then, I add every item (currently I am adding only one for testing purposes) to a List, iterate that list, and add the prefab to the Content of a ScrollView:
for(int i = 0; i < listaSkills.Count; i++)
{
GameObject listItem = Instantiate(SkillPrefab) as GameObject;
listItem.GetComponent<SkillManager>().skill = listaSkills[i];
//listItem.transform.SetParent(SkillsContent.transform, false);
listItem.transform.parent = SkillsContent.transform;
}
When I run this, no item is seen in the ScrollView, but I can see the SkillItem added to the hierarchy:
If I move to Scene tab after playing, I see a square with red lines crossing it:
Why is my item not displaying? Why the red cross? How can I populate my ScrollView?
EDIT:
This is the code of SkillManager, the script added to SkillPrefab:
public class SkillManager : MonoBehaviour
{
public TMP_InputField toSpend;
public TMP_InputField rangos;
public TMP_InputField modificadores;
public TMP_InputField total;
public Button plusButton;
public Button minusButton;
public TMP_Text nombre;
public Skill skill;
private int modificador;
private int pointsToSpend;
private int totalPoints;
// Start is called before the first frame update
void Start()
{
print("Start");
if(total!=null)
total.text = "0";
if(modificadores!=null)
modificadores.text = "0";
if (toSpend != null)
{
toSpend.GetComponent<TMP_InputField>().text = GetSkillPoints();
totalPoints = int.Parse(total.GetComponent<TMP_InputField>().text);
pointsToSpend = int.Parse(toSpend.GetComponent<TMP_InputField>().text);
}
else
{
GameObject GameObjectToSpend = GameObject.FindGameObjectWithTag("tospend");
toSpend = GameObjectToSpend.GetComponent<TMP_InputField>();
if (toSpend == null)
{
print("Sigue siendo nulo");
}
else
{
toSpend.text= GetSkillPoints();
//totalPoints = int.Parse(total.GetComponent<TMP_InputField>().text);
if(total!=null)
totalPoints = int.Parse(total.text);
if(toSpend!=null)
pointsToSpend = int.Parse(toSpend.text);
}
}
if (skill != null)
{
modificador = GetModificador(skill);
string sModificador = modificadores.GetComponent<TMP_InputField>().text;
int iModificador = int.Parse(sModificador);
modificadores.GetComponent<TMP_InputField>().text = iModificador.ToString();
}
if (plusButton != null)
{
plusButton.onClick.AddListener(PlusButtonClicked);
minusButton.onClick.AddListener(MinusButtonClicked);
}
}
private string GetSkillPoints()
{
return "1";
}
public void MinusButtonClicked()
{
string ranks = rangos.GetComponent<TMP_InputField>().text;
int ranksInt = int.Parse(ranks);
if (ranksInt > 0)
{
int newRank = ranksInt - 1;
pointsToSpend += 1;
rangos.GetComponent<TMP_InputField>().text = newRank.ToString();
toSpend.GetComponent<TMP_InputField>().text = pointsToSpend.ToString();
total.GetComponent<TMP_InputField>().text = (newRank + modificador).ToString();
skill.Puntos = newRank;
}
}
public void PlusButtonClicked()
{
string ranks=rangos.GetComponent<TMP_InputField>().text;
int ranksInt = int.Parse(ranks);
Character character = Almacen.instance.Character;
int level = character.CharacterLevel;
if (ranksInt < level && pointsToSpend > 0)
{
int newRank = ranksInt + 1;
rangos.GetComponent<TMP_InputField>().text = newRank.ToString();
pointsToSpend -= 1;
toSpend.GetComponent<TMP_InputField>().text = pointsToSpend.ToString();
total.GetComponent<TMP_InputField>().text = (newRank + modificador).ToString();
skill.Puntos = newRank;
}
}
private int GetModificador(Skill skill)
{
int retorno=0;
if (skill.Clasea)
{
retorno += 3;
}
else
{
retorno += 0;
}
retorno += GetModificadorCaracteristica();
return retorno;
}
private int GetModificadorCaracteristica()
{
Utils utils = new Utils();
int retorno = 0;
int characteristic=0;
switch (skill.Caracteristica)
{
case "Fue":
characteristic = Almacen.instance.Character.EffectiveStr;
break;
case "Des":
characteristic = Almacen.instance.Character.EffectiveDex;
break;
case "Con":
characteristic = Almacen.instance.Character.EffectiveCon;
break;
case "Int":
characteristic = Almacen.instance.Character.EffectiveInt;
break;
case "Sab":
characteristic = Almacen.instance.Character.EffectiveWis;
break;
case "Car":
characteristic = Almacen.instance.Character.EffectiveCha;
break;
}
retorno = utils.GetCharModifier(characteristic);
return retorno;
}
}
it looks like you instantiate the object as a GameObject. but this will not be seen in the canvas because it isn't a UI component. you may want to add a sprite or image to the component and instantiate that into the Canvas. it will look something like this:
public class SkillPrefab
{
//put all your variables here!!!
public Sprite skillSprite;
}
public class YourClassName : MonoBehaviour
{
[SerializeField]
public List<SkillPrefab> skills = new List<SkillPrefab>();
private void Update()
{
Sprite listItem = Instantiate(skills[0].skillSprite); //the index is the skill you want to spawn in the list.
}
}
this does take into account that you have made your skills into a list of skills that you can acces.

C# Instances share same values

programming student here, quite new to what i am about to ask but i'm sure you people will know. I have to make a game where several pictureboxes are created using an array. I also have to make a class that has a health variable of 5. When you click on one of the pictureboxes, its health has to go down by 1. I am as far as this, but the problem is that the health variable is shared by all pictureboxes, where in reality I want every picturebox to have it's own health.
This is my code:
public partial class Form1 : Form
{
Invader monster; // Invader is the name of the class
Random rand = new Random();
PictureBox[] pb = new PictureBox[5];
private void Spawner()
{
for (int i = 0; i < 5; i++)
{
this.monster = new Invader();
this.pb[i] = new PictureBox();
this.pb[i].Name = "pb" + i.ToString();
this.pb[i].Location = new Point(rand.Next(10, 300), monster.LocY);
this.pb[i].BackgroundImageLayout = ImageLayout.Stretch;
this.pb[i].BackgroundImage = Image.FromFile(#"Path");
this.pb[i].BackColor = Color.Transparent;
this.pb[i].Size = new System.Drawing.Size(40, 30);
this.Controls.Add(this.pb[i]);
this.pb[i].Click += this.Form1_Click;
}
}
private void Form1_Click(object sender, EventArgs e)
{
PictureBox currentpicturebox = (PictureBox)sender;
this.monster.HealthDown();
if (this.monster.Health == 0)
{
currentpicturebox.Dispose();
}
}
and my class:
class Invader
{
// Fields
private int health;
// Properties
public int Health
{
get { return this.health; }
}
// Constructor
public Invader()
{
this.health = 5;
}
// Methods
public void HealthDown()
{
this.health -= 1;
}
Lets say i click 1 picture box 4 times, and click another one 1 time. With this code the picturebox last clicked on will be disposed. Any ideas on how to fix this?
Your Invader monster is an instance variable of Form1 and in your method Spawner() inside the for loop you are reassigning it again every time: this.monster = new Invader();
Basically when you click on a picturebox (isn't different what) in your Form1_Click method happen that is everytime your last monster istance that get it's health down and not the supposed one.
In order to fix this you can:
transform monster in an array of Invader object instead of an Invader object, the number of elements must be the same as the number of pictureboxes
foreach picturebox assign as a Tag the index of the corrispondent Invader on the monster array
Here an example:
public partial class Form1 : Form
{
// EDIT - Become an array
Invader[] monster = new Invader[5]; // Invader is the name of the class
Random rand = new Random();
PictureBox[] pb = new PictureBox[5];
private void Spawner()
{
for (int i = 0; i < 5; i++)
{
this.monster[i] = new Invader(); // EDIT
this.pb[i] = new PictureBox();
this.pb[i].Name = "pb" + i.ToString();
this.pb[i].Location = new Point(rand.Next(10, 300), monster.LocY);
this.pb[i].BackgroundImageLayout = ImageLayout.Stretch;
this.pb[i].BackgroundImage = Image.FromFile(#"Path");
this.pb[i].BackColor = Color.Transparent;
this.pb[i].Size = new System.Drawing.Size(40, 30);
this.Controls.Add(this.pb[i]);
this.pb[i].Click += this.Form1_Click;
this.pb[i].Tag = i; // EDIT - Added tag assignation
}
}
private void Form1_Click(object sender, EventArgs e)
{
PictureBox currentpicturebox = (PictureBox)sender;
this.monster[(int)currentpicturebox.Tag].HealthDown(); // EDIT
if (this.monster[(int)currentpicturebox.Tag].Health == 0) //EDIT
{
currentpicturebox.Dispose();
}
}

Chart JavaFX, my hover Label are hidden by the edges of chart

I have a problem again with the JavaFX Chart : D
Context :
I had Popup/Label on my chart to display the value on hover : JavaFX LineChart Hover Values (Jewelsea answer)
Problem :
But when the point are near the edges of chart, the popup is hidden by them.
The chart with problem, I highlighted the edges of chart.
This is a problem, because my popup is bigger and display more informations (x value, y value and the data serie)
Possible solutions :
May I can check where the edges are, and if the popup is hide. In this case, I should shift the popup. But when I look doc, I didn't found the right method :
XYChart
XYChart.Data#nodeProperty
May I can put the popup above the chart. Like z-index in CSS.
The code :
import javafx.application.Application;
import javafx.collections.*;
import javafx.event.EventHandler;
import javafx.scene.*;
import javafx.scene.chart.*;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
/**
* Displays a LineChart which displays the value of a plotted Node when you hover over the Node.
* #author original, jewelsea https://gist.github.com/jewelsea
*/
public class LineChartWithHover extends Application {
#SuppressWarnings("unchecked")
#Override public void start(Stage stage) {
final LineChart lineChart = new LineChart(
new NumberAxis(), new NumberAxis(),
FXCollections.observableArrayList(
new XYChart.Series(
"My portfolio",
FXCollections.observableArrayList(
plot(0, 14, 15, 24, 34, 36, 22, 55, 43, 17, 29, 25)
)
)
)
);
lineChart.setCursor(Cursor.CROSSHAIR);
lineChart.setTitle("Stock Monitoring, 2013");
stage.setScene(new Scene(lineChart, 500, 400));
stage.show();
System.out.println("test 1 = "+lineChart.getProperties());
}
/** #return plotted y values for monotonically increasing integer x values, starting from x=1 */
public ObservableList<XYChart.Data<Integer, Integer>> plot(int... y) {
final ObservableList<XYChart.Data<Integer, Integer>> dataset = FXCollections.observableArrayList();
int i = 0;
while (i < y.length) {
final XYChart.Data<Integer, Integer> data = new XYChart.Data<>(i + 1, y[i]);
data.setNode(
new HoveredThresholdNode(
(i == 0) ? 0 : y[i-1],
y[i]
)
);
dataset.add(data);
i++;
}
return dataset;
}
/** a node which displays a value on hover, but is otherwise empty */
class HoveredThresholdNode extends StackPane {
HoveredThresholdNode(int priorValue, int value) {
setPrefSize(15, 15);
final Label label = createDataThresholdLabel(priorValue, value);
setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
getChildren().setAll(label);
setCursor(Cursor.NONE);
toFront();
}
});
setOnMouseExited(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
getChildren().clear();
setCursor(Cursor.CROSSHAIR);
}
});
}
private Label createDataThresholdLabel(int priorValue, int value) {
final Label label = new Label(value + "");
label.getStyleClass().addAll("default-color0", "chart-line-symbol", "chart-series-line");
label.setStyle("-fx-font-size: 20; -fx-font-weight: bold;");
if (priorValue == 0) {
label.setTextFill(Color.DARKGRAY);
} else if (value > priorValue) {
label.setTextFill(Color.FORESTGREEN);
} else {
label.setTextFill(Color.FIREBRICK);
}
label.setMinSize(Label.USE_PREF_SIZE, Label.USE_PREF_SIZE);
return label;
}
}
public static void main(String[] args) { launch(args); }
}
Thank you in advance ! And my apologies about my english, still learning !
I have been looking on the JavaFX CSS reference guide and I could'nt find anything to simply solve your problem.
A possible solution is to translate your symbol depending on how near it is to the max or min value.
I wrote something like this, based on your code :
/**
* Displays a LineChart which displays the value of a plotted Node when you hover over the Node.
* #author original, jewelsea https://gist.github.com/jewelsea
*/
public class LineChartWithHover extends Application {
#SuppressWarnings("unchecked")
#Override public void start(Stage stage) {
final LineChart lineChart = new LineChart(
new NumberAxis(), new NumberAxis(),
FXCollections.observableArrayList(
new XYChart.Series(
"My portfolio",
FXCollections.observableArrayList(
plot(0, 14, 15, 24, 34, 36, 22, 55, 43, 17, 29, 25)
)
)
)
);
lineChart.setCursor(Cursor.CROSSHAIR);
lineChart.setTitle("Stock Monitoring, 2013");
stage.setScene(new Scene(lineChart, 500, 400));
stage.show();
System.out.println("test 1 = "+lineChart.getProperties());
}
/** #return plotted y values for monotonically increasing integer x values, starting from x=1 */
public ObservableList<XYChart.Data<Integer, Integer>> plot(Integer... y) {
final ObservableList<XYChart.Data<Integer, Integer>> dataset = FXCollections.observableArrayList();
int i = 0;
List<Integer> list = Arrays.asList(y);
int min = Collections.min(list);
int max = Collections.max(list);
int minThreshold = 5;
int maxThreshold = 5;
while (i < y.length) {
final XYChart.Data<Integer, Integer> data = new XYChart.Data<>(i + 1, y[i]);
int topMargin = 0;
if(y[i] <= min + minThreshold) {
topMargin = -50;
} else if (y[i] >= max - maxThreshold) {
topMargin = 50;
}
StackPane stackPane = new HoveredThresholdNode(
(i == 0) ? 0 : y[i-1],
y[i],
topMargin
);
data.setNode(stackPane);
dataset.add(data);
i++;
}
return dataset;
}
/** a node which displays a value on hover, but is otherwise empty */
class HoveredThresholdNode extends StackPane {
HoveredThresholdNode(int priorValue, int value, int topMargin) {
setPrefSize(15, 15);
final Label label = createDataThresholdLabel(priorValue, value);
setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
getChildren().setAll(label);
setCursor(Cursor.NONE);
toFront();
setMargin(label, new Insets(topMargin,0,0,0));
}
});
setOnMouseExited(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
getChildren().clear();
setCursor(Cursor.CROSSHAIR);
}
});
}
private Label createDataThresholdLabel(int priorValue, int value) {
final Label label = new Label(value + "");
label.getStyleClass().addAll("default-color0", "chart-line-symbol", "chart-series-line");
label.setStyle("-fx-font-size: 20; -fx-font-weight: bold;");
if (priorValue == 0) {
label.setTextFill(Color.DARKGRAY);
} else if (value > priorValue) {
label.setTextFill(Color.FORESTGREEN);
} else {
label.setTextFill(Color.FIREBRICK);
}
label.setMinSize(Label.USE_PREF_SIZE, Label.USE_PREF_SIZE);
return label;
}
}
public static void main(String[] args) { launch(args); }
}
Basically, I am just saying that all values <= min+5 and >= max-5 must be translated.
The +/- 5 is arbitrary and should be calculated from the ticks gap and plot scale to have a perfect repositioning. Anyway, without performing any maths, it is still quite satisfying.
Based on Mr Kwoinkwoin solution, I wrote my own.
Im not sure if its possible to optimize it or improve it. But seems to be working for me so far.
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Cursor;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.NumberAxis.DefaultFormatter;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import static javafx.scene.layout.StackPane.setMargin;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class StockLineChartApp extends Application {
private LineChart<Number, Number> chart;
private Series<Number, Number> series;
private NumberAxis xAxis;
private ZonedDateTime time;
public StockLineChartApp() {
time = ZonedDateTime.of(LocalDateTime.of(LocalDate.of(2000, Month.JANUARY, 1), LocalTime.NOON), ZoneId.systemDefault());
}
public Parent createContent() {
xAxis = new NumberAxis();
xAxis.setLabel("Date/Time");
xAxis.setForceZeroInRange(false);
xAxis.setAutoRanging(true);
xAxis.setTickLabelFormatter(new StringConverter<Number>() {
#Override
public String toString(Number t) {
long longValue = t.longValue();
ZonedDateTime zd = convertLongToZonedDateTime(longValue);
String formatDate = formatDate(zd, "dd/MM/yyyy");
return formatDate;
}
#Override
public Number fromString(String string) {
ZonedDateTime dl = ZonedDateTime.parse(string, DateTimeFormatter.ofPattern("dd/MM/yyyy"));
long toEpochMilli = dl.toEpochSecond();
//DateTimeFormatter.ofPattern(string).p
return toEpochMilli;
}
});
final NumberAxis yAxis = new NumberAxis();
yAxis.setAutoRanging(true);
chart = new LineChart<>(xAxis, yAxis);
chart.setCursor(Cursor.CROSSHAIR);
chart.setAlternativeRowFillVisible(true);
chart.setAlternativeColumnFillVisible(true);
// setup chart
//final String stockLineChartCss= getClass().getResource("StockLineChart.css").toExternalForm();
//chart.getStylesheets().add(stockLineChartCss);
chart.setCreateSymbols(true);
chart.setAnimated(true);
chart.setLegendVisible(true);
chart.setTitle("ACME Company Stock");
yAxis.setLabel("Share Price");
yAxis.setTickLabelFormatter(new DefaultFormatter(yAxis, "$", null));
// add starting data
series = new Series<>();
series.setName("Data por Peça");
for (double m = 0; m < (10); m++) {
long data = nextTime();
addData(data, (long) (Math.random() * 10));
System.out.println(data);
}
//chart.
chart.getData().add(series);
//chart.getData().add(hourDataSeries);
return chart;
}
private void addData(long x, long y) {
Data<Number, Number> data = new Data<Number, Number>(x, y);
series.getData().add(data);
ZonedDateTime zd = convertLongToZonedDateTime(x);
String formatDate = formatDate(zd, "dd/MM/yyyy");
//String text = "(" + formatDate + ";" + y + ")";
String text = y + "";
if (text.length() > 4) {
text = text.substring(0, 4);
}
String t = formatDate + "\nValor: " + text;
data.setNode(new HoveredThresholdNode(t, data));
}
public static long convertZonedDateTimeToLong(ZonedDateTime zonedDateTime) {
long e = zonedDateTime.toInstant().toEpochMilli();
return e;
}
private long nextTime() {
time = time.plusYears(10);
return convertZonedDateTimeToLong(time);
}
#Override
public void start(Stage primaryStage) throws Exception {
Parent createContent = createContent();
//
final StackPane pane = new StackPane();
pane.getChildren().add(createContent);
final Scene scene = new Scene(pane, 500, 400);
//new ZoomManager(pane, chart, series);
//
primaryStage.setScene(scene);
primaryStage.show();
}
public static String formatDate(ZonedDateTime ts, String format) {
try {
if (ts == null) {
return "";
}
String format1 = ts.format(DateTimeFormatter.ofPattern(format));
return format1;
} catch (Exception ex) {
ex.printStackTrace();
}
return "";
}
public static ZonedDateTime convertLongToZonedDateTime(long e) {
Instant i = Instant.ofEpochMilli(e);
ZonedDateTime ofInstant = ZonedDateTime.ofInstant(i, ZoneId.systemDefault());
return ofInstant;
}
/**
* Java main for when running without JavaFX launcher
*/
public static void main(String[] args) {
launch(args);
}
public class HoveredThresholdNode extends StackPane {
//Reference
private Data<Number, Number> data;
private Label label;
private String value;
public HoveredThresholdNode(String value, Data<Number, Number> data) {
this.data = data;
this.value = value;
this.label = new Label(value);
this.label.getStyleClass().clear();
this.getStyleClass().clear();
this.label.setStyle("-fx-font-size: 12; fx-text-fill: black;");
this.label.getStyleClass().addAll("default-color0", "chart-line-symbol", "chart-series-line");
this.label.setWrapText(true);
this.label.setMinSize(Label.USE_PREF_SIZE, Label.USE_PREF_SIZE);
setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
getChildren().setAll(label);
toFront();
boolean close_top = false, close_right = false, close_bottom = false, close_left = false;
long min_x = Long.MAX_VALUE;
long max_x = Long.MIN_VALUE;
long min_y = Long.MAX_VALUE;
long max_y = Long.MIN_VALUE;
ObservableList<Series<Number, Number>> chartSeries = chart.getData();
for (Series<Number, Number> s : chartSeries) {
ObservableList<Data<Number, Number>> chartData = s.getData();
for (Data<Number, Number> d : chartData) {
Number xValue = d.getXValue();
Number yValue = d.getYValue();
long kx = xValue.longValue();
long ky = yValue.longValue();
if (kx < min_x) {
min_x = kx;
}
if (kx > max_x) {
max_x = kx;
}
if (ky < min_y) {
min_y = ky;
}
if (ky > max_y) {
max_y = ky;
}
}
}
if (data.getXValue().longValue() - max_x == 0) {
close_right = true;
}
if (data.getXValue().longValue() - min_x == 0) {
close_left = true;
}
if (data.getYValue().longValue() - min_y == 0) {
close_bottom = true;
}
if (data.getYValue().longValue() - max_y == 0) {
close_top = true;
}
// System.out.println("\n");
// System.out.println(" close_right " + close_right);
// System.out.println(" close_left " + close_left);
// System.out.println(" close_bottom " + close_bottom);
// System.out.println(" close_top " + close_top);
double top = 0;
double right = 0;
double bottom = 0;
double left = 0;
if (close_top) {
top = 50;
}
if (close_bottom) {
bottom = 50;
}
if (close_right) {
right = 50;
}
if (close_left) {
left = 50;
}
setMargin(label, new Insets(top, right, bottom, left));
}
});
setOnMouseExited(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
getChildren().clear();
}
});
}
public HoveredThresholdNode copy() {
HoveredThresholdNode copy = new HoveredThresholdNode(value, data);
return copy;
}
}
}

Java - I think my boolean is defaulting to true for some reason

I'm having an issue with my hangman program. When I run it, the label holding the int variable "lives" is supposed to update when you guess a wrong letter. But for some reason it isn't. I've placed this in my code as a test mechanism, and it isn't appearing even here.
if (used[letter] = false) {
System.out.println("test");
However, when I place it here.. It DOES work..
if (finished == false) {
boolean found = false;
boolean www = false;
System.out.println("test");
if (used[letter] = false) {
It almost leads me to believe that used[letter] is true by default, when it really shouldn't be. The variable is declared at the very top. Any thoughts?
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
import java.util.ArrayList;
public class Hangman implements ActionListener {
JFrame frame;
JPanel stats = new JPanel();
JLabel currentWordLA = new JLabel("Current word:");
JLabel triedLettersLA = new JLabel("Tried letters:");
JLabel triesLeftLA = new JLabel("Tries remaining:");
private String[] wordList = {"computer","java","activity","alaska","appearance","article",
"automobile","basket","birthday","canada","central","character","chicken","chosen",
"cutting","daily","darkness","diagram","disappear","driving","effort","establish","exact",
"establishment","fifteen","football","foreign","frequently","frighten","function","gradually",
"hurried","identity","importance","impossible","invented","italian","journey","lincoln",
"london","massage","minerals","outer","paint","particles","personal","physical","progress",
"quarter","recognise","replace","rhythm","situation","slightly","steady","stepped",
"strike","successful","sudden","terrible","traffic","unusual","volume","yesterday" };
public String mysteryWord;
public int lives;
private boolean finished = false;
private boolean won = false;
private Button a[];
public boolean used[] = new boolean[26];
public static void main (String[] args) {
Hangman gui = new Hangman();
gui.go();
}
class myDrawPanel extends JPanel {
public void paintComponent(Graphics g) {
setBackground(Color.white);
g.setColor(Color.gray);
g.fillRect(50, 200, 150, 20);
g.fillRect(90,20,10,200);
g.fillRect(90,20,60,10);
g.setColor(Color.black);
g.fillRect(145,20,5,25);
g.setColor(Color.green);
if (lives < 6 )
g.drawOval(132,45,30,30);
if (lives < 5 )
g.drawLine(147,75,147,100);
if (lives < 4 )
g.drawLine(147,100,167,133);
if (lives < 3 )
g.drawLine(147,100,127,133);
if (lives < 2 )
g.drawLine(147,75,167,85);
if (lives < 1 )
g.drawLine(147,75,127,85);
StringBuffer guessed = new StringBuffer();
for (int cl = 0; cl < mysteryWord.length(); cl++) {
if (used[(int)mysteryWord.charAt(cl)-'a'])
guessed.append(mysteryWord.charAt(cl));
else
guessed.append("*");
}
currentWordLA.setText("Current word: " + guessed.toString());
if (lives < 1) {
g.setColor(Color.white);
g.fillRect(70, 200, 200, 30);
g.setColor(Color.black);
g.drawString(mysteryWord.toString(),75,230);
Font fff = new Font("Helvetica",Font.BOLD,36);
g.setFont(fff);
g.setColor(Color.red);
g.drawString("You lose!",200,100);
//finished = true;
}
if (won) {
Font fff = new Font("Helvetica",Font.BOLD,36);
g.setFont(fff);
// Color red=new Color.red
g.setColor(Color.red);
g.drawString("You Win!",200,100);
//finished = true;
}
}
}
public void go() {
///////////////////////DESIGN BEGIN//////////////////////////////////////////////
frame = new JFrame("Hangman");
JPanel topPanel = new JPanel();
myDrawPanel noosePanel = new myDrawPanel();
JPanel bottomPanel = new JPanel();
JPanel scorePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout( new GridLayout( 2, 0) );
bottomPanel.setLayout( new GridLayout( 0, 2) );
scorePanel.setSize(20,100);
noosePanel.setBorder(BorderFactory.createTitledBorder("Your progress."));
topPanel.setBorder(BorderFactory.createTitledBorder("Your arsenal."));
scorePanel.setBorder(BorderFactory.createTitledBorder("Your score."));
frame.add(topPanel);
frame.add(bottomPanel);
bottomPanel.add(scorePanel);
bottomPanel.add(noosePanel);
//Just the stats panel.
JButton restart = new JButton("Reset");
currentWordLA.setFont(new Font("Verdana", Font.PLAIN, 10));
currentWordLA.setForeground(Color.black);
triedLettersLA.setFont(new Font("Verdana", Font.PLAIN, 10));
triedLettersLA.setForeground(Color.black);
triesLeftLA.setFont(new Font("Verdana", Font.PLAIN, 10));
triesLeftLA.setForeground(Color.black);
restart.setFont(new Font("Verdana", Font.PLAIN, 16));
restart.setForeground(Color.red);
stats.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.gridx = 0;
c.gridy = 0;
c.insets = new Insets(20,0,0,0);
c.anchor = GridBagConstraints.LINE_START;
stats.add(currentWordLA, c);
c.gridx = 0;
c.gridy = 1;
c.anchor = GridBagConstraints.LINE_START;
stats.add(triedLettersLA, c);
c.gridx = 0;
c.gridy = 2;
c.anchor = GridBagConstraints.LINE_START;
stats.add(triesLeftLA, c);
c.gridx = 0;
c.gridy = 3;
c.anchor = GridBagConstraints.LINE_START;
stats.add(restart, c);
scorePanel.add(stats);
///////////////////////DESIGN END//////////////////////////////////////////////
///////////////////////ALPHABET BEGIN//////////////////////////////////////////
int i;
StringBuffer buffer;
a = new Button[26];
topPanel.setLayout( new GridLayout( 4,0, 10, 10) );
for (i = 0; i <26; i++) {
buffer = new StringBuffer();
buffer.append((char)(i+'a'));
a[i] = new Button(buffer.toString());
a[i].setSize(100,100);
a[i].addActionListener( this );
topPanel.add(a[i]);
}
///////////////////////ALPHABET END//////////////////////////////////////////
//Just shows the entire window.
frame.setSize(500, 500);
frame.setResizable(false);
frame.setVisible(true);
//////////////////////GAMEPLAY BEGIN////////////////////////////////////////
lives = 6;
triesLeftLA.setText("Tries remaining: " + lives);
mysteryWord = wordGen();
}
//Returns a random word from the wordList bank.
private String wordGen() {
return wordList[0 + (int)(Math.random() * ((63 - 0) + 1)) ]; //Make sure to set these to nonprinted chars eventually
}
public void consultWord(int letter) {
if (finished == false) {
boolean found = false;
boolean www = false;
if (used[letter] = false) {
System.out.println("test");
for (int cl = 0 ; cl < mysteryWord.length(); cl++) {
if (mysteryWord.charAt(cl)==((char)(letter + 'a'))) {
found = true;
}
}
if (found == false) {
lives = lives - 1;
triesLeftLA.setText ("Tries remaining: " + lives);
}
}
used[letter] = true;
for (int cl = 0; cl < mysteryWord.length(); cl++) {
if (!used[(int)(mysteryWord.charAt(cl)) - 'a']){
www = true;
}
}
if (www == false) {
won = true;
}
frame.repaint();
}
}
public void actionPerformed( ActionEvent e) {
int i;
for (i = 0; i < 26; i++) {
if (e.getSource() == a[i]) {
consultWord(i); }
}
}
}
Make that:
if (used[letter] == false) {
System.out.println("test");
if (used[letter] = false) {
You just set used[letter] to false. Try ==
Of course, to avoid this typo you shouldn't be using == but rather ...
if (!used[letter]) {

How do I incorporate a scroll bar after a few instances of actionListener?

Ok. so I'm writing a grocery checkout system code and on the right side of the frame, I'm displaying a Jpanel with Labels. Every time I click the 'Scan' button, I add a new label into the JPanel so that everytime I click, it displays the output. However, I can only fit so many entries in my window, how do I incorporate a scroll bar so that I can scroll through the groceries?
I tried inputting a scroll bar EAST with a BorderLayout and all the labels CENTER, however, everytime I click the button, it only reprints it at the same exact spot instead of top/down so that I can scroll. (Please, bare in mind that this is a very very rough draft. I'll be incorporating the logic beneath the code once I get the nitty-gritty with the GUI)
Here's my code:
public class Checkout extends JFrame implements ActionListener
{
private int numOfItems = 21;
private Integer[] itemCode = new Integer[numOfItems];
private StringBuffer[] itemDesc = new StringBuffer[numOfItems];
private Double[] unitPrice = new Double[numOfItems];
private Integer[] taxCode = new Integer[numOfItems];
private Integer[] quantity = new Integer[numOfItems];
private Integer[] reorderLevel = new Integer[numOfItems];
private double subTotal, tax, grandTotal;
private JButton scan = new JButton("Scan");
private JButton pay = new JButton("Finish & Pay");
private JButton readFile = new JButton("Read from File");
private JTextField itemNumber = new JTextField(4);
private JTextField itemQuantity = new JTextField(4);
private JLabel displayReceipt = new JLabel();
private JPanel p4;
public Checkout()
{
setValues();
JPanel p2 = new JPanel(new FlowLayout());
p2.add(scan);
p2.add(pay);
p2.add(readFile);
JPanel p1 = new JPanel(new FlowLayout());
p1.add(new JLabel("Item Number:"));
p1.add(itemNumber);
p1.add(new JLabel("Item Quantity:"));
p1.add(itemQuantity);
p1.setBorder(new TitledBorder("Scan Items First. Then Hit 'Finish and Pay'"));
JPanel p3 = new JPanel(new BorderLayout(5, 5));
p3.add(p1, BorderLayout.NORTH);
p3.add(p2, BorderLayout.CENTER);
p4 = new JPanel(new GridLayout(0, 1, 0, 0));
JScrollPane scroll = new JScrollPane(p4);
add(p3, BorderLayout.WEST);
add(p4, BorderLayout.CENTER);
scan.addActionListener(this);
pay.addActionListener(this);
readFile.addActionListener(this);
}
public void setValues()
{
String[] tokens = new String[6];
String[][] multi = new String[numOfItems][6];
try{BufferedReader textReader = new BufferedReader(new FileReader("inventory.txt"));
for(int i = 0; i < numOfItems; i++) // Split the file
{
tokens = textReader.readLine().split("\t");
for(int j = 0; j < tokens.length; j++)
{
multi[i][j] = tokens[j];
}
}
for(int i = 0; i < numOfItems; i++)
{
itemCode[i] = (Integer.parseInt(multi[i][0]));
itemDesc[i] = new StringBuffer(multi[i][1]);
unitPrice[i] = (Double.parseDouble(multi[i][2]));
taxCode[i] = (Integer.parseInt(multi[i][3]));
quantity[i] = (Integer.parseInt(multi[i][4]));
reorderLevel[i] = (Integer.parseInt(multi[i][5]));
}}
catch(Exception e){}
}
public void setQuantity(int item, int itemCount)
{
for(int i = 0; i < numOfItems; i++)
{
if(itemCode[i].equals(item))
{
quantity[i] = quantity[i] - itemCount;
subTotal = itemCount * unitPrice[i];
if(quantity[i] < reorderLevel[i])
itemDesc[i] = itemDesc[i].append("**MARKED FOR REORDER**\t");
}
}
}
public String toString()
{
System.out.println("CUST TICKET");
//for(int i = 0; i < numOfItems; i++)
//return (itemCode[i] + "\t" + itemDesc[i] + "\t" + unitPrice[i] + "\t" + taxCode[i] + "\t" + quantity[i] + "\t" + reorderLevel[i]);
System.out.println("SUBTOTAL\t" + subTotal);
System.out.println("TAX\t\t" + tax);
System.out.println("GRAND TOTAL\t" + grandTotal);
return "";
}
public static void main(String[] args) throws Exception
{
Checkout frame = new Checkout(); // Frame layout
frame.setSize(600, 135);
frame.setTitle("Computer Science 202: Final Project - Supermarket Checkout System");
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
/*Scanner input = new Scanner(System.in);
System.out.print("Please enter item number: ");
Integer itemNumber = Integer.parseInt(input.nextLine());
System.out.print("Please enter quantity: ");
Integer quantity = Integer.parseInt(input.nextLine());
System.out.println(frame.stockCheck(itemNumber, quantity));*/
}
public void actionPerformed(ActionEvent e)
{
if(e.getSource() == scan)
{
try
{
Integer quantityOrdered = 1;
Integer item = Integer.parseInt(itemNumber.getText());
quantityOrdered = Integer.parseInt(itemQuantity.getText());
for(int i = 0; i < numOfItems; i++)
{
if(itemCode[i].equals(item))
{
if(quantityOrdered > 1)
{
p4.add(new JLabel(itemDesc[i] + "" + unitPrice[i]));
p4.add(new JLabel(quantityOrdered + " # " + unitPrice[i]));
p4.revalidate();
p4.repaint();
}
else
{
p4.add(new JLabel(itemDesc[i] + "\t" + unitPrice[i]));
p4.revalidate();
p4.repaint();
}
}
}
setQuantity(item, quantityOrdered);
}
catch(Exception ex){}
}
else if(e.getSource() == pay)
{
displayReceipt.setText(toString());
}
else if(e.getSource() == readFile)
{
System.out.println("READ FILE");
}
}
}