/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2013 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 java.util.LinkedList;
import java.util.List;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ExtendedModifyEvent;
import org.eclipse.swt.custom.ExtendedModifyListener;
import org.eclipse.swt.custom.LineStyleListener;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MenuDetectEvent;
import org.eclipse.swt.events.MenuDetectListener;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
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;
public class StyledTextComp extends Composite {
private static Class<?> PKG = StyledTextComp.class; // for i18n purposes, needed by Translator2!!
// Modification for Undo/Redo on Styled Text
private static final int MAX_STACK_SIZE = 25;
private List<UndoRedoStack> undoStack;
private List<UndoRedoStack> redoStack;
private boolean bFullSelection = false;
private StyledText styledText;
private Menu styledTextPopupmenu;
private String strTabName;
private Composite xParent;
private KeyListener kls;
private VariableSpace variables;
private boolean varsSensitive;
public StyledTextComp( VariableSpace space, Composite parent, int args, String strTabName ) {
this( space, parent, args, strTabName, true );
}
public StyledTextComp( VariableSpace space, Composite parent, int args, String strTabName, boolean varsSensitive ) {
super( parent, SWT.NONE );
this.varsSensitive = varsSensitive;
this.variables = space;
undoStack = new LinkedList<UndoRedoStack>();
redoStack = new LinkedList<UndoRedoStack>();
styledText = new StyledText( this, args );
styledTextPopupmenu = new Menu( parent.getShell(), SWT.POP_UP );
xParent = parent;
this.strTabName = strTabName;
// clipboard = new Clipboard(parent.getDisplay());
this.setLayout( new FillLayout() );
buildingStyledTextMenu();
addUndoRedoSupport();
kls = new KeyAdapter() {
public void keyPressed( KeyEvent e ) {
if ( e.keyCode == 'h' && ( e.stateMask & SWT.MOD1 & SWT.SHIFT ) != 0 ) {
new StyledTextCompReplace( styledTextPopupmenu.getShell(), styledText ).open();
} else if ( e.keyCode == 'z' && ( e.stateMask & SWT.MOD1 ) != 0 ) {
undo();
} else if ( e.keyCode == 'y' && ( e.stateMask & SWT.MOD1 ) != 0 ) {
redo();
} else if ( e.keyCode == 'a' && ( e.stateMask & SWT.MOD1 ) != 0 ) {
bFullSelection = true;
styledText.selectAll();
} else if ( e.keyCode == 'f' && ( e.stateMask & SWT.MOD1 ) != 0 ) {
new StyledTextCompFind( styledTextPopupmenu.getShell(), styledText, BaseMessages.getString(
PKG, "WidgetDialog.Styled.Find" ) ).open();
}
}
};
styledText.addKeyListener( kls );
if ( this.varsSensitive ) {
ControlDecoration controlDecoration = new ControlDecoration( styledText, SWT.TOP | SWT.RIGHT );
Image image = GUIResource.getInstance().getImageVariable();
controlDecoration.setImage( image );
controlDecoration
.setDescriptionText( BaseMessages.getString( PKG, "StyledTextComp.tooltip.InsertVariable" ) );
PropsUI.getInstance().setLook( controlDecoration.getControl() );
styledText.addKeyListener( new ControlSpaceKeyAdapter( this.variables, styledText ) );
}
// Create the drop target on the StyledText
DropTarget dt = new DropTarget( styledText, DND.DROP_MOVE );
dt.setTransfer( new Transfer[] { TextTransfer.getInstance() } );
dt.addDropListener( new DropTargetAdapter() {
public void dragOver( DropTargetEvent e ) {
styledText.setFocus();
Point location = xParent.getDisplay().map( null, styledText, e.x, e.y );
location.x = Math.max( 0, location.x );
location.y = Math.max( 0, location.y );
try {
int offset = styledText.getOffsetAtLocation( new Point( location.x, location.y ) );
styledText.setCaretOffset( offset );
} catch ( IllegalArgumentException ex ) {
int maxOffset = styledText.getCharCount();
Point maxLocation = styledText.getLocationAtOffset( maxOffset );
if ( location.y >= maxLocation.y ) {
if ( location.x >= maxLocation.x ) {
styledText.setCaretOffset( maxOffset );
} else {
int offset = styledText.getOffsetAtLocation( new Point( location.x, maxLocation.y ) );
styledText.setCaretOffset( offset );
}
} else {
styledText.setCaretOffset( maxOffset );
}
}
}
public void drop( DropTargetEvent event ) {
// Set the buttons text to be the text being dropped
styledText.insert( (String) event.data );
}
} );
}
public String getSelectionText() {
return styledText.getSelectionText();
}
public String getText() {
return styledText.getText();
}
public void setText( String text ) {
styledText.setText( text );
}
public int getCaretOffset() {
return styledText.getCaretOffset();
}
public int getLineAtOffset( int iOffset ) {
return styledText.getLineAtOffset( iOffset );
}
public void insert( String strInsert ) {
styledText.insert( strInsert );
}
public void addModifyListener( ModifyListener lsMod ) {
styledText.addModifyListener( lsMod );
}
public void addLineStyleListener( LineStyleListener lineStyler ) {
styledText.addLineStyleListener( lineStyler );
}
public void addKeyListener( KeyAdapter keyAdapter ) {
styledText.addKeyListener( keyAdapter );
}
public void addFocusListener( FocusAdapter focusAdapter ) {
styledText.addFocusListener( focusAdapter );
}
public void addMouseListener( MouseAdapter mouseAdapter ) {
styledText.addMouseListener( mouseAdapter );
}
public int getSelectionCount() {
return styledText.getSelectionCount();
}
public void setSelection( int arg0 ) {
styledText.setSelection( arg0 );
}
public void setSelection( int arg0, int arg1 ) {
styledText.setSelection( arg0, arg1 );
}
public void setFont( Font fnt ) {
styledText.setFont( fnt );
}
private void buildingStyledTextMenu() {
// styledTextPopupmenu = new Menu(, SWT.POP_UP);
MenuItem undoItem = new MenuItem( styledTextPopupmenu, SWT.PUSH );
undoItem.setText( OsHelper.customizeMenuitemText( BaseMessages.getString( PKG, "WidgetDialog.Styled.Undo" ) ) );
undoItem.addListener( SWT.Selection, new Listener() {
public void handleEvent( Event e ) {
undo();
}
} );
MenuItem redoItem = new MenuItem( styledTextPopupmenu, SWT.PUSH );
redoItem.setText( OsHelper.customizeMenuitemText( BaseMessages.getString( PKG, "WidgetDialog.Styled.Redo" ) ) );
redoItem.addListener( SWT.Selection, new Listener() {
public void handleEvent( Event e ) {
redo();
}
} );
new MenuItem( styledTextPopupmenu, SWT.SEPARATOR );
MenuItem cutItem = new MenuItem( styledTextPopupmenu, SWT.PUSH );
cutItem.setText( OsHelper.customizeMenuitemText( BaseMessages.getString( PKG, "WidgetDialog.Styled.Cut" ) ) );
cutItem.addListener( SWT.Selection, new Listener() {
public void handleEvent( Event e ) {
styledText.cut();
}
} );
MenuItem copyItem = new MenuItem( styledTextPopupmenu, SWT.PUSH );
copyItem.setText( OsHelper.customizeMenuitemText( BaseMessages.getString( PKG, "WidgetDialog.Styled.Copy" ) ) );
copyItem.addListener( SWT.Selection, new Listener() {
public void handleEvent( Event e ) {
styledText.copy();
}
} );
MenuItem pasteItem = new MenuItem( styledTextPopupmenu, SWT.PUSH );
pasteItem
.setText( OsHelper.customizeMenuitemText( BaseMessages.getString( PKG, "WidgetDialog.Styled.Paste" ) ) );
pasteItem.addListener( SWT.Selection, new Listener() {
public void handleEvent( Event e ) {
styledText.paste();
}
} );
MenuItem selectAllItem = new MenuItem( styledTextPopupmenu, SWT.PUSH );
selectAllItem.setText( OsHelper.customizeMenuitemText( BaseMessages.getString(
PKG, "WidgetDialog.Styled.SelectAll" ) ) );
selectAllItem.addListener( SWT.Selection, new Listener() {
public void handleEvent( Event e ) {
styledText.selectAll();
}
} );
new MenuItem( styledTextPopupmenu, SWT.SEPARATOR );
MenuItem findItem = new MenuItem( styledTextPopupmenu, SWT.PUSH );
findItem.setText( OsHelper.customizeMenuitemText( BaseMessages.getString( PKG, "WidgetDialog.Styled.Find" ) ) );
findItem.addListener( SWT.Selection, new Listener() {
public void handleEvent( Event e ) {
StyledTextCompFind stFind =
new StyledTextCompFind( styledText.getShell(), styledText, BaseMessages.getString(
PKG, "WidgetDialog.Styled.FindString", strTabName ) );
stFind.open();
}
} );
MenuItem replaceItem = new MenuItem( styledTextPopupmenu, SWT.PUSH );
replaceItem.setText( OsHelper.customizeMenuitemText( BaseMessages.getString(
PKG, "WidgetDialog.Styled.Replace" ) ) );
replaceItem.setAccelerator( SWT.MOD1 | 'H' );
// (helpMenu, SWT.PUSH, "&About\tCtrl+A",
// null, SWT.CTRL + 'A', true, "doAbout");
replaceItem.addListener( SWT.Selection, new Listener() {
public void handleEvent( Event e ) {
StyledTextCompReplace stReplace = new StyledTextCompReplace( styledText.getShell(), styledText );
stReplace.open();
}
} );
styledText.addMenuDetectListener( new MenuDetectListener() {
public void menuDetected( MenuDetectEvent e ) {
// Enable menus, if the Selection is ok
if ( undoStack.size() > 0 ) {
styledTextPopupmenu.getItem( 0 ).setEnabled( true );
} else {
styledTextPopupmenu.getItem( 0 ).setEnabled( false );
}
if ( redoStack.size() > 0 ) {
styledTextPopupmenu.getItem( 1 ).setEnabled( true );
} else {
styledTextPopupmenu.getItem( 1 ).setEnabled( false );
}
styledTextPopupmenu.getItem( 5 ).setEnabled( checkPaste() );
if ( styledText.getSelectionCount() > 0 ) {
styledTextPopupmenu.getItem( 3 ).setEnabled( true );
styledTextPopupmenu.getItem( 4 ).setEnabled( true );
} else {
styledTextPopupmenu.getItem( 3 ).setEnabled( false );
styledTextPopupmenu.getItem( 4 ).setEnabled( false );
}
}
} );
styledText.setMenu( styledTextPopupmenu );
}
// Check if something is stored inside the Clipboard
private boolean checkPaste() {
try {
Clipboard clipboard = new Clipboard( xParent.getDisplay() );
TextTransfer transfer = TextTransfer.getInstance();
String text = (String) clipboard.getContents( transfer );
if ( text != null && text.length() > 0 ) {
return true;
} else {
return false;
}
} catch ( Exception e ) {
return false;
}
}
// Start Functions for Undo / Redo on wSrcipt
private void addUndoRedoSupport() {
styledText.addSelectionListener( new SelectionListener() {
public void widgetSelected( SelectionEvent event ) {
if ( styledText.getSelectionCount() == styledText.getCharCount() ) {
bFullSelection = true;
try {
event.wait( 2 );
} catch ( Exception e ) {
// Ignore errors
}
}
}
public void widgetDefaultSelected( SelectionEvent event ) {
}
} );
styledText.addExtendedModifyListener( new ExtendedModifyListener() {
public void modifyText( ExtendedModifyEvent event ) {
int iEventLength = event.length;
int iEventStartPostition = event.start;
// Unterscheidung um welche Art es sich handelt Delete or Insert
String newText = styledText.getText();
String repText = event.replacedText;
String oldText = "";
int iEventType = -1;
// if((event.length!=newText.length()) || newText.length()==1){
if ( ( event.length != newText.length() ) || ( bFullSelection ) ) {
if ( repText != null && repText.length() > 0 ) {
oldText =
newText.substring( 0, event.start ) + repText + newText.substring( event.start + event.length );
iEventType = UndoRedoStack.DELETE;
iEventLength = repText.length();
} else {
oldText = newText.substring( 0, event.start ) + newText.substring( event.start + event.length );
iEventType = UndoRedoStack.INSERT;
}
if ( ( oldText != null && oldText.length() > 0 ) || ( iEventStartPostition == event.length ) ) {
UndoRedoStack urs =
new UndoRedoStack( iEventStartPostition, newText, oldText, iEventLength, iEventType );
if ( undoStack.size() == MAX_STACK_SIZE ) {
undoStack.remove( undoStack.size() - 1 );
}
undoStack.add( 0, urs );
}
}
bFullSelection = false;
}
} );
}
private void undo() {
if ( undoStack.size() > 0 ) {
UndoRedoStack urs = undoStack.remove( 0 );
if ( redoStack.size() == MAX_STACK_SIZE ) {
redoStack.remove( redoStack.size() - 1 );
}
UndoRedoStack rro =
new UndoRedoStack( urs.getCursorPosition(), urs.getReplacedText(), styledText.getText(), urs
.getEventLength(), urs.getType() );
bFullSelection = false;
styledText.setText( urs.getReplacedText() );
if ( urs.getType() == UndoRedoStack.INSERT ) {
styledText.setCaretOffset( urs.getCursorPosition() );
} else if ( urs.getType() == UndoRedoStack.DELETE ) {
styledText.setCaretOffset( urs.getCursorPosition() + urs.getEventLength() );
styledText.setSelection( urs.getCursorPosition(), urs.getCursorPosition() + urs.getEventLength() );
if ( styledText.getSelectionCount() == styledText.getCharCount() ) {
bFullSelection = true;
}
}
redoStack.add( 0, rro );
}
}
private void redo() {
if ( redoStack.size() > 0 ) {
UndoRedoStack urs = redoStack.remove( 0 );
if ( undoStack.size() == MAX_STACK_SIZE ) {
undoStack.remove( undoStack.size() - 1 );
}
UndoRedoStack rro =
new UndoRedoStack( urs.getCursorPosition(), urs.getReplacedText(), styledText.getText(), urs
.getEventLength(), urs.getType() );
bFullSelection = false;
styledText.setText( urs.getReplacedText() );
if ( urs.getType() == UndoRedoStack.INSERT ) {
styledText.setCaretOffset( urs.getCursorPosition() );
} else if ( urs.getType() == UndoRedoStack.DELETE ) {
styledText.setCaretOffset( urs.getCursorPosition() + urs.getEventLength() );
styledText.setSelection( urs.getCursorPosition(), urs.getCursorPosition() + urs.getEventLength() );
if ( styledText.getSelectionCount() == styledText.getCharCount() ) {
bFullSelection = true;
}
}
undoStack.add( 0, rro );
}
}
public StyledText getStyledText() {
return styledText;
}
public boolean isEditable() {
return styledText.getEditable();
}
public void setEditable( boolean canEdit ) {
styledText.setEditable( canEdit );
}
}