Implementing a recently used or favorites dropdown in JComboBox - java

I am looking for code that will add favorites / MRU type behavior to a JComboBox.
I could code this myself, but it sure seems like someone else has probably already done it.
I found the following (which looks exactly like what I want, but the source code is nowhere near complete): http://java.sys-con.com/node/36658
Any suggestions? I need to keep this relatively light, so I'd prefer to not use a component that's part of a monolithic widget library, and open source is preferred.

Consider extending DefaultComboBoxModel: override addElement() and insertElementAt() to insert at zero and remove the last element.
Addendum: Here's an example; per SO, the license is cc-wiki. I'd use Preferences to persist the entries.
class MRUComboBoxModel extends DefaultComboBoxModel {
#Override
public void addElement(Object element) {
this.insertElementAt(element, 0);
}
#Override
public void insertElementAt(Object element, int index) {
super.insertElementAt(element, 0);
int size = this.getSize();
if (size > 10) {
this.removeElementAt(size - 1);
}
}
}

What about just subclassing JComboBox and overriding the
public void addItem(Object anObject)
to give it the functionality you want?
You can just keep an internal list of items synched with the effective one, and whenever you add a new item it can check if size() >= maxItems and trim down least recent ones.
Then you should find a way to refresh an item whenever it is used. If its selection it's enough to be refreshed you can write an ItemListener that does it. Otherwise you'll need a specified external action or an observer/observable pattern..

Related

JavaFX TreeItem css style for different class field

I have TreeView filled by my own tree. In class Node I have field "type" which is one of NodeType. The problem is that I want have style for each type of NodeType, e.g. "type1" text color should be green, "type2" text color should be red. I'm new in javaFX. I found solution by james-d ( https://github.com/james-d/heterogeneous-tree-example ), but in this example css style depends on the class name, how can I make it for class field ?
View of TreeView
My understanding is you want a TreeCell that styles differently depending on the NodeType of the Node contained within the TreeItem of said TreeCell. All via CSS. Am I correct?
Assuming I am correct, there are 2 ways I can think of to accomplish this; both of which work best if there is a small number of known NodeTypes. The first involves the use of PseudoClass and the second uses the same strategy as the JavaFX Chart API.
First Option
Create a custom TreeCell that is tailored to using your Node type (i.e. specify the generic signature appropriately). In this custom TreeCell you declare as many PseudoClass static final fields as you need; one for each NodeType. Then you observe the NodeType of the whatever Node is currently displayed in the TreeCell and update the PseudoClass states accordingly.
Here is an example assuming NodeType is an enum that has two constants: HAPPY and SAD.
public class CustomTreeCell<T extends Node> extends TreeCell<T> {
private static final PseudoClass HAPPY = PseudoClass.getPseudoClass("happy");
private static final PseudoClass SAD = PseudoClass.getPseudoClass("sad");
// this listener will activate/deactivate the appropriate PseudoClass states
private final ChangeListener<NodeType> listener = (obs, oldVal, newVal) -> {
pseudoClassStateChanged(HAPPY, newVal == NodeType.HAPPY);
pseudoClassStateChanged(SAD, newVal == NodeType.SAD);
};
// use a weak listener to avoid a memory leak
private final WeakChangeListener<NodeType> weakListener = /* wrap listener */;
public CustomTreeCell() {
getStyleClass().add("custom-tree-cell");
itemProperty().addListener((obs, oldVal, newVal) -> {
if (oldVal != null) {
oldVal.nodeTypeProperty().removeListener(weakListener);
}
if (newVal != null) {
newVal.nodeTypeProperty().addListener(weakListener);
// need to "observe" the initial NodeType of the new Node item.
// You could call the listener manually to avoid code duplication
pseudoClassStateChanged(HAPPY, newVal.getNodeType() == NodeType.HAPPY);
pseudoClassStateChanged(SAD, newVal.getNodeType() == NodeType.SAD);
} else {
// no item in this cell so deactivate all PseudoClass's
pseudoClassStateChanged(HAPPY, false);
pseudoClassStateChanged(SAD, false);
}
});
}
}
Then in your CSS file you can use:
.custom-tree-cell:happy {
/* style when happy */
}
.custom-tree-cell:sad {
/* style when sad */
}
Second Option
Do what the JavaFX Chart API does when dealing with multiple series of data. What it does is dynamically update the style class of the nodes depending on the series' index in a list (e.g. .line-chart-series-data-<index> <-- probably not exactly this).
/*
* Create a custom TreeCell like in the first option but
* without any of the PseudoClass code. This listener should
* be added/removed from the Node item just like weakListener
* is above.
*/
ChangeListener<NodeType> listener = (obs, oldVal, newVal) -> {
// You have to make sure you keep "cell", "indexed-cell", and "tree-cell"
// in order to keep the basic modena styling.
if (newVal == NodeType.HAPPY) {
getStyleClass().setAll("cell", "indexed-cell", "tree-cell", "custom-tree-cell-happy");
} else if (newVal == NodeType.HAPPY) {
getStyleClass().setAll("cell", "indexed-cell", "tree-cell", "custom-tree-cell-sad");
} else {
getStyleClass().setAll("cell", "indexed-cell", "tree-cell"); // revert to regular TreeCell style
}
};
Then in CSS:
.custom-tree-cell-happy {
/* styles */
}
.custom-tree-cell-sad {
/* styles */
}
Both of these options are really only viable when there is a small set of known types. It might become unmaintainable when you have something like 10+ NodeTypes. It becomes pretty much impossible if the number of NodeTypes is dynamic at runtime.
It might be easier to have NodeType, or some intermediate class/data structure, know what color the text should be and set the color programmatically based on the NodeType.
Note: I quickly typed up the code in my answer and did not test it. There may be compiler errors, runtime exceptions, or logic errors in my code.
Edit
Something else came to mind. My code above assumes that NodeType is held in a property and can be changed during runtime. If NodeType is static (unchanging) for each Node then the code can be vastly simplified. Instead of using any listeners you can simple override the following method declared in javafx.scene.control.Cell:
protected void updateItem(Node item, boolean empty)
This method is called every time a new item is set on the cell. Read the documentation, however, as overriding this method requires certain things from the developer (such as calling the super implementation).

how to auto fill with composite pattern, polymorphism?

Hi I'm working with composite pattern. I'm going to use this example of Head First Design Pattern to explain https://github.com/bethrobson/Head-First-Design-Patterns/tree/master/src/headfirst/designpatterns/composite/menuiterator
Imagine that every menu and submenu have an Id to indentify, it is 10 length.
Something like this
0100000000 menu_1
0101000000 menu_1's subMenu_1
0102000000 menu_1's subMenu_2
0102010000 subMenu_2's subMenu_3
0200000000 menu_2
And what I have at random is the menu item, but it has an ID, which is a Menu ID to which it belongs. For example
0101000000 menuItem_1
0200000000 menuItem_2
So MenuItem 1 belongs to Menu 1's SubMenu 1 and MenuItem 2 belongs to Menu 2.
It would be coded like this.
menu_1.add(subMenu_1);
subMenu_1.add(menuItem_1);
menu_2.add(menuItem_2);
Now how am I filling the menus?
What I'm doing because of I get only the MenuItems, is that I'm cutting the Id to determine where it belongs.
For example you can see that there two Menus, Menu 1 (0100000000) and Menu 2 (0200000000) so I have to cut the first 2 Strings.
I'm coding like this:
class AllMenus implements MenuComponent {
MenuComponent menu_1
MenuComponent subMenu_1
MenuComponent subMenu_2
MenuComponent subMenu_3
MenuComponent menu_2
#Override
add(MenuComponent menu) {
if(menu instanceof Menu) {
super.add(menu)
} else if(menu instanceof MenuItem) {
String subId = menuItem.getId().subString(0,2)
if(subId.equals("01")) {
if(menu_1 == null) {
menu_1 = new Menu();
add(menu_1);
}
subId = menuItem.getId().subString(0,4);
if(subId.equals("0101")) {
if(subMenu_1 == null) {
subMenu_1 = new Menu();
menu_1.add(subMenu_1);
}
subMenu_1.add(menuItem);
} else if(subId.equals("0102")) {
if(subMenu_2 == null) {
subMenu_2 = new Menu();
menu_1.add(subMenu_2);
}
subId = menuItem.getId().subString(0,6);
if(subId.equals("010201")) {
if(subMenu_3 == null) {
subMenu_3 = new Menu();
subMenu_2.add(subMenu_3);
}
subMenu_3.add(menuItem);
}
}
} else if(subId.equals("02") {
if(menu_2 == null) {
menu_2 = new Menu();
add(menu_2);
}
menu_2.add(menuItem);
}
}
}
}
This is for every MenuItem I get. So as you can see this code is to long just for four Menus,imagine thousands of menu, how can it get better?.
I have read that i should use polymorphism to something that repeat, but I don't know how in this case.
If I understand your question properly, I think there's too much assumption of the eventual structure in your code.
In general, number literals in your code should be treated with suspicion - e.g.
if(subId.equals("010201")
... because it looks like configuration, not code. Of course it's fine to put "configuration" in your code, and sometimes it's practical for that configuration to be program code rather than XML/CSV/JSON/etc. -- but even then, it's good to have a logical separation between a "config" class and a "code" class. For example your "config" class might just contain a method that returns arrays of strings:
public class MenuConfig() {
public String[][] configs() {
return new String[][] {
new String[] {"0100000000", "Main menu"},
new String[] {"0101000000", "Settings"},
new String[] {"0101010000", "Look and feel"},
new String[] {"0102000000", "My account"},
// etc.
}
}
}
You're looking to create a tree of submenus -- look for inspiration in code to manipulate trees (e.g. binary trees) -- see how simple the code is and how it doesn't make assumptions beyond the fact that each node has zero or more subnodes.
You're already using polymorphism -- your menus and submenus conform to a common type (I can't tell from your code whether they all have the same concrete type, but in principle I might expect you to have a variety of classes that implement MenuComponent.
Your code to process one menu just needs to parse the ID to work out where it's supposed to go, then find the place it needs to go, and insert it:
(I'm presenting the IDs with hyphens to make it easier to read)
// split "01-02-03-00-00" into [1,2,3] -- ignoring trailing zeros
List<Integer> path = parse(currentMenu.getId());
MenuComponent m = rootMenuComponent;
while(path.size() > 1) {
m = m.getSubMenu(path.remove(0));
}
m.add(path.remove(0), currentMenu());
This simple algorithm assumes that menus are added in the right order -- that is you must have handled 01-02-00-00-00 before 01-02-01-00-00, or you'll get a null pointer.
If you can't live with that constraint, you need to define what could happen, and decide how to deal with it.
Actually with your current scheme, sorting the keys alphabetically should be sufficient
If they're just going to come out-of-order, you could treat the to-do-list of menus to be added as a queue. If a particular item can't yet be added, put it to the back of the queue and retry after processing the rest.
If you want it not to be necessary to explicitly define intermediate menus, then when m.getSubMenu(subMenuNum) returns null (because you've implicitly "defined" it in the middle of a path) you'll need to create the missing menu at that point.
If you want it to be possible to define the same menu more than once (maybe both implicitly and explicitly) then you need MenuComponent.add() to handle that case, either by merging or overwriting, depending on your requirement.
Assembling a graph of nodes like this is what Spring dependency-injection does -- handling building the graph in order, when the dependencies are defined in arbitrary order. If your aim is study, keep building your own. If you just want build a composite of menus, consider using Spring (or some other DI framework).

ComboBox not showing bound values

I have a comboBox cb and an ObservableList<StringProperty> data
I have bound the cb's Items to data as follows:
Bindings.bindContent(cb.getItems(), data);
Suppose data has the following items: str1, str2, str3, str4
When I change data, the combobox gets the new list without any problem.
But if str3 is selected in cb and I change the value of str3 to NewStr3 in data, that change is not getting displayed in cb. And sometimes the list displayed is also wrong (it shows str3 instead of NewStr3) eventhough underlying data it refers is correct.
How can I force combobox to display new values when the underlying model is changed?
The selected item in a combo box is not required to be an element of the combo box's items list. (For example, in an editable combo box, you can type in an item which is not in the list.) If you think about your example from this perspective, it's no surprise that it behaves as you describe.
If you want to force the selected value to be an element of the underlying list when that list may change, you need to define how the selected item should change if the list changes in a way in which it no longer contains the selected item (it is not obvious how you will do this, and probably depends on your application logic). Once you know what you want to do, you can implement it with a ListChangeListener:
cb.getItems().addListener((ListChangeListener.Change change) -> {
String newSelectedItem = ... ; // figure item that should be selected instead
cb.setValue(newSelectedItem);
});
The simplest implementation would be just cb.setValue(null);, which would mean no item was selected if the list changed so that it no longer contained the currently selected item.
Oops ... mis-read the comboBox for a choiceBox - while the basics of this answer apply to both combo- and choiceBox, I don't have a custom ComboBoxX - yet :-)
Basically, it's the responsibility of the SelectionModel to update itself on changes to the items. The intended behaviour implemented in core is to completely clear the selection - that is, null the selectedItem and set selectedIndex to -1 - if the old item was the selectedItem and is replaced or removed. The typical solution for custom behaviour is to implement a custom selection model and set it:
/**
* A SelectionModel that updates the selectedItem if it is contained in
* the data list and was replaced/updated.
*
* #author Jeanette Winzenburg, Berlin
*/
public static class MySelectionModel<T> extends ChoiceBoxSelectionModel<T> {
public MySelectionModel(ChoiceBoxX<T> cb) {
super(cb);
}
#Override
protected void itemsChanged(Change<? extends T> c) {
// selection is in list
if (getSelectedIndex() != -1) {
while (c.next()) {
if (c.wasReplaced() || c.wasUpdated()) {
if (getSelectedIndex() >= c.getFrom()
&& getSelectedIndex() < c.getTo()) {
setSelectedItem(getModelItem(getSelectedIndex()));
return;
}
}
}
}
// super expects a clean change
c.reset();
super.itemsChanged(c);
}
}
// usage
myChoiceBox.setSelectionModel(new MySelectionModel(myChoiceBox));
Unfortunately, core choiceBox doesn't play by the rule - it severely interferes with model's responsibilities (probably because the model implementation doesn't stand up to its duties) which requires a complete re-write of the whole collaborator-stack (choiceBox, -skin, copied -behaviour) such as ChoiceBoxX - which I did just to learn a bit, try remove some of its smells and fix some bugs.

How to insert items to a jcombobox at runtime and save it

I need to save the values in my jcombobox at the runtime. What I am trying to do is after clicking on a button, I am setting it to editable = true. Then type the value in the combobox, but it doesn't save.
private void btadbknameActionPerformed(java.awt.event.ActionEvent evt) {
if(evt.getSource()== btadbkname){
cb_bkname.setEditable(true);
cb_bkname.getText();
cb_bkname.addItem(evt);
}else{
cb_bkname.setEditable(false);
}
}
I have already added some elements in it on the designing level, but it's limited if some random value comes then its a problem.
Because it is possible to add / remove Item(s) to / from the DefaultComboBoxModel underlaying the JComboBox, the same action (by default) is possible from outside.
You have to use MutableComboBoxMode to add / remove Item(s) to / from JComboBox that fires event from itself (view_to_model).
There are excellent examples of MutableComboBoxModel by #Robin here and here.
For better help sooner post an SSCCE, for future readers, otherwise search for extends AbstractListModel implements MutableComboBoxModel.
it can't possibly work the way you're trying it.
the comboBox has to be editable before you click the button then you just need this line
cb_bkname.addItem(((JTextField)cb_bkname.getEditor().getEditorComponent()).getText());
Try this
private void btadbknameActionPerformed(java.awt.event.ActionEvent evt) {
if(evt.getSource()== btadbkname){
cb_bkname.setEditable(true);
String newItem=cb_bkname.getText();
cb_bkname.addItem(newItem);
}else{
cb_bkname.setEditable(false);
}
}

Issue with default Sorting on Virtual Table & ViewerComparator

We have a Virtual Table in my Eclipse RCP application. We make a call to the backend to retrieve the data to be populated in the virtual table.
We want default sorting on the table on a single column. We use ViewerComparator to achieve sorting functionality. My problem is, I am not able to get this sorting working when the table loads with the data for the 1st time. But when I click on the column, everything works fine as expected.
This is how, I set the Comparator to the column
TableViewerColumn tvc = viewer.addColumn(100, SWT.LEFT, "Name");
viewer.setColumnComparator(tvc,
new Comparator<Person>() {
#Override
public int compare(Person o1,Person o2) {
double firstValue = Double.parseDouble(o1
.getAge());
double secondValue = Double.parseDouble(o2
.getAge());
return firstValue > secondValue ? 1 : -1;
}
});
setColumnComparator method in custom viewer
public void setColumnComparator(TableViewerColumn tvc, Comparator<T> cmp){
final MyViewerComparator c = new MyViewerComparator(cmp);
final TableColumn tc = tvc.getColumn();
setComparator(c);
getTable().setSortDirection(c.getDirection());
getTable().setSortColumn(tc);
refresh();
tc.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
<same code as above>
}
});
MyViewerComparator
class MyViewerComparator extends ViewerComparator{
Comparator<T> cmp;
boolean desc = true;
MyViewerComparator(Comparator<T> cmp){
this.cmp = cmp;
}
int getDirection(){
return desc?SWT.UP:SWT.DOWN;
}
void flipDirection(){
desc = !desc;
}
#Override
public int compare(Viewer viewer, Object e1, Object e2) {
if(e1 == null || e2==null){
return 0;
}
int rc = cmp.compare((T)e1, (T)e2);
if(desc)
return -rc;
return rc;
}
}
When the table loads the data for the 1st time, it goes inside the Bolded condition in the above code as one of the object is ALWAYS NULL
Note: This functionality works totally fine if I use a Standard table rather than VIRTUAL TABLE. I am not sure whether I can change it to use Standard table as we want the lazy load functionality as well..
ContentProvider used is: ObservableListContentProvider
Please advise..
A late answer that hopefully still helps others. I encountered exactly the same problem when using SWT.VIRTUAL with an ObservableListContentProvider in combination with sorting.
The original intent of SWT.VIRTUAL is that not all elements in the contents need to be fetched to show only part of the contents. A custom content provider needs to be implemented which only has to return the elements that need to be currently shown on the screen. You also have to tell the table the total number of elements in existence. In such a use case, a table cannot be sorted in the normal way with a ViewerComparator because not all elements are known. However SWT.VIRTUAL can also be used as a performance optimization for rendering a table with many elements. This seems to work fine with the non-observable ArrayContentProvider.
But when using ObservableListContentProvider I am seeing exactly the same issue as you have. Somehow it tries to be smart and update only the elements that have actually changed. Somewhere in the depths of it's implementation something goes wrong for virtual tables, I have no clue exactly what. But I do have a solution: don't use ObservableListContentProvider at all and simply refresh the table viewer. You can e.g. use a plain ArrayContentProvider and add the following listener to the IObservableList contents of the viewer:
new IListChangeListener() {
#Override
public void handleListChange(ListChangeEvent event) {
viewer.refresh();
}
};
I actually implemented my own "SimpleObservableListContentProvider" that does exactly this, but also takes care of switching table input by implementing the inputChanged method to remove this listener from the old input list and add it to the new one.

Categories