Package net.pleso.framework.client.ui.custom

Source Code of net.pleso.framework.client.ui.custom.CustomFormWindow

/*
* Copyright 2007 Pleso.net
*
* Licensed under the GNU Lesser General Public License, Version 2.1 (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.gnu.org/licenses/lgpl.html
*
* 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 net.pleso.framework.client.ui.custom;

import net.pleso.framework.client.bl.actions.IAddRowExecuter;
import net.pleso.framework.client.bl.actions.IEmptyRowCreator;
import net.pleso.framework.client.bl.actions.IRowLoader;
import net.pleso.framework.client.bl.actions.IUpdateRowExecutor;
import net.pleso.framework.client.bl.auth.AuthorizationProvider;
import net.pleso.framework.client.bl.exceptions.AsyncCallbackFailureException;
import net.pleso.framework.client.bl.forms.IAddForm;
import net.pleso.framework.client.bl.forms.IEditForm;
import net.pleso.framework.client.bl.forms.IForm;
import net.pleso.framework.client.bl.forms.IParametersForm;
import net.pleso.framework.client.bl.forms.IViewForm;
import net.pleso.framework.client.bl.forms.items.IFormItemsGroup;
import net.pleso.framework.client.bl.providers.IAddFormProvider;
import net.pleso.framework.client.dal.IDataRow;
import net.pleso.framework.client.localization.FrameworkLocale;
import net.pleso.framework.client.ui.custom.controls.ActionButtonPanel;
import net.pleso.framework.client.ui.custom.controls.FormItemsGroupWidget;
import net.pleso.framework.client.ui.custom.controls.FormItemsGroupWidgetCollection;
import net.pleso.framework.client.ui.custom.controls.data.ActionSliderControl;
import net.pleso.framework.client.ui.interfaces.IBindableDataControl;
import net.pleso.framework.client.ui.interfaces.IFocusControl;
import net.pleso.framework.client.ui.interfaces.IUpdateControlListener;
import net.pleso.framework.client.ui.windows.Slider;
import net.pleso.framework.client.ui.windows.Window;

import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.KeyboardListener;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

/**
* Customizable form for any type of forms (see {@link CustomFormType}). Builds
* action controls (text, numbers, enums, etc.), action sliders (search, edit,
* insert, etc.) and buttons by business-logic interfaces.
*
* <h3>CSS Style Rules</h3>
* <ul>
* <li>.pf-custom-form { custom form itself }</li>
* <li>.pf-custom-form-header { header text of custom form )</li>
* <li>.pf-custom-form-status { status text of custom form }</li>
* <li>.pf-custom-form-datagroups-grid {grid with form items groups}</li>
* <li>.pf-custom-form-datagroups-grid-cell {each cell in grid with form items
* groups}</li>
* </ul>
*
*
* @author Scater
*
*/
public class CustomFormWindow extends Window implements ClickListener,
    IUpdateControlListener, KeyboardListener {

  /**
   * Main widget of this composite widget
   */
  private VerticalPanel panel = new VerticalPanel();

  /**
   * Grid with form items groups
   */
  private Grid formItemsGroupsGrid = new Grid();

  /**
   * Panel with buttons
   */
  private ActionButtonPanel buttonsPanel = new ActionButtonPanel(
      HorizontalPanel.ALIGN_RIGHT);

  /**
   * Default cancel button (closes form without changes)
   */
  private Button btnCancel = new Button(FrameworkLocale.constants()
      .cancel_button_caption(), this);

  /**
   * Default ok button (comfirm user input)
   */
  private Button btnOk = new Button(FrameworkLocale.constants()
      .ok_button_caption(), this);

  /**
   * Default reset button (resets form to initial state)
   */
  private Button btnReset = new Button(FrameworkLocale.constants()
      .reset_button_caption(), this);

  /**
   * Default "save and add" button. Used in insert forms. Workes like Ok
   * button, but after that opens new insert form. Convenient when user
   * inserts many values into single data source.
   */
  private Button btnSaveAndAdd = new Button(FrameworkLocale.constants()
      .save_and_add_button_caption(), this);

  /**
   * Current data row in form
   */
  private IDataRow dataRow;

  private FormItemsGroupWidgetCollection groupWidgets = null;

  private IForm form = null;

  private IAddForm addForm = null;

  private IEditForm editForm = null;

  private IParametersForm searchForm = null;

  private IViewForm viewForm = null;

  private Boolean canceled = null;

  /**
   * Indicates that data in form was changed
   */
  private boolean dataChanged = false;

  /**
   * Status text of custom form
   */
  private HTML status = new HTML();

  /**
   * Header of custom form
   */
  private HTML header = new HTML();

  /**
   * Save data callback handler
   *
   * @author Scater
   */
  private class SaveDataAsyncCallback implements AsyncCallback {

    /**
     * Form will be shown next after this form
     */
    private IForm nextForm;

    /**
     * Constructor
     *
     * @param nextForm
     *            form will be shown next after this form
     */
    public SaveDataAsyncCallback(IForm nextForm) {
      this.nextForm = nextForm;
    }

    /*
     * (non-Javadoc)
     *
     * @see com.google.gwt.user.client.rpc.AsyncCallback#onFailure(java.lang.Throwable)
     */
    public void onFailure(Throwable caught) {
      if (!isHidden()) {
        status.setText(FrameworkLocale.messages().error());
        setButtonsEnable(true);
        throw new AsyncCallbackFailureException(FrameworkLocale
            .messages().asyncerror_save(), caught);
      }
    }

    /*
     * (non-Javadoc)
     *
     * @see com.google.gwt.user.client.rpc.AsyncCallback#onSuccess(java.lang.Object)
     */
    public void onSuccess(Object result) {
      if (!isHidden()) {
        // Fix that something changed. This should be able to reload
        // reference book.
        dataChanged = true;

        // If data was saved and handler must show next form
        if (this.nextForm != null) {
          // reset status
          status.setText("");
          // build next form
          buildForm(this.nextForm);
          setButtonsEnable(true);
        } else
          // If next form is not avaliable - only close this form
          hideWindow();
      }
    }
  }

  /**
   * Constructor for {@link IAddForm}
   *
   * @param parentSlider
   *            slider to show form in it
   * @param form
   *            {@link IAddForm} instance
   */
  public CustomFormWindow(Slider parentSlider, IAddForm form) {
    this(parentSlider, (IForm) form);
  }

  /**
   * Constructor for {@link IEditForm}
   *
   * @param parentSlider
   *            slider to show form in it
   * @param form
   *            {@link IEditForm} instance
   */
  public CustomFormWindow(Slider parentSlider, IEditForm form) {
    this(parentSlider, (IForm) form);
  }

  /**
   * Constructor for {@link IParametersForm}
   *
   * @param parentSlider
   *            slider to show form in it
   * @param form
   *            {@link IParametersForm} instance
   */
  public CustomFormWindow(Slider parentSlider, IParametersForm form) {
    this(parentSlider, (IForm) form);
  }

  /**
   * Constructor for {@link IViewForm}
   *
   * @param parentSlider
   *            slider to show form in it
   * @param form
   *            {@link IViewForm} instance
   */
  public CustomFormWindow(Slider parentSlider, IViewForm form) {
    this(parentSlider, (IForm) form);
  }

  /**
   * Universal constructor
   *
   * @param parentSlider
   *            slider to show form in it
   * @param form
   *            {@link IForm} instance
   */
  private CustomFormWindow(Slider parentSlider, IForm form) {
    super(parentSlider);
   
    if (form == null)
      throw new IllegalArgumentException("Form can`t be null.");

    this.form = form;

    // Add header
    this.panel.add(header);
    this.header.setStyleName("pf-custom-form-header");

    // Add status text widget
    this.panel.add(status);
    this.status.setStyleName("pf-custom-form-status");

    // Add grid for data groups
    this.panel.add(formItemsGroupsGrid);
    this.formItemsGroupsGrid.setStyleName("pf-custom-form-datagroups-grid");

    // init composite widget
    initWidget(this.panel);
    this.setStyleName("pf-custom-form");
  }

  /**
   * Adds button panel into main widget if it was not added
   */
  private void addButtonsPanel() {
    // if panel with buttons is not added
    if (this.panel.getWidgetIndex(buttonsPanel) == -1) {
      this.panel.add(buttonsPanel);
    }
  }

  /**
   * Sets enabled for action buttons
   *
   * @param enabled
   *            is enabled
   */
  private void setButtonsEnable(boolean enabled) {
    this.btnOk.setEnabled(enabled);
    this.btnSaveAndAdd.setEnabled(enabled);
    this.btnReset.setEnabled(enabled);
  }

  /**
   * Build panel with buttons
   */
  private void buildButtonsPanel() {
    this.buttonsPanel.clear();

    if (form instanceof IAddFormProvider) {
      // check authorization
      if (AuthorizationProvider
          .isObjectAuthorized(((IAddFormProvider) form)
              .getAddForm(null)))
        buttonsPanel.add(this.btnSaveAndAdd);
    }

    if (this.searchForm != null || this.addForm != null
        || this.editForm != null)
      this.buttonsPanel.add(this.btnOk);

    this.buttonsPanel.add(this.btnReset);
    this.buttonsPanel.add(this.btnCancel);
  }

  /**
   * Builds form by {@link IForm}
   *
   * @param form
   *            {@link IForm} instance
   */
  private void buildForm(IForm form) {
    if (form == null)
      throw new IllegalArgumentException("Form can`t be null.");

    this.form = form;

    // check possible interface implementation
    if (form instanceof IAddForm)
      this.addForm = (IAddForm) form;
    else
      this.addForm = null;

    if (form instanceof IEditForm)
      this.editForm = (IEditForm) form;
    else
      this.editForm = null;

    if (form instanceof IParametersForm)
      this.searchForm = (IParametersForm) form;
    else
      this.searchForm = null;

    if (form instanceof IViewForm)
      this.viewForm = (IViewForm) form;
    else
      this.viewForm = null;

    // Sets header and form caption
    this.header.setText(this.form.getCaption());
    this.setCaption(this.form.getCaption());

    // Builds button panel
    buildButtonsPanel();

    // Build form depending on implemented interfaces
    if (this.addForm != null) {
      createFormForEmptyRow(this.addForm);
    } else if (this.editForm != null) {
      createFormForExistingRow(this.editForm);
    } else if (this.searchForm != null) {
      createFormForEmptyRow(this.searchForm);
    } else if (this.viewForm != null) {
      createFormForExistingRow(this.viewForm);
    }
  }

  /**
   * Builds form for empty row.
   *
   * @param rowCreator
   *            {@link IEmptyRowCreator} instance for creating empty row
   */
  private void createFormForEmptyRow(IEmptyRowCreator rowCreator) {
    this.dataRow = null;

    if (rowCreator != null)
      this.dataRow = rowCreator.createEmptyRow();

    if (this.dataRow == null)
      throw new NullPointerException(FrameworkLocale.messages()
          .error_emptyrowisnull());

    buildFormItemsGroups();
    addButtonsPanel();

    // size of form is changed - scroll it to top
    scrollToTop();
  }

  /**
   * Builds form for existing row (for edit forms). Loads row from
   * asynchronous {@link IRowLoader} instance.
   *
   * @param rowLoader
   *            {@link IRowLoader} instance
   */
  private void createFormForExistingRow(IRowLoader rowLoader) {
    // Set "loading" status
    status.setText(FrameworkLocale.messages().loading());
    setButtonsEnable(false);

    // Loading data row
    rowLoader.GetData(new AsyncCallback() {

      public void onFailure(Throwable caught) {
        if (!isHidden()) {
          status.setText(FrameworkLocale.messages().error());
          throw new AsyncCallbackFailureException(FrameworkLocale
              .messages().asyncerror_loadrow(), caught);
        }
      }

      public void onSuccess(Object result) {
        if (!isHidden()) {
          // Sets result row
          dataRow = (IDataRow) result;
          // Reset status
          status.setText("");
          buildFormItemsGroups();
          setButtonsEnable(true);
          addButtonsPanel();
          // size of form is changed - scroll it to top
          scrollToTop();
          focusOnFirstControl();
        }
      }
    });
  }

  /**
   * Builds form item groups by {@link IFormItemsGroup} array in form
   */
  private void buildFormItemsGroups() {
    // Removing all sliders.
    for (int i = 0; i < this.panel.getWidgetCount();)
      if (this.panel.getWidget(i) instanceof ActionSliderControl)
        this.panel.remove(i);
      else
        i++;

    // Clears grid with groups
    this.formItemsGroupsGrid.clear();
    // Gets groups array from form
    IFormItemsGroup[] groups = form.getGroups();
    // create
    this.groupWidgets = new FormItemsGroupWidgetCollection();
    // check data row
    if (this.dataRow == null)
      throw new NullPointerException(FrameworkLocale.messages()
          .error_rowisnull());
    // Builds groups in group grid:
    for (int i = 0; i < groups.length; i++) {
      if (groups[i] == null)
        throw new IllegalArgumentException("Group cann`t be null.");

      // Create FormItemsGroupWidget for this group.
      FormItemsGroupWidget groupWidget = new FormItemsGroupWidget(this,
          this, this, this.panel);
      groupWidget.initFormItemsGroup(groups[i], this.dataRow);
      this.groupWidgets.addFormItemsGroupWidget(groupWidget);

      // Adds group to grid
      if (i == 0) {
        // if this is first group, than create first row
        this.formItemsGroupsGrid.resize(1, 2);
        this.formItemsGroupsGrid.getCellFormatter().setStyleName(0, 0,
            "pf-custom-form-datagroups-grid-cell");
        // sets into first cell of first row current group widget
        this.formItemsGroupsGrid.setWidget(0, 0, groupWidget);
      } else {
        if ((i % 2) > 0) {
          // If index of group in not paired number (widget must be on
          // right), then
          // put group widget into right cell (grid row must be exists
          // already).
          this.formItemsGroupsGrid.setWidget(this.formItemsGroupsGrid
              .getRowCount() - 1, 1, groupWidget);
          this.formItemsGroupsGrid.getCellFormatter().setStyleName(
              this.formItemsGroupsGrid.getRowCount() - 1, 1,
              "pf-custom-form-datagroups-grid-cell");
        } else {
          // If index of group in paired number (widget must be on
          // left), then
          // create new row in grid.
          this.formItemsGroupsGrid
              .resizeRows(this.formItemsGroupsGrid.getRowCount() + 1);
          // put group widget into left cell of new row
          this.formItemsGroupsGrid.setWidget(this.formItemsGroupsGrid
              .getRowCount() - 1, 0, groupWidget);
          this.formItemsGroupsGrid.getCellFormatter().setStyleName(
              this.formItemsGroupsGrid.getRowCount() - 1, 0,
              "pf-custom-form-datagroups-grid-cell");
        }
      }
    }
  }

  /**
   * Updates {@link #dataRow} by values from all form controls.
   */
  private void updateAllData() {
    this.groupWidgets.updateAllData();
  }

  /**
   * Forces bindable controls to read binded data.
   */
  private void readReadableControls() {
    this.groupWidgets.reReadBindableControls();
  }

  /*
   * (non-Javadoc)
   *
   * @see com.google.gwt.user.client.ui.ClickListener#onClick(com.google.gwt.user.client.ui.Widget)
   */
  public void onClick(Widget sender) {
    // If cancel button.
    if (sender == this.btnCancel) {
      // Setting cancelled status.
      this.canceled = Boolean.TRUE;
      // Hiding window.
      hideWindow();
    } else
    // If "save" button pressed.
    if (sender == this.btnOk || sender == this.btnSaveAndAdd) {
      // Disabling action buttons.
      setButtonsEnable(false);
      // Setting not cancelled status.
      this.canceled = Boolean.FALSE;
      // If validation us successful.
      if (internalValidate()) {
        // Update form data row by control values.
        updateAllData();

        // If form can create new entity then preparing next form for
        // "save and add new" feature.
        IForm nextForm = null;
        if (sender == this.btnSaveAndAdd
            && this.form instanceof IAddFormProvider)
          nextForm = ((IAddFormProvider) this.form)
              .getAddForm(this.dataRow);

        // Calling appropriate method.
        if (this.addForm != null) {
          insertRow(nextForm);
        } else if (this.editForm != null) {
          editRow(nextForm);
        } else if (this.searchForm != null) {
          // if this was search form then hiding it.
          hideWindow();
        }
      } else {
        // If validation failed enabling buttons to continue user's
        // work.
        setButtonsEnable(true);
      }
    } else
    // If "reset" button pressed then rebuilding form.
    if (sender == this.btnReset) {
      buildForm(this.form);
    }
  }

  /**
   * Inserts row to specified by form data set.
   *
   * @param nextForm
   *            next form to build after operation success. Can be null if no
   *            next form expected.
   */
  private void insertRow(IForm nextForm) {
    // Checking form for implementing required interface.
    if ((form instanceof IAddRowExecuter) == false)
      throw new UnsupportedOperationException(
          "Can't insert row. Form is not implement IAddRowExecuter.");

    // Setting visual "loading" status.
    status.setText(FrameworkLocale.messages().loading());
    // Executing add operation.
    ((IAddRowExecuter) form).addRow(dataRow, new SaveDataAsyncCallback(
        nextForm));
  }

  /**
   * Updates row in specified by form data set.
   *
   * @param nextForm
   *            next form to build after operation success. Can be null if no
   *            next form expected.
   */
  private void editRow(IForm nextForm) {
    // Checking form for implementing required interface.
    if ((form instanceof IUpdateRowExecutor) == false)
      throw new UnsupportedOperationException(
          "Can't update row. Form is not implement IUpdateRowExecutor.");

    // Setting visual "loading" status.
    status.setText(FrameworkLocale.messages().loading());
    // Executing add operation.
    ((IUpdateRowExecutor) form).updateRow(dataRow,
        new SaveDataAsyncCallback(nextForm));
  }

  /**
   * Validates form input controls and returns validation success result.
   *
   * @return <code>true</code> if all form controls' values are valid
   */
  private boolean internalValidate() {
    return this.groupWidgets.validate();
  }

  /*
   * (non-Javadoc)
   *
   * @see net.pleso.framework.client.ui.interfaces.IUpdateControlListener#controlUpdated(net.pleso.framework.client.ui.interfaces.IBindableDataControl)
   */
  public void controlUpdated(IBindableDataControl sender) {
    readReadableControls();
  }

  /**
   * @return a {@link IDataRow} being edited in form
   */
  public IDataRow getDataRow() {
    return dataRow;
  }

  /**
   * @return a {@link CustomFormType} indicating form type
   */
  public CustomFormType getCustomFormType() {

    if (this.addForm != null)
      return CustomFormType.Insert;
    else if (this.editForm != null)
      return CustomFormType.Edit;
    else if (this.searchForm != null)
      return CustomFormType.Search;
    else if (this.viewForm != null)
      return CustomFormType.Show;

    return null;
  }

  /**
   * @return <code>true</code> if form changed data set
   */
  public boolean dataChanged() {
    return this.dataChanged;
  }

  /**
   * @return <code>true</code> if form was closed because of cancel
   *         operation; <code>false</code> if form was closed on user "ok"
   *         button click.
   */
  public boolean isCanceled() {
    if (canceled != null)
      return canceled.booleanValue();
    else
      // If cancelled status not set explicitly than it is cancelled in
      // deed.
      return true;
  }

  /*
   * (non-Javadoc)
   *
   * @see net.pleso.framework.client.ui.windows.Window#hideWindow()
   */
  public void hideWindow() {
    // Hiding all windows in chils sliders.
    this.groupWidgets.hideWindows();

    // If cancelled status not set then setting it to true.
    if (canceled == null)
      canceled = Boolean.TRUE;

    super.hideWindow();
  }

  /**
   * Makes first {@link IFocusControl} controls in all controlls list to be
   * focused.
   */
  private void focusOnFirstControl() {
    if (this.groupWidgets != null)
      this.groupWidgets.focusOnFirstControl();
  }

  /*
   * (non-Javadoc)
   *
   * @see net.pleso.framework.client.ui.windows.Window#showEvent()
   */
  protected void showEvent() {
        // build this form
    buildForm(this.form);
    focusOnFirstControl();
  }

  /*
   * (non-Javadoc)
   *
   * @see com.google.gwt.user.client.ui.KeyboardListener#onKeyDown(com.google.gwt.user.client.ui.Widget,
   *      char, int)
   */
  public void onKeyDown(Widget sender, char keyCode, int modifiers) {
  }

  /*
   * (non-Javadoc)
   *
   * @see com.google.gwt.user.client.ui.KeyboardListener#onKeyPress(com.google.gwt.user.client.ui.Widget,
   *      char, int)
   */
  public void onKeyPress(Widget sender, char keyCode, int modifiers) {
    switch (keyCode) {
    case 13:
    case 10:
      onClick(this.btnOk);
      break;
    case 27:
      onClick(this.btnCancel);
      break;
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see com.google.gwt.user.client.ui.KeyboardListener#onKeyUp(com.google.gwt.user.client.ui.Widget,
   *      char, int)
   */
  public void onKeyUp(Widget sender, char keyCode, int modifiers) {
  }
}
TOP

Related Classes of net.pleso.framework.client.ui.custom.CustomFormWindow

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.