diff --git a/bundles/org.eclipse.jface/src/org/eclipse/jface/messages.properties b/bundles/org.eclipse.jface/src/org/eclipse/jface/messages.properties index 9355238ce7a..eac00949f9f 100644 --- a/bundles/org.eclipse.jface/src/org/eclipse/jface/messages.properties +++ b/bundles/org.eclipse.jface/src/org/eclipse/jface/messages.properties @@ -232,4 +232,5 @@ ConfigureColumnsDialog_up = &Up ConfigureColumnsDialog_down = Dow&n # org.eclipse.jface.viewers.internal.ExpandableNode -ExpandableNode.defaultLabel = Show {0}...{1} ({2}) +ExpandableNode.defaultLabel = Show next {0} items from remaining {1} +ExpandableNode.showRemaining = Show remaining {0} item{1} diff --git a/bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnViewer.java b/bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnViewer.java index 97684484029..01e8a995551 100644 --- a/bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnViewer.java +++ b/bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/ColumnViewer.java @@ -872,7 +872,7 @@ final Object[] applyItemsLimit(Object parent, Object[] sorted) { // limit the number of items to be created. sorted always gets the remaining // elements to be created. final int itemsLimit = getItemsLimit(); - if (itemsLimit <= 0 || sorted.length <= itemsLimit) { + if (itemsLimit <= 0 || sorted.length <= itemsLimit || sorted.length == itemsLimit + 1) { return sorted; } @@ -958,6 +958,12 @@ Object[] getChildrenWithLimitApplied(final Object parent, Item[] visibleChildren // there can any number of elements in the model. but viewer was showing // ExpandableNode. Then return the same length. if (visibleChildren[visibleItemsLength - 1].getData() instanceof ExpandableNode) { + if (sortedAll.length == visibleItemsLength) { + // model returns now exact the visible number of elements (note, last visible is + // expandable node): just return all without expandable node + return sortedAll; + } + // Now we need exactly previously visible length. Object[] subArray = new Object[visibleItemsLength]; System.arraycopy(sortedAll, 0, subArray, 0, visibleItemsLength - 1); diff --git a/bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/internal/ExpandableNode.java b/bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/internal/ExpandableNode.java index 0b36f12b650..d237f3c5fad 100644 --- a/bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/internal/ExpandableNode.java +++ b/bundles/org.eclipse.jface/src/org/eclipse/jface/viewers/internal/ExpandableNode.java @@ -98,6 +98,13 @@ public int getOffset() { return startOffSet; } + /** + * @return limit value + */ + public int getLimit() { + return limit; + } + /** * This method returns those children of the current node which are supposed to * be not created / shown yet in the viewer. @@ -157,15 +164,21 @@ public Object[] getAllElements() { /** * {@return label shown for the node in the viewer} */ + @SuppressWarnings("boxing") public String getLabel() { - Integer start = Integer.valueOf(this.start + 1); - Integer length = Integer.valueOf(orginalArray.length + addedElements.size()); - int next = this.start + this.limit; - if (next > orginalArray.length + addedElements.size()) { - next = orginalArray.length + addedElements.size(); + int all = orginalArray.length + addedElements.size(); + int remaining = all - start; + String label; + if (remaining > limit) { + if (remaining == limit + 1) { + String suffix = remaining == 1 ? "" : "s"; //$NON-NLS-1$ //$NON-NLS-2$ + return JFaceResources.format("ExpandableNode.showRemaining", remaining, suffix); //$NON-NLS-1$ ; + } + label = JFaceResources.format("ExpandableNode.defaultLabel", limit, remaining); //$NON-NLS-1$ + } else { + String suffix = remaining == 1 ? "" : "s"; //$NON-NLS-1$ //$NON-NLS-2$ + label = JFaceResources.format("ExpandableNode.showRemaining", remaining, suffix); //$NON-NLS-1$ } - Integer nextBlock = Integer.valueOf(next); - String label = JFaceResources.format("ExpandableNode.defaultLabel", start, nextBlock, length); //$NON-NLS-1$ return label; } diff --git a/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/viewers/BaseLimitBasedViewerTest.java b/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/viewers/BaseLimitBasedViewerTest.java index cb89de5f219..37fda56f845 100644 --- a/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/viewers/BaseLimitBasedViewerTest.java +++ b/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/viewers/BaseLimitBasedViewerTest.java @@ -27,6 +27,7 @@ public class BaseLimitBasedViewerTest extends ViewerTestCase { List rootModel; protected static final int VIEWER_LIMIT = 4; + protected static final int DEFAULT_ELEMENTS_COUNT = 40; public BaseLimitBasedViewerTest(String name) { super(name); @@ -37,16 +38,16 @@ protected StructuredViewer createViewer(Composite parent) { return null; } - protected static List createModel() { + protected static List createModel(final int maxCount) { List rootModel = new ArrayList<>(); - for (int i = 0; i < 40; i++) { + for (int i = 0; i < maxCount; i++) { if (i % 2 == 0) { DataModel rootLevel = new DataModel(Integer.valueOf(i)); - for (int j = 0; j < 40; j++) { + for (int j = 0; j < maxCount; j++) { if (j % 2 == 0) { DataModel level1 = new DataModel(Integer.valueOf(j)); level1.parent = rootLevel; - for (int k = 0; k < 40; k++) { + for (int k = 0; k < maxCount; k++) { if (k % 2 == 0) { DataModel level2 = new DataModel(Integer.valueOf(k)); level2.parent = level1; diff --git a/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/viewers/TableViewerWithLimitTest.java b/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/viewers/TableViewerWithLimitTest.java index bcef0b8a7f5..0f7a29ab019 100644 --- a/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/viewers/TableViewerWithLimitTest.java +++ b/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/viewers/TableViewerWithLimitTest.java @@ -25,6 +25,7 @@ import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.internal.ExpandableNode; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; @@ -49,6 +50,7 @@ public void testLimitedItemsCreatedWithExpansionNode() { } public void testAddElement() { + processEvents(); Table table = tableViewer.getTable(); assertLimitedItems(table.getItems()); DataModel data = (DataModel) table.getItems()[2].getData(); @@ -73,9 +75,31 @@ public void testAddElement() { assertLimitedItems(table.getItems()); data = (DataModel) table.getItems()[2].getData(); assertEquals("wrong item is found at given location", Integer.valueOf(3), data.id); + + // Add elements one by one + rootModel = createModel(1); + fViewer.setInput(rootModel); + processEvents(); + + while (rootModel.size() < VIEWER_LIMIT) { + DataModel element = new DataModel(Integer.valueOf(rootModel.size() + 1)); + rootModel.add(element); + tableViewer.add(element); + processEvents(); + TableItem[] items = table.getItems(); + Object last = items[items.length - 1].getData(); + assertFalse("Last item shouln't be expandable: " + last, tableViewer.isExpandableNode(last)); + } + + DataModel element = new DataModel(Integer.valueOf(rootModel.size() + 1)); + rootModel.add(element); + tableViewer.add(element); + processEvents(); + assertLimitedItems(table.getItems()); } public void testRemoveElement() { + processEvents(); Table table = tableViewer.getTable(); assertLimitedItems(table.getItems()); DataModel data = (DataModel) table.getItems()[2].getData(); @@ -85,19 +109,35 @@ public void testRemoveElement() { DataModel removed = rootModel.remove(2); tableViewer.remove(removed); processEvents(); - // check items and label after addition. + // check items and label after removal. assertLimitedItems(table.getItems()); data = (DataModel) table.getItems()[2].getData(); assertEquals("wrong item is found at given location", Integer.valueOf(6), data.id); // this element must not be visible only expandable node label must be updated. - DataModel removed1 = rootModel.remove(7); - tableViewer.remove(removed1); + removed = rootModel.remove(7); + tableViewer.remove(removed); processEvents(); - // check items and label after addition. + // check items and label after removal. assertLimitedItems(table.getItems()); data = (DataModel) table.getItems()[2].getData(); assertEquals("wrong item is found at given location", Integer.valueOf(6), data.id); + + while (rootModel.size() > VIEWER_LIMIT + 1) { + removed = rootModel.remove(rootModel.size() - 1); + tableViewer.remove(removed); + + processEvents(); + TableItem[] items = table.getItems(); + // check items and label after removal. + if (rootModel.size() > VIEWER_LIMIT + 1) { + assertLimitedItems(items); + } else { + Object last = items[items.length - 1].getData(); + assertFalse("Last item shouln't be expandable: " + last, tableViewer.isExpandableNode(last)); + } + } + } public void testClickExpandableNode() { @@ -109,10 +149,10 @@ public void testClickExpandableNode() { Item[] itemsBefore = table.getItems(); assertEquals("There are more/less items rendered than viewer limit", VIEWER_LIMIT * 2 + 1, itemsBefore.length); Item item = itemsBefore[itemsBefore.length - 1]; - assertTrue("Last node must be an Expandable Node", tableViewer.isExpandableNode(item.getData())); + Object data = item.getData(); + assertTrue("Last node must be an Expandable Node", tableViewer.isExpandableNode(data)); - String expected = JFaceResources.format("ExpandableNode.defaultLabel", Integer.valueOf(VIEWER_LIMIT * 2 + 1), - Integer.valueOf(VIEWER_LIMIT * 3), Integer.valueOf(rootModel.size())); + String expected = calculateExpandableLabel(data); assertEquals("Expandable node has an incorrect text", expected, item.getText()); // click until all expandable nodes are expanded. @@ -220,13 +260,32 @@ public void testRefresh() { private void assertLimitedItems(TableItem[] itemsBefore) { assertEquals("There are more/less items rendered than viewer limit", VIEWER_LIMIT + 1, itemsBefore.length); TableItem tableItem = itemsBefore[itemsBefore.length - 1]; - assertTrue("Last node must be an Expandable Node", tableViewer.isExpandableNode(tableItem.getData())); + Object data = tableItem.getData(); + assertTrue("Last node must be an Expandable Node", tableViewer.isExpandableNode(data)); - String expectedLabel = JFaceResources.format("ExpandableNode.defaultLabel", Integer.valueOf(VIEWER_LIMIT + 1), - Integer.valueOf(VIEWER_LIMIT + VIEWER_LIMIT), Integer.valueOf(rootModel.size())); + String expectedLabel = calculateExpandableLabel(data); assertEquals("Expandable node has an incorrect text", expectedLabel, tableItem.getText()); } + @SuppressWarnings("boxing") + private String calculateExpandableLabel(Object data) { + ExpandableNode node = (ExpandableNode) data; + int all = rootModel.size(); + int remaining = all - node.getOffset(); + String expectedLabel; + if (remaining > node.getLimit()) { + if (remaining == node.getLimit() + 1) { + String suffix = remaining == 1 ? "" : "s"; //$NON-NLS-1$ //$NON-NLS-2$ + return JFaceResources.format("ExpandableNode.showRemaining", remaining, suffix); //$NON-NLS-1$ ; + } + expectedLabel = JFaceResources.format("ExpandableNode.defaultLabel", node.getLimit(), remaining); //$NON-NLS-1$ + } else { + String suffix = remaining == 1 ? "" : "s"; //$NON-NLS-1$ + expectedLabel = JFaceResources.format("ExpandableNode.showRemaining", remaining, suffix); //$NON-NLS-1$ + } + return expectedLabel; + } + public void testSetInput() { List rootModel = new ArrayList<>(); DataModel rootLevel = new DataModel(Integer.valueOf(100)); @@ -234,7 +293,7 @@ public void testSetInput() { tableViewer.setInput(rootModel); processEvents(); assertEquals("there must be only one item", 1, tableViewer.getTable().getItems().length); - tableViewer.setInput(createModel()); + tableViewer.setInput(createModel(DEFAULT_ELEMENTS_COUNT)); processEvents(); assertLimitedItems(tableViewer.getTable().getItems()); } @@ -309,7 +368,7 @@ protected StructuredViewer createViewer(Composite parent) { @Override protected void setInput() { - rootModel = createModel(); + rootModel = createModel(DEFAULT_ELEMENTS_COUNT); fViewer.setInput(rootModel); } diff --git a/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/viewers/TreeViewerWithLimitTest.java b/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/viewers/TreeViewerWithLimitTest.java index 8525082f20d..3929782fb9e 100644 --- a/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/viewers/TreeViewerWithLimitTest.java +++ b/tests/org.eclipse.jface.tests/src/org/eclipse/jface/tests/viewers/TreeViewerWithLimitTest.java @@ -180,10 +180,14 @@ public void testRemoveItemsAtParent() { } public void testRemoveItem() { + processEvents(); + treeViewer.expandAll(); + processEvents(); DataModel firstEle = rootModel.remove(0); TreeItem firstItem = treeViewer.getTree().getItem(0); assertEquals("element contains unexpected data", firstEle, firstItem.getData()); treeViewer.remove(firstEle); + processEvents(); firstEle = rootModel.get(0); firstItem = treeViewer.getTree().getItem(0); assertEquals("element contains unexpected data", firstEle, firstItem.getData()); @@ -243,7 +247,7 @@ public void testSetInput() { treeViewer.setInput(rootModel); processEvents(); assertEquals("there must be only one item", 1, treeViewer.getTree().getItems().length); - treeViewer.setInput(createModel()); + treeViewer.setInput(createModel(DEFAULT_ELEMENTS_COUNT)); processEvents(); assertLimitedItems(); } @@ -279,7 +283,7 @@ protected StructuredViewer createViewer(Composite parent) { @Override protected void setInput() { - rootModel = createModel(); + rootModel = createModel(DEFAULT_ELEMENTS_COUNT); treeViewer.setInput(rootModel); } diff --git a/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/preferences/ViewerItemsLimitTest.java b/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/preferences/ViewerItemsLimitTest.java index 112155b5c51..932f0ef7677 100644 --- a/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/preferences/ViewerItemsLimitTest.java +++ b/tests/org.eclipse.ui.tests/Eclipse UI Tests/org/eclipse/ui/tests/preferences/ViewerItemsLimitTest.java @@ -242,14 +242,25 @@ private void assertLimitedItems(int currentLimit, int realInputSize, Item[] item if (nextBlock > realInputSize) { nextBlock = realInputSize; } - String expLabel = JFaceResources.format("ExpandableNode.defaultLabel", currentLimit + 1, nextBlock, - realInputSize); - - assertEquals("Incorrect text for expand node", expLabel, lastItem.getText().trim()); + String expLabel = calculateExpandableLabel(lastItem.getData(), realInputSize); ExpandableNode node = (ExpandableNode) lastItem.getData(); assertEquals(expLabel, node.getLabel()); } + @SuppressWarnings("boxing") + private String calculateExpandableLabel(Object data, int realInputSize) { + ExpandableNode node = (ExpandableNode) data; + int remaining = realInputSize - node.getOffset(); + String expectedLabel; + if (remaining > node.getLimit()) { + expectedLabel = JFaceResources.format("ExpandableNode.defaultLabel", node.getLimit(), remaining); //$NON-NLS-1$ + } else { + String suffix = remaining == 1 ? "" : "s"; //$NON-NLS-1$ + expectedLabel = JFaceResources.format("ExpandableNode.showRemaining", remaining, suffix); //$NON-NLS-1$ + } + return expectedLabel; + } + private void setNewViewerLimit(int viewerLimit) { preferenceStore.setValue(IWorkbenchPreferenceConstants.LARGE_VIEW_LIMIT, viewerLimit); int readViewLimit = preferenceStore.getInt(IWorkbenchPreferenceConstants.LARGE_VIEW_LIMIT);