JFace TreeViewer Sorting - Updated - java

Linking in from Sort rows in JFace Treeviewer where I asked #greg-449 why my comparator doesn't work when (like the question on that page) I just wanted to sort by the label provider.
My TreeViewer is somewhat extended with some generic features used in my application. There are then at 3 derivations extending it, but essentially the example below of it being an abstract class is because it's not intended to be directly instantiated, and the rest of the Eclipse plugin code must choose a concrete implementation for various view parts. However, I need sorting functionality in all of them, so this is probably where it should go.
I anonymized the code below, but it is in essence the abstract wrapper around TreeViewer which in all derivations has 3 columns. The first column (index 0) is always a tree of some type, so the nodes are expandable and this produces more visible rows of data in the 2nd and 3rd columns (indexes 1 and 2). These columns contain text data only.
As such, what I hope to achieve is a sortable control, where clicking header column with index 0 will clear any sorting and render the data as it was initially loaded, while clicking any other headers will do the following:
Sort Ascending (UP) if it was NOT already the sort column
Invert sort direction if same sort column is repeatedly clicked
Here's what I have tried, starting with the ViewerComparator class mentioned in the link at he top of this post:
public abstract class MyTreeViewer extends TreeViewer {
public static final int ACTIVE_TOTAL_COLUMN_WIDTH = 120;
public static final int FIRST_COLUMN_WIDTH = 180;
private static final String SPECIAL_FIELD = "specialField";
private TreeColumn sortColumn = null;
final RMService rmService;
final PService pService;
public MyTreeViewer(Composite parent) {
this(parent, SWT.V_SCROLL | SWT.H_SCROLL | SWT.FULL_SELECTION);
}
public MyTreeViewer(Composite parent, int style) {
super(parent, style);
this.rmService = UIActivator.getDefault().getInjector().getInstance(RMService.class);
this.pService = UIActivator.getDefault().getInjector().getInstance(PService.class);
this.setUseHashlookup(true);
this.addSelectionChangedListener(new ISelectionChangedListener() {
#Override
public void selectionChanged(SelectionChangedEvent event) {
if (!event.getSelection().isEmpty()) {
Tree tree = getTree();
if (tree != null) {
List<MyTreeItem> treeItems = Lists.newArrayList();
for (int i = 0; i < tree.getSelectionCount(); i++) {
TreeItem item = tree.getSelection()[i];
Object obj = item.getData();
if (obj instanceof MyTreeItem) {
treeItems.add((MyTreeItem) obj);
}
}
handleSelectionChanged(treeItems);
}
}
}
});
this.setComparator(new ViewerComparator());
}
protected abstract void handleSelectionChanged(Collection<MyTreeItem> treeItemList);
public void initTree(List<ViewField> fields, IMemento memento) {
TableLayout tableLayout = new TableLayout();
Tree tree = getTree();
for (ViewField field : fields) {
TreeViewerColumn column = new TreeViewerColumn(this, SWT.NONE);
if (SystemPropertiesLoader.OPERATING_SYSTEM_NAME.equalsIgnoreCase(IConstants.LINUX_OS)) {
column.getColumn().setResizable(false);
column.getColumn().setMoveable(false);
} else {
column.getColumn().setResizable(true);
column.getColumn().setMoveable(true);
}
column.getColumn().setData(SPECIAL_FIELD, field);
column.getColumn().setText(field.getFieldName());
tableLayout.addColumnData(new ColumnPixelData(field.getWidth(), false));
column.getColumn().addSelectionListener(new SelectionListener() {
#Override
public void widgetSelected(SelectionEvent selectionEvent) {
if (selectionEvent.getSource() instanceof TreeColumn) {
TreeColumn column = (TreeColumn)selectionEvent.getSource();
Tree tree = getTree();
if (column.getText().equalsIgnoreCase("") && sortColumn != null) {
// file column clicked - use it to clear any sort order
sortColumn = null;
tree.setSortColumn(sortColumn);
tree.setSortDirection(0);
refresh();
} else {
sortColumn = column;
tree.setSortColumn(sortColumn);
tree.setSortDirection(invertSortDirection(tree.getSortDirection()));
refresh();
}
}
}
#Override
public void widgetDefaultSelected(SelectionEvent selectionEvent) {
// not currently used, but required as part of implementation of SelectionListener interface
}
});
}
tree.setLayout(tableLayout);
tree.setLinesVisible(false);
tree.setHeaderVisible(true);
tree.layout(true);
// prevent expanding/collapsing tree item on dbl click
tree.addListener(SWT.MeasureItem, new Listener() {
#Override
public void handleEvent(Event event) {
// nothing
}
});
}
private int invertSortDirection(int sortDirection) {
if (sortDirection != SWT.UP && sortDirection != SWT.DOWN) {
return SWT.UP;
} else if (sortDirection == SWT.UP) {
return SWT.DOWN;
}
return SWT.UP;
}
#Override
public void refresh() {
super.refresh();
}
#Override
public void refresh(boolean updateLabels) {
super.refresh(updateLabels);
}
}
I inherited this code, so there are some peculiar things that fixed bugs that I won't touch without knowing it won't produce a regression in QA, such as the crude way preventing expanding/collapsing tree item on double-click is implemented. In fact, the only part of this particular code I amended thus far is the insertion of the addSelectionListener closure for handling column header clicks, and the invertSortDirection method.
What happens when I run this and click on the headers is as I expect. I see the UI caret indicating the sort direction on column index 1 or 2, but I do not see the data sorted. Clicking the header of column index 0 will clear the sort column and the order. If the data was sorted, I'd like the viewer to refresh in the UI to its original loaded state before any column ordering is requested.
On the prior question (linked at top), I interpreted that if sorting by label text was required, I should just add the this.setComparator(new ViewerComparator()); line. I've no idea what I would override or change if I have to write a class that extends ViewComparator.
None of the derived classes from the above code implement a listener on a column. I can trace the code, and the above code for handling header clicks does execute.
So, do I need to extend ViewComparator and what should I be changing in it to get the desired behaviour, if I do?
(I can also probably do away with the TreeColumn sortColumn field since the tree itself 'remembers' this. The Google Guice injected services are used by derivations of this abstract tree viewer)
UPDATE 1:
My intention was to show a derived class of this generic viewer defined above, but after I examined this, it was clear it shows little of use for the current issue of why the sort does not occur.
I had found what I thought was the 'smoking gun' of why the sort does not occur in the custom label provider itself from one of my predecessors that I've inherited the project from. Here's the label provider:
public class MyCustomLabelProvider extends LabelProvider implements ITableLabelProvider {
final IDecoratorManager decorator;
public MyCustomLabelProvider() {
decorator = PlatformUI.getWorkbench().getDecoratorManager();
}
#Override
public Image getImage(Object element) {
return super.getImage(element);
}
#Override
public String getText(Object element) {
return null;
}
#Override
public Image getColumnImage(Object element, int columnIndex) {
if (element instanceof MyTreeItem) {
if (columnIndex == 0) {
final MyTreeItem item = (MyTreeItem)element;
switch (item.getType()) {
// snipped cases returning different images
}
}
}
return null;
}
#Override
public String getColumnText(Object element, int columnIndex) {
if (element instanceof MyTreeItem) {
return showColumns((MyTreeItem) element, columnIndex);
}
return null;
}
private String showColumns(MyTreeItem element, int columnIndex) {
if (columnIndex == 0) {
return element.getName();
}
if (columnIndex == 1) {
return String.valueOf(element.getCustomProperty1());
}
if (columnIndex == 2) {
return String.valueOf(element.getCustomProperty2());
}
return "";
}
}
Via tracing the ViewerComparator code, the program eventually calls getText which always returning null.
The ViewerComparator transpires only to attempt to grab the label text, which due to the above is null, which it amends to an empty String. It then uses the Java String compareTo method for the comparison. Since both are "", there is no comparison result to signal the elements order needs to be swapped.
I wondered about changing the getText method to somehow obtain the original column index of the clicked column and to have logic in it to determine which property to read from my underlying data object used to populate a row in the viewer. For me, this transpired to not work because the underlying object I used has non-String properties that are used to populate 2 of the 3 columns.
User greg-449 had indicated in comments I would need to extend and override the ViewerComparator to make my own version, but until I got this far, or until he stated ...
The standard ViewerComparator only supports one column
... it had not been clear why. The originally linked post doesn't have that clarification. Or at least, not at time of writing this.
At the point he mentioned that, I had not indicated the issue as resolved, just that I thought I had found the issue. I had simply started with the ViewerComparator, traced running code, and found the reason the existing comparator could not re-order the items, assuming that would likely be the end of it once I update code.
I would go further to what greg-449 said, in that even if you have a single column, the default ViewerComparator will not support comparing elements where the property of the underlying data object is not a Java String. You need to implement your own comparator for that. Thus is became clear why greg-449 suggested that at the outset of me stating I had 3 columns - but I remained confused because I simply thought I had text data on the 2 columns I wanted to be sortable, even if that text is actually converted from an int. I just thought the linked post I read was probably apt because it mentioned sorting on text, without mentioning the limitation of a single column, or Java String data types.
So moving on, I have now got my code working as I wanted. More importantly, I did not have to change the getText method, since this was called specifically by the default ViewerComparator and I found an approach to follow where this wasn't needed. I shall be posting these in the answer.

So in addition to my updates in the question...
I tried to find the example of a different comparator that greg-449 referenced, but the specific package was not available in my environment if I tried to import it.
I then searched for a means to writing a custom comparator and found a Vogella post:
https://www.vogella.com/tutorials/EclipseJFaceTable/article.html#sort-content-of-table-columns
I noticed that the column index issue I mentioned above was handled by creating a handler per column in the Vogella code. I mostly copied it, with slight changes to how it performs the comparison to meet my requirements:
public class MyCustomViewerComparator extends ViewerComparator {
private int propertyIndex;
private static final int DESCENDING = 1;
private int direction;
public MyCustomViewerComparator() {
this.propertyIndex = 0;
direction = DESCENDING;
}
public int getDirection() {
return direction == 1 ? SWT.DOWN : SWT.UP;
}
public void setColumn(int column) {
if (column == this.propertyIndex) {
// Same column as last sort; toggle the direction
direction = 1 - direction;
} else {
// New column; do an ascending sort
this.propertyIndex = column;
direction = DESCENDING;
}
}
#Override
public int compare(Viewer viewer, Object e1, Object e2) {
MyTreeItem p1 = (MyTreeItem) e1;
MyTreeItem p2 = (MyTreeItem) e2;
int rc = 0;
switch (propertyIndex) {
case 1:
rc = p1.getCustomProperty1() - p2.getCustomProperty1();
break;
case 2:
rc = p1.getCustomProperty2() - p2.getCustomProperty2();
break;
default:
rc = 0;
}
// If descending order, flip the direction
if (direction == DESCENDING) {
rc = -rc;
}
return rc;
}
}
As can be seen, we have a standard compare function that returns a negative number, zero when matching, or a positive number, that indicates which order two items should be in. Or as the standard JavaDoc states:
the value {#code 0} if the argument string is equal to
* this string; a value less than {#code 0} if this string
* is lexicographically less than the string argument; and a
* value greater than {#code 0} if this string is
* lexicographically greater than the string argument.
Just in my version, the compare is using int values from my data object instead.
In addition to this, if the user clicked the column header of the column that by default is column index 0, I wanted it to clear any sort and return to the format the viewer was initially loaded as.
To achieve this, I simply changed the SelectionListener I originally used in MyTreeViewer (see second code listing in question) for each column, and made it use the following from the Vogella example (again tweaked):
private SelectionAdapter getSelectionAdapter(final TreeColumn column,
final int index) {
SelectionAdapter selectionAdapter = new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
comparator.setColumn(index);
if (index == 0) {
viewer.getTree().setSortDirection(0);
viewer.getTree().setSortColumn(null);
} else {
int dir = comparator.getDirection();
viewer.getTree().setSortDirection(dir);
viewer.getTree().setSortColumn(column);
}
viewer.refresh();
}
};
return selectionAdapter;
}
The if (index == 0) check in the above, clears the sorting for me when true, otherwise sorts on the whichever other column header has been clicked. Since the column index pertains to how the columns were set up, they are not affected by having movable columns in the UI. You can change the visible column ordering without deleterious effects.
From what I've learnt, it's only the logic for comparing elements that is instrumental and would need overriding in a custom comparator. Outside of this, the only thing to recognize is you then want the comparator specifically wired to each column, by implementing the SelectionAdapter interface and adding this to the column, while also being able to track the column indexes. The Vogella link does show this. Basically you will see on the comparator that there exists a setColumn method, and this is used in the SelectionAdapter to set the column index to be tested in the compare.

Related

Condition says List<X> 's are not .equal(), even tho they are the same

The new List comes from an SQLite database.
The oldList, is storing the first and second rows of a JOINED table via the #Embedded interface that android provides.
The query that populates it, is supposed to fetch only those 2 rows, because they comply to a specific WHERE clause.
While doing transactions in the right table, like inserting a 3rd row whose foreign key is not yet "pointing"(?) towards the left table, and while no changes have been made to the first 2 rows (joined rows), the Observer gets called, and gives me my same 2 rows, but the java code tells me they are different.
It is understandable that the Observer is giving me a response as if the data changed, because the joined table HAS indeed changed as a whole, and the the SQL query needs to check if the third row inserted complies with the condition imposed (which it does not), but what is not clear is why the java code fetches the same 2 rows, and tells me that they are different from the same 2 rows stored moments ago.
At first I thought it had something to do with the fact that the list was being .get() from a HashMap<K,V> , it could also be that.
#Override
protected void onFound(List<X> xes, int liveDataId) {
List<X> xOldList = listHashMap.get(liveDataId);
if (xOldList == null) {
Log.d(TAG, "onFound: list not found on HasMap adding response");
listHashMap.put(liveDataId, xes);
if (listHashMap.size() == sources.size()) {
Log.d(TAG, "onFound: HashMap complete, converting HasMap");
ends(listHashMap);
}
} else {
if (!xOldList.equals(xes)) {
Log.d(TAG, "onFound: oldList not the same as new one!");
if (xes.size() > 0) {
Log.d(TAG, "onFound: new list size greater than 0");
synchronizedSubmitList(xes, xOldList, liveDataId);
} else {
Log.d(TAG, "onFound: newList size is zero");
listHashMap.put(liveDataId, xes);
ends(listHashMap);
}
}
}
}
The logs
The #Embedded Pojo
public class PieceQuantityAndPiece implements EntityInterface {
private static final String TAG = "PieceQuantityAndPiece";
#Embedded
public PieceQuantity pieceQuantity;
#Relation(
parentColumn = "child_piece_id",
entityColumn = "piece_id"
)
public Piece piece;
public PieceQuantityAndPiece() {
}
}
The Query imposed for left table
#Transaction
#Query("SELECT * FROM piece_quantity_table WHERE folder_id = :folderId")
LiveData<List<PieceQuantityAndPiece>> getPieceQuantityAndPiecesAtFolder(int folderId);
The transactions being performed on the right table that trigger the onChanged():
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(Piece piece);
#Query("UPDATE piece_table SET _total = :total, _date_of_last_edit = :lastEdited WHERE piece_id = :pieceId")
void updatePieceTotalAt(int total, long lastEdited, int pieceId);
I managed to compare the equality of both Lists by iterating their separate elements, and then, by using 2 functional interfaces that I had hanging around in the class that returned a boolean value if their Id's were the same on one, and if a field of interest (contents) were also the same on the other one; I compared both items independently.
The silver lining here, in relation to how optimal it is to be iterating this much, is that at most the difference in sizes between old and new List will always be of 1 item at a time, but If given the case that I would made the option of deleting more items, everything still happens on the background, and also there are, I believe, enough clauses to be met that make sure to do whats absolutely necessary.
The order of the items is not really a problem because they are always gonna be fetched in the same order, and if they are in disorder, the list would still go straight to a list differentiator class that manages them cleanly. Also, there is a 100% chance that if the contents are in a different order, it's because something truly changed anyways.
As for the debugging, they logs seemed to be of more help, maybe because I'm not experienced enough with debugging.
The logs showed that the items where truly the same in every field, but their hash codes where different, that's clear, LiveData does not have memory just set() and fin.
#MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
Even tho if that T value is not declared new is it still "pointing" to its original source?? I doubt it cause the source is SQLite.
Still even if by type casting every element to their respective subtypes independently, they where still different.
But going deep in the List.equal(List) method I found that my objects will never be equal because this:
public boolean equals(Object obj) {
return (this == obj);
}
So thats an == ... and they will always have different hash codes...
This is where I began comparing their fields one by one (they were the same), and their primitives where finally comparable.
Having a supertype interface for all SQLite entities, I can compare their ids, and their most important field (in this case last_date_edited), and using the 2 functional interfaces mentioned before I did a clause condition:
private boolean verifyEquality(
List<X> oldList, List<X> newList,
#NonNull ListDifferentiator4.AreIdsTheSame<X> areIdsTheSame,
#NonNull ListDifferentiator4.AreContentsTheSame<X> areContentsTheSame
) {
for (int i = 0; i < oldList.size(); i++) {
X oldX = oldList.get(i);
X newX = newList.get(i);
if (!areIdsTheSame.areIdsTheSame(newX, oldX) || !areContentsTheSame.areContentsTheSame(newX, oldX)) {
Log.d(TAG, "verifyEquality: items are different");
return false;
}
}
Log.d(TAG, "verifyEquality: Lists are the same");
return true;
}
The onFound Method()...
#Override
protected void onFound(List<X> xes, int liveDataId) {
List<X> xOldList = listHashMap.get(liveDataId);
if (xOldList == null) {
Log.d(TAG, "onFound: list not found on HasMap adding response");
listHashMap.put(liveDataId, xes);
if (listHashMap.size() == sources.size()) {
Log.d(TAG, "onFound: HashMap complete, converting HasMap");
convertAndSet(listHashMap);
}
} else {
Log.d(TAG, "onFound: livedataId is: " + liveDataId);
if (xOldList.size() == xes.size()) {
Log.d(TAG, "onFound: Lists are the same size!");
if (xOldList.size() != 0) {
Log.d(TAG, "onFound: sizes are not 0");
boolean areEqual = verifyEquality(
xOldList,
xes,
areIdsTheSame,
areContentsTheSame
);
Log.println(Log.ASSERT, TAG, "onFound: are equal boolean 1st is: " + areEqual);
Log.d(TAG, "onFound: list size is: " + xes.size());
if (!areEqual) {
Log.println(Log.ASSERT, TAG, "onFound: oldList not the same as new one!");
listHashMap.put(liveDataId, xes);
convertAndSet(listHashMap);
} else {
Log.println(Log.ASSERT, TAG, "onFound: LISTS WHERE THE SAME!");
}
}
} else {
Log.println(Log.ASSERT, TAG, "onFound: Lists are not the same size");
if (xes.size() > 0) {
Log.d(TAG, "onFound: new list size greater than 0");
synchronizedSubmitList(xes, xOldList, liveDataId);
} else {
Log.d(TAG, "onFound: newList size is zero");
listHashMap.put(liveDataId, xes);
convertAndSet(listHashMap);
}
}
}
}
The entire class being initialized with the list differentiator interfaces...
liveDataListSource = new SourceMergingLiveData<>(
(newX, oldX) -> newX.getPrimaryKey() == oldX.getPrimaryKey(),
(newX, oldX) -> newX.getFieldElement().equals(oldX.getFieldElement())
);
The logs ...
As a final thought, this proved to be an instance where 2 completely equal objects had different Hashcodes each and every time, which is the opposite of what this answer states.
https://stackoverflow.com/a/5443140/11214643

Get collection element one by one when needed

I have a class Game, which contains a class Board, which contains an ArrayList of Rows.
Currently Game can getCurrentRow() and when the game progresses it does a getNextRow(). Board gets the currentRow, loops over the ArrayList, from the wrong end and at each step holds that row in lastCheckedRow and breaks when it finds currentRow. The nextRow will then become lastCheckedRow. Simple enough, but ugly.
I want to change this approach into streams instead. Is it possible to make a stream that remains reachable, only returning one element at a time when called?
public class Board implements Skinnable{
private Stream<Skinnable> stream;
protected List<Skinnable> rowItems = new ArrayList<>();
private BoardRow currentRow;
private final BoardSkin skin;
public Board(Game game) {
for (int i = 0; i < 10; i++) {
rowItems.add(new BoardRow(game));
if (i == 9) {
setCurrentRow((BoardRow) rowItems.get(rowItems.size() - 1));
}
}
stream = rowItems.stream();
skin = new BoardSkin(this);
}
public void setCurrentRow(BoardRow row) {
currentRow = row;
currentRow.activate();
}
public Row getStreamedRowItem () {
List<Skinnable> collect = stream.limit(1).collect(Collectors.toList());
return (Row) collect.get(0);
}
}
This works, one time, then the stream is closed. This is more of a general question. I have searched, both here and other places, but this is too new to me so I can't even phrase my questions correctly and therefor I am stuck.
Streams can be traversed only once. So you cannot keep it open and yet run the .collect terminal operation.
The best alternative for retrieving an element at a time is to use an iterator:
private Iterator<Skinnable> stream; //Please rename the variable
Then create the iterator in the constructor with:
stream = rowItems.iterator();
Retrieving the next element changes to:
public Row getStreamedRowItem () {
//you'll need to check if there's a next element
//if(stream.hasNext())
return stream.next();
}
If I were you, I would follow JB Nizet's idea which will give you more space/chance to control.
Since you already had rowItems, just by adding another index you can fly anywhere you want in the collection.
Have something as:
int index; // always pointing to the element to be fetched;
public Board(Game game) { {
index = 0; // pointing to the first element;
// or if you want it to point to the last using rowItems.size() - 1 instead;
}
public boolean hasNext() {
return index < rowItems.size();
}
public Row next() {
return (Row) rowItems.get(index++);
}
public boolean hasPrevious() {
return index > -1;
}
public Row previous() {
return (Row) rowItems.get(index--);
}

Sorting an ArrayList in Java

So I'm having trouble figuring out how to update a TextArea with information that I submit from an generics arraylist. As of now the program creates a new Order:
Order d1 = new Order();
Then the user selects some data and pushes an add button, and the order is added to a TextArea. The problem I have is that I have to add the order to the correct spot in the list and update it each time. I"m only sorting it by one item. I'm not really sure how to do that using the CompareTo method.
public void actionPerformed(ActionEvent event)
{
ArrayList<Drink> DrinkArray = new ArrayList<Drink>();
if (event.getSource() == addcoffeeButton)
{
String coffeesize = (String) sizecoffeelist.getSelectedItem();
double coffeeprice = Double.parseDouble(pricecoffeeTextfield.getText());
String coffeetype = (String) cuptypecoffeelist.getSelectedItem();
String coffeecaffeine = (String) caffeineList.getSelectedItem();
String coffeeroom = (String) roomforcreamList.getSelectedItem();
String coffeeadditional = additionalflavorList.getText();
if ((coffeeadditional.isEmpty()))
coffeeadditional = "No Additional Flavor";
Drink d1 = new Coffee(coffeesize, coffeeprice, coffeetype, coffeecaffeine, coffeeroom, coffeeadditional);
DrinkArray.add(d1);
orderTextArea.append(d1);
So I would have to add the drink to the correct spot before adding it to the array and printing to the text area, but I'm not quite sure how to do that.
I'll assume that Drink implements Comparable. Look at the javadocs if you don't know what that means.
If that's true, you can do this:
List<Drink> drinks = new ArrayList<Drink>();
// add Drinks
Collections.sort(drinks); // now they're sorted according to your Comparable.
You can also instantiate a Comparator and pass it to the sorts method.
Something like this (make the getValue() function whatever you want):
public class DrinkComparator implements Comparator<Drink> {
public int compare(Drink d1, Drink d2) {
if (d1.getValue() < d2.getValue()) {
return -1;
} else if (d1.getValue() > d2.getValue()) {
return 1;
} else {
return 0;
}
}
public boolean equals(Object obj) {
return this.compare(this, (Drink)obj) == 0;
}
}
You basically need to pre-determine the insertion point where the "object" would be inserted...
Take a look at Collections.binarySearch(List<T>, T)
From the Java Docs
Returns:
the index of the search key, if it is contained in the list;
otherwise, (-(insertion point) - 1). The insertion point is defined as
the point at which the key would be inserted into the list: the index
of the first element greater than the key, or list.size() if all
elements in the list are less than the specified key. Note that this
guarantees that the return value will be >= 0 if and only if the key
is found.

How to set the default sort direction when clicking grid header column in GXT 2.2.5

Is there a way to control the default sorting order used when first clicking a grid header? Suppose, I am having two columns one is name and another is downloads. i want to set name as ASC order and downloads as DESC on first click on grid header.that means when i first click on download column header it should be display most downloaded first.
Is it possible to set initial sorting order of column?
I got a different solution
I had a similar situation, in which I wanted date columns to be sorted DESC on the first click, while others should be sorted ASC on the first click. I wrote my own GridView, and inside it I overridden the onHeaderClick function like so:
/**
* Make sure that Date columns are sorted in a DESCENDING order by default
*/
#Override
protected void onHeaderClick(Grid<ModelData> grid, int column)
{
if (cm.getColumn(column).getDateTimeFormat() != null)
{
SortInfo state = getSortState();
if (state.getSortField() != null && state.getSortField().equals(cm.getColumn(column).getId()))
{
super.onHeaderClick(grid, column);
return;
}
else
{
this.headerColumnIndex = column;
if (!headerDisabled && cm.isSortable(column))
{
doSort(column, SortDir.DESC);
}
}
}
else
{
super.onHeaderClick(grid, column);
return;
}
}
I got solution.
You can Set initial Sorting direction using Store Sorter.
store.setStoreSorter(new StoreSorter<TemplateContentItem>(){
#Override
public int compare(Store<TemplateContentItem> store,
TemplateContentItem m1, TemplateContentItem m2,
String property) {
if(property.equals("downloads")){
return (super.compare(store, m1, m2, property) * -1);
}
return super.compare(store, m1, m2, property);
}
});
In above code, It will check if column is download than it will sort result in reverse.

GUI application to show employee record one at a time and stop at the end of the record

I have this program that allows a user to click next or previous to view employee records.i have 4 empployees in the txt.The problem am having is when i click next or previous it keeps reading passed the last employee on record.What can i change to stop it?please advise.
Here the action listener part of the code.
public Employee1(EmployeeDataSet employees)
{
this.numberOfEmployees = employees.getEmployeeCount();
this.employees = employees;
}
public void loadEmployeeInGUI(Employee e)
{
// set text field with name
Q1Field.setText(e.getName());
// set text field with title
Q2Field.setText(e.getName());
// set textfield with salary
Q3Field.setText(String.valueOf(e.getSalary()));
}
public void actionPerformed(ActionEvent x)
{
if (x.getSource() == Next)
{
index++;
if(index == this.numberOfEmployees) index=0;
Employee e = employees.getEmployeeAtIndex(index);
loadEmployeeInGUI(e);
}
}
if (x.getSource() == Previous) index--;
if (index ==-1) index = this.numberOfEmployees -1;
}
This code appears to be using Java Swing. That being said, you should use CardLayout. By associating an individual Employee with each "card", you'll be able to easily navigate (forward and backward) through the records.
This approach essentially delegates the "hard work" to the layout manager, which is how it should be.
I disagree about the CardLayout. Using a CardLayout means you will create a Component for each Employee, which might be a significant number.
It is mentioned in the question that he currently only has 4 Employee instances for which you can still opt for a CardLayout, but it is not scalable at all.
Having a UI on which you can 'set' an Employee, and which will update its value accordingly (as it is already the case in the code provided in the start post) seems like the way to go.
Why his specific code does not result in the desired behavior is hard to tell since the open/close braces do not seem to match, and we have no idea how this EmployeeDataSet is implemented. But I prefer the used approach above a CardLayout
I personally would restructure as follows:
public void actionPerformed(ActionEvent x) {
// handle increment / decrement
if (x.getSource() == Next) {
index++;
}
if (x.getSource() == Previous) {
index--;
}
// range check
if( index > this.numberOfEmployees ) {
index = 0;
}
if( index < 0 ) {
index = this.numberOfEmployees - 1;
}
// load up data
Employee e = employees.getEmployeeAtIndex(index);
loadEmployeeInGUI(e);
}

Categories

Resources