Im using a org.eclipse.jface.viewers.ComboViewer to show Names of some objects. If i have like hundreds of those objects it gets very difficult to find the right object very quickly.
By default the ComboViewer filters only for the first entered "Letter" but that is not the best solution for me, cause i might have Objects with names like:
"MyObject 1"
"MyObejct 2"
"MyObject 3"
and so on.
Currently im having this:
List<MyObject> myObjects = new ArrayList<MyObject>();
myObjects.add(MyObject("One"));
myObjects.add(MyObject("Two"));
myObjects.add(MyObject("ThreeOne"));
myObjects.add(MyObject("ThreeTwo"));
ComboViewer comboViewer = new ComboViewer(parent, SWT.DROP_DOWN | SWT.READ_ONLY);
comboViewer.setContentProvider(ArrayContentProvider.getInstance());
comboViewer.setLabelProider(new LabelProvider(){
#Override
public String getText(final Object element){
if(element instanceof MyObject){
return MyObject.getName();
}
return super.getText(element);
}
comboViewer.setInput(myObjects);
Now i want to achieve the following.
When the User hits "O" then the MyObject "One" should be selected in the dropdownlist. When he hits "Th" then the MyObject "ThreeOne" should be selected in the dropdownlist.
When entering "ThreeT" then the MyObejct "ThreeTwo" should be selected and so on.
I hope it is clear what i mean with this.
I already tried org.eclipse.jface.fieldassist.AutoCompleteField but that does not always filter the correct Items. Cause there can be empty spaces in the MyObject's Name.
A simple Textfield with Autocompletion oder Autosuggest is not an option for me. I need this ComboViewer.
If you need any further infos please let me know.
EDIT:
I now have come pretty far.
comboViewer.getCombo().addKeyListener(new KeyListener() {
#Override
public void keyPressed(final KeyEvent e) {
}
#Override
public void keyReleased(final KeyEvent e) {
if (e.keyCode == SWT.ESC) {
myFilter.clearSearchString();
} else if (e.keyCode >= 97 && e.keyCode <= 122) {
myFilter.appendToSearchString(e.character);
} else if (e.keyCode >= 48 && e.keyCode <= 57) {
myFilter.appendToSearchString(e.character);
}
comboViewer.refresh();
}
});
The only thing I'm missing is: I'd like to see the entered Text somewhere in the ComboViewer.
Your ComboViewer supports filtering of data via the setFilters() or addFilter() methods. These methods expect ViewerFilter objects as arguments.
For each registered ViewerFilter object the select() method is called. The method returns true if the data should be shown and false if it should be filtered.
So add the filter once.
Then add a KeyListener to your
control updating the filter and trigger a viewer.refresh() afterwards.
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
public class PersonFilter extends ViewerFilter {
private String searchString;
public void setSearchText(String s) {
// ensure that the value can be used for matching
this.searchString = ".*" + s + ".*";
}
#Override
public boolean select(Viewer viewer,
Object parentElement,
Object element) {
if (searchString == null || searchString.length() == 0) {
return true;
}
Person p = (Person) element;
if (p.getFirstName().matches(searchString)) {
return true;
}
if (p.getLastName().matches(searchString)) {
return true;
}
return false;
}
}
Related
I have a DAOImplementation class with the method definition below.
#Override
public Registration getRegistrationInfoById(int aRegistrationId) {
String SQL = "{CALL getRegistrationInfoById(?)}";
Registration aRegistration = new Registration();
try (Connection con = DBUtil.getConnection(DBType.MYSQL);
CallableStatement cs = con.prepareCall(SQL);) {
cs.setInt(1, aRegistrationId);
try (ResultSet rs = cs.executeQuery();) {
while (rs.next()) {
int gradeLevel = Integer.parseInt(rs.getString(RegistrationTable.GRADELEVEL));
aRegistration.setGradeLevel(gradeLevel);
}
}
} catch (SQLException e) {
JOptionPane.showMessageDialog(null, e.getErrorCode() + "\n" + e.getMessage());
}
return aRegistration;
}//end of method
This returns an integer value of Grade Level (1,2,3,4,5,6,7...so on...) which I've verified because I tried printing the output returned by aRegistration.getGradeLevel();
Now my problem is with my JComboBox. I have set a ListCellRenderer for my JComboBox which holds all the GradeLevel values
public class JComboBoxRenderer_GradeLevel extends JLabel implements ListCellRenderer<Object> {
public JComboBoxRenderer_GradeLevel() {
this.setOpaque(true);
}
#Override
public Component getListCellRendererComponent(JList<? extends Object> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
if (value instanceof GradeLevel) {
this.setText("" + ((GradeLevel) value).getGradelevel());
} else {
this.setText("--");
}
if (isSelected) {
this.setBackground(Color.YELLOW);
this.setForeground(list.getSelectionForeground());
} else {
this.setBackground(list.getBackground());
this.setForeground(list.getForeground());
}
return this;
}
}
And looks like this JComboBox as expected. (GradeLevel model is renderered to simply show an int value of gradelevel), ((GradeLevel) value).getGradelevel());returns an integer value.
I understand that even when JComboBox has its renderer that displays an integer value of GradeLevel by using ((GradeLevel)value).getGradeLevel(), the actual value on the JComboBox is still treated as instance of GradeLevel or object. But not a String or int.
So my problem is when I try to set the selected value to an int value, it won't change the selected value of the JComboBox. Nothing happens when I use setSelectedItem();
This is what I tried to do for the GUI.
//Grade Level
GradeLevelDaoImpl gldi = new GradeLevelDaoImpl();
List<GradeLevel> gradeLevels = gldi.getAllGradeLevelsInfo();
DefaultComboBoxModel gradeLevelModel = new DefaultComboBoxModel(gradeLevels.toArray());
jcmbGradeLevel.setModel(gradeLevelModel);
jcmbGradeLevel.setRenderer(new JComboBoxRenderer_GradeLevel());
jcmbGradeLevel.setSelectedIndex(-1);
GradeLevel gradeLevel = new GradeLevel();
gradeLevel.setGradelevel(registration.getGradeLevel());
jcmbGradeLevel.setSelectedItem(gradeLevel); //PROBLEM HERE, it doesn't change
JOptionPane displays this.
JOptionPane.showMessageDialog(null,"GradeLevel: "+gradeLevel);
JOptionPane.showMessageDialog(null,"GradeLevel: "+gradeLevel.getGradeLevel());
It doesn't seem to be able to compare the object I'm trying to set it to(gradeLevel) with the objects JComboBox has(gradeLevels). Notice the singular and plural.
How do I manipulate the types so that setSelectedItem() will match with what the JComboBox have?
Thanks.
If you want to do this by using different instances of the object, but with the same properties, then you need to override the class's equals and hashcode methods, so that the combination of properties are unique. This is very important, this is a relationship expectation that any object which is equal to another will have the same hashcode
This is a really quick example and I used by IDE's auto generation process (because I'm lazy), but, if your Registration class has other properties which should be considered when comparing to instances of the class, you will need to modify it to support them (again, any good IDE should be able to do this)
public class Registration {
private int gradeLevel;
public Registration(int gradeLevel) {
this.gradeLevel = gradeLevel;
}
public int getGradeLevel() {
return gradeLevel;
}
public void setGradeLevel(int gradeLevel) {
this.gradeLevel = gradeLevel;
}
#Override
public int hashCode() {
int hash = 7;
hash = 73 * hash + this.gradeLevel;
return hash;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Registration other = (Registration) obj;
if (this.gradeLevel != other.gradeLevel) {
return false;
}
return true;
}
}
Then using something like...
Registration a = new Registration(1);
Registration b = new Registration(1);
Registration c = new Registration(2);
System.out.println(a.equals(b));
System.out.println(a.equals(c));
System.out.println(b.equals(c));
will print...
true
false
false
which shows us that the code is working.
Once you get this setup, you should then be able to change the selected item by creating an instance of Registration, seeding it with the required properties and passing it to the JComboBox.
This is very important and very common concept used a lot within Java, well worth taking the time to learn and understand
I want to make a simple note reminder app that uses hashMap when the date is the key and the value is the text.
I have a Panel class(GUI), the hash-table class (Reminder.java), and a "MyDateClass.java" that represent a date for my purposes.
My gui is made of 3 JComboBox (day,month,year), One text area and 2 buttons - "Save", "Load".
The 2 buttons in the GUI Panel:
butSave.addActionListener(new ActionListener () {
public void actionPerformed(ActionEvent e) {
MyDateClass chosenDate = new MyDateClass(cbYear.getSelectedIndex()+2013,cbMonth.getSelectedIndex()+1, cb.getSelectedIndex()+1);
if(!remind.isReminderExists(chosenDate)){
remind.save(chosenDate, tfReminder.getText());
System.out.println("reminder doesnt exists");
}}
});
butLoad.addActionListener(new ActionListener () {
public void actionPerformed(ActionEvent e) {
System.out.println("tryin to load");
MyDateClass chosenDate = new MyDateClass(cbYear.getSelectedIndex()+2013,cbMonth.getSelectedIndex()+1, cb.getSelectedIndex()+1);
if(remind.isReminderExists(chosenDate)){
remind.Load(chosenDate);
System.out.println("reminder exists");
}}
});
Reminder class:
public class Reminder {
Map<MyDateClass,String> reminderMap;
public Reminder(){
reminderMap = new HashMap<MyDateClass,String>();
}
public boolean isReminderExists(MyDateClass date){
return reminderMap.containsKey(date);
}
public void save(MyDateClass date, String Input){
System.out.println("Trying to save");
reminderMap.put(date, Input);
}
public void Load(MyDateClass date){
System.out.println("Trying to load");
String output;
output = reminderMap.get(date);
System.out.println(output);
}
So after i push the save button i get from the console:
Trying to save
reminder doesnt exists
But then i push the Load button for the same date and
if(remind.isReminderExists(chosenDate))
Isnt triggerd.
What might be the problem?
Do i need to override hashCode() and equals() ? I genereted them but i dont if and how to change the equals() (do i need to manipulate it to compare both dates? How do i do that if "this" refers to the reminder Object)
Try something like this.
#Override public boolean equals(Object o) {
if(o == this) return true;
if(!(o instanceof MyDateClass)) return false;
MyDateClass that = (MyDateClass) o;
// use == for primitives
// use .compare for primitive wrappers where available
// use .equals for objects
return this.ivar1 == that.ivar2 &&
this.ivar2 == that.ivar2; //etc...
}
// equal objects must have equal hash codes
#Override public int hashCode() {
int result = 17;
result = 31 * result + ivar1;
result = 31 * result + ivar2;
return result;
}
Write JUnits to test for reflexive, symmetric, transitive, and consistent results.
As I know your problem clearly, I think that this function always return false. Because date(parameter) and the object in reminderMap have difference reference. So they can not be equal.
public boolean isReminderExists(MyDateClass date){
return reminderMap.containsKey(date);
}
If you want to use containsKey function of HashMap, maybe you should use String or Number instead of MyDateClass. I mean you should convert object of MyDateClass to String value before inserting it into reminderMap.
public boolean isReminderExists(String date){
return reminderMap.containsKey(date);
}
Otherwise, you can implement your code as Hot Licks mention.
I've overridden ListCell.updateItem(T, boolean) to provide a custom renderer for my ComboBox items (as per Oracle ComboBox tutorial) and this is working fine except when I programmatically set an item using ComboBox.setValue(T).
Instead the toString() method of T is being called. The item being set is already in the ObservableList which backs the ComboBox.
comboBox.setCellFactory(new Callback<ListView<MyType>, ListCell<MyType>>()
{
#Override
public ListCell<MyType> call(ListView<MyType> arg0)
{
return new ListCell<MyType>()
{
#Override
protected void updateItem(MyType item, boolean empty)
{
super.updateItem(item, empty);
if (item == null || empty)
{
setText("");
}
else
{
setText(item.myCustomRenderMethod());
}
}
};
}
});
Is there another method I need to override?
JavaFX2 on JDK1.7.0_45.
Thanks.
OK, found the answer here: JavaFx Editable ComboBox : Showing toString on item selection
You also need to override ComboBox.setConverter() to ensure that the selected object shows the correct text. This is not in the Oracle tutorial and violates the principle of least surprise for me as it duplicates some of the code from ListCell.updateItem()
comboBox.setConverter(new StringConverter<MyType>() {
#Override
public String toString(MyType obj) {
if (obj == null)
{
return "";
}
else
{
return obj.myCustomRenderMethod();
}
}
#Override
public MyType fromString(String s)
{
return null;
}
});
In my case using Platform.runLater() solved the issue:
Platform.runLater(() -> comboBox.setValue(value));
My best guess is that setting a value before the ComboBox is part of a Scene causes the problem. Also, be sure to use the setButtonCell(...) method of ComboBox.
I would like to search into my tree only if the user type 3 character at least.
How i can catch "doSelect" event to do this?
If i add a keylistener on the text field to check its value, the handler is invoked after the doSelect.
Can anyone help me?
This is an example...
filterText = new StoreFilterField<ModelData>() {
#Override
protected boolean doSelect(Store<ModelData> store,
ModelData parent, ModelData record, String property,
String filter) {
String name = record.get("name");
name = name.toLowerCase();
if (name.startsWith(filter.toLowerCase()))
return true;
else
return false;
}
};
KeyListener keyListener = new KeyListener() {
public void componentKeyUp(ComponentEvent event) {
loadingImage.setVisible(false);
if(filterText.isValid() && filterText.getRawValue().length()>=FILTER_MIN_SIZE) {
filterText.bind(store);
} else {
filterText.unbind(store);
}
}
};
filterText.setMinLength(FILTER_MIN_SIZE);
filterText.bind(store);
(Appears to be GXT 2, let me know if you are actually using GXT 3)
StoreFilterField relies on the applyFilters method to actually apply all of these. It has a check already that the text has at least one character:
protected void applyFilters(Store<M> store) {
if (getRawValue().length() > 0) {
store.addFilter(filter);
store.applyFilters(property);
} else {
store.removeFilter(filter);
}
}
The easiest way I see to override this would be to subclass StoreFilterField and redefine this method:
filterText = new StoreFilterField<ModelData>() {
#Override
protected void applyFilters(Store<M> store) {
if (getRawValue().length() > 3) {
store.addFilter(filter);
store.applyFilters(getProperty());
} else {
store.removeFilter(filter);
}
}
}
I'm now looking into JTables and have a bunch of business objects that I retrieve from the DB with Hibernate + Spring Data JPA.
I love that Spring Data JPA handles all the cumbersome implementation of the DAL and was wondering if there's something similar for TableModel.
Basically, I would have something along the lines of:
public class GenericTableModel<T> extends AbstractTableModel
And the GenericTableModel would use reflection and/or annotations to look into T.
Does something like this exist? I hope I don't have to have a TableModel for each object I want to display on a JTable..
And the GenericTableModel would use reflection to look into T.
The Bean Table Model does this.
http://java.net/projects/beansbinding/
Will let you bind business objects to view components (including tables) as long as you have Bean-Style getters and setters.
I am in the process of writing a library for Swing Developers. Its a work in progress. But i have done what you are looking for. Take a look at the classes in this package:
http://code.google.com/p/swingobjects/source/browse/#git%2FSwingObjects%2Fsrc%2Forg%2Faesthete%2Fswingobjects%2Fview%2Ftable
For an example of how to use this, please take a look at this class- Look at line numbers - 70-85
http://code.google.com/p/swingobjects/source/browse/SwingObjects/src/test/CompTest.java
I havent got the documentation written yet. But if you dont follow anything, please comment back here.
Update - Code sample
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import org.aesthete.swingobjects.annotations.Column;
import org.aesthete.swingobjects.view.table.RowDataBean;
import org.aesthete.swingobjects.view.table.SwingObjTable;
import org.jdesktop.swingx.JXFrame;
public class TableDemo {
public static void main(String[] args) {
try {
//For this demo the Framework need not be initialised.. If you plan on using the entire framework, then
//its best you initialise it before working on anything...
// SwingObjectsInit.init("/swingobjects.properties", "/error.properties");
//Here's the data to show on the table
final List<Row> rows = new ArrayList<Row>();
rows.add(new Row("Data 1", "Data 2", "Yes", true));
rows.add(new Row("Data 3", "Data 4", "No", false));
//Create the swing table as below.. Provide the Row.class to say that the data in the rows
// will be from this class
final SwingObjTable<Row> table = new SwingObjTable<Row>(Row.class);
table.setData(rows);
table.setVisibleRowCount(4);
//Make any column into a combo box by calling the below method.
//A column can be automatically made into a checkbox, by defining your property in the Row class as a boolean
table.makeColumnsIntoComboBox(new String[] { "Yes", "No" }, 2);
//Initialise the frame and show it on the screen
final JXFrame frame = new JXFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new JScrollPane(table));
frame.pack();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
public static class Row extends RowDataBean{
#Column(index=0,name="Column 1",editable=true)
private String column1;
#Column(index=1,name="Column 2",editable=true)
private String column2;
#Column(index=2,name="Column 3",editable=true)
private String column3;
#Column(index=3,name="Column 4",editable=true)
private boolean column4;
public Row(String column1, String column2, String column3, boolean column4) {
super();
this.column1 = column1;
this.column2 = column2;
this.column3 = column3;
this.column4 = column4;
}
public String getColumn1() {
return column1;
}
public void setColumn1(String column1) {
this.column1 = column1;
}
public String getColumn2() {
return column2;
}
public void setColumn2(String column2) {
this.column2 = column2;
}
public String getColumn3() {
return column3;
}
public void setColumn3(String column3) {
this.column3 = column3;
}
public boolean getColumn4() {
return column4;
}
public void setColumn4(boolean column4) {
this.column4 = column4;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((column1 == null) ? 0 : column1.hashCode());
result = prime * result + ((column2 == null) ? 0 : column2.hashCode());
result = prime * result + ((column3 == null) ? 0 : column3.hashCode());
result = prime * result + (column4 ? 1231 : 1237);
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Row other = (Row) obj;
if (column1 == null) {
if (other.column1 != null)
return false;
} else if (!column1.equals(other.column1))
return false;
if (column2 == null) {
if (other.column2 != null)
return false;
} else if (!column2.equals(other.column2))
return false;
if (column3 == null) {
if (other.column3 != null)
return false;
} else if (!column3.equals(other.column3))
return false;
if (column4 != other.column4)
return false;
return true;
}
}
}
Update: Answer to queries in Comments
1) The table can be inserted in any Component, right? (JPanel, JScrollPane, etc.),
Yes. The table extends JXTable (swingx) which extends JTable. So it can be placed inside any component. You would ideally place into a JScrollPane though.
2) We have control over the column names? (my app is localized in several langs)
Thanks for this. I didnt think about localisation when I started making the framework. You pointed me in the right direction. I was able to modify the framework quickly to achieve this. Its easy actually:
To make use of l10n - Before you start coding, make sure you initialize the Swing Objects Framework with the below line. If you dont provide a Locale, the default Locale will be applied.
SwingObjectsInit.init("swingobjects", "application",new Locale("fr", "FR"));
You need to have to have 2 sets of properties file.
1) swingobjects - This provides the defaults for the swingobjects framework. The file is already provided in my code base. Just copy paste the file onto a classpath location.
2) application - This is where you will need to put in your application's GUI texts/messages. You will have to make a new application properties file for every Locale you need.
Then finally, instead of saying
#Column(index=0,name="Column 1",editable=true)
private String column1;
You will have to use:
#Column(index=0,key="test.column1",editable=true)
private String column1;
Change name to key. This will make it read the Resource Bundle and search for a property test.column1 rather than your hard coded column Name.
3) Does SwingObjTable require hashCode and equals to be implemented?
The equals and hashcode is required if you use the Swing Objects Framework in its entirety. The Swing Objects Framework, allows you to set data for the GUI elements in a bean in your model class - Read as in MVC. The framework, then automatically updates the GUI if the bean's value has changed. In order to check if the bean's value has indeed changed, it needs to call the equals method. Hence you need to override it. You can make Eclipse generate this for you. Or if you dont use anything in the framework, then just call super.equals() from there.
Just check out / clone the git repo and you'll have be access the TableDemo example..
git clone https://writetosethu#code.google.com/p/swingobjects/