Package org.eclipse.sapphire.ui.forms.swt

Source Code of org.eclipse.sapphire.ui.forms.swt.CheckBoxListPropertyEditorPresentation$Entry

/******************************************************************************
* Copyright (c) 2014 Oracle
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*    Konstantin Komissarchik - initial implementation and ongoing maintenance
*    Ling Hao - [404482] CheckBoxListPropertyEditorRenderer needs a listener
******************************************************************************/

package org.eclipse.sapphire.ui.forms.swt;

import static org.eclipse.sapphire.ui.forms.swt.GridLayoutUtil.gd;
import static org.eclipse.sapphire.ui.forms.swt.GridLayoutUtil.gdfill;
import static org.eclipse.sapphire.ui.forms.swt.GridLayoutUtil.gdvalign;
import static org.eclipse.sapphire.ui.forms.swt.GridLayoutUtil.gdwhint;
import static org.eclipse.sapphire.ui.forms.swt.GridLayoutUtil.glayout;
import static org.eclipse.sapphire.ui.forms.swt.GridLayoutUtil.glspacing;
import static org.eclipse.sapphire.ui.forms.swt.SwtUtil.makeTableSortable;
import static org.eclipse.sapphire.ui.forms.swt.SwtUtil.suppressDashedTableEntryBorder;
import static org.eclipse.sapphire.util.CollectionsUtil.equalsBasedOnEntryIdentity;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;

import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ICheckStateProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.sapphire.Disposable;
import org.eclipse.sapphire.Element;
import org.eclipse.sapphire.ElementList;
import org.eclipse.sapphire.ElementType;
import org.eclipse.sapphire.Event;
import org.eclipse.sapphire.FilteredListener;
import org.eclipse.sapphire.ImageData;
import org.eclipse.sapphire.ImageService;
import org.eclipse.sapphire.ListProperty;
import org.eclipse.sapphire.Listener;
import org.eclipse.sapphire.LocalizableText;
import org.eclipse.sapphire.LoggingService;
import org.eclipse.sapphire.PossibleValuesService;
import org.eclipse.sapphire.Property;
import org.eclipse.sapphire.PropertyContentEvent;
import org.eclipse.sapphire.PropertyDef;
import org.eclipse.sapphire.PropertyValidationEvent;
import org.eclipse.sapphire.Sapphire;
import org.eclipse.sapphire.Text;
import org.eclipse.sapphire.Unique;
import org.eclipse.sapphire.Value;
import org.eclipse.sapphire.ValueProperty;
import org.eclipse.sapphire.modeling.CapitalizationType;
import org.eclipse.sapphire.modeling.localization.LocalizationService;
import org.eclipse.sapphire.services.PossibleTypesService;
import org.eclipse.sapphire.services.ValueImageService;
import org.eclipse.sapphire.services.ValueLabelService;
import org.eclipse.sapphire.ui.ListSelectionService;
import org.eclipse.sapphire.ui.ListSelectionService.ListSelectionChangedEvent;
import org.eclipse.sapphire.ui.assist.internal.PropertyEditorAssistDecorator;
import org.eclipse.sapphire.ui.forms.FormComponentPart;
import org.eclipse.sapphire.ui.forms.PropertyEditorDef;
import org.eclipse.sapphire.ui.forms.PropertyEditorPart;
import org.eclipse.sapphire.ui.forms.swt.internal.ColumnSortComparator;
import org.eclipse.sapphire.util.ListFactory;
import org.eclipse.sapphire.util.SetFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;

/**
* @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a>
* @author <a href="mailto:ling.hao@oracle.com">Ling Hao</a>
*/

public final class CheckBoxListPropertyEditorPresentation extends ListPropertyEditorPresentation
{
    @Text( "<empty>" )
    private static LocalizableText emptyIndicator;
   
    static
    {
        LocalizableText.init( CheckBoxListPropertyEditorPresentation.class );
    }

    private Table table;
    private CheckboxTableViewer tableViewer;
    private ElementType memberType;
    private ValueProperty memberProperty;
    private PossibleValuesService possibleValuesService;
    private Listener possibleValuesServiceListener;

    public CheckBoxListPropertyEditorPresentation( final FormComponentPart part, final SwtPresentation parent, final Composite composite )
    {
        super( part, parent, composite );
    }

    @Override
   
    protected void createContents( final Composite parent )
    {
        // Initialize
       
        final PropertyEditorPart part = part();
        final Property property = part.property();
       
        this.memberType = property.definition().getType();
       
        final SortedSet<PropertyDef> allMemberProperties = this.memberType.properties();
       
        if( allMemberProperties.size() == 1 )
        {
            final PropertyDef prop = allMemberProperties.first();
           
            if( prop instanceof ValueProperty )
            {
                this.memberProperty = (ValueProperty) prop;
            }
            else
            {
                throw new IllegalStateException();
            }
        }
        else
        {
            throw new IllegalStateException();
        }
       
        this.possibleValuesService = property.service( PossibleValuesService.class );
       
        this.possibleValuesServiceListener = new Listener()
        {
            @Override
            public void handle( final Event event )
            {
              CheckBoxListPropertyEditorPresentation.this.tableViewer.refresh();
            }
        };
       
        this.possibleValuesService.attach( this.possibleValuesServiceListener );

        // Create Controls
       
        final Composite mainComposite = createMainComposite( parent );
        mainComposite.setLayout( glspacing( glayout( 2, 0, 0 ), 2 ) );
       
        final PropertyEditorAssistDecorator decorator = createDecorator( mainComposite );
        decorator.control().setLayoutData( gdvalign( gd(), SWT.TOP ) );
       
        // Setting the whint in the following code is a hacky workaround for the problem
        // tracked by the following JFace bug:
        //
        // https://bugs.eclipse.org/bugs/show_bug.cgi?id=215997
        //
       
        final Composite tableComposite = new Composite( mainComposite, SWT.NONE );
        tableComposite.setLayoutData( gdwhint( gdfill(), 1 ) );
       
        final TableColumnLayout tableColumnLayout = new TableColumnLayout();
        tableComposite.setLayout( tableColumnLayout );
       
        this.tableViewer = CheckboxTableViewer.newCheckList( tableComposite, SWT.BORDER | SWT.FULL_SELECTION );
        this.table = this.tableViewer.getTable();
       
        final TableViewerColumn viewerColumn = new TableViewerColumn( this.tableViewer, SWT.NONE );
        final TableColumn column = viewerColumn.getColumn();
        column.setText( this.memberProperty.getLabel( false, CapitalizationType.TITLE_STYLE, false ) );
        tableColumnLayout.setColumnData( column, new ColumnWeightData( 1, 100, true ) );
       
        decorator.addEditorControl( mainComposite );
       
        suppressDashedTableEntryBorder( this.table );
       
        // Bind to Model
       
        final ColumnSortComparator comparator = new ColumnSortComparator()
        {
            @Override
            protected String convertToString( final Object obj )
            {
                return ( (Entry) obj ).value;
            }
        };
       
        final IStructuredContentProvider contentProvider = new IStructuredContentProvider()
        {
            private List<Entry> entries = new ArrayList<Entry>();
           
            public Object[] getElements( final Object input )
            {
                if( this.entries != null )
                {
                    for( Entry entry : this.entries )
                    {
                        entry.dispose();
                    }
                   
                    this.entries = null;
                }
               
                final Map<String,LinkedList<Element>> valueToElements = new HashMap<String,LinkedList<Element>>();
               
                for( final Element element : property() )
                {
                    final String value = readMemberProperty( element );
                    LinkedList<Element> elements = valueToElements.get( value );
                   
                    if( elements == null )
                    {
                        elements = new LinkedList<Element>();
                        valueToElements.put( value, elements );
                    }
                   
                    elements.add( element );
                }
               
                this.entries = new ArrayList<Entry>();
               
                Set<String> possibleValues;
               
                try
                {
                    possibleValues = CheckBoxListPropertyEditorPresentation.this.possibleValuesService.values();
                }
                catch( Exception e )
                {
                    Sapphire.service( LoggingService.class ).log( e );
                    possibleValues = SetFactory.empty();
                }

                for( String value : possibleValues )
                {
                    final Entry entry;
                    final LinkedList<Element> elements = valueToElements.get( value );
                   
                    if( elements == null )
                    {
                        entry = new Entry( value, null );
                    }
                    else
                    {
                        final Element element = elements.remove();
                       
                        if( elements.isEmpty() )
                        {
                            valueToElements.remove( value );
                        }
                       
                        entry = new Entry( value, element );
                    }
                   
                    this.entries.add( entry );
                }
               
                for( Map.Entry<String,LinkedList<Element>> entry : valueToElements.entrySet() )
                {
                    final String value = entry.getKey();
                   
                    for( Element element : entry.getValue() )
                    {
                        this.entries.add( new Entry( value, element ) );
                    }
                }
               
                return this.entries.toArray();
            }
           
            public void dispose()
            {
                for( Entry entry : this.entries )
                {
                    entry.dispose();
                }
               
                this.entries = null;
            }

            public void inputChanged( final Viewer viewer,
                                      final Object oldInput,
                                      final Object newInput )
            {
            }
        };
       
        this.tableViewer.setContentProvider( contentProvider );
       
        final ColumnLabelProvider labelProvider = new ColumnLabelProvider()
        {
            @Override
            public String getText( final Object element )
            {
                return ( (Entry) element ).label();
            }

            @Override
            public Image getImage( final Object element )
            {
                return ( (Entry) element ).image();
            }
           
            @Override
            public Color getForeground( final Object element )
            {
                return ( (Entry) element ).foreground();
            }
        };
       
        viewerColumn.setLabelProvider( labelProvider );
       
        final ICheckStateProvider checkStateProvider = new ICheckStateProvider()
        {
            public boolean isChecked( final Object element )
            {
                return ( (Entry) element ).selected();
            }
           
            public boolean isGrayed( final Object element )
            {
                return false;
            }
        };
       
        this.tableViewer.setCheckStateProvider( checkStateProvider );
       
        if( part.getRenderingHint( PropertyEditorDef.HINT_SHOW_HEADER, true ) == true )
        {
            this.table.setHeaderVisible( true );

            makeTableSortable
            (
                this.tableViewer,
                Collections.<TableColumn,Comparator<Object>>singletonMap( column, comparator ),
                CheckBoxListPropertyEditorPresentation.this.possibleValuesService.ordered() ? null : column
            );
        }
       
        final ListSelectionService selectionService = part.service( ListSelectionService.class );
       
        this.tableViewer.addSelectionChangedListener
        (
            new ISelectionChangedListener()
            {
                public void selectionChanged( final SelectionChangedEvent event )
                {
                    selectionService.select( getSelectedElements() );
                }
            }
        );
       
        final Listener selectionServiceListener = new FilteredListener<ListSelectionChangedEvent>()
        {
            @Override
            protected void handleTypedEvent( final ListSelectionChangedEvent event )
            {
                setSelectedElements( event.after() );
            }
        };

        selectionService.attach( selectionServiceListener );

        addOnDisposeOperation
        (
            new Runnable()
            {
                public void run()
                {
                    selectionService.detach( selectionServiceListener );
                   
                    if( CheckBoxListPropertyEditorPresentation.this.possibleValuesService != null )
                    {
                      CheckBoxListPropertyEditorPresentation.this.possibleValuesService.detach( CheckBoxListPropertyEditorPresentation.this.possibleValuesServiceListener );
                    }
                }
            }
        );
       
        this.tableViewer.addCheckStateListener
        (
            new ICheckStateListener()
            {
                public void checkStateChanged( final CheckStateChangedEvent event )
                {
                    final Entry entry = (Entry) event.getElement();
                   
                    entry.flip();
                    selectionService.select( getSelectedElements() );
                }
            }
        );
       
        this.table.addMouseListener
        (
            new MouseAdapter()
            {
                public void mouseDoubleClick( final MouseEvent event )
                {
                    Entry entry = null;
                   
                    for( TableItem item : CheckBoxListPropertyEditorPresentation.this.table.getItems() )
                    {
                        if( item.getBounds().contains( event.x, event.y ) )
                        {
                            entry = (Entry) item.getData();
                            break;
                        }
                    }
                   
                    if( entry != null )
                    {
                        entry.flip();
                        selectionService.select( getSelectedElements() );
                    }
                }
            }
        );
       
        // Finish Up
       
        this.tableViewer.setInput( new Object() );
        addControl( this.table );
    }

    @Override
    protected boolean canScaleVertically()
    {
        return true;
    }
   
    @Override
    protected void handleFocusReceivedEvent()
    {
        this.table.setFocus();
    }
   
    @Override
    protected void handlePropertyChangedEvent()
    {
        super.handlePropertyChangedEvent();
        refreshTable();
    }
   
    @Override
    protected void handleChildPropertyEvent( final PropertyContentEvent event )
    {
        super.handleChildPropertyEvent( event );
        refreshTable();
    }
   
    public final Element getSelectedElement()
    {
        final IStructuredSelection sel = (IStructuredSelection) CheckBoxListPropertyEditorPresentation.this.tableViewer.getSelection();
       
        if( sel == null )
        {
            return null;
        }
        else
        {
            return ( (Entry) sel.getFirstElement() ).element;
        }
    }
   
    public final List<Element> getSelectedElements()
    {
        final IStructuredSelection sel = (IStructuredSelection) CheckBoxListPropertyEditorPresentation.this.tableViewer.getSelection();
        final ListFactory<Element> elements = ListFactory.start();
       
        if( sel != null )
        {
            for( Iterator<?> itr = sel.iterator(); itr.hasNext(); )
            {
                final Element element = ( (Entry) itr.next() ).element;
               
                if( element != null )
                {
                    elements.add( element );
                }
            }
        }
       
        return elements.result();
    }
   
    public final void setSelectedElements( final List<Element> elements )
    {
        if( ! equalsBasedOnEntryIdentity( getSelectedElements(), elements ) )
        {
            final ListFactory<Entry> entries = ListFactory.start();
           
            for( Element element : elements )
            {
                for( TableItem item : this.table.getItems() )
                {
                    final Entry entry = (Entry) item.getData();
                   
                    if( entry.element == element )
                    {
                        entries.add( entry );
                    }
                }
            }
           
            this.tableViewer.setSelection( new StructuredSelection( entries.result() ) );
        }
    }
   
    private void refreshTable()
    {
        final int oldItemCount = this.table.getItemCount();
        final Entry oldSelection = (Entry) ( (IStructuredSelection) this.tableViewer.getSelection() ).getFirstElement();
       
        this.tableViewer.refresh();
       
        final int newItemCount = this.table.getItemCount();
       
        if( oldSelection != null )
        {
            final String oldSelectionValue = oldSelection.value;
            Entry newSelection = null;
           
            for( int i = 0; i < newItemCount && newSelection == null; i++ )
            {
                final Entry entry = (Entry) this.table.getItem( i ).getData();
               
                if( oldSelectionValue.equals( entry.value ) )
                {
                    newSelection = entry;
                }
            }
           
            if( newSelection != null )
            {
                this.tableViewer.setSelection( new StructuredSelection( newSelection ) );
            }
        }
       
        if( oldItemCount != newItemCount )
        {
            this.table.getParent().layout( true, true );
        }
    }

    private ElementType getMemberType()
    {
        return this.memberType;
    }
   
    private ValueProperty getMemberProperty()
    {
        return this.memberProperty;
    }
   
    private String readMemberProperty( final Element element )
    {
        final String text = element.property( this.memberProperty ).text();
        return ( text == null ? "" : text );
    }
   
    public final class Entry
    {
        private final LocalizationService localizationService;
        private String value;
        private Element element;
        private Value<?> property;
        private ValueLabelService valueLabelService;
        private ValueImageService valueImageService;
        private ImageService elementImageService;
        private Listener elementImageServiceListener;
        private Listener propertyValidationListener;
       
        public Entry( final String value, final Element element )
        {
            this.localizationService = part().definition().adapt( LocalizationService.class );
            this.value = value;
           
            this.valueLabelService = CheckBoxListPropertyEditorPresentation.this.memberProperty.service( ValueLabelService.class );
            this.valueImageService = CheckBoxListPropertyEditorPresentation.this.memberProperty.service( ValueImageService.class );
           
            rebase( element );
        }
       
        private void rebase( final Element element )
        {
            if( this.element != null )
            {
                if( this.elementImageService != null )
                {
                    this.elementImageService.detach( this.elementImageServiceListener );
                }
               
                if( this.elementImageService != null || this.valueImageService != null )
                {
                    this.property.detach( this.propertyValidationListener );
                }
            }
           
            this.element = element;
           
            if( this.element != null )
            {
                this.property = this.element.property( getMemberProperty() );
                this.elementImageService = this.element.service( ImageService.class );
               
                if( this.elementImageService != null )
                {
                    if( this.elementImageServiceListener == null )
                    {
                        this.elementImageServiceListener = new Listener()
                        {
                            @Override
                            public void handle( final Event event )
                            {
                                CheckBoxListPropertyEditorPresentation.this.tableViewer.update( Entry.this, null );
                            }
                        };
                    }
                   
                    this.elementImageService.attach( this.elementImageServiceListener );
                }
               
                if( this.elementImageService != null || this.valueImageService != null )
                {
                    if( this.propertyValidationListener == null )
                    {
                        this.propertyValidationListener = new FilteredListener<PropertyValidationEvent>()
                        {
                            @Override
                            protected void handleTypedEvent( final PropertyValidationEvent event )
                            {
                                CheckBoxListPropertyEditorPresentation.this.tableViewer.update( Entry.this, null );
                            }
                        };
                    }
                   
                    this.property.attach( this.propertyValidationListener );
                }
            }
            else
            {
                this.property = null;
            }
        }
       
        public String label()
        {
            String label = null;
       
            if( this.value.length() == 0 )
            {
                label = emptyIndicator.text();
            }
            else
            {
                try
                {
                    label = this.valueLabelService.provide( this.value );
                }
                catch( Exception e )
                {
                    Sapphire.service( LoggingService.class ).log( e );
                }
               
                if( label == null )
                {
                    label = this.value;
                }
                else if( ! label.equals( this.value ) )
                {
                    label = this.localizationService.transform( label, CapitalizationType.FIRST_WORD_ONLY, false );
                }
            }
           
            return label;
        }

        public Image image()
        {
            ImageData image = null;
           
            if( this.elementImageService != null )
            {
                image = this.elementImageService.image();
            }
            else
            {
                try
                {
                    image = this.valueImageService.provide( this.value );
                }
                catch( Exception e )
                {
                    Sapphire.service( LoggingService.class ).log( e );
                }
               
                if( image == null )
                {
                    image = getMemberType().image();
                }
            }
           
            if( this.property == null )
            {
                return resources().image( image );
            }
            else
            {
                return resources().image( image, this.property.validation().severity() );
            }
        }
       
        public Color foreground()
        {
            Color color = null;
           
            if( this.value.length() == 0 )
            {
                color = Display.getCurrent().getSystemColor( SWT.COLOR_DARK_GRAY );
            }
           
            return color;
        }
       
        public boolean selected()
        {
            return ( this.element != null );
        }
       
        public void flip()
        {
            final ElementList<?> list = property();
           
            if( this.element == null )
            {
                final Disposable suspension = list.suspend();
               
                try
                {
                    rebase( list.insert() );
                    this.property.write( this.value, true );
                }
                finally
                {
                    suspension.dispose();
                }
            }
            else
            {
                // Must null the element field before trying to remove the element as remove will
                // trigger property change event and it is possible for the resulting refresh to
                // set the element field to a new value before returning.
               
                final Element el = this.element;
                rebase( null );
                list.remove( el );
            }
        }
       
        public void dispose()
        {
            rebase( null );
        }
    }
   
    public static final class Factory extends PropertyEditorPresentationFactory
    {
        @Override
        public PropertyEditorPresentation create( final PropertyEditorPart part, final SwtPresentation parent, final Composite composite )
        {
            return new CheckBoxListPropertyEditorPresentation( part, parent, composite );
        }
    }
   
    public static final class EnumFactory extends PropertyEditorPresentationFactory
    {
        @Override
        public PropertyEditorPresentation create( final PropertyEditorPart part, final SwtPresentation parent, final Composite composite )
        {
            final Property property = part.property();
           
            if( property.definition() instanceof ListProperty &&
                property.service( PossibleTypesService.class ).types().size() == 1 )
            {
                final SortedSet<PropertyDef> properties = property.definition().getType().properties();
               
                if( properties.size() == 1 )
                {
                    final PropertyDef memberProperty = properties.first();
                   
                    if( memberProperty instanceof ValueProperty &&
                        memberProperty.hasAnnotation( Unique.class ) &&
                        Enum.class.isAssignableFrom( memberProperty.getTypeClass() ) )
                    {
                        return new CheckBoxListPropertyEditorPresentation( part, parent, composite );
                    }
                }
            }
   
            return null;
        }
    }
   
}
TOP

Related Classes of org.eclipse.sapphire.ui.forms.swt.CheckBoxListPropertyEditorPresentation$Entry

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.