/**
* Copyright (c) 2012 by JP Moresmau
* This code is made available under the terms of the Eclipse Public License,
* version 1.0 (EPL). See http://www.eclipse.org/legal/epl-v10.html
*/
package net.sf.eclipsefp.haskell.debug.ui.repl;
import java.util.Iterator;
import java.util.LinkedList;
import net.sf.eclipsefp.haskell.core.HaskellCorePlugin;
import net.sf.eclipsefp.haskell.core.preferences.ICorePreferenceNames;
import net.sf.eclipsefp.haskell.debug.ui.internal.HaskellDebugUI;
import net.sf.eclipsefp.haskell.debug.ui.internal.util.UITexts;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.preferences.IPreferencesService;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuCreator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
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.events.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.ui.console.TextConsolePage;
import org.eclipse.ui.console.TextConsoleViewer;
import org.eclipse.ui.plugin.AbstractUIPlugin;
/**
* the action writing the history command
* @author JP Moresmau
*
*/
public class HistoryAction extends Action {
private final StringBuilder current=new StringBuilder();
private final LinkedList<String> commands=new LinkedList<>();
private int maxHistory=20;
private int insertOffset=0;
private int insertIndex=-1;
/**
* where the caret is in the line
*/
private int caretPos = 0;
public HistoryAction(final TextConsolePage p){
super(UITexts.command_history,AbstractUIPlugin.imageDescriptorFromPlugin( HaskellDebugUI.getDefault().getBundle().getSymbolicName(), "icons/etool16/history16.gif" )); //$NON-NLS-1$
IPreferencesService service = Platform.getPreferencesService();
maxHistory=service.getInt(HaskellCorePlugin.getPluginId(), ICorePreferenceNames.RUN_COMMAND_HISTORY_MAX ,20,null);
final TextConsoleViewer viewer=p.getViewer();
final StyledText text=viewer.getTextWidget();
text.addVerifyListener( new VerifyListener() {
@Override
public void verifyText( final VerifyEvent paramVerifyEvent ) {
// more than one printable character: not a key press, append to current buffer
if (paramVerifyEvent.text!=null && paramVerifyEvent.text.trim().length()>1){
String t=paramVerifyEvent.text.trim();
current.insert(caretPos,t);
caretPos+=t.length();
if (paramVerifyEvent.text.endsWith( "\r" ) || paramVerifyEvent.text.endsWith( "\n" )){ //$NON-NLS-1$ //$NON-NLS-2$
String s=current.toString().trim();
if (s.length()>0){
commands.remove(s);
commands.addFirst(s);
if (commands.size()>maxHistory){
commands.removeLast();
}
setEnabled( true );
}
insertIndex=-1;
current.setLength( 0 );
caretPos=0;
}
}
}
} );
text.addKeyListener( new KeyAdapter() {
/* (non-Javadoc)
* @see org.eclipse.swt.events.KeyAdapter#keyReleased(org.eclipse.swt.events.KeyEvent)
*/
@Override
public void keyReleased( final KeyEvent e ) {
if ((e.keyCode==SWT.ARROW_UP || e.keyCode==SWT.ARROW_DOWN) && (e.stateMask & SWT.CONTROL) == SWT.CONTROL && commands.size()>0){
if (current.length()==0){
insertOffset=text.getCharCount();
} else {
text.replaceTextRange( insertOffset, current.length(), "" ); //$NON-NLS-1$
current.setLength( 0 );
caretPos=0;
}
if (e.keyCode==SWT.ARROW_DOWN ){
insertIndex--;
if (insertIndex<0){
insertIndex=commands.size()-1;
}
} else if (e.keyCode==SWT.ARROW_UP ){
insertIndex++;
if (insertIndex>=commands.size()){
insertIndex=0;
}
}
Iterator<String> it=commands.iterator();
String toInsert=it.next();
for (int a=0;a<insertIndex;a++){
toInsert=it.next();
}
//current.append(toInsert); // done by verify listener
text.append(toInsert);
caretPos+=toInsert.length();
text.setCaretOffset( text.getCharCount() );
}
}
@Override
public void keyPressed(final org.eclipse.swt.events.KeyEvent e) {
char c=e.character;
synchronized(current){
// new line
if (c=='\r'){
String s=current.toString().trim();
if (s.length()>0){
commands.remove(s);
commands.addFirst(s);
if (commands.size()>maxHistory){
commands.removeLast();
}
setEnabled( true );
}
insertIndex=-1;
current.setLength( 0 );
caretPos=0;
// printable character
} else if (!Character.isISOControl( c )){
if (current.length()==0){
insertOffset=text.getCharCount();
}
current.insert(caretPos,c);
caretPos++;
// backspace
} else if (c=='\b' && current.length()>0){
current.setLength( current.length()-1 );
caretPos--;
} else if (e.keyCode==SWT.ARROW_LEFT){
caretPos--;
} else if (e.keyCode==SWT.ARROW_RIGHT){
if (caretPos<current.length()){
caretPos++;
}
}
}
}
} );
setMenuCreator( new IMenuCreator() {
@Override
public Menu getMenu( final Menu arg0 ) {
Menu m=new Menu(arg0);
for (final String s:commands){
MenuItem mi=new MenuItem( m, SWT.NONE );
mi.setText( s );
mi.addSelectionListener( new HistoryMenuSelectionListener( text, s ) );
}
return m;
}
@Override
public Menu getMenu( final Control arg0 ) {
Menu m=new Menu(arg0);
for (final String s:commands){
MenuItem mi=new MenuItem( m, SWT.NONE );
mi.setText( s );
mi.addSelectionListener( new HistoryMenuSelectionListener( text, s ) );
}
return m;
}
@Override
public void dispose() {
// NOOP
}
} );
setEnabled( false );
}
public void dispose(){
//NOOP
}
private class HistoryMenuSelectionListener extends SelectionAdapter {
private final StyledText text;
private final String command;
public HistoryMenuSelectionListener( final StyledText text, final String command ) {
super();
this.text = text;
this.command = command;
}
/* (non-Javadoc)
* @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
@Override
public void widgetSelected( final SelectionEvent e ) {
text.append( command);
text.setCaretOffset( text.getCharCount() );
Event event = new Event();
event.widget = text;
event.stateMask = 0;
event.keyCode = 13;
event.character = '\r';
text.notifyListeners( SWT.KeyDown, event );
}
}
/* (non-Javadoc)
* @see org.eclipse.jface.action.Action#run()
*/
@Override
public void run() {
// NOOP
}
}