Skip to content

Commit 7c4e17e

Browse files
ptzieglervogella
authored andcommitted
Wrap viewer in CheckboxTablePart within a FilteredTable
By using the FilteredTable, we get a quick-filter for all extending parts for free. This includes: - The Cross-platform page of the "Deployable features" export wizard. - The Update Java Classpath page of the "Update classpath..." action - The Template Selection page of the "Plug-in Project" page. Together with the quick-filter comes a caching of the checked table items. Those elements remain checked, even if currently hidden by the filter and will be restored once they become visible again.
1 parent 5dc5f26 commit 7c4e17e

File tree

11 files changed

+380
-75
lines changed

11 files changed

+380
-75
lines changed

ui/org.eclipse.pde.ui/.settings/.api_filters

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,14 @@
120120
</message_arguments>
121121
</filter>
122122
</resource>
123+
<resource path="src/org/eclipse/pde/internal/ui/shared/CachedCheckboxTableViewer.java" type="org.eclipse.pde.internal.ui.shared.CachedCheckboxTableViewer">
124+
<filter comment="Extends CheckboxTableViewer to match CachedCheckboxTreeviewer" id="571473929">
125+
<message_arguments>
126+
<message_argument value="CheckboxTableViewer"/>
127+
<message_argument value="CachedCheckboxTableViewer"/>
128+
</message_arguments>
129+
</filter>
130+
</resource>
123131
<resource path="src/org/eclipse/pde/internal/ui/shared/target/EditTargetContainerPage.java" type="org.eclipse.pde.internal.ui.shared.target.EditTargetContainerPage">
124132
<filter id="640712815">
125133
<message_arguments>

ui/org.eclipse.pde.ui/META-INF/MANIFEST.MF

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ Require-Bundle:
7777
org.eclipse.pde.core;bundle-version="[3.17.200,4.0.0)";visibility:=reexport,
7878
org.eclipse.core.runtime;bundle-version="[3.29.0,4.0.0)",
7979
org.eclipse.e4.core.services;bundle-version="[2.4.200,3.0.0)",
80+
org.eclipse.e4.ui.dialogs;bundle-version="[1.6.0,2.0.0)",
8081
org.eclipse.ui.ide;bundle-version="[3.21.200,4.0.0)",
8182
org.eclipse.ui.views;bundle-version="[3.12.100,4.0.0)",
8283
org.eclipse.jface.text;bundle-version="[3.24.200,4.0.0)",

ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/parts/CheckboxTablePart.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2018 IBM Corporation and others.
2+
* Copyright (c) 2000, 2025 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -13,15 +13,17 @@
1313
*******************************************************************************/
1414
package org.eclipse.pde.internal.ui.parts;
1515

16-
import org.eclipse.jface.viewers.CheckboxTableViewer;
1716
import org.eclipse.jface.viewers.IStructuredSelection;
1817
import org.eclipse.jface.viewers.StructuredViewer;
18+
import org.eclipse.pde.internal.ui.shared.CachedCheckboxTableViewer;
19+
import org.eclipse.pde.internal.ui.shared.FilteredCheckboxTable;
1920
import org.eclipse.swt.SWT;
2021
import org.eclipse.swt.widgets.Button;
2122
import org.eclipse.swt.widgets.Composite;
2223
import org.eclipse.ui.forms.widgets.FormToolkit;
2324

2425
public class CheckboxTablePart extends StructuredViewerPart {
26+
2527
public CheckboxTablePart(String[] buttonLabels) {
2628
super(buttonLabels);
2729
}
@@ -34,15 +36,16 @@ protected StructuredViewer createStructuredViewer(Composite parent, int style, F
3436
} else {
3537
style |= toolkit.getBorderStyle();
3638
}
37-
CheckboxTableViewer tableViewer = CheckboxTableViewer.newCheckList(parent, style);
39+
FilteredCheckboxTable filteredTable = new FilteredCheckboxTable(parent, toolkit, style);
40+
CachedCheckboxTableViewer tableViewer = filteredTable.getViewer();
3841
tableViewer
3942
.addSelectionChangedListener(e -> CheckboxTablePart.this.selectionChanged(e.getStructuredSelection()));
4043
tableViewer.addCheckStateListener(event -> elementChecked(event.getElement(), event.getChecked()));
4144
return tableViewer;
4245
}
4346

44-
public CheckboxTableViewer getTableViewer() {
45-
return (CheckboxTableViewer) getViewer();
47+
public CachedCheckboxTableViewer getTableViewer() {
48+
return (CachedCheckboxTableViewer) getViewer();
4649
}
4750

4851
@Override

ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/parts/WizardCheckboxTablePart.java

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2015 IBM Corporation and others.
2+
* Copyright (c) 2000, 2025 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -14,9 +14,11 @@
1414
package org.eclipse.pde.internal.ui.parts;
1515

1616
import org.eclipse.jface.viewers.CheckboxTableViewer;
17+
import org.eclipse.jface.viewers.IStructuredContentProvider;
1718
import org.eclipse.jface.viewers.StructuredViewer;
1819
import org.eclipse.osgi.util.NLS;
1920
import org.eclipse.pde.internal.ui.PDEUIMessages;
21+
import org.eclipse.pde.internal.ui.shared.CachedCheckboxTableViewer;
2022
import org.eclipse.pde.internal.ui.util.SWTUtil;
2123
import org.eclipse.pde.internal.ui.wizards.ListUtil;
2224
import org.eclipse.swt.SWT;
@@ -33,7 +35,6 @@ public class WizardCheckboxTablePart extends CheckboxTablePart {
3335
private int selectIndex = -1;
3436
private int deselectIndex = -1;
3537
private final String tableName;
36-
private int counter;
3738
private Label counterLabel;
3839

3940
/**
@@ -92,7 +93,7 @@ public Object[] getSelection() {
9293
public void setSelection(Object[] selected) {
9394
CheckboxTableViewer viewer = getTableViewer();
9495
viewer.setCheckedElements(selected);
95-
updateCounter(viewer.getCheckedElements().length);
96+
updateCounterLabel();
9697
}
9798

9899
public void createControl(Composite parent) {
@@ -105,7 +106,7 @@ public void createControl(Composite parent, int span) {
105106
GridData gd = new GridData(GridData.VERTICAL_ALIGN_BEGINNING | GridData.HORIZONTAL_ALIGN_FILL);
106107
gd.horizontalSpan = span;
107108
counterLabel.setLayoutData(gd);
108-
updateCounter(0);
109+
updateCounterLabel();
109110
}
110111

111112
public void createControl(Composite parent, int span, boolean multiselect) {
@@ -118,7 +119,7 @@ public void createControl(Composite parent, int span, boolean multiselect) {
118119
GridData gd = new GridData(GridData.VERTICAL_ALIGN_BEGINNING | GridData.HORIZONTAL_ALIGN_FILL);
119120
gd.horizontalSpan = span;
120121
counterLabel.setLayoutData(gd);
121-
updateCounter(0);
122+
updateCounterLabel();
122123
}
123124

124125
@Override
@@ -147,24 +148,19 @@ protected void createMainLabel(Composite parent, int span, FormToolkit toolkit)
147148
label.setLayoutData(gd);
148149
}
149150

150-
protected void updateCounter(int amount) {
151-
counter = amount;
152-
updateCounterLabel();
153-
}
154-
155-
public void updateCount(int amount) {
156-
updateCounter(amount);
157-
}
158-
159-
protected void updateCounterLabel() {
151+
public void updateCounterLabel() {
160152
String number = "" + getSelectionCount(); //$NON-NLS-1$
161153
String totalNumber = "" + getTotalCount(); //$NON-NLS-1$
162154
String message = NLS.bind(PDEUIMessages.WizardCheckboxTablePart_counter, (new String[] {number, totalNumber}));
163155
counterLabel.setText(message);
164156
}
165157

166158
public int getSelectionCount() {
167-
return counter;
159+
CachedCheckboxTableViewer viewer = getTableViewer();
160+
if (viewer == null) {
161+
return 0;
162+
}
163+
return viewer.getUnfilteredCheckedCount();
168164
}
169165

170166
public void selectAll(boolean select) {
@@ -173,19 +169,21 @@ public void selectAll(boolean select) {
173169

174170
private int getTotalCount() {
175171
CheckboxTableViewer viewer = getTableViewer();
176-
return viewer.getTable().getItemCount();
172+
if (viewer == null) {
173+
return 0;
174+
}
175+
176+
IStructuredContentProvider contentProvider = (IStructuredContentProvider) viewer.getContentProvider();
177+
if (contentProvider == null) {
178+
return 0;
179+
}
180+
return contentProvider.getElements(viewer.getInput()).length;
177181
}
178182

179183
protected void handleSelectAll(boolean select) {
180184
CheckboxTableViewer viewer = getTableViewer();
181185
viewer.setAllChecked(select);
182-
int selected;
183-
if (!select) {
184-
selected = 0;
185-
} else {
186-
selected = getTotalCount();
187-
}
188-
updateCounter(selected);
186+
updateCounterLabel();
189187
}
190188

191189
protected void handleSelect(boolean select) {
@@ -195,14 +193,13 @@ protected void handleSelect(boolean select) {
195193
for (TableItem selectedItem : selected) {
196194
selectedItem.setChecked(select);
197195
}
198-
updateCounter(viewer.getCheckedElements().length);
196+
updateCounterLabel();
199197
}
200198
}
201199

202200
@Override
203201
protected void elementChecked(Object element, boolean checked) {
204-
int count = getSelectionCount();
205-
updateCounter(checked ? count + 1 : count - 1);
202+
updateCounterLabel();
206203
}
207204

208205
public Label getCounterLabel() {
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Patrick Ziegler and others.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* Patrick Ziegler - initial API and implementation
13+
*******************************************************************************/
14+
package org.eclipse.pde.internal.ui.shared;
15+
16+
import java.util.Collections;
17+
import java.util.HashSet;
18+
import java.util.Set;
19+
20+
import org.eclipse.jface.viewers.CheckboxTableViewer;
21+
import org.eclipse.jface.viewers.CheckboxTreeViewer;
22+
import org.eclipse.swt.widgets.Table;
23+
import org.eclipse.swt.widgets.Tree;
24+
25+
/**
26+
* Counterpart of the {@link CachedCheckboxTreeViewer} which is specialized for
27+
* use with {@link FilteredCheckboxTable}. This viewer caches the check state of
28+
* the table items. When a filter is applied the cache stores which nodes are
29+
* checked. When a filter is removed the viewer can be told to restore check
30+
* state from the cache.
31+
* <p>
32+
* Note: If duplicate items are added to the table the cache will treat them as
33+
* a single entry.
34+
* </p>
35+
*/
36+
public class CachedCheckboxTableViewer extends CheckboxTableViewer {
37+
38+
private Set<Object> checkState = new HashSet<>();
39+
private static final Object[] NO_ELEMENTS = new Object[0];
40+
41+
/**
42+
* Constructor for ContainerCheckedTreeViewer.
43+
* @see CheckboxTreeViewer#CheckboxTreeViewer(Tree)
44+
*/
45+
protected CachedCheckboxTableViewer(Table table) {
46+
super(table);
47+
addCheckStateListener(event -> updateCheckState(event.getElement(), event.getChecked()));
48+
setUseHashlookup(true);
49+
}
50+
51+
protected void updateCheckState(Object element, boolean state) {
52+
if (state) {
53+
checkState.add(element);
54+
} else {
55+
checkState.remove(element);
56+
}
57+
}
58+
59+
/**
60+
* Restores the checked state of items based on the cached check state.No
61+
* events will be fired.
62+
*/
63+
public void restoreCachedState() {
64+
getTable().setRedraw(false);
65+
// Call the super class so we don't mess up the cache
66+
super.setCheckedElements(NO_ELEMENTS);
67+
setGrayedElements(NO_ELEMENTS);
68+
// Now we are only going to set the check state of the leaf nodes
69+
// and rely on our container checked code to update the parents properly.
70+
super.setCheckedElements(checkState.toArray());
71+
getTable().setRedraw(true);
72+
}
73+
74+
@Override
75+
protected void preservingSelection(Runnable updateCode) {
76+
super.preservingSelection(updateCode);
77+
// The super class implementation will preserve a root element's check
78+
// mark but that can cause newly unfiltered children to become check
79+
// marked.
80+
// See https://github.com/eclipse-pde/eclipse.pde/issues/62
81+
restoreCachedState();
82+
}
83+
84+
/**
85+
* Returns the contents of the cached check state. The contents will be all
86+
* checked leaf nodes ignoring any filters.
87+
*
88+
* @return checked leaf elements
89+
*/
90+
public Object[] getUnfilteredCheckedElements() {
91+
return checkState.toArray(Object[]::new);
92+
}
93+
94+
/**
95+
* Returns the number of checked nodes. This method uses its internal check
96+
* state cache to determine what has been checked, not what is visible in
97+
* the viewer. The cache does not count duplicate items in the table.
98+
*
99+
* @return number of elements checked according to the cached check state
100+
*/
101+
public int getUnfilteredCheckedCount() {
102+
return checkState.size();
103+
}
104+
105+
/**
106+
* Returns whether {@code element} is checked. This method uses its internal
107+
* check state cache to determine what has been checked, not what is visible
108+
* in the viewer.
109+
*/
110+
public boolean isCheckedElement(Object element) {
111+
return checkState.contains(element);
112+
}
113+
114+
@Override
115+
public boolean setChecked(Object element, boolean state) {
116+
updateCheckState(element, state);
117+
return super.setChecked(element, state);
118+
}
119+
120+
@Override
121+
public void setCheckedElements(Object[] elements) {
122+
checkState.clear();
123+
Collections.addAll(checkState, elements);
124+
super.setCheckedElements(elements);
125+
}
126+
127+
@Override
128+
public void setAllChecked(boolean state) {
129+
super.setAllChecked(state);
130+
if (state) {
131+
// Find all visible children and check them
132+
Object[] visible = getFilteredChildren(getRoot());
133+
Collections.addAll(checkState, visible);
134+
} else {
135+
// Remove any item in the check state that is visible (passes the filters)
136+
Object[] visible = filter(checkState.toArray());
137+
for (Object visibleObject : visible) {
138+
checkState.remove(visibleObject);
139+
}
140+
}
141+
}
142+
143+
@Override
144+
public void remove(Object[] elements) {
145+
for (Object element : elements) {
146+
updateCheckState(element, false);
147+
}
148+
super.remove(elements);
149+
}
150+
151+
@Override
152+
public void remove(Object element) {
153+
updateCheckState(element, false);
154+
super.remove(element);
155+
}
156+
}

0 commit comments

Comments
 (0)