What items are visible in Table/Tree?

Have you often asked yourself which items are visibile in Table/Tree. Maybe you need these informations to dispose system-resources (e.g. if you have different images in every row you can easily run out of system resources).

What to do? There’s no SWT-API available to calculate this information. So we need a bunch of custom code to make this work. The following codefragements are only a first rough test case and they need JFace from 3.3M4 and a patch from bug 151295 .

Step 1: Setup the infrastructure
– Event object to inform consumers about the change of visible rows

public class ViewerRowStateChangedEvent extends EventObject {
  private static final long serialVersionUID = 1L;

  public ArrayList itemsHidden;
  public ArrayList itemsVisible;

  public ViewerRowStateChangedEvent(Object source) {
    super(source);
  }
}

– ViewerRowStateChangeListener for consumers to implement

public interface ViewerRowStateChangeListener {
  public void itemStateChangedListener(ViewerRowStateChangedEvent event);
}

Step 2: Create an AbstractClass implementing the visible row logic on base of ViewerRow and the new API whichn will hopefully added by bug 151295.

public abstract class AbstractViewerRowVisibilityStateSupport {
  private ArrayList currentItems = new ArrayList();
  private ColumnViewer columnViewer;
  private ListenerList listenerList = new ListenerList();

  public AbstractViewerRowVisibilityStateSupport(ColumnViewer columnViewer) {
    this.columnViewer = columnViewer;

    Listener l = new Listener() {
      public void handleEvent(Event event) {
        ArrayList list = recalculateVisibleItems();
        ArrayList itemsVisible = new ArrayList();
 
        Iterator it = list.iterator();
        Object obj;

        while( it.hasNext() ) {
          obj = it.next();

          if( ! currentItems.remove(obj) ) {
            itemsVisible.add(obj);
          }
        }

        ArrayList hiddenItems = currentItems;
        currentItems = list;

        if( itemsVisible.size() > 0 || hiddenItems.size() > 0 ) {
          if( ! listenerList.isEmpty() ) {
            ColumnViewer v;
            v = AbstractViewerRowVisibilityStateSupport.this.columnViewer;

            ViewerRowStateChangedEvent ev = new ViewerRowStateChangedEvent(v);
            ev.itemsHidden = hiddenItems;
            ev.itemsVisible = itemsVisible;

            Object[] listeners = listenerList.getListeners();
            ViewerRowStateChangeListener l;

            for( int i = 0; i < listeners.length; i++ ) {
              l = (ViewerRowStateChangeListener)listeners[i];
              l.itemStateChangedListener(ev);
            }
          }
        } 
      }
    };
    addListeners(getControl(),l);
  }

  protected abstract void addListeners(Scrollable control, Listener l);

  protected abstract ViewerRow getTopRow();
  protected Scrollable getControl() {
    return (Scrollable)columnViewer.getControl();
  }

  public void addItemStateListener(ViewerRowStateChangeListener listener) {
    listenerList.add(listener);
  }

  private ArrayList recalculateVisibleItems() {
    ArrayList list = new ArrayList(100);
    ViewerRow topRow = getTopRow();

    if( topRow != null ) {
      int totalHeight = getControl().getClientArea().height;
      int itemHeight = topRow.getBounds().height;

      list.add(topRow);

      int tmp = topRow.getBounds().x+itemHeight;
      // tmp += itemHeight;
      // this would be more precise but half rows
      // would be marked as non-visible
      // run until we reached the end of the client-area
      while( tmp < totalHeight ) {
        tmp += itemHeight;
        topRow = topRow.getNeighbor(ViewerRow.BELOW, false);
 
        if( topRow == null ) {
          break;
        }
        list.add(topRow);
      }
    }
    return list;
  }
}

We simply listen to events who can change the items shown in the Scrollable make a diff to the last state and inform all consumers about the change.

But what are the events how modify the items shown? This is delegated to specialized classes for Table and Tree because those may differ from control to control.

Step 3: Provide specialized implementation for Tree and Table
– An implementation for SWT-Table

public class TableViewerRowVisibilityStateSupport extends AbstractViewerRowVisibilityStateSupport {
  public TableViewerRowVisibilityStateSupport(TableViewer columnViewer) {
    super(columnViewer);
  }

  protected void addListeners(Scrollable control, Listener l) {
    control.getVerticalBar().addListener(SWT.Selection, l);
    control.addListener(SWT.Resize, l);
    control.addListener(SWT.KeyUp, l);
  }

  protected ViewerRow getTopRow() {
    Table t = (Table)getControl();
    int index = t.getTopIndex();
    TableItem topItem = t.getItem(index);

    if( topItem != null ) {
      return (ViewerRow) topItem.getData(ViewerRow.ROWPART_KEY);
    }

    return null;
  }
}

– An implementation for SWT-Tree

public class TreeViewerRowVisibilityStateSupport extends AbstractViewerRowVisibilityStateSupport {
  public TreeViewerRowVisibilityStateSupport(TreeViewer columnViewer) {
    super(columnViewer);
  }

  protected void addListeners(Scrollable control, Listener l) {
    control.getVerticalBar().addListener(SWT.Selection, l);
    control.addListener(SWT.Resize, l);
    control.addListener(SWT.MouseUp, l);
    control.addListener(SWT.KeyUp, l);
  }

  protected ViewerRow getTopRow() {
    TreeItem topItem = ((Tree)getControl()).getTopItem();
    if( topItem != null ) {
      return (ViewerRow) topItem.getData(ViewerRow.ROWPART_KEY);
    }
    return null;
  }
}

As said this is very rough first draft how this could work. I’ll post this got some coments about possible issues this could provoke.

Posted in 3.x | 17 Comments

the new Faces of JFace (part 1)

Some of you may have noticed JFace provides new API to streamline JFace usage and to support new features like:

  • custom owner draw
  • tooltips for tree/table-cells
  • keyboard editing support for viewers

To provide all those new features we (Tod Creasey, Boris Bokowski and me) decide to refactor the underling JFace code, provide new classes and to move things from specialized classes to more generice ones. The central of the whole refactoring brought up a whole bunch of new classes where the most important are the following:

  • ColumnViewer: Provides all things common to Viewers who deal with the concept of columns and rows
  • ViewerRow: Represents a row in the column viewer and wraps TableItem and TreeItem from SWT
  • ViewerCell: Represents one cell in the table/tree

We decide to create those new classe because it gives us the possibility to push as much code in the widget independent ColumnViewer instead of the widget centric classes derived from it.

All the changes mentionned above might not be interesting to you because most of the time you want use them directly so let’s take a look at the more interesting things from a user point of view.

The most interesting thing to most of you might be that JFace has now adopted the programming style from SWT and JFace-Coding feels now much more like SWT-Coding. Look at the next few lines and you understand what I mean:

Composite parent = ...;
TableViewer v = new TableViewer(parent);
v.getTable().setLinesVisible(true);

TableViewerColumn vColumn = new TableViewerColumn(v,SWT.NONE);
vColumn.getColumn().setWidth(200);
vColumn.setLabelProvider(new MyLabelProvider());

v.setContentProvider(new MyContentProvider());
v.setInput(model);
Posted in 3.x | 8 Comments

Are OS-ToolTips not flexible enough / Use JFace ToolTips

How often have you wished that OS-Tooltips would provide more functionalities like MultiLine-Text, Background-Color, Embedded Images, … . The time you faced those problems is over since today where JFace ToolTips are available to the public.

Usage is fairly straight forward:

Text text = new Text(parent,SWT.BORDER);
text.setText("Hello World");
DefaultToolTip toolTip = new DefaultToolTip(text);
toolTip.setText("Hello World");
toolTip.setBackgroundColor(parent.getDisplay().getSystemColor(SWT.COLOR_RED));

Easy isn’t it but there are many many more things available to you. You can add any control you want to the tooltip by subclassing org.eclipse.jface.window.ToolTip and providing your own implementation for createToolTipContentArea.

public class MyToolTip extends ToolTip {
  private Shell parentShell;

  public MyToolTip(Control control) {
    super(control);
    this.parentShell = control.getShell();
  }

  protected Composite createToolTipContentArea(Event event, Composite parent) {
    Composite comp = new Composite(parent,SWT.NONE);
    comp.setLayout(new FillLayout());

    Button b = new Button(comp,SWT.PUSH);
    b.setText("Say Hello");
    b.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        hide();
        MessageBox box = new MessageBox(parentShell,SWT.ICON_INFORMATION);
        box.setMessage("Hello World!");
        box.setText("Hello World");
        box.open();
      }
    });
  }
}

Usage is the same than before:

Text text = new Text(parent,SWT.BORDER);
text.setText("Hello World");

MyToolTip myTooltipLabel = new MyToolTip(text);
myTooltipLabel.setShift(new Point(-5, -5));
myTooltipLabel.setHideOnMouseDown(false);
myTooltipLabel.activate();

The small difference is that you configure the tip to not hide when the user clicks into the ToolTip the ToolTip-Code needs to ensure the hiding by its own by calling hide().

There are other nice feature available to you:

  • possibility to define delay before the ToolTip pops up
  • possibility to define delay how long the ToolTip is shown
  • possibility to define the pop up position
  • possibility to define a shift like shown in the code above
  • possibility to completely control the popup position by overloading ToolTip#getLocation(Point,Event)
  • configure whether the bounds of the display and/or monitors should be:

    • setRespectDisplayBounds(boolean)
    • setRespectMonitorBounds(boolean)

Posted in 3.x | Leave a comment

at.bestsolution.jface32-0.0.1 released

As promised I have released my JFace backport from 3.3 just a view days after 3.2 came out. You can get the ready plugin from my companies SVN-Repository.

Posted in 3.x | Leave a comment

JFace and features not part of Eclipse

Today I sit down and thought it is time to provide features JFace won’t provide (some of them are marked as later or are refused) in an own project named at.bestsolution.jface. Well 2 features I already had around somewhere in my workspaces:

  • TableViewer working with multiple instances of the same object
  • Dialog which could restore it’s values because it’s only hidden

All people interested in the work could as always fetch the sources from my companies SVN-Server. At the moment this plugin only depends on JFace but it’s likely that this will change in future and org.eclipse.swt.nebula and at.bestsolution.jface32 will be added as dependencies. I haven’t thoroughly tested because I never use it in my daily work so the QA is your part 😉

Posted in 3.x | Leave a comment

How much can happen within one week?

Back from holiday I see JFace’s new viewer API evolving from week to week and getting more stable and userfriendly but some parts are not as smooth as they should be some work has to spent. The most markable things changed is that the caching of column-indices is gone completely.

Another important news is that Boris sorted out some VIRTUAL-Table bugs. As of this writing I have integrated all those changes to at.bestsolution.jface32 project. Simply fetch all changes from my companies (BestSolution Systemhaus GmbH) public SVN-Repository.

At the moment all things from JFace HEAD are integrated and because Tod, Boris and I all think that my widget-independency work can go in as is I’ve already integrated it. JFace integration will follow at the beginning 3.3M3 timeframe.

And last but not less important a new API-function has been added to set multiple filters at once this should avoid us to use all those nasty hacks to avoid flickering, … .

I think I’ll release a first version of the plugin with M2.

Finally to a new JFace project providing snippets to all show common JFace. The new project has been approved by the eclipse PMC and can be found in eclipse’s CVS with contribution from Tod and myself

Posted in 3.x | 2 Comments

Viewer-Features form 3.3 in Eclipse-3.2

There are many many exciting features added in 3.3 cycle to jface but many people need a solution now and not in 3.3-Timeframe. I myself have a need for some of those feature so I started back-porting them which is at the moment not really hard because at the moment JFace-Viewers doesn’t use anything not available in 3.2-sources.

I decided to make them publicly available because I think many also have a need for them. So for those who want to use exciting features added in 3.3 can fetch the sources at in my companies SVN-Directory. Don’t forget to do your own QA to ensure everything is working as expected. If you find bugs you can file them to my companies bug-tracking system at http://phpot.bestsolution.at/mantis/login_page.php and I’ll try to incooperate them in eclipse bug-tracking system providing a patch for current HEAD-Trunk of CVS.

Before releasing a first version I’m trying to get the patches to 3.3-CVS-HEAD:

The current features/bug fixes against 3.2:

This project will only continue until 3.3 is out afterwards people will need to recompile their code replacing at.bestsolution.jface32.* through org.eclipse.jface.*.

At the end please note the following things:

  • this is nothing official from Eclipse and I only thought that there are maybe others who have the same needs than I have.
  • There’s a great likelihood that new API added in 3.3 at some point will change while 3.3 evolves so the API you find now is maybe a subject to change in the next release.
Posted in 3.x | 2 Comments

Viewers-Refactoring

Banging my head against Viewers since about a month we finally reached a state where it’s possible the first time to use viewers with different widget than org.eclipse.swt.widgets.Table. At the moment only TableViewer supports widget independency but once this is in head. TreeViewer is the next to come.

Posted in 3.x | Leave a comment