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

Source Code of org.eclipse.sapphire.ui.forms.swt.internal.HyperlinkTable$Controller

/******************************************************************************
* 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
******************************************************************************/

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

import static org.eclipse.sapphire.ui.forms.swt.GridLayoutUtil.gd;
import static org.eclipse.sapphire.ui.forms.swt.GridLayoutUtil.gdhfill;
import static org.eclipse.sapphire.ui.forms.swt.GridLayoutUtil.gdhindent;
import static org.eclipse.sapphire.ui.forms.swt.GridLayoutUtil.gdwhint;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.resource.JFaceColors;
import org.eclipse.sapphire.LocalizableText;
import org.eclipse.sapphire.Text;
import org.eclipse.sapphire.ui.Presentation;
import org.eclipse.sapphire.ui.SapphireAction;
import org.eclipse.sapphire.ui.SapphireActionGroup;
import org.eclipse.sapphire.ui.SapphireActionHandler;
import org.eclipse.sapphire.ui.SapphireActionSystem;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.TextLayout;
import org.eclipse.swt.graphics.TextStyle;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;

/**
* Implements Ctrl+Click navigation in a standard SWT table widget.
*
* @author <a href="konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a>
*/

public final class HyperlinkTable
{
    public static abstract class Controller
    {
        public boolean isHyperlinkEnabled( final TableItem item,
                                           final int column )
        {
            return true;
        }
       
        public abstract void handleHyperlinkEvent( final TableItem item,
                                                   final int column );
    }
   
    @Text( "Select Jump Destination" )
    private static LocalizableText jumpDialogTitle;
   
    @Text( "Where do you want to jump to?" )
    private static LocalizableText jumpDialogPrompt;

    static
    {
        LocalizableText.init( HyperlinkTable.class );
    }

    private static final Point IMAGE_OFFSET_PRIMARY_COLUMN;
    private static final Point IMAGE_OFFSET_SECONDARY_COLUMN;
    private static final Point TEXT_OFFSET_PRIMARY_COLUMN;
    private static final Point TEXT_OFFSET_SECONDARY_COLUMN;
   
    static
    {
        final String os = Platform.getOS();
        final String osName = System.getProperties().getProperty( "os.name" );
       
        if( os.equals( Platform.OS_WIN32 ) )
        {
            if( osName.equals( "Windows XP" ) )
            {
                IMAGE_OFFSET_PRIMARY_COLUMN = new Point( 0, 0 );
                IMAGE_OFFSET_SECONDARY_COLUMN = new Point( 0, 0 );
                TEXT_OFFSET_PRIMARY_COLUMN = new Point( 0, 2 );
                TEXT_OFFSET_SECONDARY_COLUMN = new Point( -1, 2 );
            }
            else
            {
                IMAGE_OFFSET_PRIMARY_COLUMN = new Point( 0, 1 );
                IMAGE_OFFSET_SECONDARY_COLUMN = new Point( 0, 1 );
                TEXT_OFFSET_PRIMARY_COLUMN = new Point( 0, 2 );
                TEXT_OFFSET_SECONDARY_COLUMN = new Point( 0, 2 );
            }
        }
        else
        {
            // This number has been derived on openSUSE 11.0, but we will use it
            // for all non-windows systems for now.
           
            IMAGE_OFFSET_PRIMARY_COLUMN = new Point( 1, 3 );
            IMAGE_OFFSET_SECONDARY_COLUMN = new Point( 1, 3 );
            TEXT_OFFSET_PRIMARY_COLUMN = new Point( 1, 3 );
            TEXT_OFFSET_SECONDARY_COLUMN = new Point( 1, 3 );
        }
    }
   
    private boolean controlKeyActive;
    private final Table table;
    private TableItem mouseOverTableItem;
    private int mouseOverColumn;
    private Controller controller;
   
    public HyperlinkTable( final Table table,
                           final SapphireActionGroup actions )
    {
        this.table = table;
        this.controlKeyActive = false;
        this.mouseOverTableItem = null;
        this.mouseOverColumn = -1;
       
        final Listener keyListener = new Listener()
        {
            public void handleEvent( final Event event )
            {
                handleKeyEvent( event );
            }
        };
       
        final Display display = this.table.getDisplay();
       
        display.addFilter( SWT.KeyDown, keyListener );
        display.addFilter( SWT.KeyUp, keyListener );
       
        this.table.addListener
        (
            SWT.EraseItem,
            new Listener()
            {
                public void handleEvent( final Event event )
                {
                    handleEraseItem( event );
                }
            }
        );

        this.table.addListener
        (
            SWT.PaintItem,
            new Listener()
            {
                public void handleEvent( final Event event )
                {
                    handlePaintItem( event );
                }
            }
        );

        this.table.addListener
        (
            SWT.MouseMove,
            new Listener()
            {
                public void handleEvent( final Event event )
                {
                    handleMouseMove( event );
                }
            }
        );

        this.table.addListener
        (
            SWT.MouseExit,
            new Listener()
            {
                public void handleEvent( final Event event )
                {
                    handleMouseExit( event );
                }
            }
        );

        this.table.addListener
        (
            SWT.MouseDown,
            new Listener()
            {
                public void handleEvent( final Event event )
                {
                    handleMouseDown( event );
                }
            }
        );
       
        final SapphireActionHandler jumpActionHandler = new SapphireActionHandler()
        {
            @Override
            protected Object run( final Presentation context )
            {
                handleJumpCommand();
                return null;
            }
        };
       
        final SapphireAction jumpAction = actions.getAction( SapphireActionSystem.ACTION_JUMP );
        jumpActionHandler.init( jumpAction, null );
        jumpAction.addHandler( jumpActionHandler );
       
        this.table.addDisposeListener
        (
            new DisposeListener()
            {
                public void widgetDisposed( final DisposeEvent event )
                {
                    display.removeFilter( SWT.KeyDown, keyListener );
                    display.removeFilter( SWT.KeyUp, keyListener );
                    jumpAction.removeHandler( jumpActionHandler );
                }
            }
        );
    }
   
    public void setController( final Controller controller )
    {
        this.controller = controller;
    }
   
    public Table getTable()
    {
        return this.table;
    }
   
    private void handleKeyEvent( final Event event )
    {
        if( event.keyCode == SWT.CONTROL )
        {
            this.controlKeyActive = ( event.type == SWT.KeyDown );
           
            // Only force update when user releases the control key. We want the hyperlink
            // to show only after user starts moving the mouse after holding down the
            // control key.
           
            if( ! this.controlKeyActive )
            {
                update();
            }
        }
    }
   
    private void handleMouseMove( final Event event )
    {
        this.mouseOverTableItem = null;
        this.mouseOverColumn = -1;
       
        for( int i = this.table.getTopIndex(), n = this.table.getItemCount(); i < n; i++ )
        {
            final TableItem item = this.table.getItem( i );
           
            for( int j = 0, m = getColumnCount( this.table ); j < m; j++ )
            {
                final Rectangle bounds = item.getTextBounds( j );
               
                if( bounds.contains( event.x, event.y ) )
                {
                    final GC gc = new GC( this.table );
                    final Point textExtent = gc.textExtent( item.getText( j ) );
                    gc.dispose();
                   
                    bounds.width = textExtent.x;
                    bounds.height = textExtent.y;
                   
                    if( bounds.contains( event.x, event.y ) && this.controller.isHyperlinkEnabled( item, j ) )
                    {
                        this.mouseOverTableItem = item;
                        this.mouseOverColumn = j;
                    }

                    break;
                }
            }
        }
       
        update();
    }
   
    private void handleMouseExit( final Event event )
    {
        this.mouseOverTableItem = null;
        this.mouseOverColumn = -1;

        update();
    }
   
    private void handleMouseDown( final Event event )
    {
        if( this.controlKeyActive && this.mouseOverTableItem != null )
        {
            final TableItem item = this.mouseOverTableItem;
           
            this.table.setCursor( null );
           
            // Ideally, it would be best to prevent table selection from taking place. Haven't found
            // a way to do that yet. At the very least, the following makes sure that Ctrl+Click hyperlink
            // action doesn't also have a multi-select behavior.
           
            this.table.setSelection( item );

            handleJumpCommand( this.mouseOverTableItem, this.mouseOverColumn );
        }
    }
   
    private void handleJumpCommand()
    {
        final TableItem[] items = HyperlinkTable.this.table.getSelection();
       
        if( items.length == 1 )
        {
            final TableItem item = items[ 0 ];
            final List<Integer> columnsWithHyperlinks = new ArrayList<Integer>();
           
            for( int i = 0, n = getColumnCount( HyperlinkTable.this.table ); i < n; i++ )
            {
                if( this.controller.isHyperlinkEnabled( item, i ) )
                {
                    columnsWithHyperlinks.add( i );
                }
            }
           
            if( columnsWithHyperlinks.size() == 1 )
            {
                handleJumpCommand( item, columnsWithHyperlinks.get( 0 ) );
            }
            else if( ! columnsWithHyperlinks.isEmpty() )
            {
                final Dialog dialog = new Dialog( this.table.getShell() )
                {
                    private int choice = columnsWithHyperlinks.get( 0 );
                   
                    @Override
                    protected Control createDialogArea( final Composite parent )
                    {
                        getShell().setText( jumpDialogTitle.text() );
                       
                        final Composite composite = (Composite) super.createDialogArea( parent );
                       
                        final Label prompt = new Label( composite, SWT.WRAP );
                        prompt.setLayoutData( gdwhint( gdhfill(), 300 ) );
                        prompt.setText( jumpDialogPrompt.text() );
                       
                        final SelectionListener listener = new SelectionAdapter()
                        {
                            public void widgetSelected( final SelectionEvent event )
                            {
                                setChoice( (Integer) event.widget.getData() );
                            }
                        };
                       
                        boolean first = true;
                       
                        for( Integer col : columnsWithHyperlinks )
                        {
                            final Button button = new Button( composite, SWT.RADIO | SWT.WRAP );
                            button.setLayoutData( gdhindent( gd(), 10 ) );
                            button.setText( item.getText( col ) );
                            button.setData( col );
                           
                            if( first )
                            {
                                button.setSelection( true );
                                first = false;
                            }
                           
                            button.addSelectionListener( listener );
                        }
                       
                        return composite;
                    }

                    @Override
                    protected void okPressed()
                    {
                        super.okPressed();
                        handleJumpCommand( item, this.choice );
                    }
                   
                    private void setChoice( final int choice )
                    {
                        this.choice = choice;
                    }
                };
               
                dialog.open();
            }
        }
    }
   
    private void handleJumpCommand( final TableItem item,
                                    final int column )
    {
        final Runnable op = new Runnable()
        {
            public void run()
            {
                HyperlinkTable.this.controller.handleHyperlinkEvent( item, column );
            }
        };
       
        BusyIndicator.showWhile( this.table.getDisplay(), op );
    }
   
    private void handleEraseItem( final Event event )
    {
        final TableItem item = (TableItem) event.item;
       
        if( this.controlKeyActive && this.mouseOverTableItem == item )
        {
            event.detail &= ~SWT.FOREGROUND;
        }
    }
   
    private void handlePaintItem( final Event event )
    {
        final TableItem item = (TableItem) event.item;
       
        if( this.controlKeyActive && this.mouseOverTableItem == item )
        {
            for( int i = 0, n = getColumnCount( this.table ); i < n; i++ )
            {
                final Display display = this.table.getDisplay();
                final String text = item.getText( i );

                final Font font = item.getFont( i );
                final TextStyle style = new TextStyle( font, null, null );
               
                if( this.mouseOverColumn == i )
                {
                    final Color hyperlinkColor = JFaceColors.getActiveHyperlinkText( display );
                    style.underline = true;
                    style.foreground = hyperlinkColor;
                    style.underlineColor = hyperlinkColor;
                }
               
                final Image image = item.getImage( i );
               
                if( image != null )
                {
                    final Rectangle bounds = item.getBounds( i );
                    final Point offset = ( i == 0 ? IMAGE_OFFSET_PRIMARY_COLUMN : IMAGE_OFFSET_SECONDARY_COLUMN );
                    event.gc.drawImage( image, bounds.x + offset.x, bounds.y + offset.y );
                }
               
                final TextLayout layout = new TextLayout( display );
                layout.setText( text );
                layout.setStyle( style, 0, text.length() - 1 );
               
                final Point offset = ( i == 0 ? TEXT_OFFSET_PRIMARY_COLUMN : TEXT_OFFSET_SECONDARY_COLUMN );
               
                final Rectangle clientArea = item.getTextBounds( i );
                layout.setWidth( clientArea.width );
                layout.draw( event.gc, clientArea.x + offset.x, clientArea.y + offset.y );
            }
        }
    }
   
    private void update()
    {
        if( this.controlKeyActive && this.mouseOverTableItem != null )
        {
            this.table.setCursor( this.table.getDisplay().getSystemCursor( SWT.CURSOR_HAND ) );
        }
        else
        {
            this.table.setCursor( null );
        }
       
        this.table.redraw();
    }
   
    private static int getColumnCount( final Table table )
    {
        final int count = table.getColumnCount();
        return ( count == 0 ? 1 : count );
    }
   
}
TOP

Related Classes of org.eclipse.sapphire.ui.forms.swt.internal.HyperlinkTable$Controller

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.