Package org.pentaho.openformula.ui

Source Code of org.pentaho.openformula.ui.DefaultFunctionParameterEditor$FieldSelectorUpdateHandler

/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program 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.
*
* Copyright (c) 2009 Pentaho Corporation.  All rights reserved.
*/

package org.pentaho.openformula.ui;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Locale;
import javax.swing.Box;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.border.EmptyBorder;

import org.pentaho.openformula.ui.model2.FormulaElement;
import org.pentaho.openformula.ui.model2.FunctionInformation;
import org.pentaho.openformula.ui.util.InlineEditTextField;
import org.pentaho.openformula.ui.util.SelectFieldAction;
import org.pentaho.openformula.ui.util.TooltipLabel;
import org.pentaho.reporting.libraries.base.util.StringUtils;
import org.pentaho.reporting.libraries.designtime.swing.BorderlessButton;
import org.pentaho.reporting.libraries.formula.function.FunctionDescription;
import org.pentaho.reporting.libraries.formula.util.FormulaUtil;

public class DefaultFunctionParameterEditor extends JPanel implements FunctionParameterEditor, FieldDefinitionSource
{
  private class FieldSelectorUpdateHandler implements PropertyChangeListener
  {
    private int paramIndex;

    private FieldSelectorUpdateHandler(final int paramIndex)
    {
      this.paramIndex = paramIndex;
    }

    @Override
    public void propertyChange(final PropertyChangeEvent evt)
    {
      final FieldDefinition value = (FieldDefinition) evt.getNewValue();
      //noinspection MagicCharacter,StringConcatenation
      if (value != null)
      {
        final String text = FormulaUtil.quoteReference(value.getName());
        final String parameterValue = getParameterValue(paramIndex);
        final TextFieldHolderStruct fieldStruct = getParameterField(paramIndex);
        final InlineEditTextField field = fieldStruct.getTextFields();

        final StringBuilder b = new StringBuilder(parameterValue);
        // remove the selected content, if any
        b.delete(field.getSelectionStart(), field.getSelectionEnd());
        // then insert the new content at the cursor position
        final int caretPosition = field.getCaretPosition();
        b.insert(caretPosition, text);
        fieldStruct.setText(b.toString());
      }
    }
  }

  private class FocusListenerHandler extends FocusAdapter
  {
    private InlineEditTextField paramTextField;
    private int parameterIndex;

    private FocusListenerHandler(final InlineEditTextField paramTextField, final int parameterIndex)
    {
      this.paramTextField = paramTextField;
      this.parameterIndex = parameterIndex;
    }

    public void focusLost(final FocusEvent e)
    {
      handleFocusChange();
    }

    @Override
    public void focusGained(final FocusEvent e)
    {
      handleFocusChange();
    }

    private void handleFocusChange()
    {
      if (inSetupUpdate)
      {
        return;
      }

      final String s = paramTextField.getText();
      fireParameterUpdate(parameterIndex, s);
    }
  }

  private static class TextFieldHolderStruct
  {
    private InlineEditTextField textFields;
    private SelectFieldAction selectFieldAction;
    private FocusListenerHandler focusHandler;
    private Component[] extraComponents;

    protected TextFieldHolderStruct(final InlineEditTextField textFields,
                                    final SelectFieldAction selectFieldAction,
                                    final FocusListenerHandler focusHandler,
                                    final Component... extraComponents)
    {
      this.textFields = textFields;
      this.selectFieldAction = selectFieldAction;
      this.focusHandler = focusHandler;
      this.extraComponents = extraComponents;
    }

    protected InlineEditTextField getTextFields()
    {
      return textFields;
    }

    public void setText(final String text)
    {
      textFields.setText(text);
      if (text != null)
      {
        textFields.setCaretPosition(text.length());
      }
    }

    public String getText()
    {
      return textFields.getText();
    }

    public void dispose()
    {
      selectFieldAction.dispose();
      textFields.getParent().remove(textFields);
      for (final Component c : extraComponents)
      {
        c.getParent().remove(c);
      }
    }
  }

  public static final int FIELDS_ADD = 2;

  private static final TextFieldHolderStruct[] EMPTY_FIELDS = new TextFieldHolderStruct[0];
  private static final FieldDefinition[] EMPTY_FIELDDEF = new FieldDefinition[0];

  private FunctionDescription selectedFunction;
  private JPanel parameterPane;
  private FieldDefinition[] fields;
  private TextFieldHolderStruct[] textFields;

  private boolean inSetupUpdate;
  private int parameterUpdatedCount;

  /**
   * Creates a new <code>JPanel</code> with a double buffer and a flow layout.
   */
  public DefaultFunctionParameterEditor()
  {
    parameterPane = new JPanel();
    parameterPane.setLayout(new GridBagLayout());

    this.inSetupUpdate = false;
    this.parameterUpdatedCount = -1;

    this.textFields = EMPTY_FIELDS;
    this.fields = EMPTY_FIELDDEF;

    final JPanel parameterPaneCarrier = new JPanel();
    parameterPaneCarrier.setLayout(new BorderLayout());
    parameterPaneCarrier.add(parameterPane, BorderLayout.NORTH);

    final JScrollPane comp = new JScrollPane(parameterPaneCarrier);
    comp.setBorder(new EmptyBorder(0, 0, 0, 0));
    comp.setViewportBorder(new EmptyBorder(0, 0, 0, 0));

    setLayout(new CardLayout());
    add("2", comp);
    add("1", Box.createRigidArea(new Dimension(650, 250)));
  }

  public FunctionDescription getSelectedFunction()
  {
    return selectedFunction;
  }

  @Override
  public void clearSelectedFunction()
  {
    setSelectedFunction(new FunctionParameterContext());
  }


  /**
   * Determines whether the current context formula is the main one (the first
   * formula following the '=').  So '=COUNT(1;SUM(1;2;3))', COUNT would be
   * the main formula.  If context points to SUM then we return false.
   *
   * @param context
   * @return - true if the context points to the left most outer formula.
   */
  public boolean isMainFormula(final FunctionParameterContext context)
  {
    final FormulaEditorModel editorModel = context.getEditorModel();
    if ((editorModel == null) || (editorModel.getLength() < 1))
    {
      return true;
    }

    final FormulaElement mainFormulaElement = editorModel.getFormulaElementAt(1);
    final FunctionInformation currentFunction = editorModel.getCurrentFunction();
    if ((mainFormulaElement != null) && (currentFunction != null) && (currentFunction.getFunctionOffset() == 1) &&
        (mainFormulaElement.getText().equals(currentFunction.getCanonicalName())))
    {
      return true;
    }
    else
    {
      return false;
    }
  }


  /**
   * If user is typing in formula text-area, this method updates the appropriate parameter
   * field.  Note that the parameter fields are not always visible so if they are not visible
   * then return false.  Note that when user is typing in formula text-area and they are typing
   * an embedded formula, the parameter fields for that embedded formula don't get displayed.
   * They get displayed if user points cursor over the formula or arrows over the formula -
   * just not when typing.
   *
   * @param context
   * @return
   */
  private boolean updateCurrentParameterField(final FunctionParameterContext context)
  {
    final FunctionDescription selectedFunction = context.getFunction();
    final String[] parameterValues = context.getParameterValues();

    // Iterate over each parameter field looking to find the field associated with
    // the embedded formula.  If we find it, build up the formula in parameter field
    // to reflect what was typed into the formula text-area
    for (int i = 0; i < textFields.length; i++)
    {
      final String parameterValue = textFields[i].getText();
      if ((parameterValue != null) && (parameterValue.startsWith(selectedFunction.getCanonicalName()) == true))
      {
        String updatedFormula = selectedFunction.getCanonicalName() + "(";
        for (int paramIndex = 0; paramIndex < parameterValues.length; paramIndex++)
        {
          if (parameterValues[paramIndex] != null)
          {
            updatedFormula = updatedFormula + parameterValues[paramIndex];
            updatedFormula += ";";
          }
        }

        // Remove the trailing semicolon
        if (updatedFormula.endsWith(";"))
        {
          updatedFormula = updatedFormula.substring(0, updatedFormula.length() - 1);
        }

        if (parameterValue.endsWith(")"))
        {
          updatedFormula += ")";
        }

        textFields[i].setText(updatedFormula);
        return true;
      }
    }

    // We did not find the corresponding parameter field as it is not being displayed
    return false;
  }

  private void updateParameterFields(final String[] parameterValues)
  {
    if (parameterValues != null && parameterValues.length <= textFields.length)
    {
      for (int i = 0; i < parameterValues.length; i++)
      {
        final String string = parameterValues[i];
        if (textFields[i] != null)
        {
          textFields[i].setText(string);
        }
      }
    }
  }

  @Override
  public void setSelectedFunction(final FunctionParameterContext context)
  {
    try
    {
      inSetupUpdate = true;

      final FunctionDescription fnDesc = context.getFunction();

      //this is empty function?
      if (fnDesc == null)
      {
        for (int i = 0; i < textFields.length; i++)
        {
          textFields[i].dispose();
        }

        this.textFields = EMPTY_FIELDS;
        return;
      }

      final boolean functionChanged = (selectedFunction != fnDesc);
      this.selectedFunction = fnDesc;

      //currently editing one
      final String[] parameterValues = context.getParameterValues();
      final String[] parameterFieldValues =
          getParametersValues(context.getFunctionInformation(), context.getFunction());

      //recreate whole text fields
      if (functionChanged)
      {
        parameterPane.removeAll();

        this.textFields = new TextFieldHolderStruct[parameterFieldValues.length];
        final int fieldFocus = Math.max(0, parameterUpdatedCount);
        for (int i = 0; i < parameterFieldValues.length; i++)
        {
          this.textFields[i] = addTextField(parameterFieldValues[i], i, (i == fieldFocus));
        }
      }
      else if (textFields.length != parameterFieldValues.length)
      {
        final TextFieldHolderStruct[] oldTextFields = this.textFields;
        this.textFields = new TextFieldHolderStruct[parameterFieldValues.length];
        System.arraycopy(oldTextFields, 0, textFields, 0, Math.min(oldTextFields.length, textFields.length));
        final int fieldFocus = Math.max(0, parameterUpdatedCount);
        for (int i = parameterFieldValues.length; i < oldTextFields.length; i++)
        {
          oldTextFields[i].dispose();
        }
        for (int i = oldTextFields.length; i < parameterFieldValues.length; i++)
        {
          this.textFields[i] = addTextField(parameterFieldValues[i], i, (i == fieldFocus));
        }
      }

      if (isMainFormula(context) == true)
      {
        updateParameterFields(parameterValues);
        //return;
      }
      else
      {
        // If we are in an embedded formula, update the main
        // formula's parameter field that is associated with
        // this embedded formula.
        if (updateCurrentParameterField(context) == false)
        {
          // The parameter field is pointing to the embedded
          // formula - update it
          updateParameterFields(parameterValues);
        }
      }
    }
    finally
    {
      inSetupUpdate = false;
      invalidate();
      revalidate();
      repaint();
    }
  }

  private TextFieldHolderStruct addTextField(final String parameterValue,
                                             final int parameterPosition,
                                             final boolean requestFocus)
  {
    //this value is used to compute field hints.
    final int paramPos = Math.max(0, Math.min(selectedFunction.getParameterCount() - 1, parameterPosition));
    final String displayName = selectedFunction.getParameterDisplayName(paramPos, Locale.getDefault());
    final String description = selectedFunction.getParameterDescription(paramPos, Locale.getDefault());

    final JLabel paramNameLabel = new JLabel(displayName);
    final InlineEditTextField paramTextField = new InlineEditTextField();

    paramTextField.setText(parameterValue);
    if (parameterValue != null)
    {
      paramTextField.setCaretPosition(parameterValue.length());
    }
    paramTextField.setFont
        (new Font(Font.MONOSPACED, paramTextField.getFont().getStyle(), paramTextField.getFont().getSize()));

    final FocusListenerHandler handler = new FocusListenerHandler(paramTextField, parameterPosition);
    paramTextField.addFocusListener(handler);

    if (requestFocus)
    {
      paramTextField.setFocusable(true);
      paramTextField.requestFocusInWindow();
    }

    final SelectFieldAction selectFieldAction =
        new SelectFieldAction(this, new FieldSelectorUpdateHandler(parameterPosition), this);
    // treat insert field as parameter edit
    selectFieldAction.setFocusReturn(paramTextField);

    final BorderlessButton button = new BorderlessButton(selectFieldAction);
    final TooltipLabel tooltipLabel = new TooltipLabel(description);

    GridBagConstraints gbc = new GridBagConstraints();
    gbc.gridx = 0;
    gbc.gridy = parameterPosition;
    gbc.anchor = GridBagConstraints.WEST;
    this.parameterPane.add(paramNameLabel, gbc);

    gbc = new GridBagConstraints();
    gbc.gridx = 2;
    gbc.gridy = parameterPosition;
    gbc.anchor = GridBagConstraints.WEST;
    gbc.weightx = 1;
    gbc.gridwidth = 1;
    gbc.fill = GridBagConstraints.HORIZONTAL;
    this.parameterPane.add(paramTextField, gbc);

    gbc = new GridBagConstraints();
    gbc.gridx = 3;
    gbc.gridy = parameterPosition;
    gbc.anchor = GridBagConstraints.WEST;
    this.parameterPane.add(button, gbc);

    gbc = new GridBagConstraints();
    gbc.gridx = 1;
    gbc.gridy = parameterPosition;
    gbc.anchor = GridBagConstraints.WEST;
    gbc.gridwidth = 1;
    gbc.fill = GridBagConstraints.HORIZONTAL;
    gbc.insets = new Insets(3, 5, 3, 5);
    this.parameterPane.add(tooltipLabel, gbc);

    return new TextFieldHolderStruct(paramTextField, selectFieldAction, handler,
        paramNameLabel, button, tooltipLabel);
  }

  //returns expected number of fields for formula editor
  private static int computeFunctionParameterCount(final FunctionInformation info, final FunctionDescription desc)
  {
    if (!desc.isInfiniteParameterCount())
    {
      return desc.getParameterCount();
    }

    final String[] parameters = info.getParameters();
    int lastNonEmpty = 0;
    for (int i = 0; i < parameters.length; i += 1)
    {
      final String p = parameters[i];
      if (StringUtils.isEmpty(p))
      {
        continue;
      }
      lastNonEmpty = i;
    }

    return Math.max (lastNonEmpty + 1, desc.getParameterCount()) + FIELDS_ADD;
  }

  public String[] getParametersValues(final FunctionInformation fnInfo, final FunctionDescription fnDesc)
  {
    final int paramCount = computeFunctionParameterCount(fnInfo, fnDesc);

    final String[] parameterValues = new String[paramCount];
    final int definedParameterCount = Math.min(fnInfo.getParameterCount(), paramCount);
    for (int i = 0; i < definedParameterCount; i++)
    {
      final String text = fnInfo.getParameterText(i);
      parameterValues[i] = text;
    }

    //if there is more than FIELDS_MAX_NUMBER parameters -
    if (definedParameterCount > 0 && fnInfo.getParameterCount() > paramCount)
    {
      final StringBuilder lastParamEatsAllBuffer = new StringBuilder(100);
      final int lastParamIdx = definedParameterCount - 1;
      for (int i = lastParamIdx; i < fnInfo.getParameterCount(); i++)
      {
        if (i > lastParamIdx)
        {
          lastParamEatsAllBuffer.append(';');
        }
        lastParamEatsAllBuffer.append(fnInfo.getParameterText(i));
      }
      parameterValues[lastParamIdx] = lastParamEatsAllBuffer.toString();
    }

    return parameterValues;
  }

  protected TextFieldHolderStruct getParameterField(final int field)
  {
    return textFields[field];
  }

  public String getParameterValue(final int param)
  {
    return textFields[param].getText();
  }

  @Override
  public void addParameterUpdateListener(final ParameterUpdateListener listener)
  {
    if (listenerList.getListenerCount(ParameterUpdateListener.class) == 0)
    {
      listenerList.add(ParameterUpdateListener.class, listener);
    }
  }

  @Override
  public void removeParameterUpdateListener(final ParameterUpdateListener listener)
  {
    listenerList.remove(ParameterUpdateListener.class, listener);
  }

  protected void fireParameterUpdate(final int param, final String text)
  {
    final boolean catchAllParameter =
        selectedFunction.isInfiniteParameterCount() && (param >= selectedFunction.getParameterCount());
    final ParameterUpdateListener[] updateListeners = listenerList.getListeners(ParameterUpdateListener.class);
    for (int i = 0; i < updateListeners.length; i++)
    {
      final ParameterUpdateListener listener = updateListeners[i];
      listener.parameterUpdated(new ParameterUpdateEvent(this, param, text, catchAllParameter));
    }
  }

  @Override
  public void setFields(final FieldDefinition[] fields)
  {
    this.fields = fields.clone();
  }

  @Override
  public FieldDefinition[] getFields()
  {
    if (fields == null)
    {
      return new FieldDefinition[0];
    }
    return fields.clone();
  }

  @Override
  public Component getEditorComponent()
  {
    return this;
  }
}
TOP

Related Classes of org.pentaho.openformula.ui.DefaultFunctionParameterEditor$FieldSelectorUpdateHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.