I have the following method within a thread, and it works 100%, but once I remove System.out.println("here"); the code stops doing what it was doing. There are no errors, it just has the appearance of not doing anything. What this does is brightens the colors of an image, with the debug line in it brightens, without it, it doesn't brighten. Why is it causing that?
The thread Class:
package pocketshop.threads;
import com.jogamp.opencl.CLBuffer;
import java.awt.Container;
import java.awt.image.BufferedImage;
import java.nio.FloatBuffer;
import pocketshop.Canvas;
import pocketshop.graphics.CL;
import pocketshop.graphics.Preview;
/**
*
* #author Ryan
*/
public class AdjustThread extends Thread {
protected float amount = 0;
protected CLBuffer<FloatBuffer> buffer;
protected String adjustment;
protected Container parent;
public AdjustThread(Container parent, String adjustment) {
this.parent = parent;
this.adjustment = adjustment;
}
public void setAmount(float amount){
this.amount = amount;
}
public CLBuffer<FloatBuffer> getBuffer() {
return buffer;
}
public void run() {
float cAmount = 0;
while(true){
System.out.println("here");
if(cAmount != this.amount){
cAmount = this.amount;
CL.start(adjustment, this.amount);
buffer = CL.getBuffer();
float[] pixels = CL.getPixels();
BufferedImage newimage = new BufferedImage(Canvas.image.getWidth(), Canvas.image.getHeight(), BufferedImage.TYPE_INT_RGB);
buffer.getBuffer().get(pixels).rewind();
newimage.getRaster().setPixels(0, 0, Canvas.image.getWidth(), Canvas.image.getHeight(), pixels);
Preview.setImage(newimage);
Canvas.preview = Preview.getImage();
parent.repaint();
}
}
}
}
And the Dialog box relevant code:
package pocketshop.dialogs;
import java.awt.image.BufferedImage;
import pocketshop.Canvas;
import pocketshop.graphics.adjustments.Contrast;
import pocketshop.threads.AdjustThread;
/**
*
* #author Ryan
*/
public class BrightnessContrastDialog extends javax.swing.JDialog {
AdjustThread adj;
/**
* Creates new form BrightnessContrastDialog
*/
public BrightnessContrastDialog(java.awt.Frame parent, boolean modal) {
super(parent, modal);
initComponents();
adj = new AdjustThread(this.getParent(), "Brightness");
adj.start();
}
// Run everytime the JSlider moves
private void sldBrightnessStateChanged(javax.swing.event.ChangeEvent evt) {
float val = sldBrightness.getValue();
txtBrightness.setText("" + (int) val);
adj.setAmount(val);
}
}
Is this.amount declared as volatile?
I take it this field changes by virtue of assignment in another thread. If it's not declared as volatile, there's no reason to assume that the thread running the run() method above will ever observe a change to it. After the first time you assign this.amount to cAmount, they remain equal thereafter—from the point of view of this thread, anyway.
Once you clarify for us the declared qualifiers on this.amount and show the snippet of code where it's changed elsewhere, we can help specify the proper synchronization devices necessary to restore the behavior you desire.
As for why the call to PrintStream#println() seems to make a difference here, it's likely causing not only a delay but may also be hitting a happens-before memory visibility edge that's allowing the changes to this.amount to become visible to this thread. That's a lot of hand-waving, but I think that there are larger problems here to solve first before pegging the root cause for that particular side effect.
Related
I'm recently using JavaFX and would like to implement the Observer pattern by binding my stack update, with a ListView or TableView from JavaFX. However, I don't know what changes to make to my ComplexNumberStack class.
public class ComplexNumberStack extends Stack<ComplexNumber> {
private static ComplexNumberStack instance = null;
/** This method provide the unique instance of ComplexNumberStack. */
public static ComplexNumberStack getInstance() {
if (instance == null)
instance = new ComplexNumberStack();
return instance;
}
/**
* This method provides a secure implementation of massive pop of operandNumber operands from the stack
*
* #param operandNumber specifies the number of operands to be taken from the stack
* #return an iterator of complexNumber taken
*/
public Iterator<ComplexNumber> getOperand(int operandNumber) {
List<ComplexNumber> operands = new ArrayList<>();
for (int i = 0; i < operandNumber; i++) {
try {
operands.add(pop());
} catch (EmptyStackException e) {
Collections.reverse(operands);
operands.forEach(this::push);
throw new InvalidParameterException("There aren't enough operands into the stack");
}
}
return operands.iterator();
}
}
This example adds a wrapper class around your stack implementation which provides an ObservableList that can be:
Placed in a ListView AND
Respond to bindings (see the pop button disable property binding in the example app).
For it to work, the mutation operations (e.g. push/pop) must be called on the wrapper class rather than the underlying class.
There are more efficient ways of implementing this (e.g. don't subclass stack, instead implement the Deque interface and use an ObservableList directly as storage, extending ObservableListBase).
However, this is what I came up with that still kept your underlying class and it might be fine or easily adaptable for your purposes.
public record ComplexNumber(double real, double imaginary) {}
The underlying stack implementation is unchanged from the class in your question.
import java.security.InvalidParameterException;
import java.util.*;
public class ComplexNumberStack extends Stack<ComplexNumber> {
private static ComplexNumberStack instance = null;
/**
* This method provide an instance of a ComplexNumberStack.
*/
public static ComplexNumberStack getInstance() {
if (instance == null)
instance = new ComplexNumberStack();
return instance;
}
/**
* This method provides a secure implementation of massive pop of operandNumber operands from the stack
*
* #param operandNumber specifies the number of operands to be taken from the stack
* #return an iterator of complexNumber taken
*/
public Iterator<ComplexNumber> getOperand(int operandNumber) {
List<ComplexNumber> operands = new ArrayList<>();
for (int i = 0; i < operandNumber; i++) {
try {
operands.add(pop());
} catch (EmptyStackException e) {
Collections.reverse(operands);
operands.forEach(this::push);
throw new InvalidParameterException("There aren't enough operands into the stack");
}
}
return operands.iterator();
}
}
Provides observability for the stack.
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import java.util.EmptyStackException;
public class ObservedComplexNumberStack {
private final ObservableList<ComplexNumber> observableList;
public ObservedComplexNumberStack(ComplexNumberStack complexNumberStack) {
observableList = FXCollections.observableList(complexNumberStack);
}
public ComplexNumber pop() {
if (observableList.size() == 0) {
throw new EmptyStackException();
}
return observableList.remove(observableList.size() - 1);
}
public ComplexNumber push(ComplexNumber number) {
observableList.add(number);
return number;
}
public ObservableList<ComplexNumber> getObservableList() {
return FXCollections.unmodifiableObservableList(observableList);
}
}
Test application.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.util.concurrent.ThreadLocalRandom;
public class StackApplication extends Application {
#Override
public void start(Stage stage) {
ObservedComplexNumberStack stack = new ObservedComplexNumberStack(
ComplexNumberStack.getInstance()
);
ListView<ComplexNumber> listView = new ListView<>(stack.getObservableList());
listView.setPrefSize(80, 150);
listView.setCellFactory(param -> new ListCell<>() {
#Override
protected void updateItem(ComplexNumber item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText("");
return;
}
setText(String.format("%.2f + %.2fi", item.real(), item.imaginary()));
}
});
Button push = new Button("Push");
push.setOnAction(e -> {
stack.push(randomNum());
scrollToLastItem(listView);
});
Button pop = new Button("Pop");
pop.setOnAction(e -> {
stack.pop();
scrollToLastItem(listView);
});
pop.disableProperty().bind(Bindings.isEmpty(listView.getItems()));
HBox controls = new HBox(10, push, pop);
VBox layout = new VBox(10, controls, listView);
layout.setPadding(new Insets(10));
Scene scene = new Scene(layout);
stage.setScene(scene);
stage.show();
}
private void scrollToLastItem(ListView<ComplexNumber> listView) {
if (listView.getItems().size() > 0) {
listView.scrollTo(listView.getItems().size() - 1);
}
}
private ComplexNumber randomNum() {
ThreadLocalRandom r = ThreadLocalRandom.current();
return new ComplexNumber(r.nextDouble(9), r.nextDouble(9));
}
public static void main(String[] args) {
launch();
}
}
Potential alteratives or improvements
hmm .. this looks a bit brittle - external code could change the stack without notifying the list (especially, since is a singleton and potential collaborators spread across the world
Yes, it's true, its buyer beware :-)
The alternate proposed solution of implementing Deque with operations directly on a backing observableList is probably preferred, but I'm not going to write that at this time (it would be quite a bit more work to do well).
The solution in this answer uses the FXCollections list wrapper, which, incidentally, on its own is another simple solution to this problem:
FXCollections.observableList(ComplexNumberStack.getInstance());
Though, it has some disadvantages:
Changes to the underlying stack will not be observed (also true of the solution in this answer).
You need to change the list to observe changes and the list won't have push/pop ops (unlike the solution in this answer, which does at least provide push/pop ops which will be observed).
If you are interested in how the JavaFX framework implementation wrapper works, you can see the code for ObservableListWrapper.
If you wished to, you could copy a version of ObservableListWrapper to your own package (you don't want to depend on com.sun code directly), then subclass it and adapt it to add your additional push/pop ops (as suggested by kleopatra in comments).
I have an ObservableSet<DoubleProperty> itemSet which holds the DoubleProperty prop1 for any number of instances of Item.
I want to create another DoubleProperty total that will reflect an up to date total of all DoubleProperty's in itemSet.
The double value of each DoubleProperty in the set can change independently. The total value would need to reflect those changes.
This is the Item class:
class Item {
DoubleProperty prop1;
DoubleProperty prop2;
public Item() {
this.prop1 = new SimpleDoubleProperty(1.0);
this.prop2 = new SimpleDoubleProperty(2.0);
itemSet.add(this.prop1);
}
}
This is a sort of global variable class...
class ItemValue {
private ItemValue itemValue = null;
ObservableSet<DoubleProperty> itemSet = FXCollections.observableSet();
DoubleProperty total;
private ItemValue() {
this.total = new SimpleDoubleProperty(0.0);
// create several Item's here...
itemSet.addListener((InvalidationListener) observable -> {
/*
Something which binds the total
I figure it will need to go here so that if new items get added the total will reflect that?
*/
});
}
public ItemValue get() {
if (itemValue == null) itemValue = new ItemValue();
return itemValue;
}
As far as I know there is no built in way to do this simply. However, there's a couple of ways you could do this. The most(?) efficient, but more complicated way would be to listen to the ObservableSet for additions/removals, observe any current DoubleProperty elements, and modify the total property yourself.
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyDoubleWrapper;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.beans.value.WeakChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ObservableSet;
import javafx.collections.SetChangeListener;
public class SomeClass {
private final ReadOnlyDoubleWrapper total = new ReadOnlyDoubleWrapper(this, "total");
private void setTotal(double total) { this.total.set(total); }
public final double getTotal() { return total.get(); }
public final ReadOnlyDoubleProperty totalProperty() { return total.getReadOnlyProperty(); }
private final ObservableSet<DoubleProperty> propertySet = FXCollections.observableSet();
private final ChangeListener<Number> elementListener = this::elementValueChanged;
private final WeakChangeListener<Number> weakElementListener =
new WeakChangeListener<>(elementListener);
public SomeClass() {
propertySet.addListener(this::propertySetChanged);
}
private void propertySetChanged(SetChangeListener.Change<? extends DoubleProperty> change) {
if (change.wasRemoved()) {
change.getElementRemoved().removeListener(weakElementListener);
setTotal(getTotal() - change.getElementRemoved().get());
}
if (change.wasAdded()) {
change.getElementAdded().addListener(weakElementListener);
setTotal(getTotal() + change.getElementAdded().get());
}
}
private void elementValueChanged(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue) {
setTotal(getTotal() - oldValue.doubleValue() + newValue.doubleValue());
}
}
Here the SetChangeListener, whose value is a method reference to propertySetChanged, watches for any changes to the ObservableSet. When a DoubleProperty is added it adds said property's value to the current total. When a DoubleProperty is removed it subtracts said property's value from the current total. This listener also adds or removes a ChangeListener to or from the DoubleProperty when it is added or removed from the ObservableSet, respectively.
The ChangeListener, whose value is a method reference to elementValueChanged, updates the total property when the value of any DoubleProperty changes. It does this by first subtracting the old value and then adding the new value to the current total. It is actually the WeakChangeListener, which wraps the original ChangeListener, that is added or removed. This helps avoid potential memory leaks. Remember to maintain a strong reference to the original ChangeListener when using WeakChangeListener otherwise the original ChangeListener may be garbage collected too soon.
A second option is to rebuild a binding every time the ObservableSet is invalidated and then bind the total property to said binding.
import javafx.beans.Observable;
import javafx.beans.binding.DoubleExpression;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyDoubleWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableSet;
public class SomeClass {
private final ReadOnlyDoubleWrapper total = new ReadOnlyDoubleWrapper(this, "total");
private void setTotal(double total) { this.total.set(total); }
public final double getTotal() { return total.get(); }
public final ReadOnlyDoubleProperty totalProperty() { return total.getReadOnlyProperty(); }
private final ObservableSet<DoubleProperty> propertySet = FXCollections.observableSet();
public SomeClass() {
propertySet.addListener(this::propertySetInvalidated);
}
private void propertySetInvalidated(Observable observable) {
if (propertySet.isEmpty()) {
total.unbind();
setTotal(0.0);
} else if (propertySet.size() == 1) {
total.bind(propertySet.iterator().next());
} else {
DoubleExpression sum = null;
for (DoubleProperty property : propertySet) {
sum = (sum != null) ? sum.add(property) : property;
}
total.bind(sum);
}
}
}
In this case we add an InvalidationListener to the ObservableSet. This listener will be invoked whenever an element(s) is added to or removed from the ObservableSet. When this happens 1 of 3 things will happen:
If the ObservableSet is now empty unbind the total property and set it to zero.
This is a special case to deal with no elements
If the ObservableSet now only contains a single element simply bind the total property to said element.
Another special case dealing with a single element. It stops us from creating unnecessary objects and preforming unnecessary computations that would happen if we just skipped to the third branch.
Otherwise create one big binding that calculates the sum and then bind total to that binding.
This branch uses DoubleExpression.add(ObservableNumberValue). The resulting DoubleBinding from that method call will update whenever one of the two observables change. This is reduced into a single DoubleExpression which we then bind the total property to.
This second option will be less efficient because it requires iterating the entire ObservableSet every time. It also potentially leads to a lot of DoubleBinding objects being created. However, you may find it simpler to code/understand and the performance hit may not be significant enough for your application.
I would give the itemSet a change listener and just recalculate the total with some method every time its called.
See https://stackoverflow.com/a/44141262/8729420.
Greetings dear Stackoverflowians,
A couple of months ago I was dealing with a ILazyTreeContentProvider, and finally fixed it as per Eclipse RCP - ILazyTreeContentProvider implementation is unexpectedly eager
But I am facing the exact same problem with a ILazyContentProvider, and despite having followed similar steps as with the tree, I am at a loss.
In this table I am adding around 1000 elements per second in the table, and triggering a refresh via setItemCount() on the viewer every 100 ms.
The window size is smaller than 100 rows, and hence the updateElement() method should not start from the first index every time I call setItemCount() on the viewer.
Unfortunately, though, it does. It updates from 0 till the last index, each time.
Here's the code:
package manyelementscontentprovider;
import java.util.List;
import java.util.Vector;
import org.eclipse.jface.viewers.ILazyContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
public class LargeDataSetTable {
private class MyContentProvider implements ILazyContentProvider {
private TableViewer viewer;
public List<MyEntity> elements;
private int lastIndex=0;
public MyContentProvider(TableViewer viewer) {
this.viewer = viewer;
}
public void dispose() {
}
#SuppressWarnings("unchecked")
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
this.elements = (List<MyEntity>) newInput;
}
#Override
public void updateElement(int index) {
System.out.println(index);
if (!viewer.isBusy())
viewer.replace(elements.get(index), index);
}
}
public static class MyEntity {
public int counter;
public MyEntity(int counter) {
this.counter = counter;
}
public String toString() {
return "Item " + this.counter;
}
}
List<MyEntity> model;
private int counter;
private Display display;
private TableViewer v;
public LargeDataSetTable(Shell shell, Display display) {
model = createModel();
this.display=display;
v= new TableViewer(shell, SWT.VIRTUAL);
v.setLabelProvider(new LabelProvider());
v.setContentProvider(new MyContentProvider(v));
v.setInput(null);
v.setUseHashlookup(true);
counter = 0;
v.setInput(model);
v.setItemCount(model.size());
v.getTable().setLinesVisible(true);
}
private void startSomeShit() {
final Runnable gooeiUpdate = new Runnable() {
#Override
public void run() {
long timeA = System.currentTimeMillis();
v.setItemCount(counter);
v.setSelection( new StructuredSelection( model.get(counter-1) ), true );
v.setSelection(null);
long timeB = System.currentTimeMillis();
System.out.println("Paint lasted:"+(timeB-timeA));
}
};
Runnable addThingsToModel = new Runnable() {
public void run() {
long currentTime=System.currentTimeMillis();
long howManyGotIn =0;
while (counter<4000000) {
for (int i = 0; i< 10; i++){
final MyEntity m = new MyEntity(counter);
model.add(m);
counter++;
}
if (System.currentTimeMillis()-currentTime>100) {
howManyGotIn=counter - howManyGotIn;
display.syncExec(gooeiUpdate);
currentTime=System.currentTimeMillis();
System.out.println("How many got in = "+howManyGotIn);
howManyGotIn=counter;
}
try {
Thread.sleep(0,25);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread th = new Thread(addThingsToModel);
th.start();
}
private List<MyEntity> createModel() {
List<MyEntity> list = new Vector<MyEntity>(4000000);
return list;
}
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new FillLayout());
LargeDataSetTable viewerCica = new LargeDataSetTable(shell,display);
shell.open();
viewerCica.startSomeShit();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
}
Any sort of suggestions, opinions and options are very appreciated. You guys rock!
The javadoc for
TableViewer.setSelection(ISelection selection, boolean reveal)
states the following:
Sets a new selection for this viewer and optionally makes it visible. The TableViewer implementation of this method is inefficient for the ILazyContentProvider as lookup is done by indices rather than elements and may require population of the entire table in worse case.
Use Table#setSelection(int[] indices) and Table#showSelection() if you wish to set selection more efficiently when using a ILazyContentProvider.
Therefore, you could write something like this:
v.getTable().setSelection(counter - 1);
v.getTable().showSelection();
Using this approach, the paint operation takes an average time of 10ms.
Here is some code snippet from the AbstractTableViewer#virtualSetSelectionToWidget(List list, boolean reveal), which is called, when you use v.setSelection(new StructuredSelection(model.get(counter - 1)), true);:
if (getContentProvider() instanceof ILazyContentProvider) {
ILazyContentProvider provider = (ILazyContentProvider) getContentProvider();
// Now go through it again until all is done or we are no longer
// virtual
// This may create all items so it is not a good
// idea in general.
// Use #setSelection (int [] indices,boolean reveal) instead
for (int i = 0; virtualElements.size() > 0 && i < doGetItemCount(); i++) {
provider.updateElement(i);
Item item = doGetItem(i);
if (virtualElements.contains(item.getData())) {
indices[count++] = i;
virtualElements.remove(item.getData());
if (firstItem == null) {
firstItem = item;
}
}
}
}
as you can see it always iterates over all elements (confessing, that It might not be the best idea), as per Eclipse 3.x. Tree viewer has different implementation (which is actually understandable, that there you actually have kind of visibility levels and in table you don't have those).
I think, that refreshing of elements in general could be handled without dependency on content provider, so that only visible elements are refreshed (at least on demand).
So I'm making a little sample application that displays train times.
Right now the JTable in which the trains are displayed isn't dynamic.
What I'd like, is for the time to be checked every 30 secs or minute and the table would eliminate a train that "should have arrived".
I'm stuck however at the constantly checking time part. I've created a TimeChecker class to run in the background but it's not much use as when I put a infinite loop or thread into my JPanel class, the user interface doesn't show.
Here's my TimeChecker:
package controller;
import java.text.SimpleDateFormat;
import java.util.Calendar;
public class TimeChecker extends Thread {
private String timeStamp;
public String getTimeStamp() {
return timeStamp;
}
public void run() {
while(true) {
try {
Thread.sleep(2000);
String dateStamp = new SimpleDateFormat(
"yyyyMMdd_HHmmss").format(
Calendar.getInstance().getTime());
timeStamp = dateStamp.substring(9,13);
System.out.println(timeStamp);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
and here's my Panel class:
package view;
import java.awt.GridLayout;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import controller.*;
public class Panel extends JPanel {
/* JPanel containing the JTabbedPane stuff */
JTabbedPane jtp = new JTabbedPane();
TimeChecker tc = new TimeChecker();
public Panel() {
super(new GridLayout(1,0));
Table t = new Table("Haymarket");
jtp.add(t);
jtp.setTitleAt(0, "Haymarket");
add(jtp);
tc.setDaemon(true);
tc.start();
}
public void addTrain(String d, String c, String dep, String a) {
/*
* Receives 4 parameters to control
* the new entry required for a train
*/
Table t = (Table) jtp.getComponentAt(jtp.getSelectedIndex());
t.addTrain(d,c,dep,a);
}
public void addStation(String s) {
/*
* Adds a station, after prompting the
* user for a station name
*/
jtp.add(new Table(s));
jtp.setTitleAt(jtp.getTabCount()-1, s);
jtp.setSelectedIndex(jtp.getTabCount()-1);
}
public void addStation(Table t) {
jtp.add(t);
jtp.setTitleAt(jtp.getTabCount()-1,t.getStation());
}
public void removeAllStations() {
jtp.removeAll();
}
public void removeStation() {
/*
* Removes currently selected station
*/
jtp.remove(jtp.getSelectedIndex());
}
public void removeTrain() {
/*
* WIP: Removes a train
*/
Table t = (Table) jtp.getComponentAt(jtp.getSelectedIndex());
t.removeTrain();
}
}
Sounds like you want a javax.swing.Timer to fire every 30 seconds.
Alternatively, use a timer in a background thread (e.g. via a ScheduledExecutorService) - then fetch the information and only get the UI involved when you actually need to update the UI.
You've created a thread from the UI, which means that the thread will run with the same priority as the UI. You should not do that, but use SwingUtilities instead.
This is a HW problem. I keep getting the following error on screen related to my if(i==3) statement...
"Exception in thread "AWT-EventQueue-1" java.lang.NullPointerException
at ui.panels.ChoicePanel$1.itemStateChanged(ChoicePanel.java:31)"
...Can someone point me in the right direction? Here is my code. Thanks for your time.
package ui.panels;
import java.awt.Choice;
import java.awt.Panel;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import model.Model;
import interfaces.Resettable;
public class ChoicePanel extends Panel implements Resettable{
public int i = 0;
/**
*
*/
private static final long serialVersionUID = 1L;
Model model;
Choice selection;
public ChoicePanel(Model mdl) {
selection = new Choice();
for (String msg : Model.selections) {
selection.add(msg);
}
selection.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
if(i==3) {//drop down clicked three times)
System.out.println("PING");
}else{
model.setMessage(selection.getSelectedItem());
//this line is what sends a value to shape that is drawn on screen
//NOT line 36 of GUIDemo.java
//
model.setCurrentShapeType(selection.getSelectedItem());
model.repaint();
++i;
}
}
});
this.add(selection);
}
public void resetComponents() {
//this resets the drop down list selection array to the first choice on the list
selection.select(0);
//this sets selected item in the selection array set in the above line
model.setMessage(selection.getSelectedItem());
//model.repaint();
}
}
I believe you are not initializing model, which would give a NullPointerException when it was first dereferenced.
You did not initialize model. If i is not 3 the else-block will get executed, which contains model.setMessage(...) - but model does not yet "exist".