diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java index bdb5c2b9ea4..87c988337f2 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java @@ -111,6 +111,8 @@ public class Tree extends Composite { private long headerCSSProvider; + private TreeItemCache itemCache = null; + static final int ID_COLUMN = 0; static final int CHECKED_COLUMN = 1; static final int GRAYED_COLUMN = 2; @@ -4346,4 +4348,11 @@ public void dispose() { headerCSSProvider = 0; } } + +TreeItemCache getItemCache(TreeItem treeItem) { + if (itemCache == null || itemCache.owner != treeItem) { + itemCache = new TreeItemCache(treeItem); + } + return itemCache; +} } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/TreeItem.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/TreeItem.java index d5f1081d29e..cc0acad1e26 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/TreeItem.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/TreeItem.java @@ -45,7 +45,6 @@ public class TreeItem extends Item { Font[] cellFont; String [] strings; boolean cached, grayed, isExpanded, updated, settingData; - int itemCountCache = -1; static final int EXPANDER_EXTRA_PADDING = 4; /** @@ -167,7 +166,9 @@ public TreeItem (TreeItem parentItem, int style, int index) { this.parent = parent; if (create) { parent.createItem (this, parentIter, index); - resetParentCache(); + if (parentIter != 0) { + parent._getItem(parentIter).resetCache(); + } } else { handle = OS.g_malloc (GTK.GtkTreeIter_sizeof ()); GTK.gtk_tree_model_iter_nth_child (parent.modelHandle, handle, parentIter, index); @@ -770,7 +771,7 @@ Rectangle getImageBoundsInPixels (int index) { public int getItemCount () { checkWidget(); if (!parent.checkData (this)) error (SWT.ERROR_WIDGET_DISPOSED); - return getItemCountImpl(); + return getCache().getItemCount(); } /** @@ -793,9 +794,12 @@ public int getItemCount () { public TreeItem getItem (int index) { checkWidget(); if (index < 0) error (SWT.ERROR_INVALID_RANGE); - int itemCount = getItemCountImpl(); - if (index >= itemCount) error (SWT.ERROR_INVALID_RANGE); - return parent._getItem (handle, index); + // It may seem that requesting GTK for an item by index is quicker, + // but it has O(N) execution time and cache of all children amortizes that. + // This avoids quadratic execution time on traversals + TreeItem[] items = getCache().getItems(); + if (index >= items.length) error (SWT.ERROR_INVALID_RANGE); + return items[index]; } /** @@ -1100,7 +1104,7 @@ public void dispose () { */ public void removeAll () { checkWidget (); - itemCountCache = 0; + resetCache(); long modelHandle = parent.modelHandle; int length = GTK.gtk_tree_model_iter_n_children (modelHandle, handle); if (length == 0) return; @@ -1631,7 +1635,7 @@ public void setImage (Image [] images) { public void setItemCount (int count) { checkWidget (); count = Math.max (0, count); - itemCountCache = count; + resetCache(); parent.setItemCount (handle, count); } @@ -1712,18 +1716,13 @@ public void setText (String [] strings) { } } -private void resetParentCache() { - TreeItem parentItem = getParentItem(); - if (parentItem != null) { - parentItem.itemCountCache = -1; - } +private void resetCache() { + getCache().reset(); } -private int getItemCountImpl() { - if (itemCountCache < 0) { - itemCountCache = GTK.gtk_tree_model_iter_n_children (parent.modelHandle, handle); - } - return itemCountCache; +private TreeItemCache getCache() { + return parent.getItemCache(this); } + } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/TreeItemCache.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/TreeItemCache.java new file mode 100644 index 00000000000..82848c9c9a1 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/TreeItemCache.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2023, 2023 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Vasili Gulevich - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.widgets; + +import java.util.*; + +import org.eclipse.swt.internal.gtk.*; + +/** Volatile information about a TreeItem, that takes long to compute + * @since 3.125**/ +class TreeItemCache { + final TreeItem owner; + private TreeItem[] children; + private int itemCount = -1; + + TreeItemCache(TreeItem owner) { + this.owner = Objects.requireNonNull(owner); + } + + int getItemCount() { + if (itemCount < 0) { + itemCount = GTK.gtk_tree_model_iter_n_children (owner.parent.modelHandle, owner.handle); + } + return itemCount; + } + + TreeItem[] getItems() { + if (children == null) { + children = owner.parent.getItems(owner.handle); + itemCount = children.length; + } + return children; + } + + public void reset() { + itemCount = -1; + children = null; + } +}