All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.twineworks.kettle.ruby.step.RubyStepDialog Maven / Gradle / Ivy

The newest version!
/*
 * Ruby for pentaho kettle
 * Copyright (C) 2017 Twineworks GmbH
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

package com.twineworks.kettle.ruby.step;

import com.twineworks.kettle.ruby.step.RubyStepSamplesHelper.SampleType;
import com.twineworks.kettle.ruby.step.meta.OutputFieldMeta;
import com.twineworks.kettle.ruby.step.meta.RoleStepMeta;
import com.twineworks.kettle.ruby.step.meta.RubyScriptMeta;
import com.twineworks.kettle.ruby.step.meta.RubyScriptMeta.Role;
import com.twineworks.kettle.ruby.step.meta.RubyVariableMeta;
import org.apache.commons.io.FileUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.*;
import org.jruby.Ruby;
import org.jruby.runtime.builtin.IRubyObject;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.Props;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.plugins.PluginRegistry;
import org.pentaho.di.core.plugins.StepPluginType;
import org.pentaho.di.core.row.RowMetaInterface;
import org.pentaho.di.core.row.ValueMetaInterface;
import org.pentaho.di.core.row.value.ValueMetaFactory;
import org.pentaho.di.i18n.BaseMessages;
import org.pentaho.di.trans.TransMeta;
import org.pentaho.di.trans.step.BaseStepMeta;
import org.pentaho.di.trans.step.StepDialogInterface;
import org.pentaho.di.ui.core.gui.GUIResource;
import org.pentaho.di.ui.core.widget.ColumnInfo;
import org.pentaho.di.ui.core.widget.StyledTextComp;
import org.pentaho.di.ui.core.widget.TableView;
import org.pentaho.di.ui.core.widget.TextVar;
import org.pentaho.di.ui.spoon.Spoon;
import org.pentaho.di.ui.trans.step.BaseStepDialog;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class RubyStepDialog extends BaseStepDialog implements StepDialogInterface {

  private static Class PKG = RubyStepMeta.class; // for i18n purposes
  final private String[] NO_YES = new String[2];
  int margin = Const.MARGIN;
  private RubyStepSyntaxHighlighter syntaxHighlighter;
  private RubyStepParseErrorHelper parseErrorHelper;

  // output field name
  private RubyStepMeta input;
  private boolean changedInDialog;
  private CTabFolder wScriptsFolder;
  private ModifyListener lsMod;
  private TableView wFields;
  private Button wClearInputFields;
  private Label wlEditingPosition;
  private Composite wTopLeft;
  private GUIResource guiResource = GUIResource.getInstance();
  private Label wlSyntaxCheck;
  private CTabFolder wLeftFolder;
  private SashForm wTop;
  private ToolItem itemSettings;
  private ToolBar wScriptToolBar;
  private Label wSeparator;
  private CTabFolder wBottomFolder;
  private String[] prevStepNames;
  private String[] nextStepNames;
  private TableView wScopeVariables;
  private TableView wInfoSteps;
  private TableView wTargetSteps;
  private Image scriptImage;
  private Image rubyImage;
  private Image checkImage;
  private Menu scriptMenu;

  private Image addImage;

  private Image renameImage;

  private MenuItem renameItem;

  private Image initScriptImage;

  private Image disposeScriptImage;

  private Image libScriptImage;

  private MenuItem rowScriptItem;

  private MenuItem libScriptItem;

  private MenuItem initScriptItem;

  private MenuItem disposeScriptItem;

  private Image rowScriptImage;
  private Image infoStepImage;

  private Tree wTree;

  private TreeItem inputTreeItem;

  private HashMap infoTreeItems;

  private Image inputImage;

  private Image fieldImage;

  private TreeItem inputFolderTreeItem;

  private Image folderImage;

  private TreeItem outputFolderTreeItem;

  private TreeItem outputTreeItem;

  private Image outputImage;

  private HashMap targetTreeItems;

  private Image targetStepImage;

  private Image fieldChangedImage;

  private TreeItem errorTreeItem;

  private Image errorOutputImage;

  private Image fieldErrorImage;

  private Label wRubyVersion;

  private Tree wSamplesTree;

  private File pluginBaseFile;

  private Image transformationImage;

  private Image webDocumentImage;

  private RubyStepSamplesHelper samplesHelper;

  private Menu sampleMenu;

  private MenuItem showSampleItem;

  private int middle;

  private TextVar wGemHome;
  private String pluginBaseDir;
  private String pluginImageDir = "com/twineworks/kettle/ruby/step/resources/";

  public RubyStepDialog(Shell parent, Object in, TransMeta transMeta, String sname) {
    super(parent, (BaseStepMeta) in, transMeta, sname);
    input = (RubyStepMeta) in;
    NO_YES[0] = BaseMessages.getString(PKG, "System.Combo.No");
    NO_YES[1] = BaseMessages.getString(PKG, "System.Combo.Yes");
  }

  private Image getResourceImage(String name) {
    return guiResource.getImage(pluginImageDir + name, this.getClass().getClassLoader(), 16, 16);
  }

  public String open() {

    changed = input.hasChanged();
    changedInDialog = false;

    Shell parent = getParent();
    Display display = parent.getDisplay();

    shell = new Shell(parent, SWT.DIALOG_TRIM | SWT.RESIZE | SWT.MIN | SWT.MAX);
    syntaxHighlighter = new RubyStepSyntaxHighlighter();
    parseErrorHelper = new RubyStepParseErrorHelper();

    props.setLook(shell);
    setShellImage(shell, input);

    lsMod = new ModifyListener() {
      public void modifyText(ModifyEvent e) {
        input.setChanged();
        changedInDialog = true;
      }
    };

    FormLayout formLayout = new FormLayout();
    formLayout.marginWidth = Const.FORM_MARGIN;
    formLayout.marginHeight = Const.FORM_MARGIN;

    shell.setLayout(formLayout);
    shell.setText(BaseMessages.getString(PKG, "RubyStep.Shell.Title"));

    middle = props.getMiddlePct();

    // load images
    if (!guiResource.getImageMap().containsKey("TypeExitRubyStep:empty16x16")) {
      guiResource.getImageMap().put("TypeExitRubyStep:empty16x16", new Image(display, 16, 16));
    }

    pluginBaseDir = PluginRegistry.getInstance().findPluginWithId(StepPluginType.class, "TypeExitRubyStep").getPluginDirectory().toString();

    URL pluginBaseURL = PluginRegistry.getInstance().findPluginWithId(StepPluginType.class, "TypeExitRubyStep").getPluginDirectory();

    try {
      pluginBaseFile = new File(pluginBaseURL.toURI());
    } catch (URISyntaxException e) {
      pluginBaseFile = new File(pluginBaseURL.getPath());
    }

    try {

      scriptImage = getResourceImage("libScript.png");
      checkImage = getResourceImage("check.png");
      rubyImage = getResourceImage("ruby_16.png");
      addImage = getResourceImage("addSmall.png");
      renameImage = getResourceImage("edit.png");
      rowScriptImage = rubyImage;
      initScriptImage = getResourceImage("startScript.png");
      disposeScriptImage = getResourceImage("endScript.png");
      libScriptImage = scriptImage;
      infoStepImage = getResourceImage("info_step.png");
      targetStepImage = getResourceImage("target_step.png");
      inputImage = getResourceImage("input.png");
      outputImage = getResourceImage("output.png");
      fieldImage = getResourceImage("field.png");
      fieldChangedImage = getResourceImage("field_changed.png");
      folderImage = getResourceImage("folder.png");
      errorOutputImage = getResourceImage("error_output.png");
      fieldErrorImage = getResourceImage("field_error.png");
//			transformationImage = getResourceImage("transformation.png");
      transformationImage = guiResource.getImage("ui/images/transformation_tree.png");
      webDocumentImage = getResourceImage("web.png");
    } catch (Exception e) {
      Image empty = guiResource.getImage("TypeExitRubyStep:empty16x16");
      scriptImage = empty;
      checkImage = empty;
      rubyImage = empty;
      addImage = empty;
      renameImage = empty;
      rowScriptImage = empty;
      initScriptImage = empty;
      disposeScriptImage = empty;
      libScriptImage = empty;
      infoStepImage = empty;
      targetStepImage = empty;
      inputImage = empty;
      outputImage = empty;
      fieldImage = empty;
      fieldChangedImage = empty;
      fieldErrorImage = empty;
      folderImage = empty;
      errorOutputImage = empty;
      transformationImage = empty;
      webDocumentImage = empty;
    }

    samplesHelper = new RubyStepSamplesHelper();
    samplesHelper.setFolderImage(folderImage);
    samplesHelper.setSampleImage(fieldImage);
    samplesHelper.setScriptImage(libScriptImage);
    samplesHelper.setTransformationImage(transformationImage);
    samplesHelper.setWebDocumentImage(webDocumentImage);

    // start construction

    // Stepname line
    wlStepname = new Label(shell, SWT.RIGHT);
    wlStepname.setText(BaseMessages.getString(PKG, "System.Label.StepName"));
    props.setLook(wlStepname);
    fdlStepname = new FormData();
    fdlStepname.left = new FormAttachment(0, 0);
    fdlStepname.right = new FormAttachment(middle, -margin);
    fdlStepname.top = new FormAttachment(0, margin);
    wlStepname.setLayoutData(fdlStepname);

    wStepname = new Text(shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
    wStepname.setText(stepname);
    props.setLook(wStepname);
    wStepname.addModifyListener(lsMod);
    fdStepname = new FormData();
    fdStepname.left = new FormAttachment(middle, 0);
    fdStepname.top = new FormAttachment(0, margin);
    fdStepname.right = new FormAttachment(100, 0);
    wStepname.setLayoutData(fdStepname);

    SashForm wSash = new SashForm(shell, SWT.VERTICAL);
    props.setLook(wSash);
    /*------------------------------------------------------------------------------------------------------------------------------------------------
		 * Upper part of form  
		 ------------------------------------------------------------------------------------------------------------------------------------------------*/

    // top part
    wTop = new SashForm(wSash, SWT.HORIZONTAL);

    FormLayout topLayout = new FormLayout();
    topLayout.marginWidth = Const.FORM_MARGIN;
    topLayout.marginHeight = Const.FORM_MARGIN;
    wTop.setLayout(topLayout);
    props.setLook(wTop);

    addLeftArea();
    addScriptArea();

    FormData fdTop = new FormData();
    fdTop.left = new FormAttachment(0, 0);
    fdTop.top = new FormAttachment(0, 0);
    fdTop.right = new FormAttachment(100, 0);
    fdTop.bottom = new FormAttachment(100, 0);
    wTop.setLayoutData(fdTop);

    wTop.SASH_WIDTH = margin;
    wTop.setWeights(new int[]{32, 68});

		/*------------------------------------------------------------------------------------------------------------------------------------------------
		 * Bottom part of form 
		 ------------------------------------------------------------------------------------------------------------------------------------------------*/

    Composite wBottom = new Composite(wSash, SWT.NONE);
    props.setLook(wBottom);

    FormLayout bottomLayout = new FormLayout();
    bottomLayout.marginWidth = Const.FORM_MARGIN;
    bottomLayout.marginHeight = Const.FORM_MARGIN;
    wBottom.setLayout(bottomLayout);

    // separator
    wSeparator = new Label(wBottom, SWT.SEPARATOR | SWT.HORIZONTAL);
    FormData fdSeparator = new FormData();
    fdSeparator.left = new FormAttachment(0, 0);
    fdSeparator.right = new FormAttachment(100, 0);
    fdSeparator.top = new FormAttachment(0, -margin);
    wSeparator.setLayoutData(fdSeparator);
    props.setLook(wSeparator);

    // bottom tab folder
    wBottomFolder = new CTabFolder(wBottom, SWT.BORDER | SWT.RESIZE);
    wBottomFolder.setSimple(false);
    wBottomFolder.setUnselectedImageVisible(true);
    wBottomFolder.setUnselectedCloseVisible(false);
    wBottomFolder.setMaximizeVisible(false);
    wBottomFolder.setMinimizeVisible(false);
    props.setLook(wBottomFolder);

    addOutputFieldsTab();

    prevStepNames = transMeta.getPrevStepNames(stepMeta);
    addInfoStepsTab();

    nextStepNames = transMeta.getNextStepNames(stepMeta);
    addTargetStepsTab();

    addScopeVariablesTab();
    addRuntimeTab();

    // set selected item in tab
    wBottomFolder.setSelection(0);

    // layout tab folder below the label
    FormData fdBottomFolder = new FormData();
    fdBottomFolder.left = new FormAttachment(0, 0);
    fdBottomFolder.top = new FormAttachment(wSeparator, margin);
    fdBottomFolder.right = new FormAttachment(100, 0);
    fdBottomFolder.bottom = new FormAttachment(100, 0);
    wBottomFolder.setLayoutData(fdBottomFolder);

    FormData fdBottom = new FormData();
    fdBottom.left = new FormAttachment(0, 0);
    fdBottom.top = new FormAttachment(0, 0);
    fdBottom.right = new FormAttachment(100, 0);
    fdBottom.bottom = new FormAttachment(100, 0);
    wBottom.setLayoutData(fdBottom);

		/*------------------------------------------------------------------------------------------------------------------------------------------------
		 * Wrapping up form 
		 ------------------------------------------------------------------------------------------------------------------------------------------------*/

    FormData fdSash = new FormData();
    fdSash.left = new FormAttachment(0, 0);
    fdSash.top = new FormAttachment(wStepname, margin);
    fdSash.right = new FormAttachment(100, 0);
    fdSash.bottom = new FormAttachment(100, -50);
    wSash.setLayoutData(fdSash);

    wSash.SASH_WIDTH = margin;
    wSash.setWeights(new int[]{75, 25});

    // OK and cancel buttons
    wOK = new Button(shell, SWT.PUSH);
    wOK.setText(BaseMessages.getString(PKG, "System.Button.OK"));
    wCancel = new Button(shell, SWT.PUSH);
    wCancel.setText(BaseMessages.getString(PKG, "System.Button.Cancel"));

    BaseStepDialog.positionBottomButtons(shell, new Button[]{wOK, wCancel}, margin, wSash);

    // Add listeners
    lsCancel = new Listener() {
      public void handleEvent(Event e) {
        cancel();
      }
    };
    lsOK = new Listener() {
      public void handleEvent(Event e) {
        ok();
      }
    };

    wCancel.addListener(SWT.Selection, lsCancel);
    wOK.addListener(SWT.Selection, lsOK);

    lsDef = new SelectionAdapter() {
      public void widgetDefaultSelected(SelectionEvent e) {
        ok();
      }
    };

    wStepname.addSelectionListener(lsDef);

    // Detect X or ALT-F4 or something that kills this window...
    shell.addShellListener(new ShellAdapter() {
      public void shellClosed(ShellEvent e) {
        if (!cancel()) {
          e.doit = false;
        }
      }
    });

    // Set the shell size, based upon previous time...
    setSize();

    getData();

    // make sure there's at least one default script
    if (wScriptsFolder.getItemCount() == 0) {
      addScriptTab(RubyScriptMeta.DEFAULT_SCRIPT);
    }

    wScriptsFolder.setSelection(wScriptsFolder.getItem(0));

    highlightSyntax();

    input.setChanged(changed);

    shell.open();
    while (!shell.isDisposed()) {
      if (!display.readAndDispatch())
        display.sleep();
    }
    return stepname;
  }

  private void addScopeVariablesTab() {

    CTabItem outFieldsItem = new CTabItem(wBottomFolder, SWT.NONE);
    outFieldsItem.setText(BaseMessages.getString(PKG, "RubyStepDialog.ScopeVariables.Label"));

    Composite wPanel = new Composite(wBottomFolder, SWT.NONE);
    wPanel.setLayout(new FormLayout());

    FormData fdPanel = new FormData();
    fdPanel.left = new FormAttachment(0, 0);
    fdPanel.top = new FormAttachment(0, 0);
    fdPanel.right = new FormAttachment(100, 0);
    fdPanel.bottom = new FormAttachment(100, 0);
    wPanel.setLayoutData(fdPanel);

    outFieldsItem.setControl(wPanel);

    final int nrRows = input.getRubyVariables().size();
    ColumnInfo[] colinf = new ColumnInfo[]{
      new ColumnInfo(BaseMessages.getString(PKG, "RubyStepDialog.ColumnInfo.ScopeVariable"), ColumnInfo.COLUMN_TYPE_TEXT, false), //$NON-NLS-1$
      new ColumnInfo(BaseMessages.getString(PKG, "RubyStepDialog.ColumnInfo.ScopeVariableValue"), ColumnInfo.COLUMN_TYPE_TEXT, false), //$NON-NLS-1$
    };
    colinf[1].setUsingVariables(true);

    wScopeVariables = new TableView(transMeta, wPanel, SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI, colinf, nrRows, lsMod, props);

    FormData fdFields = new FormData();
    fdFields.left = new FormAttachment(0, 0);
    fdFields.top = new FormAttachment(0, 0);
    fdFields.right = new FormAttachment(100, 0);
    fdFields.bottom = new FormAttachment(100, 0);
    wScopeVariables.setLayoutData(fdFields);

  }

  private void addOutputFieldsTab() {

    CTabItem outFieldsItem = new CTabItem(wBottomFolder, SWT.NONE);
    outFieldsItem.setText(BaseMessages.getString(PKG, "RubyStepDialog.Fields.Label"));

    Composite wPanel = new Composite(wBottomFolder, SWT.NONE);
    wPanel.setLayout(new FormLayout());
    props.setLook(wPanel);

    FormData fdPanel = new FormData();
    fdPanel.left = new FormAttachment(0, 0);
    fdPanel.top = new FormAttachment(0, 0);
    fdPanel.right = new FormAttachment(100, 0);
    fdPanel.bottom = new FormAttachment(100, 0);
    wPanel.setLayoutData(fdPanel);

    outFieldsItem.setControl(wPanel);

    wClearInputFields = new Button(wPanel, SWT.CHECK);
    wClearInputFields.setText(BaseMessages.getString(PKG, "RubyStepDialog.ClearFields.Label")); //$NON-NLS-1$
    props.setLook(wClearInputFields);
    FormData fdClearResultFields = new FormData();
    fdClearResultFields.right = new FormAttachment(100, 0);
    fdClearResultFields.bottom = new FormAttachment(100, 0);
    wClearInputFields.setLayoutData(fdClearResultFields);

    final int fieldsRows = input.getOutputFields().size();

    ColumnInfo[] colinf = new ColumnInfo[]{
      new ColumnInfo(BaseMessages.getString(PKG, "RubyStepDialog.ColumnInfo.Fieldname"), ColumnInfo.COLUMN_TYPE_TEXT, false), //$NON-NLS-1$
      new ColumnInfo(BaseMessages.getString(PKG, "RubyStepDialog.ColumnInfo.Type"), ColumnInfo.COLUMN_TYPE_CCOMBO, ValueMetaFactory.getAllValueMetaNames()), //$NON-NLS-1$
      new ColumnInfo(BaseMessages.getString(PKG, "RubyStepDialog.ColumnInfo.UpdateExisting"), ColumnInfo.COLUMN_TYPE_CCOMBO, NO_YES) //$NON-NLS-1$
    };

    wFields = new TableView(transMeta, wPanel, SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI, colinf, fieldsRows, lsMod, props);

    FormData fdFields = new FormData();
    fdFields.left = new FormAttachment(0, 0);
    fdFields.top = new FormAttachment(0, 0);
    fdFields.right = new FormAttachment(100, 0);
    fdFields.bottom = new FormAttachment(wClearInputFields, -margin);
    wFields.setLayoutData(fdFields);

  }

  private void addInfoStepsTab() {

    CTabItem infoStepsItem = new CTabItem(wBottomFolder, SWT.NONE);
    infoStepsItem.setText(BaseMessages.getString(PKG, "RubyStepDialog.InfoSteps.Label"));

    Composite wPanel = new Composite(wBottomFolder, SWT.NONE);
    wPanel.setLayout(new FormLayout());
    props.setLook(wPanel);

    FormData fdPanel = new FormData();
    fdPanel.left = new FormAttachment(0, 0);
    fdPanel.top = new FormAttachment(0, 0);
    fdPanel.right = new FormAttachment(100, 0);
    fdPanel.bottom = new FormAttachment(100, 0);
    wPanel.setLayoutData(fdPanel);

    infoStepsItem.setControl(wPanel);

    final int nrRows = input.getInfoSteps().size();

    ColumnInfo[] colinf = new ColumnInfo[]{
      new ColumnInfo(BaseMessages.getString(PKG, "RubyStepDialog.ColumnInfo.InfoStepTag"), ColumnInfo.COLUMN_TYPE_TEXT, false), //$NON-NLS-1$
      new ColumnInfo(BaseMessages.getString(PKG, "RubyStepDialog.ColumnInfo.InfoStepName"), ColumnInfo.COLUMN_TYPE_CCOMBO, prevStepNames), //$NON-NLS-1$
    };

    wInfoSteps = new TableView(transMeta, wPanel, SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI, colinf, nrRows, lsMod, props);

    FormData fdFields = new FormData();
    fdFields.left = new FormAttachment(0, 0);
    fdFields.top = new FormAttachment(0, 0);
    fdFields.right = new FormAttachment(100, 0);
    fdFields.bottom = new FormAttachment(100, 0);
    wInfoSteps.setLayoutData(fdFields);

  }

  private void addTargetStepsTab() {

    CTabItem targetStepsItem = new CTabItem(wBottomFolder, SWT.NONE);
    targetStepsItem.setText(BaseMessages.getString(PKG, "RubyStepDialog.TargetSteps.Label"));

    Composite wPanel = new Composite(wBottomFolder, SWT.NONE);
    wPanel.setLayout(new FormLayout());
    props.setLook(wPanel);

    FormData fdPanel = new FormData();
    fdPanel.left = new FormAttachment(0, 0);
    fdPanel.top = new FormAttachment(0, 0);
    fdPanel.right = new FormAttachment(100, 0);
    fdPanel.bottom = new FormAttachment(100, 0);
    wPanel.setLayoutData(fdPanel);

    targetStepsItem.setControl(wPanel);

    final int nrRows = input.getTargetSteps().size();

    ColumnInfo[] colinf = new ColumnInfo[]{
      new ColumnInfo(BaseMessages.getString(PKG, "RubyStepDialog.ColumnInfo.TargetStepTag"), ColumnInfo.COLUMN_TYPE_TEXT, false), //$NON-NLS-1$
      new ColumnInfo(BaseMessages.getString(PKG, "RubyStepDialog.ColumnInfo.TargetStepName"), ColumnInfo.COLUMN_TYPE_CCOMBO, nextStepNames), //$NON-NLS-1$
    };

    wTargetSteps = new TableView(transMeta, wPanel, SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI, colinf, nrRows, lsMod, props);

    FormData fdFields = new FormData();
    fdFields.left = new FormAttachment(0, 0);
    fdFields.top = new FormAttachment(0, 0);
    fdFields.right = new FormAttachment(100, 0);
    fdFields.bottom = new FormAttachment(100, 0);
    wTargetSteps.setLayoutData(fdFields);

  }

  private void addFieldSummaryTab() {

    CTabItem fieldSummaryItem = new CTabItem(wLeftFolder, SWT.NONE);
    fieldSummaryItem.setText(BaseMessages.getString(PKG, "RubyStepDialog.FieldSummary.Label"));

    Composite wPanel = new Composite(wLeftFolder, SWT.NONE);
    wPanel.setLayout(new FormLayout());
    props.setLook(wPanel);

    wTree = new Tree(wPanel, SWT.H_SCROLL | SWT.V_SCROLL);
    wTree.setHeaderVisible(true);
    TreeColumn column1 = new TreeColumn(wTree, SWT.LEFT);
    column1.setText(BaseMessages.getString(PKG, "RubyStepDialog.TreeColumn.Field"));
    column1.setWidth(180);
    TreeColumn column2 = new TreeColumn(wTree, SWT.LEFT);
    column2.setText(BaseMessages.getString(PKG, "RubyStepDialog.TreeColumn.Type"));
    column2.setWidth(90);
    TreeColumn column3 = new TreeColumn(wTree, SWT.LEFT);
    column3.setText(BaseMessages.getString(PKG, "RubyStepDialog.TreeColumn.AvailableAs"));
    column3.setWidth(160);
    props.setLook(wTree);

    inputFolderTreeItem = new TreeItem(wTree, SWT.NONE);
    inputFolderTreeItem.setText(new String[]{"input", "", ""});
    inputFolderTreeItem.setImage(folderImage);

    outputFolderTreeItem = new TreeItem(wTree, SWT.NONE);
    outputFolderTreeItem.setText(new String[]{"output", "", ""});
    outputFolderTreeItem.setImage(folderImage);

    infoTreeItems = new HashMap();

    // insert markers for info streams
    for (RoleStepMeta s : input.getInfoSteps()) {
      TreeItem item = new TreeItem(inputFolderTreeItem, SWT.NONE);
      item.setText(new String[]{s.getRoleName(), "row stream", "$info_steps[\"" + s.getRoleName() + "\"]"});
      item.setImage(infoStepImage);
      infoTreeItems.put(s.getStepName(), item);
    }

    // insert markers for input stream
    inputTreeItem = new TreeItem(inputFolderTreeItem, SWT.NONE);
    inputTreeItem.setText(new String[]{"input", "row stream", "$input"});
    inputTreeItem.setImage(inputImage);

    // insert markers for target steps
    targetTreeItems = new HashMap();

    // insert markers for target streams
    for (RoleStepMeta s : input.getTargetSteps()) {
      TreeItem item = new TreeItem(outputFolderTreeItem, SWT.NONE);
      item.setText(new String[]{s.getRoleName(), "row stream", "$target_steps[\"" + s.getRoleName() + "\"]"});
      item.setImage(targetStepImage);
      targetTreeItems.put(s.getStepName(), item);
    }

    // insert marker for output stream
    outputTreeItem = new TreeItem(outputFolderTreeItem, SWT.NONE);
    outputTreeItem.setText(new String[]{"output", "row stream", "$output"});
    outputTreeItem.setImage(outputImage);

    // insert marker for error stream
    if (input.getParentStepMeta().isDoingErrorHandling()) {
      errorTreeItem = new TreeItem(outputFolderTreeItem, SWT.NONE);
      errorTreeItem.setText(new String[]{"error", "row stream", "$error"});
      errorTreeItem.setImage(errorOutputImage);
    }

    inputFolderTreeItem.setExpanded(true);
    outputFolderTreeItem.setExpanded(true);

    final Runnable runnable = new Runnable() {
      public void run() {
        try {
          // collect main input fields
          RowMetaInterface inputFields = transMeta.getPrevStepFields(input.getParentStepMeta());

          // collect fields from input steps
          HashMap infoStepFields = new HashMap();
          for (String step : input.getStepIOMeta().getInfoStepnames()) {
            infoStepFields.put(step, transMeta.getStepFields(step));
          }

          // collect output fields
          RowMetaInterface outputFields = transMeta.getStepFields(stepname);
          RowMetaInterface errorFields = null;

          if (input.getParentStepMeta().isDoingErrorHandling()) {
            errorFields = input.getParentStepMeta().getStepErrorMeta().getErrorFields();
          }

          setTreeFields(inputFields, infoStepFields, outputFields, errorFields);
        } catch (KettleException e) {
          logError(BaseMessages.getString(PKG, "System.Dialog.GetFieldsFailed.Message"));
        }
      }
    };
    new Thread(runnable).start();

    wTree.addListener(SWT.MouseDoubleClick, new Listener() {

      @Override
      public void handleEvent(Event e) {
        if (e.button != 1)
          return;
        Point point = new Point(e.x, e.y);
        TreeItem item = wTree.getItem(point);
        if (item != null) {
          StyledTextComp wScript = (StyledTextComp) wScriptsFolder.getSelection().getControl();
          wScript.getStyledText().insert(item.getText(2));
        }

      }
    });

    FormData fdTree = new FormData();
    fdTree.left = new FormAttachment(0, 0);
    fdTree.top = new FormAttachment(0, 0);
    fdTree.right = new FormAttachment(100, 0);
    fdTree.bottom = new FormAttachment(100, 0);
    wTree.setLayoutData(fdTree);

    FormData fdPanel = new FormData();
    fdPanel.left = new FormAttachment(0, 0);
    fdPanel.top = new FormAttachment(0, 0);
    fdPanel.right = new FormAttachment(100, 0);
    fdPanel.bottom = new FormAttachment(100, 0);
    wPanel.setLayoutData(fdPanel);

    fieldSummaryItem.setControl(wPanel);

  }

  protected void setTreeFields(final RowMetaInterface inputFields, final HashMap infoFields, final RowMetaInterface outputFields, final RowMetaInterface errorFields) {

    shell.getDisplay().syncExec(new Runnable() {

      @Override
      public void run() {
        inputTreeItem.removeAll();
        if (inputFields != null) {

          for (ValueMetaInterface v : inputFields.getValueMetaList()) {
            TreeItem m = new TreeItem(inputTreeItem, SWT.NONE);
            m.setText(new String[]{v.getName(), v.getTypeDesc(), "$row[\"" + v.getName() + "\"]"});
            m.setImage(fieldImage);
          }

          if (inputFields.size() == 0) {
            inputTreeItem.dispose();
          } else {
            inputTreeItem.setExpanded(true);
          }

        }

        for (String step : infoFields.keySet()) {
          TreeItem parent = infoTreeItems.get(step);
          if (parent != null) {
            parent.removeAll();
            RowMetaInterface stepFields = infoFields.get(step);
            for (ValueMetaInterface v : stepFields.getValueMetaList()) {
              TreeItem m = new TreeItem(parent, SWT.NONE);
              m.setText(new String[]{v.getName(), v.getTypeDesc(), ""});
              m.setImage(fieldImage);
            }
            parent.setExpanded(true);
          }
        }

        // output stream
        outputTreeItem.removeAll();
        if (outputFields != null) {

          for (ValueMetaInterface v : outputFields.getValueMetaList()) {
            TreeItem m = new TreeItem(outputTreeItem, SWT.NONE);
            m.setText(new String[]{v.getName(), v.getTypeDesc(), ""});
            m.setImage(input.addsOrChangesField(v.getName()) ? fieldChangedImage : fieldImage);
          }

          outputTreeItem.setExpanded(true);

        }

        // target steps
        for (RoleStepMeta step : input.getTargetSteps()) {
          TreeItem parent = targetTreeItems.get(step.getStepName());
          if (parent != null) {
            parent.removeAll();
            for (ValueMetaInterface v : outputFields.getValueMetaList()) {
              TreeItem m = new TreeItem(parent, SWT.NONE);
              m.setText(new String[]{v.getName(), v.getTypeDesc(), ""});
              m.setImage(input.addsOrChangesField(v.getName()) ? fieldChangedImage : fieldImage);
            }
            parent.setExpanded(true);
          }
        }

        // error stream
        if (errorFields != null && input.getParentStepMeta().isDoingErrorHandling() && errorTreeItem != null) {
          errorTreeItem.removeAll();

          if (inputFields != null) {

            for (ValueMetaInterface v : inputFields.getValueMetaList()) {
              TreeItem m = new TreeItem(errorTreeItem, SWT.NONE);
              m.setText(new String[]{v.getName(), v.getTypeDesc(), ""});
              m.setImage(fieldImage);
            }

          }

          if (errorFields != null) {

            for (ValueMetaInterface v : errorFields.getValueMetaList()) {
              TreeItem m = new TreeItem(errorTreeItem, SWT.NONE);
              m.setText(new String[]{v.getName(), v.getTypeDesc(), ""});
              m.setImage(fieldErrorImage);
            }

          }

          errorTreeItem.setExpanded(true);

        }

      }
    });

  }

  private void addRuntimeTab() {

    CTabItem runtimeItem = new CTabItem(wBottomFolder, SWT.NONE);
    runtimeItem.setText(BaseMessages.getString(PKG, "RubyStepDialog.ExecutionModel.Label"));

    Composite wPanel = new Composite(wBottomFolder, SWT.NONE);
    wPanel.setLayout(new FormLayout());
    props.setLook(wPanel);

    Label lRubyVersion = new Label(wPanel, SWT.RIGHT);
    lRubyVersion.setText(BaseMessages.getString(PKG, "RubyStepDialog.ExecutionModel.Version"));
    props.setLook(lRubyVersion);

    FormData fdlRubyVersion = new FormData();
    fdlRubyVersion.left = new FormAttachment(0, 0);
    fdlRubyVersion.right = new FormAttachment(middle, -margin);
    fdlRubyVersion.top = new FormAttachment(0, margin);
    lRubyVersion.setLayoutData(fdlRubyVersion);

    wRubyVersion = new Label(wPanel, SWT.LEFT);
    IRubyObject rubyDesc = Ruby.getGlobalRuntime().evalScriptlet("RUBY_DESCRIPTION");
    wRubyVersion.setText(rubyDesc != null ? rubyDesc.asJavaString() : "error getting RUBY_DESCRIPTION");

    props.setLook(wRubyVersion);

    FormData fdRubyVersion = new FormData();
    fdRubyVersion.left = new FormAttachment(middle, 0);
    fdRubyVersion.top = new FormAttachment(0, margin);
    wRubyVersion.setLayoutData(fdRubyVersion);

    // add gem home folder entry

    Label lGemHome = new Label(wPanel, SWT.RIGHT);
    lGemHome.setText(BaseMessages.getString(PKG, "RubyStepDialog.ExecutionModel.GemHome"));
    props.setLook(lGemHome);
    FormData fdlGemHome = new FormData();
    fdlGemHome.left = new FormAttachment(0, 0);
    fdlGemHome.right = new FormAttachment(middle, -margin);
    fdlGemHome.top = new FormAttachment(wRubyVersion, margin);
    lGemHome.setLayoutData(fdlGemHome);

    wGemHome = new TextVar(transMeta, wPanel, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
    wGemHome.setText(input.getGemHome());
    props.setLook(wGemHome);
    wGemHome.addModifyListener(lsMod);
    FormData fdGemHome = new FormData();
    fdGemHome.left = new FormAttachment(middle, 0);
    fdGemHome.top = new FormAttachment(wRubyVersion, margin);
    fdGemHome.right = new FormAttachment(100, -margin);
    wGemHome.setLayoutData(fdGemHome);

    runtimeItem.setControl(wPanel);

  }

  private void addScriptArea() {

    // top right composite
    Composite wTopRight = new Composite(wTop, SWT.NONE);
    FormLayout topRightLayout = new FormLayout();
    topRightLayout.marginWidth = 0;
    topRightLayout.marginHeight = 0;
    wTopRight.setLayout(topRightLayout);
    props.setLook(wTopRight);

    // script tab folder
    wScriptsFolder = new CTabFolder(wTopRight, SWT.BORDER | SWT.RESIZE);
    wScriptsFolder.setSimple(false);
    wScriptsFolder.setUnselectedImageVisible(true);
    wScriptsFolder.setUnselectedCloseVisible(true);

    props.setLook(wScriptsFolder);
    styleTabFolder(wScriptsFolder);

    // confirms closing script tabs, and will never close the last one
    wScriptsFolder.addCTabFolder2Listener(new CTabFolder2Adapter() {
      public void close(CTabFolderEvent event) {
        CTabItem cItem = (CTabItem) event.item;
        event.doit = false;
        if (cItem != null && wScriptsFolder.getItemCount() > 1) {
          MessageBox messageBox = new MessageBox(shell, SWT.ICON_QUESTION | SWT.NO | SWT.YES);
          messageBox.setText(BaseMessages.getString(PKG, "RubyStepDialog.DeleteItem.Label"));
          messageBox.setMessage(BaseMessages.getString(PKG, "RubyStepDialog.ConfirmDeleteItem.Label", cItem.getText()));
          switch (messageBox.open()) {
            case SWT.YES:
              event.doit = true;
              input.setChanged();
              changedInDialog = true;
              break;
          }
        }
      }
    });

    addScriptFolderDnD();

    addScriptPopupMenu();

    // toolbar below the script window
    wScriptToolBar = new ToolBar(wTopRight, SWT.FLAT | SWT.RIGHT);
    props.setLook(wScriptToolBar);

    itemSettings = new ToolItem(wScriptToolBar, SWT.NONE);
    itemSettings.setImage(scriptImage);
    itemSettings.setText(BaseMessages.getString(PKG, "RubyStepDialog.AdvancedSettings.Label")); //$NON-NLS-1$
    itemSettings.setEnabled(false);

    itemSettings.addSelectionListener(new SelectionAdapter() {
      @Override
      // logic to display the advanced settings area
      public void widgetSelected(SelectionEvent e) {
        wTop.SASH_WIDTH = margin;
        wTop.setWeights(new int[]{32, 68});
        itemSettings.setEnabled(false);
      }
    });

    ToolItem item = new ToolItem(wScriptToolBar, SWT.NONE);
    item.setImage(checkImage);
    item.setText(BaseMessages.getString(PKG, "RubyStepDialog.CheckSyntax.Label")); //$NON-NLS-1$

    item.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        checkForParseErrors();
      }
    });

    wScriptToolBar.pack();

    // layout toolbar
    FormData fdBar = new FormData();
    fdBar.left = new FormAttachment(0, 0);
    fdBar.bottom = new FormAttachment(100, 0);
    wScriptToolBar.setLayoutData(fdBar);

    // layout scripts folder
    FormData fdFolder = new FormData();
    fdFolder.left = new FormAttachment(0, 0);
    fdFolder.right = new FormAttachment(100, 0);
    fdFolder.top = new FormAttachment(0, 0);
    fdFolder.bottom = new FormAttachment(wScriptToolBar, 0);
    wScriptsFolder.setLayoutData(fdFolder);

    // editing position label
    wlEditingPosition = new Label(wTopRight, SWT.RIGHT);
    wlEditingPosition.setText("0 : 0 ");
    props.setLook(wlEditingPosition);

    FormData fdPos = new FormData();
    fdPos.left = new FormAttachment(100, -60);
    fdPos.right = new FormAttachment(100, -margin);
    fdPos.bottom = new FormAttachment(100, -margin + 1);
    wlEditingPosition.setLayoutData(fdPos);

    // syntax check result label
    wlSyntaxCheck = new Label(wTopRight, SWT.LEFT);
    wlSyntaxCheck.setText("");
    FormData fdSyntaxCheck = new FormData();
    fdSyntaxCheck.left = new FormAttachment(wScriptToolBar, margin);
    fdSyntaxCheck.bottom = new FormAttachment(100, -margin + 1);
    fdSyntaxCheck.right = new FormAttachment(wlEditingPosition, -margin);
    wlSyntaxCheck.setLayoutData(fdSyntaxCheck);
    props.setLook(wlSyntaxCheck);

  }

  private void addScriptFolderDnD() {

    wScriptsFolder.addListener(SWT.DragDetect, new Listener() {

      Display display = shell.getDisplay();

      CTabFolder folder = wScriptsFolder;
      Tracker t;

      public void handleEvent(Event e) {

        CTabItem dragItem = folder.getItem(folder.toControl(display.getCursorLocation()));
        if (dragItem == null)
          return;

        // open a tracker with current item's bounds
        t = new Tracker(folder, SWT.NONE);

        Rectangle[] rects = {dragItem.getBounds()};
        t.setRectangles(rects);

        t.addListener(SWT.Move, new Listener() {

          @Override
          public void handleEvent(Event e) {

            // when moving the tracker around, set the rectangles to whatever item we're hovering over. if hovering over nothing, no rectangles
            Point p = new Point(e.x, e.y); // display coords
            Point folderPoint = folder.toControl(p);
            CTabItem hoverItem = folder.getItem(folderPoint);
            if (hoverItem != null) {
              t.setRectangles(new Rectangle[]{hoverItem.getBounds()});
            } else {
              t.setRectangles(new Rectangle[0]);
            }

          }
        });

        t.setCursor(display.getSystemCursor(SWT.CURSOR_HAND));
        t.open();

        // user finished dragging the tab around, let's drop it

        Point folderPoint = folder.toControl(display.getCursorLocation());
        CTabItem dropItem = folder.getItem(folderPoint);
        if (dropItem == null || dropItem == dragItem) {
          return;
        }
        // first find out where this is going
        int idx = folder.indexOf(dropItem);
        if (folder.indexOf(dragItem) < idx) {
          idx++;
        }

        // make a copy of the item we're dragging around, as the original is going to be disposed of
        CTabItem newItem = new CTabItem(folder, dragItem.getStyle(), idx);

        newItem.setText(dragItem.getText());
        newItem.setImage(dragItem.getImage());
        Control c = dragItem.getControl();
        dragItem.setControl(null);
        newItem.setControl(c);

        newItem.setData("role", dragItem.getData("role"));

        dragItem.dispose();
        folder.setSelection(newItem);
        folder.redraw();

      }

    });

  }

  // Read data and place it in the dialog
  public void getData() {

    wStepname.selectAll();

    // load the different scripts
    for (RubyScriptMeta rubyScriptMeta : input.getScripts()) {
      addScriptTab(rubyScriptMeta);
    }

    // load output fields
    int rowNum = 0;
    for (OutputFieldMeta outField : input.getOutputFields()) {
      TableItem row = wFields.table.getItem(rowNum++);
      row.setText(1, outField.getName());
      row.setText(2, ValueMetaFactory.getValueMetaName(outField.getType()));
      row.setText(3, outField.isUpdate() ? NO_YES[1] : NO_YES[0]);
    }
    wFields.optWidth(true);
    wFields.setRowNums();

    // load clear input fields flag
    wClearInputFields.setSelection(input.isClearInputFields());

    // load ruby vars
    int varNum = 0;
    for (RubyVariableMeta var : input.getRubyVariables()) {
      TableItem row = wScopeVariables.table.getItem(varNum++);
      row.setText(1, var.getName());
      row.setText(2, var.getValue());
    }
    wScopeVariables.optWidth(true);
    wScopeVariables.setRowNums();

    // load info steps
    int infoNum = 0;
    for (RoleStepMeta step : input.getInfoSteps()) {
      TableItem row = wInfoSteps.table.getItem(infoNum++);
      row.setText(1, step.getRoleName());
      row.setText(2, step.getStepName());
    }
    wInfoSteps.optWidth(true);
    wInfoSteps.setRowNums();

    // load target steps
    int targetNum = 0;
    for (RoleStepMeta step : input.getTargetSteps()) {
      TableItem row = wTargetSteps.table.getItem(targetNum++);
      row.setText(1, step.getRoleName());
      row.setText(2, step.getStepName());
    }
    wTargetSteps.optWidth(true);
    wTargetSteps.setRowNums();

  }

  private boolean cancel() {

    if (changedInDialog) {

      MessageBox box = new MessageBox(shell, SWT.YES | SWT.NO | SWT.APPLICATION_MODAL);
      box.setText(BaseMessages.getString(PKG, "RubyStepDialog.WarningDialogChanged.Title"));
      box.setMessage(BaseMessages.getString(PKG, "RubyStepDialog.WarningDialogChanged.Message", Const.CR));
      int answer = box.open();

      if (answer == SWT.NO) {
        return false;
      }

    }

    stepname = null;
    input.setChanged(changed);
    dispose();
    return true;
  }

  // let the plugin know about the entered data
  private void ok() {

    // if there's syntax error, warn the user
    if (!hasRowScript()) {

      MessageBox box = new MessageBox(shell, SWT.YES | SWT.NO | SWT.APPLICATION_MODAL);
      box.setText(BaseMessages.getString(PKG, "RubyStepDialog.WarningDialogNoRowScript.Title"));
      box.setMessage(BaseMessages.getString(PKG, "RubyStepDialog.WarningDialogNoRowScript.Message", Const.CR));
      int answer = box.open();

      if (answer == SWT.NO) {
        return;
      }

    }

    stepname = wStepname.getText(); // return value

    // generate scripts
    List scripts = input.getScripts();

    scripts.clear();
    scripts.addAll(collectScripts());

    // generate output fields
    List outFields = input.getOutputFields();
    outFields.clear();

    int fieldCount = wFields.nrNonEmpty();
    for (int i = 0; i < fieldCount; i++) {
      TableItem t = wFields.getNonEmpty(i);
      outFields.add(new OutputFieldMeta(t.getText(1), ValueMetaFactory.getIdForValueMeta(t.getText(2)), NO_YES[1].equalsIgnoreCase(t.getText(3))));
    }

    // save clear input fields flag
    input.setClearInputFields(wClearInputFields.getSelection());

    // generate ruby vars
    List rubyVars = input.getRubyVariables();
    rubyVars.clear();

    int varCount = wScopeVariables.nrNonEmpty();
    for (int i = 0; i < varCount; i++) {
      TableItem t = wScopeVariables.getNonEmpty(i);
      String varName = t.getText(1).trim();
      if (!varName.startsWith("$")) {
        varName = "$" + varName;
      }
      // replace white space with underscores
      varName = varName.replaceAll("\\s", "_");
      rubyVars.add(new RubyVariableMeta(varName, t.getText(2)));
    }

    // generate info steps
    List infoSteps = input.getInfoSteps();
    infoSteps.clear();

    int infoCount = wInfoSteps.nrNonEmpty();
    for (int i = 0; i < infoCount; i++) {
      TableItem t = wInfoSteps.getNonEmpty(i);
      infoSteps.add(new RoleStepMeta(t.getText(2), t.getText(1)));
    }

    // generate target steps
    List targetSteps = input.getTargetSteps();
    targetSteps.clear();

    int targetCount = wTargetSteps.nrNonEmpty();
    for (int i = 0; i < targetCount; i++) {
      TableItem t = wTargetSteps.getNonEmpty(i);
      targetSteps.add(new RoleStepMeta(t.getText(2), t.getText(1)));
    }

    // set gem home
    input.setGemHome(wGemHome.getText());

    // make sure the input finds its info and target step metas alright
    input.searchInfoAndTargetSteps(transMeta.getSteps());
    dispose();
  }

  private boolean hasRowScript() {

    for (CTabItem item : wScriptsFolder.getItems()) {
      if (item.getData("role") == RubyScriptMeta.Role.ROW_SCRIPT) {
        return true;
      }
    }

    return false;
  }

  private List collectScripts() {

    List retval = new ArrayList(wScriptsFolder.getItemCount());
    CTabItem[] items = wScriptsFolder.getItems();
    for (int i = 0; i < items.length; i++) {

      CTabItem item = items[i];
      StyledTextComp wText = (StyledTextComp) item.getControl();
      retval.add(new RubyScriptMeta(item.getText(), wText.getText(), (Role) item.getData("role")));

    }
    return retval;

  }

  private void addLeftArea() {

    // top left composite
    wTopLeft = new Composite(wTop, SWT.NONE);
    FormLayout topLeftLayout = new FormLayout();
    topLeftLayout.marginWidth = 0;
    topLeftLayout.marginHeight = 0;
    wTopLeft.setLayout(topLeftLayout);
    props.setLook(wTopLeft);

    // header line

    // top left tab folder
    wLeftFolder = new CTabFolder(wTopLeft, SWT.BORDER | SWT.RESIZE);
    wLeftFolder.setSimple(false);
    wLeftFolder.setUnselectedImageVisible(true);
    wLeftFolder.setUnselectedCloseVisible(true);
    wLeftFolder.setMaximizeVisible(false);
    wLeftFolder.setMinimizeVisible(true);
    props.setLook(wLeftFolder);

    //styleTabFolder(wLeftFolder);

    // implement minimize logic
    wLeftFolder.addCTabFolder2Listener(new CTabFolder2Adapter() {

      @Override
      public void minimize(CTabFolderEvent e) {
        itemSettings.setEnabled(true);
        wTop.SASH_WIDTH = 0;
        wTop.setWeights(new int[]{0, 100});
      }

    });

    addFieldSummaryTab();
    addSamplesTab();

    // layout tab folder below the label
    FormData fdLeftFolder = new FormData();
    fdLeftFolder.left = new FormAttachment(0, 0);
    fdLeftFolder.top = new FormAttachment(0, 0);
    fdLeftFolder.right = new FormAttachment(100, 0);
    fdLeftFolder.bottom = new FormAttachment(100, 0);
    wLeftFolder.setLayoutData(fdLeftFolder);

    // set selected item in tab
    wLeftFolder.setSelection(0);

  }

  private void addSamplesTab() {

    CTabItem samplesItem = new CTabItem(wLeftFolder, SWT.NONE);
    samplesItem.setText(BaseMessages.getString(PKG, "RubyStepDialog.Samples.Label"));

    Composite wPanel = new Composite(wLeftFolder, SWT.NONE);
    wPanel.setLayout(new FillLayout());
    props.setLook(wPanel);

    wSamplesTree = new Tree(wPanel, SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI);
    wSamplesTree.setLayout(new FillLayout());
    wSamplesTree.setHeaderVisible(true);
    props.setLook(wSamplesTree);

    TreeColumn column1 = new TreeColumn(wSamplesTree, SWT.LEFT);
    column1.setText(BaseMessages.getString(PKG, "RubyStepDialog.SampleTreeColumn.Sample"));
    column1.setWidth(280);
    TreeColumn column2 = new TreeColumn(wSamplesTree, SWT.LEFT);
    column2.setText(BaseMessages.getString(PKG, "RubyStepDialog.SampleTreeColumn.Type"));
    column2.setWidth(120);

    File samplesDir = new File(pluginBaseFile.getAbsolutePath() + Const.FILE_SEPARATOR + "step" + Const.FILE_SEPARATOR + "samples");

    TreeItem rootTreeItem = new TreeItem(wSamplesTree, SWT.NONE);
    rootTreeItem.setText(new String[]{"samples", ""});
    rootTreeItem.setImage(folderImage);
    rootTreeItem.setData("type", SampleType.DIR);
    rootTreeItem.setData("file", samplesDir);

    samplesHelper.fillSamplesDir(rootTreeItem, samplesDir);
    rootTreeItem.setExpanded(true);

    samplesItem.setControl(wPanel);

    wSamplesTree.addMouseListener(new MouseAdapter() {

      public void mouseDoubleClick(MouseEvent e) {
        if (e.button != 1)
          return;
        Point click = new Point(e.x, e.y);
        TreeItem item = wSamplesTree.getItem(click);
        if (item != null) {
          openSample(item);
        }

      }

    });

    addSamplesMenu();

  }

  private void addSamplesMenu() {

    sampleMenu = new Menu(shell, SWT.POP_UP);

    showSampleItem = new MenuItem(sampleMenu, SWT.PUSH);
    showSampleItem.setText(BaseMessages.getString(PKG, "RubyStepDialog.SamplesMenu.ShowSample"));
    //showSampleItem.setImage(libScriptImage);
    showSampleItem.addListener(SWT.Selection, new Listener() {
      public void handleEvent(Event e) {
        for (TreeItem item : wSamplesTree.getSelection()) {
          openSample(item);
        }
      }
    });

    // menu is about to show, let's enable/disable stuff
    sampleMenu.addListener(SWT.Show, new Listener() {

      @Override
      public void handleEvent(Event e) {
        showSampleItem.setEnabled(false);
        // any non-folder selection enables the show sample
        for (TreeItem item : wSamplesTree.getSelection()) {
          switch ((SampleType) item.getData("type")) {
            case TRANS:
            case SCRIPT:
            case WEB:
              showSampleItem.setEnabled(true);
              return;
          }
        }
      }
    });

    wSamplesTree.setMenu(sampleMenu);

  }

  private void openSample(TreeItem item) {

    File f = (File) item.getData("file");
    if (f != null && f.isFile()) {

      SampleType type = (SampleType) item.getData("type");
      switch (type) {

        case SCRIPT:
          try {
            // add a new sample script
            String title = RubyScriptMeta.getUniqueName(item.getText(), collectScripts());

            RubyScriptMeta script = new RubyScriptMeta(title, FileUtils.readFileToString(f, "UTF-8"), Role.LIB_SCRIPT);
            addScriptTab(script);
            wScriptsFolder.setSelection(wScriptsFolder.getItemCount() - 1); // select newest tab

            input.setChanged();
            changedInDialog = true;

          } catch (IOException e1) {
            e1.printStackTrace();
          }
          break;
        case TRANS:
          // load the file, but make it forget where it came from (prevents the original from be modified accidentally)
          // File tmpFile = new File(System.getProperty("java.io.tmpdir") + Const.FILE_SEPARATOR + f.getName());
          // FileUtils.copyFile(f, tmpFile);
          // Spoon.getInstance().openFile(tmpFile.toString(), false);
          Spoon.getInstance().openFile(f.toString(), false);

          // assuming that the new one is the last one... a shame openFile does not return the opened TransMeta
          TransMeta[] transForms = Spoon.getInstance().getLoadedTransformations();
          TransMeta newOne = transForms[transForms.length - 1];

          // make it a prestine transformation
          newOne.setFilename(null);
          newOne.setChanged();

          if (changedInDialog) {
            MessageBox messageBox = new MessageBox(shell, SWT.ICON_QUESTION | SWT.NO | SWT.YES);
            messageBox.setText(BaseMessages.getString(PKG, "RubyStepDialog.OpenedSample.Title"));
            messageBox.setMessage(BaseMessages.getString(PKG, "RubyStepDialog.OpenedSample.Message"));
            if (messageBox.open() == SWT.YES) {
              ok();
            }
          } else {
            cancel();
          }

          break;
        case WEB:
          try {
            BareBonesBrowserLaunch.openURL(f.toURI().toURL().toString());
          } catch (MalformedURLException e) {
            e.printStackTrace();
          }
          break;
      }

    }

  }

  private void styleTabFolder(CTabFolder folder) {
    Display display = folder.getDisplay();

    folder.setSelectionBackground(new Color[]{
        display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND),
        display.getSystemColor(SWT.COLOR_TITLE_BACKGROUND_GRADIENT)
      },
      new int[]{75}, true);
    folder.setBackground(display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
  }

  private void addScriptPopupMenu() {

    scriptMenu = new Menu(shell, SWT.POP_UP);

    MenuItem addNewItem = new MenuItem(scriptMenu, SWT.PUSH);
    addNewItem.setText(BaseMessages.getString(PKG, "RubyStepDialog.Menu.AddNewTab"));
    addNewItem.setImage(addImage);
    addNewItem.addListener(SWT.Selection, new Listener() {
      public void handleEvent(Event e) {
        RubyScriptMeta script = RubyScriptMeta.createScriptWithUniqueName(collectScripts());
        addScriptTab(script);
        input.setChanged();
        changedInDialog = true;

        wScriptsFolder.setSelection(wScriptsFolder.getItemCount() - 1); // select newest tab

      }
    });
    new MenuItem(scriptMenu, SWT.SEPARATOR);

    renameItem = new MenuItem(scriptMenu, SWT.PUSH);
    renameItem.setText(BaseMessages.getString(PKG, "RubyStepDialog.Menu.RenameScript"));
    renameItem.setImage(renameImage);
    renameItem.addListener(SWT.Selection, new Listener() {
      public void handleEvent(Event e) {
        RubyStepRenameDialog d = new RubyStepRenameDialog(shell);
        String newName = d.open(wScriptsFolder.getSelection().getText(), collectInactiveScriptNames());
        if (newName != null) {
          wScriptsFolder.getSelection().setText(newName);
          input.setChanged();
          changedInDialog = true;
        }
      }
    });
    new MenuItem(scriptMenu, SWT.SEPARATOR);

    rowScriptItem = new MenuItem(scriptMenu, SWT.RADIO);
    rowScriptItem.setText(BaseMessages.getString(PKG, "RubyStepDialog.Menu.RowScript"));
    rowScriptItem.setImage(rowScriptImage);
    rowScriptItem.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        MenuItem item = (MenuItem) e.widget;
        if (item.getSelection()) {
          setActiveTabRowScript();
        }
      }
    });

    libScriptItem = new MenuItem(scriptMenu, SWT.RADIO);
    libScriptItem.setText(BaseMessages.getString(PKG, "RubyStepDialog.Menu.LibScript"));
    libScriptItem.setImage(libScriptImage);
    libScriptItem.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        MenuItem item = (MenuItem) e.widget;
        if (item.getSelection()) {
          setActiveTabLibScript();
        }
      }
    });

    initScriptItem = new MenuItem(scriptMenu, SWT.RADIO);
    initScriptItem.setText(BaseMessages.getString(PKG, "RubyStepDialog.Menu.InitScript"));
    initScriptItem.setImage(initScriptImage);
    initScriptItem.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        MenuItem item = (MenuItem) e.widget;
        if (item.getSelection()) {
          setActiveTabInitScript();
        }
      }
    });

    disposeScriptItem = new MenuItem(scriptMenu, SWT.RADIO);
    disposeScriptItem.setText(BaseMessages.getString(PKG, "RubyStepDialog.Menu.DisposeScript"));
    disposeScriptItem.setImage(disposeScriptImage);
    disposeScriptItem.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        MenuItem item = (MenuItem) e.widget;
        if (item.getSelection()) {
          setActiveTabDisposeScript();
        }
      }
    });

    wScriptsFolder.setMenu(scriptMenu);

    // menu is about to show, let's enable/disable stuff
    scriptMenu.addListener(SWT.Show, new Listener() {

      @Override
      public void handleEvent(Event e) {
        // set the label on the rename item
        renameItem.setText(BaseMessages.getString(PKG, "RubyStepDialog.Menu.RenameScript", wScriptsFolder.getSelection().getText()));

        // set selection for the correct role
        rowScriptItem.setSelection(false);
        libScriptItem.setSelection(false);
        initScriptItem.setSelection(false);
        disposeScriptItem.setSelection(false);

        switch ((Role) wScriptsFolder.getSelection().getData("role")) {
          case LIB_SCRIPT:
            libScriptItem.setSelection(true);
            break;
          case ROW_SCRIPT:
            rowScriptItem.setSelection(true);
            break;
          case INIT_SCRIPT:
            initScriptItem.setSelection(true);
            break;
          case DISPOSE_SCRIPT:
            disposeScriptItem.setSelection(true);
            break;
        }

      }

    });

  }

  protected void setActiveTabRowScript() {

    CTabItem[] items = wScriptsFolder.getItems();
    for (int i = 0; i < items.length; i++) {
      if (i != wScriptsFolder.getSelectionIndex() && items[i].getData("role") == Role.ROW_SCRIPT) {
        // the old script gets its row script status revoked, simply replaced with lib
        items[i].setData("role", Role.LIB_SCRIPT);
        items[i].setImage(libScriptImage);
      }
    }

    wScriptsFolder.getSelection().setData("role", Role.ROW_SCRIPT);
    wScriptsFolder.getSelection().setImage(rowScriptImage);
    input.setChanged();
    changedInDialog = true;

  }

  protected void setActiveTabInitScript() {

    CTabItem[] items = wScriptsFolder.getItems();
    for (int i = 0; i < items.length; i++) {
      if (i != wScriptsFolder.getSelectionIndex() && items[i].getData("role") == Role.INIT_SCRIPT) {
        // the old script gets its init script status revoked, simply replaced with lib
        items[i].setData("role", Role.LIB_SCRIPT);
        items[i].setImage(libScriptImage);
      }
    }

    wScriptsFolder.getSelection().setData("role", Role.INIT_SCRIPT);
    wScriptsFolder.getSelection().setImage(initScriptImage);
    input.setChanged();
    changedInDialog = true;

  }

  protected void setActiveTabDisposeScript() {

    CTabItem[] items = wScriptsFolder.getItems();
    for (int i = 0; i < items.length; i++) {
      if (i != wScriptsFolder.getSelectionIndex() && items[i].getData("role") == Role.DISPOSE_SCRIPT) {
        // the old script gets its dispose script status revoked, simply replaced with lib
        items[i].setData("role", Role.LIB_SCRIPT);
        items[i].setImage(libScriptImage);
      }
    }

    wScriptsFolder.getSelection().setData("role", Role.DISPOSE_SCRIPT);
    wScriptsFolder.getSelection().setImage(disposeScriptImage);
    input.setChanged();
    changedInDialog = true;

  }

  protected void setActiveTabLibScript() {

    wScriptsFolder.getSelection().setData("role", Role.LIB_SCRIPT);
    wScriptsFolder.getSelection().setImage(libScriptImage);
    input.setChanged();
    changedInDialog = true;

  }

  protected List collectInactiveScriptNames() {

    List retval = new ArrayList(wScriptsFolder.getItemCount());
    CTabItem[] items = wScriptsFolder.getItems();
    for (int i = 0; i < items.length; i++) {
      if (i != wScriptsFolder.getSelectionIndex()) {
        retval.add(items[i].getText());
      }
    }
    return retval;

  }

  private void addScriptTab(RubyScriptMeta script) {

    CTabItem item = new CTabItem(wScriptsFolder, SWT.CLOSE);
    item.setText(script.getTitle());

    switch (script.getRole()) {
      case DISPOSE_SCRIPT:
        item.setImage(disposeScriptImage);
        break;
      case INIT_SCRIPT:
        item.setImage(initScriptImage);
        break;
      case LIB_SCRIPT:
        item.setImage(libScriptImage);
        break;
      case ROW_SCRIPT:
        item.setImage(rowScriptImage);
        break;
    }

    item.setData("role", script.getRole());

    StyledTextComp wScript = new StyledTextComp(transMeta, item.getParent(), SWT.MULTI | SWT.LEFT | SWT.H_SCROLL | SWT.V_SCROLL, script.getTitle(), false);
    wScript.setText(script.getScript());

    props.setLook(wScript, Props.WIDGET_STYLE_FIXED);
    wScript.addModifyListener(lsMod);
    item.setControl(wScript);
    wScript.addModifyListener(new ModifyListener() {

      @Override
      public void modifyText(ModifyEvent e) {
        hideParseErrors();
        highlightSyntax();
      }
    });

    wScript.addListener(SWT.Show, new Listener() {

        @Override
        public void handleEvent(Event e) {
          highlightSyntax();
          hideParseErrors();
        }
      }
    );

    wScript.addKeyListener(new KeyAdapter() {
      public void keyPressed(KeyEvent e) {
        updateEditingPosition();

      }

      public void keyReleased(KeyEvent e) {
        updateEditingPosition();
      }
    });
    wScript.addFocusListener(new FocusAdapter() {
      public void focusGained(FocusEvent e) {
        updateEditingPosition();
      }

      public void focusLost(FocusEvent e) {
        updateEditingPosition();
      }
    });
    wScript.addMouseListener(new MouseAdapter() {
      public void mouseDoubleClick(MouseEvent e) {
        updateEditingPosition();
      }

      public void mouseDown(MouseEvent e) {
        updateEditingPosition();
      }

      public void mouseUp(MouseEvent e) {
        updateEditingPosition();
      }
    });

    wScript.getStyledText().addMouseTrackListener(new MouseTrackListener() {

      @Override
      public void mouseHover(MouseEvent e) {
        updateErrorToolTip(e.x, e.y);
      }

      @Override
      public void mouseExit(MouseEvent arg0) {
      }

      @Override
      public void mouseEnter(MouseEvent arg0) {
      }
    });

  }

  protected void updateErrorToolTip(int x, int y) {

    CTabItem item = wScriptsFolder.getSelection();
    StyledTextComp wText = (StyledTextComp) item.getControl();

    parseErrorHelper.updateErrorToolTip(wText, x, y);

  }

  private void updateEditingPosition() {

    CTabItem item = wScriptsFolder.getSelection();
    StyledTextComp wText = (StyledTextComp) item.getControl();

    // calculate line and col
    int caret = wText.getCaretOffset();
    int line = wText.getLineAtOffset(caret) + 1;
    int col = 1;

    String txt = wText.getText();
    caret -= 1;
    while (caret >= 0) {
      if (txt.charAt(caret) != '\n' && txt.charAt(caret) != '\r') {
        caret--;
        col++;
      } else {
        break;
      }
    }

    wlEditingPosition.setText("" + line + " : " + col);
  }

  private void highlightSyntax() {
    CTabItem item = wScriptsFolder.getSelection();
    StyledTextComp wText = (StyledTextComp) item.getControl();
    syntaxHighlighter.highlight(item.getText(), wText);
  }

  private boolean checkForParseErrors() {

    boolean hasErrors = false;

    for (CTabItem item : wScriptsFolder.getItems()) {
      StyledTextComp wText = (StyledTextComp) item.getControl();
      if (parseErrorHelper.hasParseErrors(wText)) {
        wScriptsFolder.setSelection(item);
        parseErrorHelper.showParseErrors(wText, wlSyntaxCheck);
        hasErrors = true;
        break;
      }
    }

    if (!hasErrors) {
      wlSyntaxCheck.setText("OK");
    }
    return !hasErrors;
  }

  private void hideParseErrors() {
    CTabItem item = wScriptsFolder.getSelection();
    StyledTextComp wText = (StyledTextComp) item.getControl();
    parseErrorHelper.hideParseErrors(wText, wlSyntaxCheck);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy