/****************************************************************************** * Copyright (c) 2016 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 review and ongoing maintenance ******************************************************************************/ package org.eclipse.sapphire.samples.textSelectionService; import static java.lang.Math.max; import static java.lang.Math.min; import java.util.Set; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.sapphire.DisposeEvent; import org.eclipse.sapphire.Element; import org.eclipse.sapphire.ElementList; import org.eclipse.sapphire.ElementType; import org.eclipse.sapphire.Event; import org.eclipse.sapphire.ExecutableElement; import org.eclipse.sapphire.FilteredListener; import org.eclipse.sapphire.ListProperty; import org.eclipse.sapphire.Listener; import org.eclipse.sapphire.PossibleValuesService; import org.eclipse.sapphire.Property; import org.eclipse.sapphire.PropertyContentEvent; import org.eclipse.sapphire.Transient; import org.eclipse.sapphire.TransientProperty; import org.eclipse.sapphire.Type; import org.eclipse.sapphire.Unique; import org.eclipse.sapphire.Value; import org.eclipse.sapphire.ValueProperty; import org.eclipse.sapphire.modeling.ProgressMonitor; import org.eclipse.sapphire.modeling.Status; import org.eclipse.sapphire.modeling.annotations.DelegateImplementation; import org.eclipse.sapphire.modeling.annotations.Required; import org.eclipse.sapphire.modeling.annotations.Service; import org.eclipse.sapphire.ui.Presentation; import org.eclipse.sapphire.ui.SapphireAction; import org.eclipse.sapphire.ui.SapphireActionHandler; import org.eclipse.sapphire.ui.SapphirePart; import org.eclipse.sapphire.ui.TextSelectionService; import org.eclipse.sapphire.ui.TextSelectionService.Range; import org.eclipse.sapphire.ui.def.ActionHandlerDef; import org.eclipse.sapphire.ui.def.DefinitionLoader; import org.eclipse.sapphire.ui.forms.PropertyEditorPart; import org.eclipse.sapphire.ui.forms.swt.SapphireDialog; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.handlers.HandlerUtil; /** * @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a> */ public final class TextSelectionServiceSample { public interface RootElement extends Element { ElementType TYPE = new ElementType( TextSelectionServiceSample.RootElement.class ); // *** Text *** ValueProperty PROP_TEXT = new ValueProperty( TYPE, "Text" ); Value<String> getText(); void setText( String value ); // *** TextSelection *** ValueProperty PROP_TEXT_SELECTION = new ValueProperty( TYPE, "TextSelection" ); Value<String> getTextSelection(); void setTextSelection( String value ); } public static final class PropertyEditorListener extends Listener { private TextSelectionService textSelectionService; private Listener textSelectionServiceListener; @Override public void handle( final Event event ) { if( event instanceof SapphirePart.PartInitializationEvent ) { final PropertyEditorPart part = (PropertyEditorPart) ( (SapphirePart.PartInitializationEvent) event ).part(); final Property property = part.property(); final Value<?> textSelectionProperty = (Value<?>) property.element().property( property.name() + "Selection" ); this.textSelectionServiceListener = new Listener() { @Override public void handle( final Event event ) { textSelectionProperty.write( PropertyEditorListener.this.textSelectionService.selection().toString() ); } }; this.textSelectionService = part.service( TextSelectionService.class ); this.textSelectionService.attach( this.textSelectionServiceListener ); this.textSelectionServiceListener.handle( null ); } else if( event instanceof DisposeEvent ) { this.textSelectionService.detach( this.textSelectionServiceListener ); } } } public static final class OpenSampleHandler extends AbstractHandler { @Override public Object execute( final ExecutionEvent event ) { final IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindow( event ); final RootElement element = RootElement.TYPE.instantiate(); try { final SapphireDialog dialog = new SapphireDialog ( window.getShell(), element, DefinitionLoader.sdef( TextSelectionServiceSample.class ).dialog() ); dialog.open(); } finally { element.dispose(); } return null; } } public interface InsertEnvironmentVariablesOp extends ExecutableElement { ElementType TYPE = new ElementType( TextSelectionServiceSample.InsertEnvironmentVariablesOp.class ); // *** Context *** @Type( base = PropertyEditorPart.class ) TransientProperty PROP_CONTEXT = new TransientProperty( TYPE, "Context" ); Transient<PropertyEditorPart> getContext(); void setContext( PropertyEditorPart value ); // *** Variables *** interface Variable extends Element { ElementType TYPE = new ElementType( TextSelectionServiceSample.InsertEnvironmentVariablesOp.Variable.class ); // *** Name *** @Required @Unique ValueProperty PROP_NAME = new ValueProperty( TYPE, "Name" ); Value<String> getName(); void setName( String value ); } class VariablesPossibleValuesService extends PossibleValuesService { @Override protected void compute( final Set<String> values ) { for( final String variable : System.getenv().keySet() ) { values.add( variable ); } } } @Type( base = Variable.class ) @Service( impl = VariablesPossibleValuesService.class ) ListProperty PROP_VARIABLES = new ListProperty( TYPE, "Variables" ); ElementList<Variable> getVariables(); // *** execute *** class ExecuteMethodDelegate { public static Status execute( final InsertEnvironmentVariablesOp op, final ProgressMonitor monitor ) { final PropertyEditorPart context = op.getContext().content(); final Value<?> property = (Value<?>) context.property(); final TextSelectionService textSelectionService = context.service( TextSelectionService.class ); final TextSelectionService.Range initialSelection = textSelectionService.selection(); final String initialText = property.text(); final StringBuilder modifiedText = new StringBuilder(); if( initialText != null ) { modifiedText.append( initialText.substring( 0, initialSelection.start() ) ); } final StringBuilder variables = new StringBuilder(); for( final InsertEnvironmentVariablesOp.Variable variable : op.getVariables() ) { variables.append( "${" ); variables.append( variable.getName().text() ); variables.append( '}' ); } modifiedText.append( variables ); if( initialText != null ) { modifiedText.append( initialText.substring( initialSelection.end() ) ); } property.write( modifiedText.toString() ); textSelectionService.select( initialSelection.start() + variables.length() ); return Status.createOkStatus(); } } @DelegateImplementation( ExecuteMethodDelegate.class ) Status execute( ProgressMonitor monitor ); } public static final class InsertEnvironmentVariablesActionHandler extends SapphireActionHandler { @Override protected final Object run( final Presentation context ) { final InsertEnvironmentVariablesOp operation = InsertEnvironmentVariablesOp.TYPE.instantiate(); try { operation.setContext( (PropertyEditorPart) context.part() ); final SapphireDialog dialog = new SapphireDialog ( PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), operation, DefinitionLoader.sdef( TextSelectionServiceSample.class ).dialog( "InsertEnvironmentVariablesDialog" ) ); dialog.open(); } finally { operation.dispose(); } return null; } } private static abstract class MoveActionHandler extends SapphireActionHandler { private PropertyEditorPart part; private Value<?> property; private TextSelectionService textSelectionService; @Override public final void init( final SapphireAction action, final ActionHandlerDef def ) { super.init( action, def ); this.part = (PropertyEditorPart) action.getPart(); this.property = (Value<?>) this.part.property(); this.textSelectionService = this.part.service( TextSelectionService.class ); refreshEnablement(); final Listener textSelectionServiceListener = new Listener() { @Override public void handle( final Event event ) { refreshEnablement(); } }; this.textSelectionService.attach( textSelectionServiceListener ); final Listener propertyContentListener = new FilteredListener<PropertyContentEvent>() { @Override protected void handleTypedEvent( final PropertyContentEvent event ) { refreshEnablement(); } }; this.property.attach( propertyContentListener ); attach ( new FilteredListener<DisposeEvent>() { @Override protected void handleTypedEvent( final DisposeEvent event ) { MoveActionHandler.this.textSelectionService.detach( textSelectionServiceListener ); MoveActionHandler.this.part.detach( propertyContentListener ); } } ); } private void refreshEnablement() { refreshEnablement( text(), this.textSelectionService.selection() ); } protected abstract void refreshEnablement( String text, TextSelectionService.Range selection ); @Override protected final Object run( final Presentation context ) { run( text(), this.textSelectionService ); return null; } protected abstract void run( String text, TextSelectionService textSelectionService ); private String text() { final String text = this.property.text(); return ( text != null ? text : "" ); } } public static final class MoveLeftActionHandler extends MoveActionHandler { @Override protected void refreshEnablement( final String text, final Range selection ) { setEnabled( selection.end() > 0 ); } @Override protected void run( final String text, final TextSelectionService textSelectionService ) { final TextSelectionService.Range selection = textSelectionService.selection(); if( selection.end() > 0 ) { textSelectionService.select( max( 0, selection.start() - 1 ), selection.end() - 1 ); } } } public static final class MoveRightActionHandler extends MoveActionHandler { @Override protected void refreshEnablement( final String text, final Range selection ) { setEnabled( selection.start() < text.length() ); } @Override protected void run( final String text, final TextSelectionService textSelectionService ) { final TextSelectionService.Range selection = textSelectionService.selection(); if( selection.start() < text.length() ) { textSelectionService.select( selection.start() + 1, min( selection.end() + 1, text.length() ) ); } } } }