Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 251575 - [Viewers] TableViewer support for virtual selection
Summary: [Viewers] TableViewer support for virtual selection
Status: RESOLVED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 3.5   Edit
Hardware: PC Mac OS X - Carbon (unsup.)
: P3 enhancement (vote)
Target Milestone: 3.5 M6   Edit
Assignee: Boris Bokowski CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on: 251290
Blocks:
  Show dependency tree
 
Reported: 2008-10-21 13:49 EDT by Bryan Hunt CLA
Modified: 2009-03-06 22:29 EST (History)
3 users (show)

See Also:


Attachments
Proposed mixin interface (715 bytes, application/octet-stream)
2008-10-22 14:33 EDT, Peter Centgraf CLA
bokowski: iplog+
Details
TableViewer with more efficient setSelection() implementation (1.38 KB, application/octet-stream)
2008-10-22 14:35 EDT, Peter Centgraf CLA
bokowski: iplog+
Details
Previous attachments as patch (2.62 KB, patch)
2008-10-22 17:00 EDT, Peter Centgraf CLA
no flags Details | Diff
virtual selection patch (6.72 KB, patch)
2008-11-02 00:49 EDT, Bryan Hunt CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Bryan Hunt CLA 2008-10-21 13:49:24 EDT
I'd like to be able to call TableViewer.setSelection() and not have the underlying TableItems created.

I've hacked AbstractTableViewer a bit and I think I have it working.  The concept at least appears feasible, but my hack needs more work and testing.  The basis of the hack is to eliminate calls to doGetItem() in virtualSetSelectionToWidget(), and to dynamically set the real selection inside the VirtualManager.handleEvent().

I have run into one problem with this hack: bug #251290
Comment 1 Bryan Hunt CLA 2008-10-21 13:54:45 EDT
This enhancement is also related to bug #246417
Comment 2 Peter Centgraf CLA 2008-10-21 16:19:26 EDT
I have a similar but distinct requirement.  In my application, I'd like to pre-select a particular element as the default selection and scroll the Table to make this visible.  In this case, the TableViewer needs an efficient way to lookup the index of the element without loading all of the elements in the Table.  Then the TableViewer can use the same basic codepath as Table.setSelection(int[]), which we know is efficient.

I propose that we add new API to handle this case.  IIndexableLazyContentProvider (or perhaps ILookupLazyContentProvider) would extend ILazyContentProvider and add this method:

/**
 * Find the row index of the parameter element in the set of contents provided
 * by this object.  Under normal usage, this method will only be used to 
 * implement <code>StructuredViewer#setSelection(ISelection)</code> more
 * efficiently.
 *
 * @param element the element to find within the contents served here
 * @return the zero-based index of the element, or -1 if the element is not found
 */
public int findElement(Object element);

TableViewer should test for this interface on content providers in a similar way that it tests for ITableColorProvider on label providers.  If the method is available, AbstractTableViewer.doFindItem(Object) should use it to find and populate the specific Item requested.  Otherwise, the implementation should fall back to the current behavior.

What say the platform-ui API gurus?
Comment 3 Boris Bokowski CLA 2008-10-21 17:36:18 EDT
Peter's suggestion of a new mixin interface makes sense to me.

Bryan, will you be posting a patch at some point?
Comment 4 Bryan Hunt CLA 2008-10-21 18:01:17 EDT
Boris, I'd be happy to post a patch.  My big concern is that requires OSSC approval, and the last time I went through that, it took about 6 months.
Comment 5 Peter Centgraf CLA 2008-10-21 18:07:28 EDT
There is a block of code in AbstractTableViewer.virtualSetSelectionToWidget() that I don't understand completely.  There is a test for ILazyContentProvider and then what looks like initialization logic, with provider.updateElement() etc.  I think that my suggested change to doFindItem() would have the side-effect that this logic is not executed, because of the way "virtualElements" is used.  Boris, can you take a look at this and see if there's a simple way to restructure this so all of the invariants are still maintained, even if findItem() returns a TableItem?
Comment 6 Peter Centgraf CLA 2008-10-21 18:08:37 EDT
BTW, I can post a patch, assuming that Boris can help me understand the code I just mentioned.
Comment 7 Boris Bokowski CLA 2008-10-21 23:36:52 EDT
Peter, if you are talking about this section:

for (int i = 0; virtualElements.size() > 0 && i < doGetItemCount(); i++) {
	provider.updateElement(i);
	Item item = doGetItem(i);
	if (virtualElements.contains(item.getData())) {
		indices[count++] = i;
		virtualElements.remove(item.getData());
		if (firstItem == null) {
			firstItem = item;
		}
	}
}

This loops through the items, asking the content provider to update each item, followed by a check if the i'th item's data matches one of the elements we are asked to select. This will materialize items eagerly, starting at item index 0, until all elements to be selected have been found, or until we have materialized all items.

Does this make sense?
Comment 8 Peter Centgraf CLA 2008-10-22 14:33:52 EDT
Created attachment 115846 [details]
Proposed mixin interface
Comment 9 Peter Centgraf CLA 2008-10-22 14:35:46 EDT
Created attachment 115847 [details]
TableViewer with more efficient setSelection() implementation

I have run this through a few trivial tests, and it appears to do the job.  Please review, and if it passes muster, change TableViewer directly instead of using this class as-is.

As far as I know, this is enough to close the issue.
Comment 10 Peter Centgraf CLA 2008-10-22 14:41:27 EDT
I attached the code in this form so that it can be dropped into an existing RCP app running on 3.4.  I've been testing this way with my own app.

Bryan, this has the side-effect of creating the TableItems for exactly the items that are selected.  Is this still okay for your use case?  It's not clear from your initial request whether you need absolutely no TableItems to be created, or if a minimal, non-zero number is acceptable.
Comment 11 Boris Bokowski CLA 2008-10-22 14:56:14 EDT
(In reply to comment #10)
> I attached the code in this form so that it can be dropped into an existing RCP
> app running on 3.4.  I've been testing this way with my own app.

Peter, could you please also upload this in patch form? We need that format so that the automated IP tracking tools can know how much you contributed etc.

Thanks!
Comment 12 Bryan Hunt CLA 2008-10-22 15:09:52 EDT
Peter, for my case, I need exactly zero TableItems created when setSelection() is called when the VIRTUAL flag is set.
Comment 13 Peter Centgraf CLA 2008-10-22 17:00:46 EDT
Created attachment 115870 [details]
Previous attachments as patch

FYI, this patch expects a root directory inside the org.eclipse.jface.viewers package.  (I have the classes in my local SVN repo, not the JFace CVS.)

Bryan, I think you can use the same basic idea, but you'll have to rework the AbstractTableViewer.virtualSetSelectionToWidget() method a bit.  (I didn't want to mess with any private methods myself.)  Instead of this block:

Object o = list.get(i);
Widget w = findItem(o);
if (w instanceof Item) {
	Item item = (Item) w;
	indices[count++] = doIndexOf(item);
	if (firstItem == null) {
		firstItem = item;
	}
} else {
	virtualElements.add(o);
}

... and the whole block Boris pasted above, just do: 

indices[count++] = provider.findElement(list.get(i));

It appears that there is no way to call Table.showItem() without a real TableItem.  This means that if (reveal == true), we can't get to zero.  However, you can delay the doGetItem() call until inside the final block, so you'll have a max of 1 TableItem created and only in that specific case.
Comment 14 Bryan Hunt CLA 2008-10-23 11:24:40 EDT
Peter & Boris, do either of you have time to modify the selection code?  If not I can start the OSSC approval process for attaching a patch and hopefully it won't take 6 months this time.

The changes should be fairly simple.  The TableViewer needs to keep the selection independent of the Table.  The VirtualManager might be a good place to store this.  getSelection() simply needs to return this stored selection. The last piece is that the actual table selection needs to be updated when TableItems are created.  The VirtualManager.handleEvent() seems like the place to do this.
Comment 15 Boris Bokowski CLA 2008-10-23 11:52:34 EDT
(In reply to comment #14)

Does Peter's idea from comment 13 not work? I am reluctant to do what you (Bryan) suggest, because then the selection as seen through the TableViewer would be different from what the underlying Table widget thinks is selected.
Comment 16 Bryan Hunt CLA 2008-10-23 12:35:10 EDT
Boris, I believe the answer is no.  The fundamental problem is that virtualSetSelectionToWidget() calls doGetItem() which causes TableItems to be created.  The whole point of "virtual selection" is that the table viewer selection is different from the table selection.  Please have a look at the bug referenced in comment #1 for the motivation behind this request.
Comment 17 Boris Bokowski CLA 2008-10-23 14:23:11 EDT
Yes, that bug shows that we'd be fighting the native behavior if we tried to maintain a "selection" that is different from the operating system's idea of what is selected.

Btw, Peter's idea from comment 13 was to ask the content provider (using the new proposed API) for the indices, which means you would be able to issue the calls:
doDeselectAll();
doSelect(indices);
without any calls to doGetItem(). My question was if this does not work (I haven't tried it, so I don't know if doSelect() will materialize items).
Comment 18 Bryan Hunt CLA 2008-10-23 14:47:18 EDT
(In reply to comment #17)

Boris, I was basing my "no" answer on comment #10.

Peter, can you verify that I'm understanding your comment correctly.
Comment 19 Peter Centgraf CLA 2008-10-23 16:29:03 EDT
My proposal in comment 13 would avoid doGetItem() entirely, as long as (reveal == false).  As Boris says, doDeselectAll() and doSelect() would still get called.  Whether or not this results in TableItems being created is not specified in the SWT Table implementation and is not specified in the API. 

Bryan, I don't think you're going to get a rock-solid cross-platform guarantee using this approach.  If that's okay, I'd suggest that you setup a trivial test case to see whether these two methods cause TableItems to be created in your environment.  If they don't, we can proceed with the approach in comment 13 and satisfy both of us.
Comment 20 Bryan Hunt CLA 2008-10-23 16:52:13 EDT
(In reply to comment #19)

Peter, thanks for the clarification.  I'll try to test your patch tomorrow, or this weekend, and post my results.
Comment 21 Bryan Hunt CLA 2008-10-26 11:27:19 EDT
Peter, your proposed patch does not work for me.  When I select all, all of the rows are materialized as TableItems.
Comment 22 Bryan Hunt CLA 2008-10-26 11:37:24 EDT
I just realized that reveal was set to true, but when I changed it to false, the rows are still materialized.  In any event, the limitation of requiring reveal to be false to avoid row materialization probably won't work for our use case.
Comment 23 Bryan Hunt CLA 2008-10-30 11:21:38 EDT
I have received permission to work on this on my own time.  I'll try to put a patch together in the next few days.
Comment 24 Bryan Hunt CLA 2008-11-02 00:49:09 EDT
Created attachment 116722 [details]
virtual selection patch

Here's a first attempt at my proposed patch.

Using my testcase from bug #246417, the time to select all on OS X went from 1.449s to 0.006s, and on Linux went from 6.278s to 0.007s

The selection drawing problem described in bug #251290 does not occur on Linux so it may be OS X specific.
Comment 25 Bryan Hunt CLA 2008-11-10 10:17:11 EST
Boris, could you please comment on my patch?  I have time to work on it over the next three weeks.
Comment 26 Boris Bokowski CLA 2008-11-10 15:38:38 EST
I agree with Peter's comment 19:
> Bryan, I don't think you're going to get a rock-solid cross-platform guarantee
> using this approach.

I'd be fine with adding API as suggested by Peter, and otherwise enable people like Bryan to subclass TableViewer with additional behavior. Bryan, what would prevent you from moving your code into a subclass? Do you need additional "protected" method hooks, or should we make some existing private methods protected?
Comment 27 Bryan Hunt CLA 2008-11-10 17:50:07 EST
(In reply to comment #26)
Boris, Please see comment #21 and comment #22.  Peter's proposed patch does not work for me.

I'd also appreciate it if someone could explain why my patch would not work cross-platform.  So far, it seems to work fine on OS X, and Linux.  I'll see if I can test Windows and AIX this week.

I'll also see if I can put my changes into a subclass.  

IMHO, when you set the "VIRTUAL" style, the selection should also be virtual, and I personally consider the current behavior a bug.  I filed this as an enhancement because the current behavior has not been challenged by others.

Comment 28 Boris Bokowski CLA 2008-12-01 13:13:23 EST
(In reply to comment #27)
> I'll also see if I can put my changes into a subclass.  

Any progress on this?
Comment 29 Bryan Hunt CLA 2008-12-01 16:20:24 EST
(In reply to comment #28)
> (In reply to comment #27)
> > I'll also see if I can put my changes into a subclass.  
> 
> Any progress on this?
> 


I started working on this when my hard drive died.  I got my backup restored yesterday so I can try to finish the work this week.  One thing I noticed was that I was duplicating a lot of internal code.  I hope to have more info later this week.
Comment 30 Boris Bokowski CLA 2009-01-21 12:51:27 EST
(In reply to comment #29)
> ...  I hope to have more info later
> this week.

ping?
Comment 31 Bryan Hunt CLA 2009-01-23 14:56:33 EST
Sorry Boris, I haven't had time to look at this.  Thanks for pinging me.  I'll try to look at it soon.
Comment 32 Bryan Hunt CLA 2009-01-24 20:15:58 EST
Boris, I've started looking into this again, and I've run into a problem.  When StructuredViewer.widgetSelected() is called in response to clicking on a row in the table, the event.stateMask is always 0 no matter which modifier keys were pressed.  Is this just the way the event works, or a bug?  Is there any other way to tell that I need to start with a new selection vs adding or removing from the selection?  I'm currently blocked by this problem.
Comment 33 Boris Bokowski CLA 2009-01-24 21:24:43 EST
(In reply to comment #32)
> Boris, I've started looking into this again, and I've run into a problem.  When
> StructuredViewer.widgetSelected() is called in response to clicking on a row in
> the table, the event.stateMask is always 0 no matter which modifier keys were
> pressed.  Is this just the way the event works, or a bug?  Is there any other
> way to tell that I need to start with a new selection vs adding or removing
> from the selection?  I'm currently blocked by this problem.

Could you hook a key listener to track which modifier keys are pressed?
Comment 34 Bryan Hunt CLA 2009-01-24 23:03:23 EST
Boris, the key listener works except the first time the table is clicked.  When I bring up my test view, the table does not have focus, and does not receive any key events until it is selected.  Any ideas on how to work around this problem?
Comment 35 Boris Bokowski CLA 2009-03-06 22:28:54 EST
Released Peter's contribution. Bryan, we are at the API freeze for 3.5 now, so if you need additional hooks as API, we won't be able to do anything about it until 3.6.