/*! ****************************************************************************** * * Pentaho Data Integration * * Copyright (C) 2002-2016 by Pentaho : http://www.pentaho.com * ******************************************************************************* * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ package org.pentaho.di.ui.core.widget; import org.eclipse.jface.window.DefaultToolTip; import org.eclipse.jface.window.ToolTip; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CCombo; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.FocusAdapter; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.List; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.pentaho.di.core.Const; import org.pentaho.di.core.variables.VariableSpace; import org.pentaho.di.i18n.BaseMessages; import org.pentaho.di.ui.core.PropsUI; import org.pentaho.di.ui.core.gui.GUIResource; import java.util.Arrays; import java.util.Comparator; public class ControlSpaceKeyAdapter extends KeyAdapter { private static Class<?> PKG = ControlSpaceKeyAdapter.class; // for i18n purposes, needed by Translator2!! private static final PropsUI props = PropsUI.getInstance(); private GetCaretPositionInterface getCaretPositionInterface; private InsertTextInterface insertTextInterface; private VariableSpace variables; private Control control; /** * @param space * @param control * a Text or CCombo box object */ public ControlSpaceKeyAdapter( final VariableSpace space, final Control control ) { this( space, control, null, null ); } /** * * @param space * @param control * a Text or CCombo box object * @param getCaretPositionInterface * @param insertTextInterface */ public ControlSpaceKeyAdapter( VariableSpace space, final Control control, final GetCaretPositionInterface getCaretPositionInterface, final InsertTextInterface insertTextInterface ) { this.variables = space; this.control = control; this.getCaretPositionInterface = getCaretPositionInterface; this.insertTextInterface = insertTextInterface; } /** * PDI-1284 in chinese window, Ctrl-SPACE is reversed by system for input chinese character. use Ctrl-ALT-SPACE * instead. * * @param e * @return */ private boolean isHotKey( KeyEvent e ) { if ( System.getProperty( "user.language" ).equals( "zh" ) ) { return e.character == ' ' && ( ( e.stateMask & SWT.CONTROL ) != 0 ) && ( ( e.stateMask & SWT.ALT ) != 0 ); } else if ( System.getProperty( "os.name" ).startsWith( "Mac OS X" ) ) { return e.character == ' ' && ( ( e.stateMask & SWT.MOD1 ) != 0 ) && ( ( e.stateMask & SWT.ALT ) == 0 ); } else { return e.character == ' ' && ( ( e.stateMask & SWT.CONTROL ) != 0 ) && ( ( e.stateMask & SWT.ALT ) == 0 ); } } public void keyPressed( KeyEvent e ) { // CTRL-<SPACE> --> Insert a variable if ( isHotKey( e ) ) { e.doit = false; // textField.setData(TRUE) indicates we have transitioned from the textbox to list mode... // This will be set to false when the list selection has been processed // and the list is being disposed of. control.setData( Boolean.TRUE ); final int position; if ( getCaretPositionInterface != null ) { position = getCaretPositionInterface.getCaretPosition(); } else { position = -1; } // Drop down a list of variables... // Rectangle bounds = control.getBounds(); Point location = GUIResource.calculateControlPosition( control ); final Shell shell = new Shell( control.getShell(), SWT.NONE ); shell.setSize( bounds.width > 300 ? bounds.width : 300, 200 ); shell.setLocation( location.x, location.y + bounds.height ); shell.setLayout( new FillLayout() ); final List list = new List( shell, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL ); props.setLook( list ); list.setItems( getVariableNames( variables ) ); final DefaultToolTip toolTip = new DefaultToolTip( list, ToolTip.RECREATE, true ); toolTip.setImage( GUIResource.getInstance().getImageVariable() ); toolTip.setHideOnMouseDown( true ); toolTip.setRespectMonitorBounds( true ); toolTip.setRespectDisplayBounds( true ); toolTip.setPopupDelay( 350 ); list.addSelectionListener( new SelectionAdapter() { // Enter or double-click: picks the variable // public synchronized void widgetDefaultSelected( SelectionEvent e ) { applyChanges( shell, list, control, position, insertTextInterface ); } // Select a variable name: display the value in a tool tip // public void widgetSelected( SelectionEvent event ) { if ( list.getSelectionCount() <= 0 ) { return; } String name = list.getSelection()[0]; String value = variables.getVariable( name ); Rectangle shellBounds = shell.getBounds(); String message = BaseMessages.getString( PKG, "TextVar.VariableValue.Message", name, value ); if ( name.startsWith( Const.INTERNAL_VARIABLE_PREFIX ) ) { message += BaseMessages.getString( PKG, "TextVar.InternalVariable.Message" ); } toolTip.setText( message ); toolTip.hide(); toolTip.show( new Point( shellBounds.width, 0 ) ); } } ); list.addKeyListener( new KeyAdapter() { public synchronized void keyPressed( KeyEvent e ) { if ( e.keyCode == SWT.CR && ( ( e.keyCode & SWT.CONTROL ) == 0 ) && ( ( e.keyCode & SWT.SHIFT ) == 0 ) ) { applyChanges( shell, list, control, position, insertTextInterface ); } } } ); list.addFocusListener( new FocusAdapter() { public void focusLost( FocusEvent event ) { shell.dispose(); if ( !control.isDisposed() ) { control.setData( Boolean.FALSE ); } } } ); shell.open(); } } private static final void applyChanges( Shell shell, List list, Control control, int position, InsertTextInterface insertTextInterface ) { String selection = list.getSelection()[0].contains( Const.getDeprecatedPrefix() ) ? list.getSelection()[0].replace( Const.getDeprecatedPrefix(), "" ) : list.getSelection()[0]; String extra = "${" + selection + "}"; if ( insertTextInterface != null ) { insertTextInterface.insertText( extra, position ); } else { if ( control.isDisposed() ) { return; } if ( list.getSelectionCount() <= 0 ) { return; } if ( control instanceof Text ) { ( (Text) control ).insert( extra ); } else if ( control instanceof CCombo ) { CCombo combo = (CCombo) control; combo.setText( extra ); // We can't know the location of the cursor yet. All we can do is overwrite. } else if ( control instanceof StyledTextComp ) { ( (StyledTextComp) control ).insert( extra ); } else if ( control instanceof StyledText ) { ( (StyledText) control ).insert( extra ); } } if ( !shell.isDisposed() ) { shell.dispose(); } if ( !control.isDisposed() ) { control.setData( Boolean.FALSE ); } } public static final String[] getVariableNames( VariableSpace space ) { String[] variableNames = space.listVariables(); for ( int i = 0; i < variableNames.length; i++ ) { for ( int j = 0; j < Const.DEPRECATED_VARIABLES.length; j++ ) { if ( variableNames[i].equals( Const.DEPRECATED_VARIABLES[j] ) ) { variableNames[i] = variableNames[i] + Const.getDeprecatedPrefix(); break; } } } Arrays.sort( variableNames, new Comparator<String>() { public int compare( String var1, String var2 ) { if ( var1.endsWith( Const.getDeprecatedPrefix() ) && var2.endsWith( Const.getDeprecatedPrefix() ) ) { return 0; } if ( var1.endsWith( Const.getDeprecatedPrefix() ) && !var2.endsWith( Const.getDeprecatedPrefix() ) ) { return 1; } if ( !var1.endsWith( Const.getDeprecatedPrefix() ) && var2.endsWith( Const.getDeprecatedPrefix() ) ) { return -1; } return var1.compareTo( var2 ); } } ); return variableNames; } public void setVariables( VariableSpace vars ) { variables = vars; } }