I need to call an API, get a response, add items to ComboBox and update the view in the plugin.
Attached is the image of the combobox
I need to update the thread Ids as they load from an API. My custom Combobox for this is as shown below. I am not sure how to update the custom component from outside the class. Any help?
public class MyComboBox extends AnAction implements CustomComponentAction {
#Override
public void actionPerformed(#NotNull AnActionEvent e) {
}
#Override
public #NotNull JComponent createCustomComponent(#NotNull Presentation presentation, #NotNull String place) {
ComboBox<String> jComboBox = new ComboBox<>();
jComboBox.setMinLength(100);
jComboBox.addItem("Thread Id: " + UUID.randomUUID().toString());
jComboBox.addItem("Thread Id: " + UUID.randomUUID().toString());
jComboBox.setEnabled(true);
return jComboBox;
}
}
I needed a reference to the already instantiated combobox.
I got it with
MyComboBox myComboBox = (MyComboBox) ActionManager.getInstance().getAction("searchThread")
I added another method in MyComboBox:
public void updateUI(List<String> ids) {
this.ids = ids;
String[] array = this.ids.toArray(new String[0]);
jComboBox.setModel(new DefaultComboBoxModel<>(array));
jComboBox.setSelectedIndex(0);
jComboBox.updateUI();
}
and used:
myComboBox.updateUI(newList);
That solved my problem.
Related
I have two ComboBoxes in my editable Grid where the second ComboBox based on the first one. So for example you would have Car Make and then Car Model. When you change the make combobox the model combobox then changes accordingly.
With that in mind I have:
ComboBox<String> makeComboBox = new ComboBox<String>();
ComboBox<String> modelComboBox = new ComboBox<String>();
Specifically:
grid.addColumn(CarRental::getPerson)
.setEditorBinding(binder.forField(personComboxBox).bind(CarRental::getPerson, CarRental::setPerson));
grid.addColumn(CarRental::getMake)
.setEditorBinding(binder.forField(makeComboxBox).bind(CarRental::getMake, CarRental::setMake));
grid.addColumn(CarRental::getModel)
.setEditorBinding(binder.forField(modelComboxBox).bind(CarRental::getModel, CarRental::setModel));
The key here is that I want the modelComboBox to change if the makeComboBox is changed. In other words if you select Honda then I want the model ComboBox to change to Fit, Civic, Accord, and so on. To do this I add a SelectionListener (but it could also be a ValueChangeListener, it doesn't matter, the effect is still the same).
Specifcally I have:
makeComboBox.addSelectionListener(event ->
{
modelComboBox.clear();
modelComboBox.setItems(getModelsBasedOnMake(makeComboBox.getValue()));
// Assuming someone has just edited the make value,
// say changed from Toyota to Honda, then I want the model selected to be empty
});
Because the ComboBox can be different I've added some logic to update the components on theOpenListenerfor the Grid Editor. Specifically I do:
grid.getEditor().addOpenListener(open ->
{
...
CarRental selectedCarRental = (CarRental)event.getBean();
makeComboBox.setItems(makeList);
modelComboBox.setItems(getModelsBasedOnMake(carRental.getMake()));
});
The problem here is that the modelComBoxbox tends to be unselected because if you look at it there's no guarantee which value it will be because there is a conflict.
I looked at temporarily disabling the selectionListener but this all the remove listeners have been deprecated with Vaadin 8. Therefore how can I setup the grid to be able to edit both the car make and model in the grid?
I tried it by a simple example. Looks ok for me. What is the exact problem? (I don't really get your sentence "because if you look at it there's no guarantee which value it will be because there is a conflict.")
#SpringUI
public class VaadinUI extends UI {
#Override
protected void init(VaadinRequest request) {
VerticalLayout layout = new VerticalLayout();
ComboBox<String> cmb1 = new ComboBox<>();
ComboBox<String> cmb2 = new ComboBox<>();
cmb1.setItems("1", "2", "3");
cmb1.addSelectionListener(event -> {
cmb2.clear();
cmb2.setItems(getCmb2Content(event.getValue()));
});
Grid<MyBean> grid = new Grid<>();
grid.setWidth("800px");
grid.setHeightByRows(10);
grid.addColumn(System::identityHashCode).setCaption("ID");
grid.addColumn(MyBean::getProp1).setCaption("Prop 1")
.setEditorBinding(grid.getEditor().getBinder().forField(cmb1).bind(MyBean::getProp1, MyBean::setProp1));
grid.addColumn(MyBean::getProp2).setCaption("Prop 2")
.setEditorBinding(grid.getEditor().getBinder().forField(cmb2).bind(MyBean::getProp2, MyBean::setProp2));
grid.setItems(new MyBean(), new MyBean(), new MyBean());
grid.getEditor().setEnabled(true);
layout.addComponent(grid);
setContent(layout);
}
private List<String> getCmb2Content(String cmb1Content) {
return Arrays.asList(cmb1Content + "1", cmb1Content + "2", cmb1Content + "3");
}
}
public static class MyBean {
private String prop1;
private String prop2;
public String getProp1() {
return prop1;
}
public void setProp1(String prop1) {
this.prop1 = prop1;
}
public String getProp2() {
return prop2;
}
public void setProp2(String prop2) {
this.prop2 = prop2;
}
}
When I add components to Vaadin's component (such as TabSheet or Tree) , the added components are cached. When user clicks the tab (or tree nodes) , if it contains db data , it shows stale data , not reflecting the latest db state.
I wonder if there is any way to ensure loading latest data ?
I solve the problem by defining my custom interface :
public interface Reloadable {
void reload();
}
And each component implements this Reloadable interface , such as :
#SpringComponent
public class TeachersView extends VerticalLayout implements Reloadable, Serializable {
#Inject
private TeacherDao teacherDao;
private final static int PAGESIZE = 10;
private MTable<Teacher> mTable = new MTable<>(Teacher.class);
#PostConstruct
public void init() {
// mTable settings skip here
reload();
addComponent(mTable);
}
#Override
public void reload() {
mTable.setBeans(new SortableLazyList<>(
sortablePagingProvider ,
() -> (int) teacherDao.count() ,
PAGESIZE
));
}
private SortableLazyList.SortablePagingProvider<Teacher> sortablePagingProvider =
(firstRow, asc, sortProperty) -> {
return teacherDao.findAll(
new PageRequest(
firstRow / PAGESIZE, PAGESIZE,
asc ? Sort.Direction.ASC : Sort.Direction.DESC,
sortProperty == null ? "id" : sortProperty
)
).getContent();
};
}
And this view is injected to UI class :
#SpringUI(path = "/ui")
#Theme("valo")
public class VaadinUI extends UI {
#Inject
private TeacherDao teacherDao;
#Inject
private TeachersView teachersView;
#Override
protected void init(VaadinRequest vaadinRequest) {
Panel panel = new Panel("Admin Panel");
HorizontalSplitPanel splitPanel = new HorizontalSplitPanel();
splitPanel.setSplitPosition(15, Unit.PERCENTAGE);
panel.setContent(splitPanel);
Tree tree = new Tree("Menu");
splitPanel.setFirstComponent(tree);
Label home = new Label("Home");
Map<String, Component> map = new HashMap<>();
map.put("Teachers", teachersView);
map.put("Home", home);
map.forEach((k, v) -> tree.addItem(k));
tree.addItemClickListener(event -> {
Component view = map.get(event.getItemId());
if (view instanceof Reloadable) {
((Reloadable) view).reload();
}
splitPanel.setSecondComponent(view);
});
splitPanel.setSecondComponent(home);
setContent(panel);
} // init()
}
Notice the tree.addItemClickListener , I have to check each component if it implements Reloadable , if true , invoke it.
It works . But I don't know if it the standard way achieving this ? I think it should be a common scenario , there should be something like built-in interface for Components to implement , such as onRender like that (but I cannot find one) . Did I miss anything ?
Thanks.
First of all I'm going to suggest this tutorial on Spring & Vaadin that you may have already seen, but I'll be referencing it in a few places and I think it's a good starting point for Vaadin & Spring integration.
Second, out of curiosity, why are you using a tree to build the menu?
In the example provided you seem to be modelling a navigation between some views feature, which is already available in Vaadin, and since you're using Spring, the Vaadin spring & spring-boot extensions makes it really easy to define and navigate between your views. Then you can define some specific behaviour for each view in their own enter() method. I've used the Vaadin dashboard demo as inspiration for the changes below:
#SpringView(name = TeachersView.NAME)
public class TeachersView extends VerticalLayout implements View {
public static final String NAME = "Teachers";
private Label title = new Label("Teachers view");
#PostConstruct
void init() {
addComponent(title);
}
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
// recreate or reload stuff here
title.setValue("Teachers view reloaded # " + new Date());
}
}
#SpringView(name = HomeView.NAME)
public class HomeView extends VerticalLayout implements View {
public static final String NAME = "";
#PostConstruct
void init() {
addComponent(new Label("Home"));
}
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
// meh, nothing special to do here
}
}
public class SpringVaadinUI extends UI {
#Autowired
private SpringViewProvider viewProvider;
#Override
protected void init(VaadinRequest vaadinRequest) {
addStyleName(ValoTheme.UI_WITH_MENU);
Panel panel = new Panel("Admin Panel");
HorizontalSplitPanel splitPanel = new HorizontalSplitPanel();
splitPanel.setSplitPosition(15, Unit.PERCENTAGE);
panel.setContent(splitPanel);
VerticalLayout navigationBar = new VerticalLayout();
navigationBar.setPrimaryStyleName(ValoTheme.MENU_ROOT);
navigationBar.addComponent(createNavigationButton("Home", FontAwesome.HOME, HomeView.NAME));
navigationBar.addComponent(createNavigationButton("Teachers", FontAwesome.GROUP, TeachersView.NAME));
splitPanel.setFirstComponent(navigationBar);
CssLayout navigationDisplay = new CssLayout();
splitPanel.setSecondComponent(navigationDisplay);
Navigator navigator = new Navigator(this, navigationDisplay);
navigator.addProvider(viewProvider);
setContent(panel);
}
private Button createNavigationButton(String caption, FontAwesome icon, final String viewName) {
Button button = new Button(caption, icon);
button.setPrimaryStyleName(ValoTheme.MENU_ITEM);
button.addStyleName(ValoTheme.BUTTON_SMALL);
button.addStyleName(ValoTheme.BUTTON_BORDERLESS);
button.addClickListener(event -> getUI().getNavigator().navigateTo(viewName));
return button;
}
}
The result is similar to:
If for some reason you can't or don't want to use the navigator, then your solution looks fine. Nonetheless, whichever solution you chose to use, you should know that by default Spring creates singletons. Except a few such as the UI, you should probably change your components to prototypes so you'll get a new instance each time. Otherwise all your users will get the same instances when accessing the application, which I don't think you want to happen.
I want to filter GXT ComboBox Store. for example if I type 'st' in combobox I want combobox to show only values that contain 'st'
Here is my implementation
combo = new ComboBox<MerchantDTO>(store, label);
StoreFilter<MerchantDTO> filter = new StoreFilter<MerchantDTO>() {
#Override
public boolean select(Store<MerchantDTO> store, MerchantDTO parent, MerchantDTO item) {
boolean canView = (item.getName() != null && item.getName().toLowerCase().contains(combo.getText().toLowerCase()));
return canView;
}
};
store.setEnableFilters(true);
store.addFilter(filter);
This filter works and shows correct values, But combobox's dropdown list does not open automatically. I have to click on combobox manually to open dropdown list and see filtered results. I am using GXT 3.1.0 and GWT 2.7.0
I tried using combo.expand(); function but it didnt open dropdown list.
Any help would be appreciated.
I found solution. Here is sample how to add custom filter to GXT (version 3.1.0) ComboBox
1) Create class which extends ListStore and add String variable for user input text
public abstract class XListStore<M> extends ListStore<M> {
private String userText;
public XListStore(ModelKeyProvider<? super M> keyProvider) {
super(keyProvider);
}
#Override
protected boolean isFilteredOut(M item) {
return filter(item);
}
public abstract boolean filter(M item);
public String getUserText() {
return userText;
}
public void setUserText(String userText) {
this.userText = userText;
}
}
2) Initialize custom list store and implement filter method
XListStore<SampleDTO> store = new XListStore<SampleDTO>(new ModelKeyProvider<SampleDTO>() {
#Override
public String getKey(SampleDTO item) {
return item.getId();
}
}) {
public boolean filter(SampleDTO item) {
boolean result = false;
//Write you filter logic here
return result;
}
};
store.setEnableFilters(true);
3) Initialize ComboBox and add Key up handler
ComboBox<SampleDTO> comboBox = new ComboBox<SampleDTO>(store, label);
comboBox.addKeyUpHandler(new KeyUpHandler() {
#Override
public void onKeyUp(KeyUpEvent event) {
store.setUserText(comboBox.getText());
}
});
Done. Now ComboBox will filter store according to user input and will open dropdown window automatically
I am using glazedlists for JComboBox in java to make a JComboBox searchable and sortable. But there is situation I can't get to solve it.
I have a JComboBox attached with glazedlists. glazedlists takes String array to fill this combobox and make it searchable like this
String[] Values = {"ABC", "DEF", "GHI", "JKL", "MNO"};
JComboBox cmb = new JComboBox();
AutoCompleteSupport.install(cmb , GlazedLists.eventListOf(Values));
This works good but the problem is that I want to add ID along with value coming from Database and for that I am implementing my own custom ListCellRenderer like this
class MyListRenderer extends JLabel implements ListCellRenderer
{
#Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
Object[] itemData = (Object[])value;
setText((String)itemData[1]);
return this;
}
}
cmb.setRenderer(new MyListRenderer());
And to add value in JComboBox, I have to
while(rs.next()){
int id=rs.getInt("company_id");
String category=rs.getString("company_name");
Object[] itemData = new Object[] {id, category};
cmb.addItem(itemData);
}
Now how can I implement my JComboBox with glazedlists while setting my own custom renderer?
Didn't have any success with your way, but I found a solution in an earlier project. You can set the model of the JComboBox instead by doing something like this:
//load the list of objects to use
ContainerClass[] allOptions = ContainerClass.getAll();
//create an EventList with this list and set is as the combobox model
final EventList<ContainerClass> eventList = GlazedLists.eventList(Arrays.asList(allOptions));
comboBox.setModel(new DefaultComboBoxModel<ContainerClass>(allOptions));
//finally initialize the combobox by SwingUtilities if done on a non-UI thread
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
AutoCompleteSupport<ContainerClass> install =
AutoCompleteSupport.install(comboBox, eventList);
install.setFilterMode(TextMatcherEditor.CONTAINS);
install.setHidesPopupOnFocusLost(true);
install.setSelectsTextOnFocusGain(false);
install.setCorrectsCase(false);
}
});
And with a ContainerClass like:
class ContainerClass{
int id;
String company;
//helper method to get all the objects needed
static ContainerClass[] getAll(){
ContainerClass test = new ContainerClass();
test.id = 2;
test.company = "abc";
return new ContainerClass[]{test,test,test};
}
#Override
//this String is what actually will be displayed in the combobox
public String toString(){return "(" + id + ") " + company;}
}
And I'm assuming that your JComboBox has the following type:
JComboBox<ContainerClass> comboBox;
(I had to obfuscate the names of all variables, so there might be errors in the code. Let me know and I will correct them)
So to recap. GlazedLists uses the model to get the names, which again asks the ContainerClass for it's toString() method which will return the name to display in the JComboBox.
As a note, when you call comboBox.getSelectedItem() it will return an object of type ContainerClass, or null if it isn't a valid selection.
UPDATE
If you want to be able to control the order as well as the name, you would need to implement your own model for the ComboBox. Found this that seems to explain it well:
class MyComboBoxModel extends AbstractListModel implements ComboBoxModel {
String[] ComputerComps = { "Monitor", "Key Board", "Mouse", "Joy Stick",
"Modem", "CD ROM", "RAM Chip", "Diskette" };
String selection = null;
public Object getElementAt(int index) {
return ComputerComps[index];
}
public int getSize() {
return ComputerComps.length;
}
public void setSelectedItem(Object anItem) {
selection = (String) anItem; // to select and register an
} // item from the pull-down list
// Methods implemented from the interface ComboBoxModel
public Object getSelectedItem() {
return selection; // to add the selection to the combo box
}
}
I am developing my GUI according to the MVC pattern:
-GUIview: Swing components (JFrame and several JTables).
-GUIcontroller: listeners (added here, and defined here in Inner classes)
-GUImodel: modify and store data, firing change-events.
Changes in the model are passed to the view through the controller (and not directly), like in this example.
I have also written different customized JTableModels (extending AbstractTableModel) for the different JTables contained in the View class. All the JTableModels are defined in different classes within the package "GUImodel". Each JTableModel defines an ArrayList and a few methods to manipulate the ArrayList.
According to the MVC guidelines, the Model should know nothing about the view. In fact, the main() method is defined as follows:
GUImodel model = new GUImodel();
GUIcontroller controller = new GUIcontroller();
GUIview view = new GUIview(controller, model);
controller.addView(view);
controller.addModel(model);
view.setVisible(true);
controller.addControllerListerners();
My problem is:
When I am executing a method within the GUImodel (for example because a JButton has been pressed and I need to load data from an external file), I need to modify some JTableModels (to add data/rows to its ArrayList) and get the changes reflected in the JTable. My first idea would be:
ArrayList newArrayList = fileLoader(filePath); //create ArrayList and load info
guiView.getTable1Model().updateArrayList(newArrayList); //update JTableModel ArrayList
However, this approach is not valid, since GUImodel should be totally independent of GUIview.
Any idea?
It may be good to realize that MVC is primarily a pattern concerned with data encapsulation, which uses another pattern, Observer, to communicate changes. As data encapsulator, the Model knows nothing of Views and Controllers, but as an Observable it does know that it has Observers, which need to be notified when a change occurs.
A Description of the Model-View-Controller User Interface Paradigm in the Smalltalk-80 System, page 4 explains it well:
To manage change notification, the notion of objects as dependents was developed. Views and
controllers of a model are registered in a list as dependents of the model, to be informed whenever
some aspect of the model is changed. When a model has changed, a message is broadcast to notify
all of its dependents about the change. This message can be parameterized (with arguments), so
that there can be many types of model change messages. Each view or controller responds to the
appropriate model changes in the appropriate manner.
To illustrate the concept, you can start out with your own Observer/Observable classes:
public interface Observer {
public void update(int message);
}
public interface Observable {
public void registerObserver(Observer observer);
}
public class Model implements Observable {
List<Observer> observers;
public void registerObserver(Observer observer) {
observers.add(observer);
}
public void loadFile(String path) {
// load file and change data
foreach (Observer observer: observers)
observer.update(READ_NEW_DATA);
}
public ArrayList getData() { return data; }
}
public class View implements Observer {
public void update(int message) {
doWhateverWith(model.getData());
}
}
public class Controller implements Observer {
public void update(int message) {
doWhateverWith(model.getData());
}
public void onClick() {
model.loadFile("someFile");
}
}
As you can see, the Model knows nothing of the internal workings of the Views and Controllers. It doesn't even know if returning an ArrayList will be particularly useful to them (although in practice you'd like for that to be the case). So in this regard, independence is achieved.
There is no independence in the communication between the Obervable and Observers, but that isn't part of the requirements of the MVC pattern.
If you want your GUI to hitchhike on top of the existing Swing Observer pattern (Listeners), then your classes should inherit from the appropriate classes:
public class Model extends AbstractTableModel...
public class View implements TableModelListener...
public class Controller implements CellEditorListener...
Etcetera. Since JTable implements both TableModelListener and CellEditorListener, it is actually a composite of View and Controller. So you have the choice to either have a combined ViewController class extend JTable, or to have them separately. In the latter case, the View could extend JTable, overriding the control Listeners, so that they pass their events to the Controller class. But that sounds like more work than it's worth.
As discussed here, you are correct to loosely couple the model and view. JTable implements TableModelListener to listen to its own model, and your AbstractTableModel no doubt fires events that cause the listening table to update itself.
In this case, let the dependent TableModel add itself as a TableModelListener to the master TableModel. The dependent model can then fire the events needed to notify it own listeners of changes propagated from the master.
However, this approach is not valid, since GUImodel should be totally independent of GUIview.
The Swing Components themselves use the MVC model. Changes in the model have to trigger changes in the view. The question is how do you do this?
One way is for the model to have access to the view instance(s), as you've illustrated in your question.
ArrayList newArrayList = fileLoader(filePath); //create ArrayList and load info
guiView.getTable1Model().updateArrayList(newArrayList); //update JTableModel ArrayList
Another way is for the controller to update the model and update the view. This is what I usually do in a Swing application.
model.loadArrayList(filePath);
frame.getFrame().getMainPanel().repaint();
Another way is to fire actions. This is how the Swing components update the GUI.
ArrayList newArrayList = fileLoader(filePath); //create ArrayList and load info
fireAction(newArrayLiat);
The fireAction method would work with listeners. Here's a fire method I copied from AbstractListModel.
protected void fireContentsChanged(Object source, int index0, int index1) {
Object[] listeners = listenerList.getListenerList();
ListDataEvent e = null;
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == ListDataListener.class) {
if (e == null) {
e = new ListDataEvent(source,
ListDataEvent.CONTENTS_CHANGED, index0, index1);
}
((ListDataListener) listeners[i + 1]).contentsChanged(e);
}
}
}
You would have to write listeners in your model classes that the view classes can write code to change the view.
The Javadoc for the EventListenerList has more information about listeners. Thanks
Catalina Island.
My style of MVC in swing is, the model and the view is oblivious of each other as well as of the controller, but the controller knows the view and the model soo well. This way, I do all of the logic in the controller. I just left the long codes of UI + complex layouts in the view and think of all the data that the application will need for the model & decide whether a certain data should appear in my view. I put the functionality of adding listeners to the buttons, etc. to the controller via view.getBtn().setAction(new ActionForThisOrThatInnerClass()) kind of stuff
In your case, I agree that the data that the table will use should be stored in your main model in the form of, ideally, a List, but I would not bother myself to subclass a new TableModel to handle those data, I think the DefaultTableModel is powerful enough to do a lot.
Here is the runnable example if I am to code your requirements
public class Sample {
public static void main(String[] args){
View view = new View();
Model model = new Model();
Controller controller = new Controller(view, model);
JFrame frame = new JFrame("MVC Demo");
frame.getContentPane().setLayout(new BorderLayout());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(view.getUI());
frame.pack();
frame.setVisible(true);
view.getBtnFileLoader().doClick();
}
}
class View{
private JButton btnFileChooser;
private JButton btnFileLoader;
private JTable tblData;
private JPanel pnlMain;
public View(){
pnlMain = new JPanel(new BorderLayout()){
#Override public Dimension getPreferredSize(){
return new Dimension(300, 400);
}
};
JPanel pnlFileLoader = new JPanel();
pnlFileLoader.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
pnlFileLoader.setLayout(new BoxLayout(pnlFileLoader, BoxLayout.LINE_AXIS));
JTextField txtFileDir = new JTextField();
pnlFileLoader.add(txtFileDir);
btnFileLoader = new JButton();
pnlFileLoader.add(btnFileLoader);
btnFileChooser = new JButton();
pnlFileLoader.add(btnFileChooser);
tblData = new JTable();
JScrollPane pane = new JScrollPane(tblData);
pnlMain.add(pane);
pnlMain.add(pnlFileLoader, BorderLayout.PAGE_START);
}
public JPanel getUI(){
return pnlMain;
}
public JButton getBtnFileLoader(){
return btnFileLoader;
}
public JButton getBtnFileChooser(){
return btnFileChooser;
}
public JTable getTblData(){
return tblData;
}
}
class Controller implements PropertyChangeListener{
private View view;
private Model model;
private DefaultTableModel tmodel;
public Controller(View view, Model model){
this.view = view;
this.model = model;
model.addModelListener(this);
setupViewEvents();
setupTable();
}
private void setupTable(){
tmodel = new DefaultTableModel();
tmodel.addColumn("First Name");
tmodel.addColumn("Last Name");
tmodel.addColumn("Occupation");
view.getTblData().setModel(tmodel);
}
private void setupViewEvents(){
view.getBtnFileChooser().setAction(new AbstractAction("Choose"){
#Override
public void actionPerformed(ActionEvent arg0) {
//choose the file then put the dir
//in the txtfield
}
});
view.getBtnFileLoader().setAction(new AbstractAction("Load"){
#Override
public void actionPerformed(ActionEvent arg0) {
//validate if the dir in the textfield exists and the file is loadable
//load the file specified in the textfield
//assumming the list is already retrieved from the file
//and the list contains the following person
List<Person> list = new ArrayList<Person>();
Person p1 = new Person("Bernardo", "Santos", "Developer");
Person p2 = new Person("Robert", "Erasquin", "Architect");
Person p3 = new Person("Klarrise", "Caparas", "Food Scientist");
list.add(p1);
list.add(p2);
list.add(p3);
//now update the model of the new value for the list
model.setTheList(list);
}
});
}
#Override
#SuppressWarnings("unchecked")
public void propertyChange(PropertyChangeEvent evt) {
if(evt.getPropertyName().equals("theList")){
List<Person> newVal = (List<Person>) evt.getNewValue();
DefaultTableModel tmodel = (DefaultTableModel)view.getTblData().getModel();
for(Person p : newVal){
tmodel.addRow(new Object[]{p.getFirstName(), p.getLastName(), p.getOccupation()});
}
}
}
}
class Model{
private List<Person> theList;
private SwingPropertyChangeSupport propChangeFirer;
public Model(){
propChangeFirer = new SwingPropertyChangeSupport(this);
}
public void setTheList(List<Person> theList){
List<Person> oldVal = this.theList;
this.theList = theList;
//after the model has been updated, notify its listener about
//the update, in our case the controller itself listens to the model
propChangeFirer.firePropertyChange("theList", oldVal, theList);
}
public void addModelListener(PropertyChangeListener prop) {
propChangeFirer.addPropertyChangeListener(prop);
}
}
class Person{
private String firstName;
private String lastName;
private String occupation;
public Person(String firstName, String lastName, String occupation){
this.firstName = firstName;
this.lastName = lastName;
this.occupation = occupation;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getOccupation() {
return occupation;
}
public void setOccupation(String occupation) {
this.occupation = occupation;
}
}