----- /* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.ptc.gxt.framework.client.widget; import com.allen_sauer.gwt.log.client.Log; import com.extjs.gxt.ui.client.Registry; import com.extjs.gxt.ui.client.Style; import com.extjs.gxt.ui.client.Style.Scroll; import com.extjs.gxt.ui.client.core.FastMap; import com.extjs.gxt.ui.client.core.XDOM; import com.extjs.gxt.ui.client.data.ModelData; import com.extjs.gxt.ui.client.event.BaseEvent; import com.extjs.gxt.ui.client.event.ComponentEvent; import com.extjs.gxt.ui.client.event.Events; import com.extjs.gxt.ui.client.event.FieldEvent; import com.extjs.gxt.ui.client.event.Listener; import com.extjs.gxt.ui.client.event.MessageBoxEvent; import com.extjs.gxt.ui.client.event.SelectionListener; import com.extjs.gxt.ui.client.store.Store; import com.extjs.gxt.ui.client.store.StoreEvent; import com.extjs.gxt.ui.client.store.StoreListener; import com.extjs.gxt.ui.client.util.KeyNav; import com.extjs.gxt.ui.client.widget.Component; import com.extjs.gxt.ui.client.widget.ContentPanel; import com.extjs.gxt.ui.client.widget.Dialog; import com.extjs.gxt.ui.client.widget.Header; import com.extjs.gxt.ui.client.widget.HorizontalPanel; import com.extjs.gxt.ui.client.widget.Info; import com.extjs.gxt.ui.client.widget.LayoutContainer; import com.extjs.gxt.ui.client.widget.MessageBox; import com.extjs.gxt.ui.client.widget.Viewport; import com.extjs.gxt.ui.client.widget.button.Button; import com.extjs.gxt.ui.client.widget.button.ButtonBar; import com.extjs.gxt.ui.client.widget.form.AdapterField; import com.extjs.gxt.ui.client.widget.form.ComboBox; import com.extjs.gxt.ui.client.widget.form.DateField; import com.extjs.gxt.ui.client.widget.form.DateTimePropertyEditor; import com.extjs.gxt.ui.client.widget.form.Field; import com.extjs.gxt.ui.client.widget.form.FormPanel; import com.extjs.gxt.ui.client.widget.form.FormPanel.LabelAlign; import com.extjs.gxt.ui.client.widget.form.HiddenField; import com.extjs.gxt.ui.client.widget.form.LabelField; import com.extjs.gxt.ui.client.widget.form.NumberField; import com.extjs.gxt.ui.client.widget.form.Radio; import com.extjs.gxt.ui.client.widget.form.RadioGroup; import com.extjs.gxt.ui.client.widget.layout.ColumnData; import com.extjs.gxt.ui.client.widget.layout.ColumnLayout; import com.extjs.gxt.ui.client.widget.layout.FitLayout; import com.extjs.gxt.ui.client.widget.layout.FormData; import com.extjs.gxt.ui.client.widget.layout.FormLayout; import com.extjs.gxt.ui.client.widget.layout.HBoxLayout; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.Widget; import com.ptc.gxt.framework.client.EntryPointFactory; import com.ptc.gxt.framework.client.FormMode; import com.ptc.gxt.framework.client.FormType; import com.ptc.gxt.framework.client.FrameworkEntryPoint; import com.ptc.gxt.framework.client.TaskFactory; import com.ptc.gxt.framework.client.dependency.DependencyComponent; import com.ptc.gxt.framework.client.dependency.DependencyProcessor; import com.ptc.gxt.framework.client.entrypoint.CoreTaskFactory; import com.ptc.gxt.framework.client.event.ConfirmSelectListener; import com.ptc.gxt.framework.client.event.FrameworkEvents; import com.ptc.gxt.framework.client.ie.IEAtt; import com.ptc.gxt.framework.client.ie.IEElement; import com.ptc.gxt.framework.client.ie.IEGroup; import com.ptc.gxt.framework.client.ie.MsgLevel; import com.ptc.gxt.framework.client.ie.ParamMode; import com.ptc.gxt.framework.client.ie.Task; import com.ptc.gxt.framework.client.table.MergedModType; import com.ptc.gxt.framework.client.table.ReturnGroupHandler; import com.ptc.gxt.framework.client.table.TableGroupReturnGroupListener; import com.ptc.gxt.framework.client.widget.tools.DebugToolsMenu; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Defines the page. * The CATFormLayout is wrapped by StandardForm. * It defines a header, a body and footer information. * It operates in several modes, defined by the FormMode enum. * It produces several different types of form, defined by the FormType enum. * It provides standard buttons and actions associated to those buttons to support data acquisition, server update and cancel operations. * It provides for interaction with the launching window (refresh / forward to new urls) * It provides for standard actions when the action task completes (close on success, reset when apply button used, keep page when errors occur) * It provides validation services between fields via a DependencyProcessor tied to the page. * It provides for an initial data task, an action task and optionally, a cancel task. * * It provides for data transfer between in-browser pages via ReturnGroupListener. */ //public class CATFormLayout implements EntryPoint { public class CATFormLayout { private static final Boolean TRUE = true; private static final Boolean FALSE = false; /** *Identifier for use in retrieving the Apply button via getDialog().getButtonById(APPLY); */ public static final String APPLY = "apply"; private Header hdr; private String formName = "-No Form Name Specified-"; private String formLabel = ""; private String helpID = ""; private String helpURL = Registry.get("helpurl"); private ContentPanel container; private EntryPointFactory entryPoint; private String application = "FormLayout"; private Dialog dialog; private Viewport viewport = StandardComponents.getCATViewport(this); private HorizontalPanel buttonPanel; private CATFormPanel bodyPanel; private LayoutContainer hiddenPanel; private WindowMode forcedWindowAction; private ParentMode forcedParentAction; private int width = -1; private int height = -1; private int minWidth = -1; private int minHeight = -1; private int minCWidth = -1; private int minCHeight = -1; // private int formDataWidth = 250; private String oid; private String webAppURL; private FormType formType = FormType.FORM; private FormMode mode; private Map divePanel = new FastMap(); private Component focusField; private boolean hasFormBeenApplied; private boolean clearOnApply; private Task initTask; private IEGroup initTaskGroup; private Task actionTask; private IEGroup actionTaskGroup; private Task cancelTask; private IEGroup cancelTaskGroup; private IEGroup deferGroup; private IEElement initElement; private IEGroup initGroup; private ReturnGroupHandler returnGroupHandler; private DataReturnMode returnMode; // Any objects that need to be shared into inner classes // or get defined in the registry get declared here. // Use the name of the object as the registry entry // This should help with code completion private String messageBox; private String containerOid; private Button finishButton; private Button applyButton; private Button cancelButton; // private Button closeButton; private MessageBox waitBox; private boolean masked; private Map busyMap = new FastMap(); private Map hiddenParamMap; private int sbh = XDOM.getScrollBarWidth() + 12; private int sbw = XDOM.getScrollBarWidth() + 12; private StandardForm page; private List customHiddenFields; private Field currentFocusField; private LayoutContainer statusarea; private LayoutContainer footer; private Button tools; private boolean forceClose; private SelectionListener okSelectionListener; private SelectionListener applySelectionListener; private SelectionListener cancelSelectionListener; private SubmitMode submitMode = SubmitMode.OK; private String taskDataKey = "obid"; private Boolean dirty = false; private IEGroup dependencyjson; private boolean dependencySetup; private DependencyProcessor dependency; private String dependencyDescriptor; private String createdObjectOID; /** * When true, only dirty fields and hidden fields will be sent to edit tasks */ private boolean sendOnlyDirtyOnEdit; /** * The field that will display the widget's field label in the status area */ // private CATLabelField statusAreaLabel; /** * The field that will display the widget's tooltip in the status area */ private CATLabelField statusAreaMessage; /** * The Red Ball image for display between statusAreaLabel and statusAreaMessage */ private Image statusAreaImage; /** * The adapter to wrap statusAreaImage with to insure it displays properly */ private AdapterField statusAreaImgAdapter; /** * This creates a standard form layout for use by a page. It provides a standardized header with a title and a help icon, an OK / Apply / Cancel button set * and a CATFormPanel into which the developer will add the specific Field sub-classes to create the desired form. Once this method is run, the developer * should do a getBodyPanel() and start adding Field objects to the page. * * @param app The application name for this page * @return The form, already set up to take the specific fields. */ public static CATFormLayout newForm(String app) { return newForm(app, FormType.FORM); } /** * Creates a new CATFormLayout for the given EntryPointFactory enum entry. * @param app The EntryPointFactory enum entry for the StandardForm used with this CATFormLayout * @return The new CATFormLayout object */ public static CATFormLayout newForm(EntryPointFactory app) { return newForm(app, FormType.FORM); } /** * Creates a new CATFormLayout for the given app name and sets the form type as indicated. * @param app The name of the app that will resolve via the newEntryPoint method to a StandardForm * @param formtype A FormType of DIALOG, FORM or EMBEDDED * @return The new CATFormLayout object. */ public static CATFormLayout newForm(String app, FormType formtype) { EntryPointFactory epf = FrameworkEntryPoint.getEntryPointFactory(app); if (epf == null) { throw new RuntimeException("No EntryPoint found for application: " + app); } CATFormLayout newform = new CATFormLayout(); newform.setup(epf, formtype); return newform; } /** * Creates a new Form using an EntryPointFactory handle and FormType. * @param app The EntryPointFactory enum value for the desired page. * @param formtype The type of dialog being created (DIALOG, FORM, or EMBEDDED). * @return A CATFormLayout ready for use by setupLayout. */ public static CATFormLayout newForm(EntryPointFactory app, FormType formtype) { EntryPointFactory epf = app; if (epf == null) { throw new RuntimeException("No EntryPoint found for application: " + app); } CATFormLayout newform = new CATFormLayout(); newform.setup(epf, formtype); return newform; } // @Override /** * Creates the basic shell of the page * @param epf An EntryPointFactory enum value. * @param formtype The form type to be used for the form (@see FormType). */ public void setup(EntryPointFactory epf, FormType formtype) { Log.debug("FormLayout :onModuleLoad: start"); Log.debug("FormLayout : webappurl: " + Registry.get("webappurl")); Log.debug("Launching URL: " + com.google.gwt.user.client.Window.Location.getHref()); Log.debug("oid for this UI page: " + oid); Log.debug("containerOid for this UI page: " + containerOid); setEntryPoint(epf); setFormType(formtype); setMode(epf.getMode()); setFormName(epf.getName()); setHelpID(epf.getHelpID()); setApplication(epf.getName()); setMinHeight(epf.getHeight()); setMinWidth(epf.getWidth()); setWebAppURL((String) Registry.get("webappurl")); //TODO: Don't think this line is doing anything - nobody seems to set the registry entry it's looking for //setHiddenParamMap((Map) Registry.get(app + "_paramMap")); // For Creating Basic Container which is used to add all components.. container = new ContentPanel(); Log.trace("CATFormLayout container isMonitorWindowResize: " + container.isMonitorWindowResize()); container.setLayout(new FitLayout()); if (FormType.EMBEDDED != formType) { container.addStyleName("cat-background"); container.setId("background"); getViewport().removeStyleName("cat-background"); } CATFormPanel fp = getBodyPanel(); fp.setMethod(FormPanel.Method.POST); fp.setLabelAlign(LabelAlign.RIGHT); fp.setLabelWidth(150); fp.setHeaderVisible(false); fp.getHeader().setText(""); } /** * Creates a task with a taskoutgroupname generated from the taskuri. It will never re-use an existing instance of a task with a matching taskuri. * * @param task The TaskFactory enum entry of the task to be run * @return The configured task (also available via initFormTask) */ public Task setupInitTask(TaskFactory task) { return setupInitTask(task, null, false); } /** * Creates a task with a taskoutgroupname generated from the taskuri. When the useCache is true, it will re-use an already configured task with the same * uri. * * @param task The TaskFactory enum entry of the task to be run * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via initFormTask) */ public Task setupInitTask(TaskFactory task, boolean useCache) { return setupInitTask(task, null, useCache); } /** * Creates a task with a taskoutgroupname as specified (null means it will generate it from the taskuri). When the useCache is true, it will re-use an * already configured task with the same uri. * * @param task The TaskFactory enum entry of the task to be run * @param group The name of the group that will be returned by the task * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via initFormTask) */ public Task setupInitTask(TaskFactory task, String group, boolean useCache) { String taskuri = task.getTaskUri(); if (taskuri == null) { StandardComponents.alert(this, MsgLevel.FATAL, "Initializer task uri was not specified"); return null; } initTask = Task.newTask(task, group, useCache); String workgroup = group; if (group == null || group.isEmpty()) { workgroup = getInitTask().getTaskGroupOutName(); } initTaskGroup = getInitTask().getGroup(workgroup); final DefaultInitTaskListener initTaskListener = new DefaultInitTaskListener(); getInitTaskGroup().addStoreListener(initTaskListener); getInitTaskGroup().addListener(Store.Sort, new Listener() { @Override public void handleEvent(BaseEvent be) { initTaskListener.setSuppressClearBusy(true); Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { initTaskListener.setSuppressClearBusy(false); } }); } }); return getInitTask(); } /** * Invokes the page */ public void invoke() { // StandardComponents.removeLoadingMarker(); // Make sure we're not invoking a page that can't do anything. If we going to be modifying the DB, we need to either have an action task and / or a return group handler defined. if (getMode() == FormMode.CREATE || getMode() == FormMode.EDIT || getMode() == FormMode.DELETE) { if (getReturnGroupHandler() == null && (actionTask == null || actionTask.isNullTask())) { throw new RuntimeException("A page " + getPage().getClass().getName() + " in mode CREATE, EDIT or DELETE must have either an action task or a return group handler defined. This page has neither!"); } } if (dependency != null) { dependency.setProcessing(false); } // Reset the form in case we're executing this multiple times reset(); // Set up hardcoded data for the dialog (e.g., a fixed pulldown list or starting value) getPage().setupFixedData(); MergedModType mt = MergedModType.NONE; if (mode == FormMode.CREATE) { mt = MergedModType.ADD; } else if (mode == FormMode.EDIT) { mt = MergedModType.EDIT; } Field field = getBodyPanel().getField(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME); if (field == null) { addHiddenField(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME, mt.name()); } else { field.setValue(mt.name()); } setDefaultFocusField(); if (formType != FormType.DIALOG) { getViewport().layout(false); getViewport().show(); } else { getDialog().layout(true); getDialog().show(); } // Defer the rest of the work until after the form successfully paints (hopefully) // Two levels of defer to try to give all the formatting time to settle out so we don't get a Frankenscreen visible to the user Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { // Mask the form setBusy("FORM", "Please Wait...", "Initializing Form..."); Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { beginFormOperation(); } }); } }); } /** * Kicks off the init task. * Displays a message if the tasks are suppressed (debug tool for checking the layout of the page without waiting for data). * Turns on the dependency handling * Requests the form size adjustments to get the layouts to "settle" */ private void beginFormOperation() { Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { runInitTask(); String rt = (String) Registry.get("runtasks"); if (rt != null && !rt.equalsIgnoreCase("true")) { StandardComponents.alert(getSelf(), MsgLevel.WARN, "Tasks are not running - just letting you check form layouts"); } activateDependencyHandler(); initialFormAdjustment(); } }); } /** * Adjusts the form size down one pixel in height to help get the form to settle */ private void initialFormAdjustment() { // This is to force a resize to get the page to properly layout (some things just don't work right during pre-render layout) Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { jiggleForm(false); finalFormAdjustment(); } }); } /** * Adjusts the form back to its requested size, sets the default focus and clears the busy mask. */ private void finalFormAdjustment() { // This restores the original requested size and causes one more adjustment - hopefully it will be more reliable this way Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { jiggleForm(true); setDefaultFocus(); clearBusy("FORM"); } }); } private void activateDependencyHandler() { // Call the dependency processor Log.debug("Calling the DependencyProcessor"); // Restart the processor if the page is being reused if (dependency != null) { Log.debug("Restarting the dependency processor on the page"); dependency.processPage(getBodyPanel(), false); validate(); } else { // If we have already attempted to process the dependency file and it's still null, we won't get a better answer on the second attempt, so skip the task. if (!dependencySetup) { // If we are the top-level page, entrypointclassname should point to our classname - if so, use whatever descriptor is present if (getPage().getClass().getName().equals(Registry.get("entrypointclassname"))) { Log.debug("Opening the dependency descriptor for the top-level page: " + getPage().getClass().getName()); dependency = DependencyProcessor.newDependencyProcessor(getPage(), getDependencyDescriptor()); dependencySetup = true; } else { // we are not the top level page, run the task to get the dependency descriptor setBusy("DH", "Please Wait...", "Initializing Form..."); Task dependencyTask; if (Registry.get("windchill") != null) { dependencyTask = Task.newTask(CoreTaskFactory.GET_DEPENDENCY_DESCRIPTOR); } else { dependencyTask = Task.newJspTask(CoreTaskFactory.GET_DEPENDENCY_DESCRIPTOR_SERVLET, "getDependencyDescriptor", false); } dependencyTask.addTaskParam("pageclass", getPage().getClass().getName()); dependencyjson = dependencyTask.getGroup("dependencyjson"); dependencyjson.addStoreListener(new StoreListener() { @Override public void storeDataChanged(StoreEvent se) { // Retrieve the task results and use them to open the descriptor returned in the data IEElement depel; String depjson; if (dependencyjson.getElementCount() > 0) { depel = dependencyjson.getElementAt(0); depjson = depel.getValue("dependencyjson"); dependencyDescriptor = depjson; } Log.debug("Opening the dependency descriptor for the dialog: " + getPage().getClass().getName()); dependency = DependencyProcessor.newDependencyProcessor(getPage(), getDependencyDescriptor()); dependencySetup = true; clearBusy("DH"); validate(); } }); dependencyTask.invoke(); } } } } /** * Adjusts the size of the form either up or down one pixel in height. * This seems to be needed to get all of the interactions in the layouts to settle out and actually do what the developer intends. */ private void jiggleForm(boolean up) { final int upsize = 1; final int downsize = -1; int adjustSize = downsize; if (up) { adjustSize = upsize; } int theheight = Window.getClientHeight(); int thewidth = Window.getClientWidth(); if (formType == FormType.FORM) { if (getEntryPoint().getHeight() != -1 && getEntryPoint().getWidth() != -1) { StandardComponents.setWindowSize(getEntryPoint().getHeight() - adjustSize, getEntryPoint().getWidth()); } } if (formType != FormType.DIALOG) { getViewport().setSize(getViewport().getWidth(), getViewport().getHeight() - adjustSize); } else { getBodyPanel().setLayoutData(new FormData("98% 98%")); } getContainer().layout(); Log.debug("Resized window to " + theheight + ":" + thewidth); } /** * @return the dirty */ public Boolean isDirty() { return dirty; } /** * @param dirty the dirty to set */ public void setDirty(Boolean dirty) { this.dirty = dirty; } /** * Runs the page's Init Task. Only the hidden fields are sent as parameters. */ public void runInitTask() { // This setBusy call MUST appear after the show calls or you can wind up with multiple copies of the mask setBusy("INITTASK", "Please wait...", "Initializing Form..."); createTaskParameters(getInitTask(), true); if (!initTask.invoke()) { // If we're using a StartElement with field data, use it to set the form field values instead of the task results if (getInitElement() != null) { setFormFieldValues(initGroup); } clearBusy("INITTASK"); } } /** * @return the startElement * @deprecated Use getInitElement instead */ @Deprecated public IEElement getStartElement() { return initElement; } /** * @return the startElement */ public IEElement getInitElement() { return initElement; } /** * @param initElement the startElement to set. This will be treated like a single element being returned from an initTask in the default group. */ public void setInitElement(IEElement initElement) { this.initElement = initElement; initGroup = new IEGroup("initGroup"); initGroup.addElement(initElement); setInitGroup(initGroup); } /** * @param startElement the startElement to set. This will be treated like a single element being returned from an initTask in the default group. * @deprecated Use setInitElement(IEElement) instead. */ @Deprecated public void setStartElement(IEElement startElement) { setInitElement(startElement); } /** * @param initGroup the initGroup to set. This will be treated like it is the default group being returned from an initTask. * */ public void setInitGroup(IEGroup initGroup) { this.initGroup = initGroup; // if (initElement != null && getInitTask() != null && !initTask.getUri().equals(CoreTaskFactory.NULL.getTaskUri())) { // //throw new RuntimeException("Error: you cannot specify an init task and then use an initElement to initialize the page"); // } if (getInitTask() == null) { initTask = Task.newTask(CoreTaskFactory.NULL); } getInitTask().addGroup(initGroup); getInitTask().setTaskGroupOutName(initGroup.getName()); } /** * @return the initTask */ public Task getInitTask() { return initTask; } /** * @return the actionTask */ public Task getActionTask() { return actionTask; } /** * @return the cancelTask */ public Task getCancelTask() { return cancelTask; } /** * @return the submitMode */ public SubmitMode getSubmitMode() { return submitMode; } /** * @param submitMode the submitMode to set */ public void setSubmitMode(SubmitMode submitMode) { this.submitMode = submitMode; } /** * @return the initTaskGroup */ public IEGroup getInitTaskGroup() { return initTaskGroup; } /** * @return the actionTaskGroup */ public IEGroup getActionTaskGroup() { return actionTaskGroup; } /** * @return the cancelTaskGroup */ public IEGroup getCancelTaskGroup() { return cancelTaskGroup; } /** * @return the webAppURL */ public String getWebAppURL() { return webAppURL; } /** * The default task listener for the init task */ @SuppressWarnings("ProtectedInnerClass") public class DefaultInitTaskListener extends StoreListener { private boolean suppressClearBusy = false; /** * Indicates if the busy indicator should be suppressed for this page * @param suppress True to suppress, false to allow the indicator to be used. */ public void setSuppressClearBusy(boolean suppress) { suppressClearBusy = suppress; } @Override public void storeDataChanged(StoreEvent se) { //TODO: BEH: Perhaps all these data changed can be handled by the dependencyhandler, especially this type // form.getWaitBox().close(); Log.trace("InitializerTask.storeDataChanged: Enter"); if (assertGroupHasData(getInitTaskGroup(), getTaskDataKey(), "Initialization data could not be retrieved from task: " + getInitTask().getUri())) { setFormFieldValues(getInitTaskGroup()); } StandardComponents.alert(FormMode.ASSERT_EXIST, getInitTaskGroup()); isValid(); if (!suppressClearBusy) { clearBusy("INITTASK"); } suppressClearBusy = false; } } /** * Takes the values from the first element in the group and processes them into matching field names on the form. Only Field objects will be picked up. Only those * with names will be processed. If the group has a FormMap, the mappings are honored during processing. Events should not fire from the setValue calls - a * later, final validation of all fields on the form happens right after the form is shown. * * @param group The group to be processed */ protected void setFormFieldValues(IEGroup group) { // Check to make sure we have decided whether or not the dependency file has been found and read before putting values on the page, which will eventually start triggering through the DH. if (!dependencySetup) { deferGroup = group; // Defer this processing until later Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { setFormFieldValues(deferGroup); } }); return; } if (group == null) { StandardComponents.alert(this, MsgLevel.FATAL, "setFormField values was given a non-existent group"); return; } if (group.getElementCount() == 0) { StandardComponents.alert(this, MsgLevel.FATAL, "setFormField values was given an empty group"); return; } // Get the group's individual form to group map Map formToGroupMap = group.getFormToGroupMap(); if (formToGroupMap.isEmpty()) { // Group didn't have one - use the init task's map formToGroupMap = getInitTask().getFormToTaskMap(); } setFormFieldValues(group.getElementAt(0), formToGroupMap); } /** * Takes the values in the element and processes them into matching field names on the form. Only Field objects will be picked up. Only those * with names will be processed. If the group has a FormMap, the mappings are honored during processing. Events should not fire from the setValue calls - a * later, final validation of all fields on the form happens right after the form is shown. * * @param el The IEElement to be processed * @param formToGroupMap A map of form names to group attribute names that will be used during processing */ protected void setFormFieldValues(IEElement el, Map formToGroupMap) { boolean showMissingData = Log.isDebugEnabled(); StringBuilder missingData = new StringBuilder(); List> fields = getBodyPanel().getFields(); for (Field field : fields) { // for each item in the group // loop through all the fields on the form until a match is found // Don't process a field without a name String fieldName = field.getName(); String safeFieldName = fieldName; if (fieldName == null || fieldName.isEmpty()) { Log.debug("Field skipped because it has no name: " + field.getClass().getName()); continue; } if (fieldName.contains(".")) { Log.debug("Replacing dots with underscores for field param name: " + fieldName); safeFieldName = safeFieldName.replace(".", "_"); } // Pull the attribute from the element corresponding to the field name // Check the formMap to see if we need to pull a different attribute for this field String mappedAttName = formToGroupMap.get(safeFieldName); if (mappedAttName == null) { mappedAttName = safeFieldName; } // Now check for the mapped attribute name in the element that was passed to // populate the form Object obj = el.getValue(mappedAttName); Object pulldownlistgroup = el.getValue(mappedAttName + "_legalvalues"); if (pulldownlistgroup != null && !(pulldownlistgroup instanceof IEGroup)) { pulldownlistgroup = null; } if (field instanceof ComboBox) { ((ComboBox) field).setStore((IEGroup) pulldownlistgroup); } // if (fieldName.equals(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME)) { // if (obj instanceof MergedModType) { // obj = ((MergedModType) obj).toString(); // } // } String val = ""; if (obj != null) { try { // Disable validation for this field because it might depend on the values // of other fields which "have not" been set yet. //StandardComponents.setFieldValidation(field, false); field.setFireChangeEventOnSetValue(false); if (obj instanceof String) { val = (String) obj; Log.debug("Setting field '" + fieldName + "' to attribute '" + mappedAttName + "' - val: '" + val + "'"); if (field instanceof RadioGroup) { List> radios = ((RadioGroup) field).getAll(); for (Field fieldRadio : radios) { Radio radio = (Radio) fieldRadio; if (val.equals(radio.getValueAttribute())) { // radio.setFireChangeEventOnSetValue(false); radio.setValue(Boolean.TRUE); // radio.setFireChangeEventOnSetValue(true); // field.setFireChangeEventOnSetValue(false); ((RadioGroup) field).setValue(radio); ((RadioGroup) field).setOriginalValue(radio); // field.setFireChangeEventOnSetValue(true); } } } else if (field instanceof CATMultiField) { // field.setFireChangeEventOnSetValue(false); ((CATMultiField) field).setValue(val); ((CATMultiField) field).setOriginalValue(val); ((CATMultiField) field).getTextField().setOriginalValue(val); // field.setFireChangeEventOnSetValue(true); } else if (field instanceof LabelField) { // field.setFireChangeEventOnSetValue(false); ((LabelField) field).setText(val); ((LabelField) field).setValue(val); ((LabelField) field).setOriginalValue(val); // field.setFireChangeEventOnSetValue(true); } else if (field instanceof NumberField) { // field.setFireChangeEventOnSetValue(false); field.clear(); if (val == null || val.isEmpty()) { field.setValue(null); field.setOriginalValue(null); } else { field.setValue(field.getPropertyEditor().convertStringValue(val)); field.setOriginalValue(field.getValue()); } // field.setFireChangeEventOnSetValue(true); } else if (field instanceof Radio) { continue; } else if (field instanceof CATComboBox) { CATComboBox box = ((CATComboBox) field); if (box.getStore() == null) { throw new RuntimeException("ComboBox: " + box.getName() + " is not bound to a Group"); } if (box.getValueField() == null || box.getValueField().isEmpty()) { throw new RuntimeException("ComboBox: " + box.getName() + " does not have a valuefield set up"); } if (box.getDisplayField() == null || box.getDisplayField().isEmpty()) { throw new RuntimeException("ComboBox: " + box.getName() + " does not have a displayfield set up"); } // Try matching the raw value with something in the store if (box.getValue() == null) { Log.debug("Using the value from the backing store: " + val); box.setValueFromStore(val); } // Try creating a custom hidden item in the store and then match the raw value against the store if (box.getValue() == null) { Log.debug("Using the hardcoded value: " + val); box.addNewPulldownListEntry(val, val, false); // make sure that if box.getValue() is null then both value and originalValue // set to the same thing. In gxt, if I setValueFromStore("") and then // call getValue() then getValue() does some logic and returns null. box.setValueFromStore(val); final IEElement originalValue = new IEElement(); originalValue.set(box.getValueField(), box.getRawValue()); box.setOriginalValue(originalValue); Log.trace("CATFormLayout --> val = " + val + ", getValue() = " + box.getValue() + ", raw value = " + box.getRawValue() + ", originalValue = " + box.getOriginalValue()); } box.setData("rawValue", val); box.setData("forcedValue", "y"); if (box.getValue() == null) { Log.debug("Combo values: raw: " + ((CATComboBox) field).getRawValue() + " value: " + box.getValue()); // throw new RuntimeException("Failed to set value on combo box: " + box.getName()); } // box.getValue is not null then it hasn't been set yet. if (box.getValue() != null) { box.setOriginalValue(box.getValue()); } // Log.debug("Combo values: raw: " + ((CATComboBox) field).getRawValue() + " internal: " + box.getValue().get(box.getValueField()) + " display: " + box.getValue().get(box.getDisplayField())); } else if (field instanceof DateField) { if (val.isEmpty()) { val = null; } DateTimePropertyEditor editor = ((DateField) field).getPropertyEditor(); Date dt = null; if (val != null) { dt = editor.convertStringValue(val); } field.setValue(dt); field.setOriginalValue(dt); } else { if (val == null || val.isEmpty()) { field.setValue(null); field.setOriginalValue(null); } else { field.setValue(val); field.setOriginalValue(val); } } } else if (obj instanceof IEGroup) { // look for an adapter field containing a table // if it is found set the group found in this attribute // to the group for the table in the matching adapter field if (field instanceof CATAdapterField) { IEGroup g = (IEGroup) obj; CATAdapterField af = (CATAdapterField) field; if (af.getTable() != null) { IEGroup work = (IEGroup) af.getTable().getGrid().getStore(); work.setFiresEvents(false); work.removeAll(); work.add(g.deepClone().getElementList()); work.setFiresEvents(true); work.signalDataChanged(); } } } } catch (ClassCastException cce) { Log.error("ClassCastException: " + cce); Log.error("Field: " + field.getClass().getName() + " val: " + val); Log.error("Couldn't use setValue - trying setRawValue"); field.setRawValue(val); } finally { // StandardComponents.setFieldValidation(field, true); field.setFireChangeEventOnSetValue(true); } } else { if (showMissingData) { boolean fieldIsHidden = StandardComponents.isFieldHidden(field); if (!fieldIsHidden) { if (missingData.length() > 0) { missingData.append(", "); } if (fieldName.equals(mappedAttName)) { missingData.append(fieldName); } else { missingData.append(mappedAttName).append(" (mapped from ").append(fieldName).append(")"); } } } Log.warn("Field '" + mappedAttName + "' was not found"); } } // Now that all of the values are loaded, we can safely perform validation. DependencyComponent.clearValues(); validate(); } /** * Sets the field name to look for in the task data to confirm the operation was successful and to use as the object ID for any follow-on processing (defaults to 'obid'). * @param setting The field name */ public void setTaskDataKey(String setting) { taskDataKey = setting; if (setting == null) { StandardComponents.alert(FormMode.LIST, MsgLevel.ERROR, "setTaskDataKey was given a null argument"); taskDataKey = "obid"; } } /** * Retrieves the field name used as the object ID for task follow-on operations * @return */ public String getTaskDataKey() { return taskDataKey; } /** * Creates a task with a taskoutgroupname generated from the taskuri. This method will never re-use an existing instance of a task. * * @param task The TaskFactory enum entry of the task to be run * @return The configured task (also available via actionTask) */ public Task setupActionTask(TaskFactory task) { return setupActionTask(task, null, false); } /** * Creates a task with a taskoutgroupname generated from the taskuri. When the useCache is true, it will re-use an already configured task with the same * uri. * * @param task The TaskFactory enum entry of the task to be run * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via actionTask) */ public Task setupActionTask(TaskFactory task, boolean useCache) { return setupActionTask(task, null, useCache); } /** * Creates a task with a taskoutgroupname as specified (null means it will generate it from the taskuri). When the useCache is true, it will re-use an * already configured task with the same uri. * * @param task The TaskFactory enum entry of the task to be run * @param group The name of the group that will be returned by the task * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via actionTask) */ public Task setupActionTask(TaskFactory task, String group, boolean useCache) { mode = getMode(); String taskuri = task.getTaskUri(); if (taskuri == null) { throw new RuntimeException("StandardForm::setupActionTask - action task uri not specified"); } actionTask = Task.newTask(task, group, useCache); actionTaskGroup = getActionTask().getGroup(getActionTask().getTaskGroupOutName()); if (mode == FormMode.CREATE || mode == FormMode.EDIT || mode == FormMode.DELETE) { getActionTaskGroup().addStoreListener(new StoreListener() { @Override public void storeDataChanged(StoreEvent se) { //TODO: Can this be placed after postActionTaskProcessing for maximum mask coverage? clearBusy("ACTIONTASK"); postActionTaskProcessing(true); } }); } return getActionTask(); } /** * Sets the component that should receive the focus when the page is ready for input * @param c The Component that will receive the focus. Should be an input type Component */ public void setFocusField(Component c) { focusField = c; if (c instanceof Field) { currentFocusField = (Field) c; } } /** * Computes the Focus field by finding the field with the lowest tabindex or the first editable field on the page if no tabindex values are found */ public void setDefaultFocusField() { if (focusField == null) { Field firstEditable = null; Field lowestTabOrder = null; int minTabOrder = 999; // If the page forced a focusField, just leave if (focusField != null) { return; } // Scan through the fields looking for the field with the lowest Tab Order, or the first editable field and make that the focusField for (Field f : getBodyPanel().getFields()) { if (f.isEnabled() && !f.isReadOnly() && !(f instanceof HiddenField) && !(f instanceof CATHiddenField) && !(f instanceof LabelField)) { int to = f.getTabIndex(); if (to < minTabOrder) { minTabOrder = to; lowestTabOrder = f; Log.debug("Lowest Tab Index Field: " + lowestTabOrder.getId() + "(" + lowestTabOrder.getName() + ")"); } if (firstEditable == null) { firstEditable = f; Log.debug("First Editable Field: " + f.getId() + "(" + f.getName() + ")"); } if (lowestTabOrder != null) { focusField = lowestTabOrder; } else { focusField = firstEditable; } } } if (focusField != null) { Log.debug("Focus Field: " + ((Field) focusField).getId() + "(" + ((Field) focusField).getName() + ")"); } else { Log.debug("Focus Field: No available editable field found - no focus field set"); } } } /** * Gives focus to the field specified by setFocusField(). */ public void setDefaultFocus() { if (formType == FormType.FORM || formType == FormType.EMBEDDED) { getViewport().show(); if (focusField != null) { focusField.focus(); } } else if (formType == FormType.DIALOG) { getDialog().show(); if (focusField != null) { getDialog().setFocusWidget(focusField); getDialog().focus(); } } } /** * A method to return 'this' for event handlers (their 'this' is the handler itself). * @return The CATFormLayout object for the page. */ public CATFormLayout getSelf() { return this; } /** * Sets whether or not to close, ignoring any page dirty status. * @param forceClose True to force the page to close regardless of any dirty setting encountered. */ public void setForceClose(boolean forceClose) { this.forceClose = forceClose; } /** * @return the forceClose */ public boolean isForceClose() { return forceClose; } /** * Returns the EntryPointFactory used by this layout. * @return The EntryPointFactory object for this layout. */ public EntryPointFactory getEntryPoint() { return entryPoint; } /** * @param entryPoint the entryPoint to set */ public void setEntryPoint(EntryPointFactory entryPoint) { this.entryPoint = entryPoint; } /** * @return the width */ public int getWidth() { return width; } /** * @param width the width to set */ public void setWidth(int width) { this.width = width; } /** * @return the height */ public int getHeight() { return height; } /** * @param height the height to set */ public void setHeight(int height) { this.height = height; } /** * Adds a hidden field to the Form for transmission to the server during init/action tasks * * @param key The name of the field * @param value The value for the field */ public void addParam(String key, String value) { getParamMap().put(key, value); } /** * Returns a particular value from the paramMap * * @param key The name of the hidden param desired * @return The value of the hidden param */ public String getHiddenParam(String key) { return hiddenParamMap.get(key); } /** * Returns a particular value from the paramMap * * @param key The name of the entry desired * @return The value of the map entry * @deprecated Use getHiddenParam instead */ @Deprecated public String getParam(String key) { return hiddenParamMap.get(key); } /** * Sets the hidden param map - the list of hidden fields to create in the form for use in task parameters for the init and / or action tasks for the form * * @param paramMap the paramMap to set */ @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter") public void setHiddenParamMap(Map paramMap) { this.hiddenParamMap = paramMap; } /** * Sets the hidden param map - the list of hidden fields to create in the form for use in task parameters for the init and / or action tasks for the form * * @param paramMap the paramMap to set * @deprecated Use {@link #setHiddenParamMap}(Map<String, String> paramMap) instead */ @Deprecated @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter") public void setParamMap(Map paramMap) { this.hiddenParamMap = paramMap; } private void reportWindowSizes() { Log.trace("---------------------------------------------------------------"); Log.trace("----- window actuals:" + com.google.gwt.user.client.Window.getClientWidth() + "," + com.google.gwt.user.client.Window.getClientHeight()); Log.trace("----- window minimums: " + minWidth + "," + minHeight); if (container.isRendered()) { Log.trace("----- container actuals: " + container.getWidth() + "," + container.getHeight()); Log.trace("----- container minimums: " + minCWidth + "," + minCHeight); } if (getViewport().isRendered()) { Log.trace("----- viewport actuals: " + getViewport().getWidth() + "," + getViewport().getHeight()); } if (bodyPanel.isRendered()) { Log.trace("----- Body actuals: " + bodyPanel.getWidth() + "," + bodyPanel.getHeight()); } } /** * Prepares the layout for use by running all the standard behaviors not related to the page-defined data. * This is run just before the invocation of the page. */ public void finalizeLayout() { // Set up the Enter / Escape key button presses of OK / Cancel if (formType == FormType.DIALOG) { new KeyNav(getDialog()) { // Have the Enter key press the OK button @Override public void onEnter(ComponentEvent ce) { // If the form is not valid, don't click the button if (isValid()) { // If an Apply button is present, Enter will invoke it instead of OK. Button btn = getDialog().getButtonById(APPLY); if (btn != null && (!btn.isVisible() || !btn.isEnabled())) { btn = getDialog().getButtonById(Dialog.OK); } if (btn != null) { btn.fireEvent(Events.Select); } } else { Info.display("ERROR", "The form data is invalid. Correct the errors, then press OK"); } stopEventConditional(ce); } // Have the Escape key press the Cancel button @Override public void onEsc(ComponentEvent ce) { getDialog().getButtonById(Dialog.CANCEL).fireEvent(Events.Select); ce.stopEvent(); } }; } else if (formType == FormType.FORM) { new KeyNav(bodyPanel) { @Override public void onEnter(ComponentEvent ce) { stopEventConditional(ce); if (finishButton.isEnabled()) { finishButton.fireEvent(Events.Select); } } @Override public void onEsc(ComponentEvent ce) { //TODO: BEH: Add test for dirty form fields - if any found, prompt user to make sure they want to cancel cancelButton.fireEvent(Events.Select); } }; } else if (formType == FormType.EMBEDDED) { new KeyNav(bodyPanel) { //Prevent the audible "ding" which occurs for no apparent reason @Override public void onEnter(ComponentEvent ce) { stopEventConditional(ce); } }; } // This KeyNav picks up a Ctrl-Alt-D key press and toggles the debug button on and off new KeyNav(bodyPanel) { @Override public void onKeyPress(ComponentEvent ce) { super.onKeyPress(ce); if (ce.isControlKey() && ce.isAltKey()) { int code = ce.getKeyCode(); if (code == 68) { toggleDebug(); } } } }; // If formLabel is still blank, then there was no call to setFormLabel from the calling screen - use the entry point's default label if (formLabel.isEmpty()) { setFormLabel(getEntryPoint().getLabel()); } container.setStyleName("cat-top-container"); bodyPanel.setBorders(false); if (FormType.EMBEDDED != formType) { container.setLayout(new FormLayout()); container.add(bodyPanel, new FormData("100% 100%")); getViewport().add(container); getViewport().setScrollMode(Scroll.AUTO); container.setScrollMode(Scroll.NONE); } else { getViewport().setLayout(new FitLayout()); container.setLayout(new FormLayout()); container.add(bodyPanel, new FormData("100% 100%")); getViewport().add(container); getViewport().setScrollMode(Scroll.NONE); container.setScrollMode(Scroll.AUTO); } getViewport().layout(true); if (formType == FormType.FORM) { if (getEntryPoint().getHeight() != -1 && getEntryPoint().getWidth() != -1) { StandardComponents.setWindowSize(getEntryPoint().getHeight(), getEntryPoint().getWidth()); } Window.enableScrolling(false); getViewport().layout(true); RootPanel.get().add(getViewport()); container.getHeader().setTextStyle("cat-header-text"); } else if (formType == FormType.DIALOG) { getDialog().setSize(getEntryPoint().getWidth(), getEntryPoint().getHeight()); getDialog().setLayout(new FitLayout()); getDialog().add(container); container.getHeader().setTextStyle("cat-header-text"); } else if (FormType.EMBEDDED == formType) { RootPanel.get().add(getViewport()); } if (container.isRendered() && getViewport().isRendered()) { if (container.getWidth() < getViewport().getWidth()) { container.setWidth(getViewport().getWidth() - sbw); } if (container.getHeight() < getViewport().getHeight()) { container.setHeight(getViewport().getHeight() - sbh); } } reportWindowSizes(); setupFormValidation(); } /** * @return the page */ public StandardForm getPage() { return page; } /** * @param page the page to set */ public void setPage(StandardForm page) { this.page = page; } /** * Returns the string value used to invoke this layout (corresponds to the name value of EntryPointFactory) * @return The string value for the page being run. */ public String getApplication() { return application; } /** * Sets the string value used to invoke this layout (the name from the EntryPointFactory) * @param application The String to be used to invoke this layout. */ public void setApplication(String application) { this.application = application; } private void stopEventConditional(ComponentEvent ce) { if (!"TEXTAREA".equals(ce.getTarget().getTagName())) { ce.stopEvent(); } } /** * Returns the top level container that holds the form. This is the one and only child of the Viewport for the page * @return */ public ContentPanel getContainer() { return container; } /** * Returns the map object containing all the name value pairs to be put into hidden fields on the page. * @return A Map of string keys and string values. */ @SuppressWarnings("ReturnOfCollectionOrArrayField") protected Map getHiddenParamMap() { if (hiddenParamMap == null) { hiddenParamMap = new FastMap(); } return hiddenParamMap; } /** * Returns the map object containing all the name / value pairs to be put into hidden fields on the page * @return A map of string keys and string values holding the name / value pairs to be put into hidden fields on the page * @deprecated Use getHiddenParamMap instead */ @Deprecated @SuppressWarnings("ReturnOfCollectionOrArrayField") protected Map getParamMap() { if (hiddenParamMap == null) { hiddenParamMap = new FastMap(); } return hiddenParamMap; } /** * Creates the layout area with the page title and help icon * @param label The label to be used as the page title * @param helpID The help url to use under the help icon. The icon will not appear if helpUrl is null or empty */ public void createHeaderPanel(String label, final String helpID) { /* * // Set the Window title only if we are a FORM if (formType == FormType.FORM) { Window.setTitle(entryPoint.getLabel()); } else { * getDialog().setHeading(entryPoint.getLabel()); } // Vertical panel for adding Header and Help icon .. headerPanel = new LayoutContainer(); * ColumnLayout layout = new ColumnLayout(); headerPanel.setLayout(layout); headerPanel.setStyleName("cat-dialog-hdr"); * headerPanel.setId("cat-dialog-hdr"); * * // The left side sub-panel HorizontalPanel westpanel = new HorizontalPanel(); westpanel.setStyleName("cat-header-west"); hdrLabel = * StandardComponents.getCATHeaderLabel(label); hdrLabel.setStyleName("cat-header-label"); westpanel.add(hdrLabel); headerPanel.add(westpanel); * * // The right side sub-panel HorizontalPanel eastpanel = new HorizontalPanel(); eastpanel.setStyleName("cat-header-east"); ThemeSelector selector = * new ThemeSelector(); eastpanel.add(selector); String helpurl = webAppURL + "-WHC/index.jspx?id=" + helpID + "&action=show"; HTML rightWidget = * StandardComponents.getHelpIcon(helpurl); rightWidget.setStyleName("cat-helpicon"); eastpanel.add(rightWidget); headerPanel.add(eastpanel); * */ if (Window.getTitle().isEmpty()) { hdr = container.getHeader(); } else { hdr = getDialog().getHeader(); } // Set the Window title only if we are a FORM if (formType == FormType.FORM) { if (Window.getTitle().isEmpty()) { Window.setTitle(entryPoint.getLabel()); } } else if (formType == FormType.DIALOG) { // For Dialogs, use the Dialog's header and shut off the header we created hdr = getDialog().getHeader(); hdr.setVisible(true); } // Set the Window title only if we are a FORM // if (formType == FormType.FORM) { // Window.setTitle(getEntryPoint().getLabel()); // } else if (formType == FormType.DIALOG) { // getDialog().setHeading(getEntryPoint().getLabel()); // } //// container.layout(); // Header hdr = container.getHeader(); // // For Dialogs, use the Dialog's header and shut off the header we created // if (formType == FormType.DIALOG) { // hdr.setVisible(false); // hdr = getDialog().getHeader(); // } hdr.setText(getEntryPoint().getLabel()); // For Embedded forms, turn off the header if (formType == FormType.EMBEDDED) { hdr.setVisible(false); } // Only include the help button if a help ID was set or if the URL does not include a {0} string in it (meaning a fixed landing page) boolean showHelp = false; if (helpURL != null && !helpURL.isEmpty()) { if (helpURL.contains("{0}")) { if (helpID != null && !helpID.isEmpty()) { showHelp = true; } } else { showHelp = true; } } if (showHelp) { Button helpButton = new Button(); helpButton.setSize(StandardComponents.ICON_WIDTH, StandardComponents.WIDGET_HEIGHT); helpButton.addStyleName("helpbutton"); helpButton.addSelectionListener(new SelectionListener() { @Override public void componentSelected(ComponentEvent ce) { String url = helpURL; if (url.contains("{0}")) { url = url.replace("{0}", helpID); } Window.open(url, "_blank", ""); } }); hdr.addTool(helpButton); } } /** * Sets the object ID of the primary object's container. For create, this is where the object will be created. For edit, this is where the object is stored. * @param containerOid The object ID of the primary object's container or the container that launched this screen */ public void setContainerOid(String containerOid) { this.containerOid = containerOid; } /** * Creates the standard footer which includes the status area and the buttons to operate the page */ public void createFooter() { if (formType != FormType.DIALOG) { footer = new LayoutContainer(); footer.setLayout(new HBoxLayout()); getBodyPanel().setBottomComponent(footer); footer.setId("pageFooter"); } createButtonPanel(); createStatusArea(); if (formType != FormType.DIALOG) { footer.add(statusarea, new FormData("100% 26px")); footer.add(buttonPanel, new FormData("100% 26px")); } else { getDialog().getButtonBar().insert(statusarea, 0); } // Don't show the footer on EMBEDDED forms if (FormType.EMBEDDED == formType) { getFooter().setVisible(false); } } private void createButtonPanel() { final int buttonSpacing = 10; createButtons(); buttonPanel = new HorizontalPanel(); buttonPanel.setHeight("40px"); buttonPanel.addStyleName("cat-button-panel"); buttonPanel.setId("buttonPanel"); buttonPanel.setSpacing(buttonSpacing); buttonPanel.add(tools); buttonPanel.add(finishButton); buttonPanel.add(applyButton); buttonPanel.add(cancelButton); // Insert the debug button if (formType == FormType.DIALOG) { ButtonBar bar = getDialog().getButtonBar(); // The ButtonBar comes with OK/Cancel already - add in debug and apply bar.setHeight("28px"); // Since doing a add on the finish and cancel buttons will move them into the buttonPanel above, we need to move all buttons in the buttonPanel to the dialog's buttonBar. while (true) { if (buttonPanel.getItemCount() == 0) { break; } bar.add(buttonPanel.getItem(0)); } bar.insert(tools, 0); } assignButtonListeners(); } private void createButtons() { getFinishButton(); getApplyButton(); if (getMode() != FormMode.CREATE) { getApplyButton().setVisible(false); } getCancelButton(); DebugToolsMenu dtm = new DebugToolsMenu(); tools = dtm.getMenu(this); if (Log.isDebugEnabled()) { tools.setVisible(true); } else { tools.setVisible(false); } } private void assignButtonListeners() { if (FormType.EMBEDDED != formType) { disableActionButtons(); Button theOKButton = getFinishButton(); Button theApplyButton = getApplyButton(); Button theCancelButton = getCancelButton(); // For action type forms, set up the handlers on the buttons if (mode == FormMode.CREATE || mode == FormMode.EDIT || mode == FormMode.MULTIEDIT || mode == FormMode.DELETE) { if (getOkSelectionListener() == null) { // Use the DefaultOKSelectionListener setOkSelectionListener(new DefaultOKApplySelectionListener(SubmitMode.OK)); } getFinishButton().addSelectionListener(getOkSelectionListener()); if (getApplySelectionListener() == null) { // Use the DefaultOKSelectionListener setApplySelectionListener(new DefaultOKApplySelectionListener(SubmitMode.APPLY)); } theApplyButton.addSelectionListener(getApplySelectionListener()); // Set the cancelSelectionListener in case the developer registers a cancel task if (getCancelSelectionListener() == null) { setCancelSelectionListener(new DefaultCancelSelectionListener()); } theCancelButton.addSelectionListener(getCancelSelectionListener()); } else if (mode == FormMode.LIST || mode == FormMode.TREE) { // For LIST or TREE mode pages, hide the OK and APPLY buttons, change the CANCEL button to show "Close" theCancelButton = getCancelButton(); theCancelButton.setText("Close"); theApplyButton.hide(); theOKButton.hide(); if (getCancelSelectionListener() == null) { setCancelSelectionListener(new DefaultCancelSelectionListener()); } theCancelButton.addSelectionListener(getCancelSelectionListener()); ConfirmSelectListener cancelConfirmListener = new ConfirmSelectListener() { @Override public void handleEvent(BaseEvent be) { if (isDirty() && !isForceClose()) { MessageBox.confirm("Form Modified", "Do you want to discard the changes you've made?.", new Listener() { @Override public void handleEvent(MessageBoxEvent be) { if (StandardComponents.wasDialogYesOrOKClicked(be)) { confirm(); } } }); } else { confirm(); } } }; theCancelButton.addListener(FrameworkEvents.Confirm, cancelConfirmListener); } } } private LayoutContainer createStatusArea() { final int defaultSize = 50; statusarea = new LayoutContainer(); statusarea.setId("FormStatusArea"); statusarea.setLayout(new ColumnLayout()); statusarea.setWidth(defaultSize); statusarea.setHeight("25px;"); if (formType != FormType.DIALOG) { // FORM and EMBEDDED types HorizontalPanel btnpanel = getButtonPanel(); btnpanel.setId("buttonPanel"); } else { // DIALOG type ButtonBar bar = getDialog().getButtonBar(); bar.setId("buttonBar"); getDialog().setButtonAlign(Style.HorizontalAlignment.LEFT); bar.setEnableOverflow(false); } configureStatusArea(); statusarea.layout(true); statusarea.show(); return statusarea; } /** * Adds one button into the page's button bar. * The new button will be added just behind the debug button in all cases. * You shall not ever violate the OK/Apply/Cancel affinity. * So if you need to add multiple buttons, add them in right to left order. * addButton(3); addButton(2); addButton(1) will result in Debug;1;2;3;OK;Apply;Cancel * @param btn The Button to be added to the page's button bar. */ public void addButtonToBar(Button btn) { if (btn == null) { throw new RuntimeException("addButtonToBar: btn parameter is null"); } if (formType != FormType.DIALOG) { // FORM and EMBEDDED types HorizontalPanel btnpanel = getButtonPanel(); btnpanel.insert(btn, 1); } else { // DIALOG type ButtonBar bar = getDialog().getButtonBar(); bar.insert(btn, 1); } // updatePageButtonTabIndexes(); } /** * Update the tab indexes of the buttons in the button area */ // private void updatePageButtonTabIndexes() { // Container buttons; // if (formType != FormType.DIALOG) { // buttons = getButtonPanel(); // } else { // buttons = getDialog().getButtonBar(); // } // int ti = PAGE_BUTTON_STARTING_TABINDEX; // List btnlist = buttons.getItems(); // for (Component c : btnlist) { // if (c instanceof Button) { // c.setTabIndex(ti); // ti++; // } // } // } /** * @return the okSelectionListener */ public SelectionListener getOkSelectionListener() { return okSelectionListener; } /** * @param okSelectionListener the okSelectionListener to set */ public void setOkSelectionListener(SelectionListener okSelectionListener) { this.okSelectionListener = okSelectionListener; } /** * @return the applySelectionListener */ public SelectionListener getApplySelectionListener() { return applySelectionListener; } /** * @param applySelectionListener the applySelectionListener to set */ public void setApplySelectionListener(SelectionListener applySelectionListener) { this.applySelectionListener = applySelectionListener; } /** * @return the cancelSelectionListener */ public SelectionListener getCancelSelectionListener() { return cancelSelectionListener; } /** * @param cancelSelectionListener the cancelSelectionListener to set */ public void setCancelSelectionListener(SelectionListener cancelSelectionListener) { this.cancelSelectionListener = cancelSelectionListener; } private class DefaultOKApplySelectionListener extends SelectionListener { SubmitMode submitSetting = null; public DefaultOKApplySelectionListener(SubmitMode setting) { if (setting == null) { throw new RuntimeException("DefaultOKSelectionListenerII: No SubmitMode value supplied"); } submitSetting = setting; } @Override public void componentSelected(ComponentEvent ce) { setSubmitMode(submitSetting); ce.stopEvent(); Registry.register("forceClose", "true"); if (getSubmitMode() == SubmitMode.APPLY) { Log.debug("Form Apply Button clicked"); } else if (getSubmitMode() == SubmitMode.OK) { Log.debug("Form OK Button clicked"); } if (mode == FormMode.CREATE) { setBusy("ACTIONTASK", "Please wait...", "Creating..."); } else if (mode == FormMode.EDIT) { setBusy("ACTIONTASK", "Please wait...", "Updating..."); } else if (mode == FormMode.DELETE) { setBusy("ACTIONTASK", "Please wait...", "Deleting..."); } disableActionButtons(); createTaskParameters(getActionTask()); if (!actionTask.invoke()) { clearBusy("ACTIONTASK"); postActionTaskProcessing(false); } } } /** * Default Cancel Button Handler */ private class DefaultCancelSelectionListener extends SelectionListener { @Override public void componentSelected(ComponentEvent ce) { Object src = ce.getSource(); Log.debug("cancel button ::: Form Type:" + getFormType() + "Form Name:" + getFormName()); Log.debug("cancel button:" + src.getClass().getName()); Log.debug("StandardComponents.cancelButton.addSelectionListener: Clicked"); Log.debug("close dialog"); if (!isForceClose() && isDirty()) { if (isDirty() && getCancelTask() != null) { runCancelTask(); } } if (getFormType() == FormType.DIALOG) { getDialog().hide(); } else { Log.debug("close app"); StandardComponents.closeApplication(); } } } /** * Executes the Cancel Task for the page */ public void runCancelTask() { setBusy("CANCELTASK", "Please Wait", "Performing Server Cleanup for Cancel"); Log.debug("in cancel code"); if (isDirty()) { Log.debug("dirty: " + isDirty()); createTaskParameters(getCancelTask(), false); if (!cancelTask.invoke()) { clearBusy("CANCELTASK"); } } } /** * This method processes the Action Task results. * It is either called by the completion of the Action Task or by the componentSelected event handler if no task is run * It processes any ReturnGroupHandler data return, then * @param ranTask */ private void postActionTaskProcessing(boolean ranTask) { createdObjectOID = ""; String folderOID = ""; IEElement md; Log.trace("formActionTask.storeDataChanged: Enter"); // Confirm that the if (ranTask && assertGroupHasData(getActionTaskGroup(), getTaskDataKey(), "Did not find an object in the task results - does it already exist?")) { // If we get here, we know the group is ok, there is at least one element, and obid has a value so we can skip the checks, reducing the lines of code needed md = getActionTaskGroup().getElementAt(0); // get the obid of the newly created object createdObjectOID = (String) md.getValue("obid"); // Generate a folderOID by using either the returned parentFolder attribute in the data, then the openerOID, then the containerOID folderOID = (String) md.getValue("parentFolder"); if (folderOID == null || folderOID.isEmpty()) { // If they haven't supplied a specific parent, use the openerOid, then the containerOid as the target. folderOID = Registry.get("openerOid"); if (folderOID == null || folderOID.isEmpty()) { folderOID = Registry.get("containerOid"); } } } if (returnGroupHandler != null) { if (returnMode == null && !actionTask.isNullTask()) { // Default returnMode and the action task was not the NULL task, return the task group returnGroupHandler.sendReturnGroup(getActionTaskGroup()); } else if (returnMode == null && getActionTask().isNullTask()) { // Default returnMode and the action task WAS the NULL task, return the form data IEGroup rg = createReturnGroup(false); returnGroupHandler.sendReturnGroup(rg); } else if (returnMode == DataReturnMode.TASK) { // Page wants the action task data returned returnGroupHandler.sendReturnGroup(getActionTaskGroup()); } else if (returnMode == DataReturnMode.FORM) { // Page wants the form data returned IEGroup rg = createReturnGroup(false); returnGroupHandler.sendReturnGroup(rg); } } if (folderOID != null && folderOID.contains("wt.folder.SubFolder")) { handleFolderCompletion(getPage(), getActionTaskGroup(), folderOID, getSubmitMode()); } else { if (folderOID != null && mode == FormMode.DELETE) { handleCompletion(getPage(), getActionTaskGroup(), folderOID, getSubmitMode()); } else { handleCompletion(getPage(), getActionTaskGroup(), createdObjectOID, getSubmitMode()); } } if (getSubmitMode() == SubmitMode.APPLY) { handleApply(); } Log.trace("formActionTask.storeDataChanged: Exit"); } /** * Deals with the Apply Button behavior (leave all the fields alone so the user can make a single change and Apply again) * or reset all the fields to their original values so the page starts over. */ public void handleApply() { // if this is an APPLY, then reset the field values so dirty will be based on the last apply // the OK and APPLY buttons can't be enabled after an APPLY until a field is dirty // if clearOnApply is set to true, then all the fields' values will be reset to original values if created successfully // if clearOnApply is set to false, then all the fields' values will be reset to latest values if (clearOnApply) { // If clearOnApply and the task was NOT successful, then we purposefully don't do a reset. // However, we also do NOT want to do a resetOriginalValues()! if (createdObjectOID != null && !createdObjectOID.isEmpty()) { reset(); // reset() kills the hidden fields so we must set them up again // (similar to what invoke() does after IT calls reset()) setupHiddenFields(); } } else { resetOriginalValues(); } if (formType == FormType.FORM || formType == FormType.EMBEDDED) { if (focusField != null) { focusField.focus(); } } else if (formType == FormType.DIALOG) { if (focusField != null) { getDialog().setFocusWidget(focusField); getDialog().focus(); } } validate(); } /** * sets clearOnApply. If clearOnApply is true then all the fields' values will be reset to original values if created successfully when apply button clicked * if clearOnApply is set to false, then all the fields' values will be reset to latest values * * @param flag */ public void setClearOnApply(boolean flag) { clearOnApply = flag; } /** * @return the returnGroupHandler */ public ReturnGroupHandler getReturnGroupHandler() { return returnGroupHandler; } /** * @param returnGroupHandler the returnGroupHandler to set */ public void setReturnGroupHandler(ReturnGroupHandler returnGroupHandler) { this.returnGroupHandler = returnGroupHandler; } /** * Sets the source of data for a ReturnGroupHandler - FORM or TASK * @param setting A DataReturnMode enum value */ public void setReturnMode(DataReturnMode setting) { returnMode = setting; if (setting == null) { StandardComponents.alert(FormMode.LIST, MsgLevel.ERROR, "setReturnMode was given a null argument"); returnMode = DataReturnMode.FORM; } } /** * Returns the source of data for a ReturnGroupHandler - FORM or TASK * @return A DataReturnMode enum value */ public DataReturnMode getReturnMode() { return returnMode; } /** * creates a group out of all fields in the form, including the hidden ones into the specified task. Data structure objects (IEGroup and TreeStore) will be added directly to the group * * @param hiddenOnly When true, only the fields in the hidden panel will be added (used for the initialization task) * @return An IEGroup with a single element, containing the values of all fields scraped from the form */ public IEGroup createReturnGroup(boolean hiddenOnly) { //TODO: BEH: Why isn't this code used ultimately to generate the task params? This data could be captured and then used to generate the task params. Map formToGroupMap = getActionTask().getFormToTaskMap(); IEGroup group = new IEGroup("results"); IEElement el = new IEElement(); // Map taskParamMap = task.getParamMap(); List> fields = getBodyPanel().getFields(); String pid; for (Field field : fields) { // Don't generate a param for a field without a name String name = field.getName(); if (name == null || name.isEmpty()) { Log.debug("Skipping a field because it has no name. Class: " + field.getClass().getName() + " Value: " + field.getRawValue()); continue; } if (name.equals(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME)) { String val = (String) field.getValue(); if (val == null) { val = ""; } el.addAtt(new IEAtt(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME, val)); continue; } String safeName = name; if (name.contains(".")) { Log.debug("Replacing dots with underscores for field param name: " + name); safeName = safeName.replace(".", "_"); } // Pull the attribute from the element corresponding to the field name // Check the formMap to see if we need to pull a different attribute for this field String mapname = formToGroupMap.get(safeName); if (mapname == null) { mapname = safeName; } // Check to see if we are doing only the fields in the hidden panel. Continue out of the loop //if we are and the field is not parented by the panel with the id 'hidden' if (hiddenOnly) { if (!StandardComponents.isFieldHidden(field)) { Log.debug("Skipping field: " + name + " because it is not in the hidden panel"); continue; } // Widget parent = field.getParent(); // if (parent instanceof LayoutContainer) { // LayoutContainer lc = (LayoutContainer) parent; // pid = lc.getId(); // if (pid != null && !pid.equals("hidden")) { // Log.debug("Skipping field: " + name + " because it is not in the hidden panel"); // continue; // } // } } try { if (field instanceof ComboBox) { // For combo boxes, the value in the map can be colon-separated. So it could be 'a', 'a:b' or ':b'. // 'a' indicates that the internal value of the combo box should go to the attribute called a. The display value is not collected. // 'a:b' indicates that the internal value of the combo box should go to a and the display value should go to b. //':b' indicates that the display value of the combo box should go to b. The internal value is not collected. ComboBox cb = (ComboBox) field; ModelData md = cb.getValue(); String value = ""; String dvalue = ""; if (md != null) { value = md.get(cb.getValueField()); dvalue = md.get(cb.getDisplayField()); } String[] combomap = mapname.split(":"); if (combomap.length == 1) { el.addAtt(new IEAtt(combomap[0], value)); } else { if (combomap[0] == null || combomap[0].isEmpty()) { el.addAtt(new IEAtt(combomap[1], dvalue)); } else { el.addAtt(new IEAtt(combomap[0], value)); el.addAtt(new IEAtt(combomap[1], dvalue)); } } } else if (field instanceof AdapterField) { Object result = null; AdapterField af = (AdapterField) field; String widget = field.getData("type"); if (widget != null) { if (widget.equals("tree")) { Object x = af.getData("widget"); if (x != null && (x instanceof StandardTree)) { StandardTree stdTree = (StandardTree) af.getData("widget"); result = stdTree.getTreeStore(); } else { Log.error("type was set to tree, but the widget was either null or was not a TreeGrid - AdapterField: " + field.getName()); } } else if (widget.equals("table")) { Object x = af.getData("widget"); if (x != null && (x instanceof StandardTable)) { StandardTable stdTable = (StandardTable) af.getData("widget"); IEGroup g = ((IEGroup) stdTable.getGrid().getStore()).deepClone(); result = g; } else { Log.error("type was set to table, but the widget was either null or was not a Grid - AdapterField: " + field.getName()); } } else { Log.error("An ApapterField type was not valid: " + widget + " AdapterField: " + field.getName()); } } else { Log.error("An AdapterField type was null: " + field.getName()); } el.addAtt(new IEAtt(mapname, result)); } else { String val = StandardComponents.getFieldValue(field); Log.trace("Adding Att to return group - " + mapname + "=" + val); el.addAtt(new IEAtt(mapname, val)); } } catch (Exception e) { Log.error(" field has no value set, successfully caught exception " + e.toString()); } } group.addElement(el); return group; } /** * Method to handle the completion of the primary functions of a UI page - create, edit, editmulti. Consults the Action maps to see what to do with the * window, the parent window and whether to use a popup to display anything. * * @param page The StandardForm launching this method * @param group The group with the data from the server operation * @param obid The object ID of the object to which we will forward * @param submitMode Indicates whether this is an OK or an Apply press (OK will dismiss the window, but Apply will force a full revalidation and leave all * fields in place) */ public void handleCompletion(StandardForm page, IEGroup group, String obid, SubmitMode submitMode) { // TODO: This needs to be moved to a Windchill-specific CompletionHandler class //truncate the obid in all cases - the properties pages often puke on the ufids. String theobid = StandardComponents.getShortOid(obid); MsgLevel level = group.getSuccess(); // // If we ran a server side task, we still need to check to see if we have a return group handler so we can interact with a parent table or tree // if (getReturnGroupHandler() != null && returnMode==DataReturnMode.TASK) { // getReturnGroupHandler().sendReturnGroup(group); // } WindowMode windowAction; if (forcedWindowAction == null) { windowAction = StandardComponents.getWindowAction(page.form.getMode(), level, submitMode); } else { windowAction = forcedWindowAction; } ParentMode parentAction; if (forcedParentAction == null) { parentAction = StandardComponents.getParentAction(page.form.getMode(), level); } else { parentAction = forcedParentAction; } if (page.formType == FormType.FORM) { if (parentAction == ParentMode.FORWARD) { if (theobid == null || theobid.isEmpty()) { throw new RuntimeException("Cannot forward to the new object - the obid parameter was not supplied"); } //If the obid is a Version Reference (starts with VR), and the returning oid is equal to the opening oid, we need to refresh //as the browser will ignore a request to forward to the same URL if (theobid.startsWith("VR") && theobid.equals(Registry.get("oid"))) { StandardComponents.refreshParent(); } String url = group.getElementAt(0).getValue("detailsUrl"); if (url != null && !url.isEmpty()) { Log.info("Forward parent to URL - " + url); StandardComponents.forwardParent(url); } else { //R10: http://acm.corp.cat.com/acm/app/#ptc1/tcomp/infoPage?oid=OR%3Awt.part.WTPart%3A1505992&u8=1 Log.info("Forward parent to R10 URL -" + "/app/#ptc1/tcomp/infoPage?oid=" + theobid + "&u8=1"); StandardComponents.forwardParent("/app/#ptc1/tcomp/infoPage?oid=" + theobid + "&u8=1"); } } else if (parentAction == ParentMode.TOFOLDER) { if (theobid == null || theobid.isEmpty()) { throw new RuntimeException("Cannot forward to parent folder - containerOid was not specified"); } Log.info("Forward parent to R10 Folder URL -" + "/app/#ptc1/tcomp/infoPage?oid=" + theobid + "&u8=1"); StandardComponents.forwardParent("/app/#ptc1/tcomp/infoPage?oid=" + theobid + "&u8=1"); } else if (parentAction == ParentMode.REFRESH) { StandardComponents.refreshParent(); } } // TODO: If/when a console object becomes available, feed all messages to that console // Produce a message box whenever the action map indicates POPUP_NEEDED PopupMode popupAction = StandardComponents.getPopupAction(page.form.getMode(), level); if (popupAction == PopupMode.NEEDED) { StandardComponents.alert(page, level, group.getMessagesAsHtml()); } else { // If we aren't doing a popup, we may need to close the window here if (windowAction == WindowMode.CLOSE) { if (page.formType == FormType.FORM) { StandardComponents.closeApplication(); } else if (page.formType == FormType.DIALOG) { page.form.getDialog().hide(); } } } } /** * Method to handle the completion of the primary functions of a UI page launched from a Folder panel - create, edit, editmulti. Consults the Action maps to * see what to do with the window, the parent window and whether to use a popup to display anything. * * @param page The StandardForm object calling this method * @param group The group with the data from the server operation * @param obid The name of the attribute in the group's data with the object's retrieval ID (needed to create parent forwarding URLs) * @param submitMode Indicates whether this is an OK or an Apply press (OK will dismiss the window, but Apply will force a full revalidation and leave all * fields in place) */ public void handleFolderCompletion(StandardForm page, IEGroup group, String obid, SubmitMode submitMode) { //TODO: This needs to be moved to a Windchill-specific CompletionHandler class MsgLevel level = group.getSuccess(); // // If we ran a server side task, we still need to check to see if we have a return group handler so we can interact with a parent table or tree // if (getReturnGroupHandler() != null && returnMode==DataReturnMode.TASK) { // getReturnGroupHandler().sendReturnGroup(group); // } WindowMode windowAction; if (forcedWindowAction == null) { windowAction = StandardComponents.getWindowAction(page.form.getMode(), level, submitMode); } else { windowAction = forcedWindowAction; } ParentMode parentAction = null; if (forcedParentAction == null) { parentAction = StandardComponents.getParentAction(page.form.getMode(), level); } else { parentAction = forcedParentAction; } // Only run these behaviors for a plain old popup if (page.formType == FormType.FORM) { if (obid == null || obid.isEmpty()) { throw new RuntimeException("Cannot refresh / forward to the parent folder - the 'obid' parameter was not supplied"); } // The obid value will have been generated using the first non-null value of a 'parentFolder' attribute in the data, the openerOid parameter, or the containerOid parameter String theOpener = Registry.get("openerOid"); if (obid.equals(theOpener)) { StandardComponents.refreshParent(); Log.info("Parent refreshed"); } else { StandardComponents.forwardParent("/app/#ptc1/tcomp/infoPage?oid=" + obid + "&u8=1"); Log.info("Forward parent to URL -" + "/app/#ptc1/tcomp/infoPage?oid=" + obid + "&u8=1"); } } // TODO: If/when a console object becomes available, feed all messages to that console // Produce a message box whenever the action map indicates POPUP_NEEDED PopupMode popupAction = StandardComponents.getPopupAction(page.form.getMode(), level); if (popupAction == PopupMode.NEEDED) { StandardComponents.alert(page, level, group.getMessagesAsHtml()); } else { // If we aren't doing a popup, we may need to close the window here if (windowAction == WindowMode.CLOSE) { if (page.formType == FormType.FORM) { StandardComponents.closeApplication(); } else { page.form.getDialog().hide(); } } } } /** * @return the windowAction */ public WindowMode getWindowAction() { return forcedWindowAction; } /** * @param windowAction the windowAction to set */ public void setWindowAction(WindowMode windowAction) { forcedWindowAction = windowAction; } /** * @return the parentAction */ public ParentMode getParentAction() { return forcedParentAction; } /** * @param parentAction the parentAction to set */ public void setParentAction(ParentMode parentAction) { forcedParentAction = parentAction; } /** * Makes sure the group has at least one record and the specified attribute contains a non-null, non-empty value * * @param group The IEGroup object containing the task result * @param attrname The name of the expected attribute * @param alertmsg The message desired (will create a default "No data returned from the server call" if this value is null or empty * @return true if all tests pass. Otherwise, closeApplication will be called so the routine will never return. */ public static boolean assertGroupHasData(IEGroup group, String attrname, String alertmsg) { // Generate a default message String thealertmsg = alertmsg; if (thealertmsg == null || thealertmsg.isEmpty()) { thealertmsg = "No data returned from the server call"; } // The group we got was null if (group == null) { Log.error("assertGroupHasData: The group specified is null"); StandardComponents.alert(FormMode.ASSERT_EXIST, MsgLevel.FATAL, thealertmsg); return false; } // The group we got had no elements if (group.getElementCount() == 0 && group.getMessages().isEmpty()) { Log.warn("assertGroupHasData: The group has no elements and there are no other messages - show the default message"); StandardComponents.alert(FormMode.ASSERT_EXIST, MsgLevel.ERROR, thealertmsg); return false; } if (group.getElementCount() == 0) { Log.debug("assertGroupHasData: The group has no elements but there are other messages present"); return false; } // The attribute we got was null or empty if (attrname == null || attrname.isEmpty()) { group.addMessage("FATAL|" + thealertmsg); group.addMessage("FATAL|No attribute name for validating the group data was supplied"); } else { // The value of the attribute in the first element of the group is null or empty IEElement el = group.getElementAt(0); String val = (String) el.getValue(attrname); if (val == null || val.isEmpty()) { group.addMessage("FATAL|" + thealertmsg); group.addMessage("FATAL|The attribute '" + attrname + "' did not appear in the group data"); } } StandardComponents.alert(FormMode.ASSERT_EXIST, group); return true; } /** * Makes sure the group has at least one record and the specified attribute contains a non-null, non-empty value * * @param group The IEGroup object containing the task result * @return true if all tests pass. Otherwise, closeApplication will be called so the routine will never return. */ public static boolean assertGroupHasNoErrors(IEGroup group) { // The group we got was null if (group == null) { Log.error("assertGroupHasNoErrors: The group specified is null"); StandardComponents.alert(FormMode.ASSERT_EXIST, MsgLevel.FATAL, "assertGroupHasNoErrors: The group specified is null"); return false; } StandardComponents.alert(FormMode.ASSERT_EXIST, group); return true; } /** * Makes sure the group has no records * * @param group The IEGroup object containing the task result * @param alertmsg The message desired (will create a default "The object specified already exists" if this value is null or empty) * @return true if all tests pass. Otherwise, closeApplication will be called so the routine will never return. */ public static boolean assertGroupHasNoData(IEGroup group, String alertmsg) { // Generate a default message String thealertmsg = alertmsg; if (thealertmsg == null || thealertmsg.isEmpty()) { thealertmsg = "The object specified already exists"; } // The group we got was null if (group == null) { Log.error("assertGroupHasData: The group specified is null"); StandardComponents.alert(FormMode.ASSERT_NOT_EXIST, MsgLevel.FATAL, thealertmsg); return false; } // The group already had FATAL errors if (group.getSuccess() == MsgLevel.FATAL) { StandardComponents.alert(FormMode.ASSERT_NOT_EXIST, MsgLevel.FATAL, group.getMessagesAsHtml()); return false; } if (group.getElementCount() == 0) { return true; } else { return false; } } /** * Creates a task with a taskoutgroupname generated from the taskuri. This method will never re-use an existing instance of a task. * * @param task The TaskFactory enum entry of the task to be run * @return The configured task (also available via actionTask) */ public Task setupCancelTask(TaskFactory task) { return setupCancelTask(task, null, false); } /** * Creates a task with a taskoutgroupname generated from the taskuri. When the useCache is true, it will re-use an already configured task with the same * uri. * * @param task The TaskFactory enum entry of the task to be run * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via actionTask) */ public Task setupCancelTask(TaskFactory task, boolean useCache) { return setupCancelTask(task, null, useCache); } /** * Creates a task with a taskoutgroupname as specified (null means it will generate it from the taskuri). When the useCache is true, it will re-use an * already configured task with the same uri. * * @param task The TaskFactory enum entry of the task to be run * @param group The name of the group that will be returned by the task * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via actionTask) */ public Task setupCancelTask(TaskFactory task, String group, boolean useCache) { String taskuri = task.getTaskUri(); if (taskuri == null) { StandardComponents.alert(this, MsgLevel.FATAL, "Cancel task uri was not specified"); return null; } cancelTask = Task.newTask(task, group, useCache); cancelTaskGroup = cancelTask.getGroup(cancelTask.getTaskGroupOutName()); getCancelTaskGroup().addStoreListener(new StoreListener() { @Override public void storeDataChanged(StoreEvent se) { Log.trace("InitializerTask.storeDataChanged: Enter"); if (assertGroupHasData(getCancelTaskGroup(), getTaskDataKey(), "Initialization data could not be retrieved from task: " + getCancelTask().getUri())) { setFormFieldValues(getCancelTaskGroup()); } StandardComponents.alert(FormMode.ASSERT_EXIST, getCancelTaskGroup()); isValid(); clearBusy("CANCELTASK"); } }); return cancelTask; } /** * Creates focus listeners on all named fields on the page so we can show the tooltip in the button bar when the current field has an error. */ public void setupFocusHandler() { for (Field f : getBodyPanel().getFields()) { //TODO: Take this out if validation doesn't work when they tab out of the field anyway f.setValidateOnBlur(false); // add focus listener Field work; if (f.getName() != null && !f.getName().isEmpty()) { work = f; if (f instanceof CATMultiField) { work = ((CATMultiField) f).getTextField(); } work.addListener(Events.Focus, new Listener() { @Override public void handleEvent(BaseEvent be) { if (statusarea == null) { return; } Field f = (Field) be.getSource(); currentFocusField = f; showErrorInStatusArea(f); } }); work.addListener(Events.Blur, new Listener() { @Override public void handleEvent(BaseEvent be) { if (statusarea == null) { return; } Field f = (Field) be.getSource(); // currentFocusField = null; // showErrorInStatusArea(f); } }); work.addListener(Events.Valid, new Listener() { @Override public void handleEvent(BaseEvent be) { if (statusarea == null) { return; } Field f = (Field) be.getSource(); if (f == currentFocusField) { showErrorInStatusArea(f); } } }); work.addListener(Events.Invalid, new Listener() { @Override public void handleEvent(BaseEvent be) { if (statusarea == null) { return; } Field f = (Field) be.getSource(); if (f == currentFocusField) { showErrorInStatusArea(f); } } }); } } } /** * @return the sendOnlyDirtyOnEdit */ public boolean isSendOnlyDirtyOnEdit() { return sendOnlyDirtyOnEdit; } /** * @param sendOnlyDirtyOnEdit the sendOnlyDirtyOnEdit to set */ public void setSendOnlyDirtyOnEdit(boolean sendOnlyDirtyOnEdit) { this.sendOnlyDirtyOnEdit = sendOnlyDirtyOnEdit; } /** * creates task parameters for all fields in the form, including the hidden ones into the specified task. The parameters will be generated according to the * paramMode setting on the task (name=value or field=name=value) * * @param task The task to which the parameters will be added. */ public void createTaskParameters(Task task) { createTaskParameters(task, false); } /** * creates task parameters for all fields in the form, including the hidden ones into the specified task. The parameters will be generated according to the * paramMode setting on the task (name=value or field=name=value) * * @param task The task to which the parameters will be added. * @param hiddenOnly When true, only the fields in the hidden panel will be added (used for the initialization task) */ public void createTaskParameters(Task task, boolean hiddenOnly) { // Why isn't createReturnGroup used to generate the needed data instead of having separate code? if (task == null) { return; } // If we are configured to only send down changed fields, then we must clear out all task // parameter here. Here is the scenario this deals with: // 1. Bring up the dialog // 2. Edit field A // 3. Click OK // 4. You get some error back from the task. // 5. Undo your edit to field A and edit field B instead. // 6. Click OK // 7. Without this change, field A will get sent to the task since it was // added as a task param in step #3 and nobody has cleared it out again. if (sendOnlyDirtyOnEdit) { task.resetParams(); } Map taskParamMap = task.getFormToTaskMap(); List> fields = getBodyPanel().getFields(); for (Field field : fields) { // Don't generate a param for a field without a name String name = field.getName(); if (name == null || name.isEmpty()) { Log.debug("Skipping a field because it has no name. Class: " + field.getClass().getName() + " Value: " + field.getRawValue()); continue; } String safeName = name; if (name.contains(".")) { Log.debug("Replacing dots with underscores for field param name: " + name); safeName = safeName.replace(".", "_"); } // Check to see if we are doing only the fields in the hidden panel. Continue out of the loop //if we are and the field is not parented by the panel with the id 'hidden' boolean fieldIsHidden = StandardComponents.isFieldHidden(field); if (hiddenOnly && !fieldIsHidden) { Log.debug("Skipping field: " + name + " because it is not in the hidden panel"); continue; } if (!fieldIsHidden) { // Hidden field are always sent if (getMode() == FormMode.EDIT || getMode() == FormMode.MULTIEDIT) { // This only applies to Edit or Multi Edit pages if (sendOnlyDirtyOnEdit && !field.isDirty()) { // We skip if we are sending only dirty fields and the field is not dirty Log.debug("Field skipped due to edit settings: fieldIsHidden: " + fieldIsHidden + " dirty: " + field.isDirty() + " sendOnlyDirtyOnEdit: " + isSendOnlyDirtyOnEdit() + " form mode: " + getMode()); continue; } } } // Check the taskParamMap to see if we need to put this field value into a different parameter name String mapname = taskParamMap.get(safeName); if (mapname == null) { mapname = safeName; } String val = StandardComponents.getFieldValue(field); if (task.getParamMode() == ParamMode.DIRECT) { task.addTaskParam(mapname, val); //if(!mapname.equals(safeName)) // task.addTaskParam(safeName, val);//add task parameters for both mapname and safeName as numerous tasks are associated to the same widget and parameter keys differ } else { task.addTaskParam("field", mapname + "=" + val); //if(!mapname.equals(safeName)) // task.addTaskParam("field", safeName + "=" + val);//add task parameters for both mapname and safeName as numerous tasks are associated to the same widget } } } /** * Calls showErrorInStatusArea using the last field to accept focus. */ public void showErrorForCurrentFocusField() { if (currentFocusField == null) { return; } showErrorInStatusArea(currentFocusField); } /** * Shows the field label, the red ball, and the tooltip when there is a tooltip assigned to the field. * The data is shown in the left side of the button area on forms and dialogs. * @param f */ public void showErrorInStatusArea(Field f) { if (f.getName() != null && !f.getName().isEmpty() && f.getToolTip() != null && f.getToolTip().isEnabled()) { //statusarea.setVisible(true); statusAreaImage.setVisible(true); statusAreaMessage.setVisible(true); String flabel = f.getFieldLabel(); String fmsg = f.getToolTip().getToolTipConfig().getText(); String ftip = fmsg; if (!fmsg.startsWith(flabel)) { // Don't display the label field if it matches the start of the message field ftip = flabel + ": " + fmsg; } statusAreaMessage.setValue("  " + ftip); statusAreaImage.setTitle((String) statusAreaMessage.getValue()); } else { //statusarea.setVisible(false); statusAreaImage.setVisible(false); statusAreaMessage.setVisible(false); } // getBodyPanel().layout(true); int newwidth = calculateStatusareaWidth(); statusarea.setSize(newwidth, -1); statusarea.add(statusAreaImgAdapter, new ColumnData(StandardComponents.ICON_WIDTH)); statusarea.add(statusAreaMessage, new ColumnData(newwidth - StandardComponents.ICON_WIDTH)); statusarea.layout(true); if (formType == FormType.DIALOG) { // Nothing right now } else { getButtonPanel().setPosition(newwidth, 0); } // if (footer != null) { // footer.layout(); // } } private void configureStatusArea() { final int saheight = 27; if (statusAreaImage == null) { statusAreaImage = new Image("/Windchill/com/cat/gwt/org.cat.Main/images/windchill/form/exclamation.gif"); } if (statusAreaImgAdapter == null) { statusAreaImgAdapter = new AdapterField(statusAreaImage); statusAreaImgAdapter.setSize(StandardComponents.ICON_WIDTH, StandardComponents.WIDGET_HEIGHT); statusAreaImgAdapter.setId("statusAreaImgAdapter"); } if (statusAreaMessage == null) { statusAreaMessage = CATLabelField.newCATLabelField(this, "", ""); statusAreaMessage.setLabelSeparator(""); statusAreaMessage.setHideLabel(true); statusAreaMessage.setId("statusAreaMessage"); statusAreaMessage.setSize(300, saheight); } } private int calculateStatusareaWidth() { final int defaultWidth = 300; int result = defaultWidth; if (statusarea.isRendered()) { if (formType == FormType.DIALOG) { result = getDialog().getOffsetWidth() - getButtonBarWidth(defaultWidth) - 10 * (getDialog().getButtonBar().getItemCount() - 1); } else { result = getViewport().getOffsetWidth() - getButtonPanel().getOffsetWidth(); } } if (result < 0) { result = 0; } return result; } private int getButtonBarWidth(int defaultWidth) { int first = -1; int last = -1; // Find the first and last visible buttons in the button bar ButtonBar bar = getDialog().getButtonBar(); for (int i = 1; i < bar.getItemCount(); i++) { // Only look at buttons if (bar.getItem(i) instanceof Button) { // Only look at visible buttons if (bar.getItem(i).isVisible()) { // If we haven't already set first, set it with this visible button's position if (first == -1) { first = i; } // Always set the last to this visible button's position last = i; } } } // blow up if we failed to find either the first or last visible button (should never happen unless someone turns off all the buttons) if (first == -1 || last == -1) { // throw new RuntimeException("Could not find the first and last buttons in the page's button bar!"); return defaultWidth; } // get the rightmost position by adding the left and width of the last visible button, then subtract the left of the leftmost visible button int thewidth = (bar.getItem(last).getAbsoluteLeft() + bar.getItem(last).getOffsetWidth()) - bar.getItem(first).getAbsoluteLeft(); return thewidth; } /** * Returns the oid value (used to dispatch the page). * For Create screens, this will be the container object into which the object should be stored. * For Other screens, this will be a pointer to the object itself. * @return The String value of the object id used to dispatch the page */ public String getOid() { return oid; } /** * Sets the primary object id for the page execution. * For create pages, this should be the container into which the new object will be stored. * For all other pages, this should be the object being manipulated. * @param oid The object identifier for the object being manipulated. */ public void setOid(String oid) { this.oid = oid; } /** * Creates the standard Cancel button for the page */ protected void createCancelButton() { // Cancel button cancelButton = StandardComponents.createCancelButton(); } /** * Creates the standard OK button for the page */ protected void createFinishButton() { // OK button finishButton = StandardComponents.createOKButton(); finishButton.setType("submit"); } private MessageBox getWaitBox() { return waitBox; } /** * Creates the standard Apply button for Create pages */ protected void createApplyButton() { // Apply button applyButton = StandardComponents.createApplyButton(); applyButton.setType("submit"); applyButton.setItemId(APPLY); // so onEnter() can find it } private void setWaitBox(MessageBox waitBox) { this.waitBox = waitBox; waitBox.setModal(true); } /** * Makes the webAppURL for the application available to the form. * @param webAppURL the string representation of the webAppURL (like http://acm.corp.cat.com/Windchill) */ public void setWebAppURL(String webAppURL) { this.webAppURL = webAppURL; } /** * Returns the Finish (OK) Button for the form * The button will be created if necessary * @return The OK Button for the form */ public Button getFinishButton() { if (formType != FormType.DIALOG) { if (finishButton == null) { createFinishButton(); } } else { finishButton = getDialog().getButtonById(Dialog.OK); } return finishButton; } /** * Returns the Finish (OK) Button for the form * The button will be created if necessary * @return The OK Button for the form */ public Button getApplyButton() { if (applyButton == null) { createApplyButton(); } return applyButton; } /** * Returns the Finish (OK) Button for the form * The button will be created if necessary * @return The OK Button for the form */ public Button getCancelButton() { if (formType != FormType.DIALOG) { if (cancelButton == null) { createCancelButton(); } } else { cancelButton = getDialog().getButtonById(Dialog.CANCEL); } return cancelButton; } /** * Retrieves the HorizontalPanel object that holds all buttons at the bottom of the page. * This is only valid for non-DIALOG pages. * Use getDialog().getButtonBar for the equivalent functionality for a DIALOG page. * @return */ public HorizontalPanel getButtonPanel() { return buttonPanel; } /** * Retrieves the actual form area of the page (where all the page-specific content resides). * @return The CATFormPanel that is the form area for the page. */ public CATFormPanel getBodyPanel() { if (bodyPanel == null) { bodyPanel = StandardComponents.newBodyPanel(this); hiddenPanel = StandardComponents.newHiddenPanel(bodyPanel); bodyPanel.add(hiddenPanel); } return bodyPanel; } /** * @return the customHiddenFields */ @SuppressWarnings("ReturnOfCollectionOrArrayField") public List getCustomHiddenFields() { if (customHiddenFields == null) { customHiddenFields = new ArrayList(); } return customHiddenFields; } /** * Retrieves the Viewport used for the overall non-DIALOG page. * This is the object directly attached to the page root. * It is only valid when formType is not DIALOG. * @return The Viewport for the page. */ public Viewport getViewport() { return viewport; } /** * * @return The LayoutContainer holding all of the HiddenFields created to hold the data from getFormParameters. */ private LayoutContainer getHiddenPanel() { return hiddenPanel; } /** * Returns the minimum height needed for the page. * @return An int with the minimum height in pixels. */ public int getMinHeight() { return minHeight; } /** * Set the minimum height for the page (in pixels). * @param minHeight An int with the minimum height setting in pixels. */ public void setMinHeight(int minHeight) { this.minHeight = minHeight; } /** * Returns the minimum width needed for the page. * @return An int with the minimum width in pixels. */ public int getMinWidth() { return minWidth; } /** * Set the minimum width for the page (in pixels). * @param minWidth An int with the minimum width setting in pixels. */ public void setMinWidth(int minWidth) { this.minWidth = minWidth; } /** * @return the formName */ public String getFormName() { return formName; } /** * @param formName the formName to set */ public void setFormName(String formName) { this.formName = formName; } /** * @return the formLabel */ public String getFormLabel() { return formLabel; } /** * @param formLabel the formLabel to set */ public void setFormLabel(String formLabel) { this.formLabel = formLabel; if (formType == FormType.DIALOG) { container.getHeader().setVisible(false); } // if (Window.getTitle().isEmpty()) { // hdr = container.getHeader(); // } else { // hdr = getDialog().getHeader(); // } // Set the Window title only if we are a FORM if (formType == FormType.FORM) { if (Window.getTitle().isEmpty()) { Window.setTitle(entryPoint.getLabel()); } } else if (formType == FormType.DIALOG) { // For Dialogs, use the Dialog's header and shut off the header we created hdr = getDialog().getHeader(); hdr.setText(formLabel); // hdr.setVisible(false); } } /** * @return the helpID */ public String getHelpID() { return helpID; } /** * @param helpID the helpID to set */ public void setHelpID(String helpID) { this.helpID = helpID; } /** * Sets the mode of the form. If null, it defaults to LIST * @param mode The desired FormMode enum for the Form */ public void setMode(FormMode mode) { FormMode work = mode; if (work == null) { work = FormMode.LIST; } Registry.register("pageMode", work.toString()); this.mode = work; } /** * Returns the Mode of the form (CREATE, EDIT, DELETE, LIST, etc) * @return The FormMode enum value for this page. */ public FormMode getMode() { return mode; } /** * Creates new Hidden Fields for every entry found in the hiddenParamMap */ public void setupHiddenFields() { FormData fd = new FormData("100%"); fd.setWidth(getWidth()); Iterator ix = hiddenParamMap.keySet().iterator(); while (ix.hasNext()) { String key = ix.next(); String val = hiddenParamMap.get(key); addHiddenField(key, val, true); } for (HiddenField hf : getCustomHiddenFields()) { getHiddenPanel().add(hf); getBodyPanel().addHiddenField(hf); } } /** * Adds a new hidden field to the bodypanel. * @param fieldname The name of the Hidden field to be added * @param val The value for the newly created Hidden field. */ public void addHiddenField(String fieldname, String val) { addHiddenField(fieldname, val, false); } /** * Adds a new hidden field to the hidden panel (when formParam is true) or to the bodyPanel (when formParam is false). * @param fieldname The name of the Hidden field to be added * @param val The value for the newly created Hidden field. * @param formParam set to true to add to the form parameter hidden container. Otherwise, it will be added to the bodyPanel like any other field. */ private void addHiddenField(String fieldname, String val, boolean formParam) { // Field to hold object's oid on the page CATHiddenField hidden = CATHiddenField.newCATHiddenField(bodyPanel.getForm(), fieldname, fieldname); hidden.setValue(val); hidden.setAutoHeight(true); hidden.setAutoWidth(true); if (formParam) { hiddenPanel.add(hidden); } else { getBodyPanel().add(hidden); } getBodyPanel().addHiddenField(hidden); } /** * Empties all fields on the page that are in the FormPanel and that have names. Removes the hidden fields from the hidden panel (they will be rebuilt shortly). If they are also ComboBox, StandardTable or StandardTree objects, then their stores will be empties. */ public void reset() { // bodyPanel.reset(); getHiddenPanel().removeAll(); for (Field field : bodyPanel.getFields()) { if (!StandardComponents.isFieldHidden(field)) { String name = field.getName(); if (name != null && !name.isEmpty() && !name.equals("null")) { // Disable validation for this field during the reset. // Some validations depend on other field values and, // at this point in time, some of the fields are reset // but others are not. field.setFireChangeEventOnSetValue(false); field.reset(); field.setFireChangeEventOnSetValue(true); // Also reset any DependencyComponent associated with this field. // Otherwise, it will have a stale "startVal" which will cause // incorrect behavior. StandardComponents.resetFieldDependencyComponent(field); } } } formValid(false); } /** * Restores the original values for the form, stored when the init data was loaded. */ public void resetOriginalValues() { hasFormBeenApplied = true; for (Field field : bodyPanel.getFields()) { String name = field.getName(); if (name != null && !name.isEmpty() && !name.equals("null")) { Object value = field.getValue(); field.setOriginalValue(value); } } // turn off the APPLY and OK buttons formValid(false); } /** * Initializes the form validate processing. * All fields are added to a dive panel so the overall form status can be determined as validations occur. */ public void setupFormValidation() { Field focusfield = null; Log.debug("In CATFormLayoutsetupFormValidation."); divePanel = new FastMap(); // divePanel.put("$$BUSY", TRUE); for (Field field : bodyPanel.getFields()) { // field.setMessageTarget("tooltip"); // if (field instanceof CATMultiField) { // ((CATMultiField) field).getTextField().setMessageTarget("tooltip"); // } // if (field instanceof CATPicker) { // ((CATPicker) field).getText().setMessageTarget("tooltip"); // ((CATPicker) field).getSearchButtonAdapter().setMessageTarget("none"); // } String name = field.getName(); if (name != null && !name.isEmpty() && !"null".equals(name)) { Log.debug("Field: " + field.getName() + " tabindex: " + field.getTabIndex()); // Automatically focus on the field with tab index 1 Widget parent = field.getParent(); // field.getParent() returns null for the CATTextField in a MultiField so the // following null tests are a temporary work-around until the root cause is identified // of why the CATTextField in a multi-field doesn't have a parent if (parent instanceof LayoutContainer || (parent == null && field instanceof CATTextField)) { if (parent != null) { LayoutContainer lc = (LayoutContainer) parent; if (!"hidden".equals(lc.getId())) { if (!(field instanceof LabelField) && !(field instanceof HiddenField) && !(field instanceof CATHiddenField)) { if (field.isVisible() && field.isEnabled()) { if (focusfield == null) { focusfield = field; } } } } } else if (parent == null && field instanceof CATTextField) { // CATTextField in a CATMultiField if (field.isVisible() && field.isEnabled()) { if (focusfield == null) { focusfield = field; } } } divePanel.put(name, TRUE); Field thefield = field; // Special handling for the CATMultiField - redirect the events to the inner TextField. if (field instanceof CATMultiField) { thefield = ((CATMultiField) field).getTextField(); } Log.trace("Field " + name + " added to divePanel"); // Attach listeners from the fields for when they fire valid / invalid during validation thefield.addListener(Events.Valid, new Listener() { @Override public void handleEvent(FieldEvent fe) { Object objsource = fe.getSource(); //Log.trace("Field.Valid listener - Field:" + objsource); if (!(objsource instanceof Field)) { return; } Field fld = (Field) objsource; String name = fld.getName(); Log.debug("Received VALID event for field: " + name); updateDivePanel(name, true); } }); thefield.addListener(Events.Invalid, new Listener() { @Override public void handleEvent(FieldEvent fe) { Object objsource = fe.getSource(); //Log.trace("Field.Invalid listener - Field: " + objsource); if (!(objsource instanceof Field)) { return; } Field fld = (Field) objsource; String name = fld.getName(); Log.debug("Received INVALID event for field: " + name); updateDivePanel(name, false); } }); // Log.debug("About to pre-validate field: " + field.getName()); //field.focus(); // BEH: 2013-02-18 - turned off this validation step. //thefield.validate(); } } } if (focusfield != null) { Log.debug("Focusing on the first editable widget: " + focusfield.getName()); //focusfield.focus(); focusfield.validate(); } } /** * Returns the overall validity of the form. * Every dive panel object is tested to see if all are true. If so, then the form is valid. * Any false value found means the entire form is invalid. * @return True when all entries in the dive panel are marked as true. False otherwise. */ public boolean isValid() { Iterator ix = divePanel.keySet().iterator(); while (ix.hasNext()) { String key = ix.next(); boolean b = divePanel.get(key).booleanValue(); if (!b) { formValid(false); return false; } } formValid(true); return true; } // private void clearDivePanel(){ // Iterator ix=divePanel.keySet().iterator(); // while(ix.hasNext()){ // String key=ix.next(); // divePanel.put(key,TRUE); // } // } /** * Forces validation on every field on the page. */ public void validate() { // If the dependency processor looks like it's going to run, then clear the dive panel and the start values used by the DH to avoid validation. // This should make it revalidate every field on the page. // TODO: Maybe this should only re-evaluate the false items? // if(getDependencyProcessor()!=null){ // clearDivePanel(); // DependencyComponent.clearValues(); // } List> fields = bodyPanel.getFields(); for (Field field : fields) { field.isValid(); } showErrorForCurrentFocusField(); } /** * The method to override to change how the form reacts to its overall validation status (typically, the finish button is enabled or disabled according to * the validity boolean passed to the routine). * * @param valid set to true if the overall form is valid, false otherwise */ public void formValid(boolean valid) { boolean theValid = valid; if (hasFormBeenApplied) { // if the form as been applied, first check to see that at least one field is dirty // before allowing the OK and APPLY buttons to be enabled boolean isDirty = false; for (Field field : bodyPanel.getFields()) { String name = field.getName(); if (name != null && !name.isEmpty() && !"null".equals(name)) { if (field.isDirty()) { isDirty = true; break; } } } if (!isDirty) { // if nothing is dirty and this is after an apply, do not turn on the OK and APPLY buttons theValid = false; } } if (formType == FormType.FORM) { if (finishButton != null) { finishButton.setEnabled(theValid); } } else if (formType == FormType.DIALOG) { getDialog().getButtonById(Dialog.OK).setEnabled(theValid); } if (applyButton != null) { if (applyButton != null) { applyButton.setEnabled(theValid); } } } /** * Disables the OK and / or Apply buttons on a page */ public void disableActionButtons() { if (formType == FormType.FORM) { if (finishButton != null) { finishButton.setEnabled(false); } } else if (formType == FormType.DIALOG) { getDialog().getButtonById(Dialog.OK).setEnabled(false); } Button ab = applyButton; if (ab != null) { ab.setEnabled(false); } } /** * @return the formType */ public FormType getFormType() { return formType; } private void updateDivePanel(String name, boolean valid) { divePanel.put(name, Boolean.valueOf(valid)); boolean status = isValid(); Log.info("Overall form validity: " + status); } /** * @param formType the formType to set */ public void setFormType(FormType formType) { this.formType = formType; } /** * Updates the task parameters for the specified task by scraping the current form. * @param task The Task that will get the parameters from the current form. */ protected void replaceTaskParameters(Task task) { Map pmap = task.getFormToTaskMap(); Iterator ix = pmap.keySet().iterator(); // Cycle through the parameters in the task's paramMap while (ix.hasNext()) { // Fname holds the name of the field to be scraped - pname holds the parameter name to generate String fname = ix.next(); String pname = pmap.get(fname); if (fname != null && !fname.isEmpty() && pname != null && !pname.isEmpty()) { Log.trace("Searching for a field value to substitute for param '" + fname + "' from field '" + pname + "'"); for (Field field : getBodyPanel().getFields()) { String name = field.getName(); if (name == null || name.isEmpty()) { continue; } if (name.equals(fname)) { // Found the matching Field name - substitute the value into the parameter map String realval = StandardComponents.getFieldValue(field); task.addTaskParam(pname, realval); Log.trace("Found the field value: " + realval); break; } } } } } /** * @return the dialog */ public Dialog getDialog() { if (dialog == null) { dialog = new CATDialog(); dialog.setData("form", this); dialog.setButtons(Dialog.OKCANCEL); dialog.setHideOnButtonClick(false); dialog.setClosable(false); dialog.setAnimCollapse(true); dialog.setModal(true); dialog.setBlinkModal(true); } return dialog; } /** * Sets up the Form / Dialog's busy mask * * @param message The string containing the desired mask text * @param progressText The string to be presented inside the progress bar */ public void setBusy(String message, String progressText) { setBusy("INTERNAL", message, progressText); } /** * Sets up the Form / Dialog's busy mask. * Each Asynchronous operation needs to make a set / clear busy pair of calls. * The first call will mask the form. * Subsequent calls will merely add their reason to the map of current busy reasons. * A RuntimeException is thrown if a second setBusy call with the same reason is encountered. * * @param reason The ID used to track multiple set/clear busy requests. * @param message The string containing the desired mask text * @param progressText The string to be presented inside the progress bar */ public void setBusy(String reason, String message, String progressText) { if (busyMap.containsKey(reason)) { throw new RuntimeException("setBusy called with a duplicate reason: " + reason + " busyMap: " + busyMap); } if (busyMap.isEmpty()) { if (getFormType() == FormType.FORM || getFormType() == FormType.EMBEDDED) { if (waitBox == null) { setWaitBox(MessageBox.wait("Progress", message, progressText)); } } else { if (!masked) { getDialog().mask(progressText); masked = true; } } } else { Log.debug("Form's busyMap was already established - left existing mask alone"); } updateDivePanel("$$BUSY", FALSE); busyMap.put(reason, reason); } /** * Tears down the Form / Dialog's busy mask */ public void clearBusy() { clearBusy("INTERNAL"); } /** * Tears down the Form / Dialog's busy mask * @param reason A string with a value to correspond to a previous setBusy call. */ public void clearBusy(String reason) { if (!busyMap.containsKey(reason)) { Log.warn("clearBusy called without a corresponding setBusy for reason: " + reason); } busyMap.remove(reason); if (busyMap.isEmpty()) { if (getFormType() == FormType.FORM || getFormType() == FormType.EMBEDDED) { if (getWaitBox() != null) { getWaitBox().close(); waitBox = null; } } else { if (getDialog() != null) { getDialog().unmask(); masked = false; } } updateDivePanel("$$BUSY", TRUE); } else { Log.debug("Form's busyMap still has entries: " + busyMap); } } /** * Forces the page validation status to false by adding a special flag to the dive panel's status map and setting it to FALSE */ public void forceDivePanelInvalid() { updateDivePanel("$$FORCE_VALID", FALSE); } /** * Whenever markDirty is used on this layout, it will clear the special flag so normal validation can resume. */ public void markDirty() { updateDivePanel("$$FORCE_VALID", TRUE); } /** * Returns the current validation status for a field name. This is used by the widgets when no change has been detected. * Instead of returning the value of validateValueLocal (which might give a false positive), it will use the divePanel status instead. * @param fieldName The name of the Field object on the page to be checked. * @return true if the Field specified by fieldName was last validated as VALID, false otherwise. */ public boolean isFieldValid(String fieldName) { if (fieldName == null || fieldName.isEmpty()) { throw new RuntimeException("A Framework widget has no name setting"); } Boolean status = divePanel.get(fieldName); if (status == null) { // throw new RuntimeException("A field name was not found in the dive panel: " + fieldName); return true; } return status; } /** * @return the footer */ public LayoutContainer getFooter() { return footer; } /** * @param footer the footer to set */ public void setFooter(LayoutContainer footer) { this.footer = footer; } /** * Toggles the Debug button on and off */ public void toggleDebug() { boolean newsetting = !tools.isVisible(); if (formType == FormType.EMBEDDED) { getFooter().setVisible(newsetting); } tools.setVisible(!tools.isVisible()); } /** * @return the dependencyDescriptor */ public String getDependencyDescriptor() { if (dependencyDescriptor == null) { dependencyDescriptor = getParamMap().get("dependencyjson"); } if (dependencyDescriptor == null) { dependencyDescriptor = ""; } Log.trace("Length of dependencyDescriptor: " + dependencyDescriptor.length()); return dependencyDescriptor; } /** * @param dependencyDescriptor the dependencyDescriptor to set */ public void setDependencyDescriptor(String dependencyDescriptor) { this.dependencyDescriptor = dependencyDescriptor; } /** * Returns the page's dependency processor * @return The DependencyProcessor for the page */ public DependencyProcessor getDependencyProcessor() { return dependency; } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 147858; to = 147765.Please, attach your messages.log to new issue! ----- /* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.ptc.gxt.framework.client.widget; import com.allen_sauer.gwt.log.client.Log; import com.extjs.gxt.ui.client.Registry; import com.extjs.gxt.ui.client.Style; import com.extjs.gxt.ui.client.Style.Scroll; import com.extjs.gxt.ui.client.core.FastMap; import com.extjs.gxt.ui.client.core.XDOM; import com.extjs.gxt.ui.client.data.ModelData; import com.extjs.gxt.ui.client.event.BaseEvent; import com.extjs.gxt.ui.client.event.ComponentEvent; import com.extjs.gxt.ui.client.event.Events; import com.extjs.gxt.ui.client.event.FieldEvent; import com.extjs.gxt.ui.client.event.Listener; import com.extjs.gxt.ui.client.event.MessageBoxEvent; import com.extjs.gxt.ui.client.event.SelectionListener; import com.extjs.gxt.ui.client.store.Store; import com.extjs.gxt.ui.client.store.StoreEvent; import com.extjs.gxt.ui.client.store.StoreListener; import com.extjs.gxt.ui.client.util.KeyNav; import com.extjs.gxt.ui.client.widget.Component; import com.extjs.gxt.ui.client.widget.ContentPanel; import com.extjs.gxt.ui.client.widget.Dialog; import com.extjs.gxt.ui.client.widget.Header; import com.extjs.gxt.ui.client.widget.HorizontalPanel; import com.extjs.gxt.ui.client.widget.Info; import com.extjs.gxt.ui.client.widget.LayoutContainer; import com.extjs.gxt.ui.client.widget.MessageBox; import com.extjs.gxt.ui.client.widget.Viewport; import com.extjs.gxt.ui.client.widget.button.Button; import com.extjs.gxt.ui.client.widget.button.ButtonBar; import com.extjs.gxt.ui.client.widget.form.AdapterField; import com.extjs.gxt.ui.client.widget.form.ComboBox; import com.extjs.gxt.ui.client.widget.form.DateField; import com.extjs.gxt.ui.client.widget.form.DateTimePropertyEditor; import com.extjs.gxt.ui.client.widget.form.Field; import com.extjs.gxt.ui.client.widget.form.FormPanel; import com.extjs.gxt.ui.client.widget.form.FormPanel.LabelAlign; import com.extjs.gxt.ui.client.widget.form.HiddenField; import com.extjs.gxt.ui.client.widget.form.LabelField; import com.extjs.gxt.ui.client.widget.form.NumberField; import com.extjs.gxt.ui.client.widget.form.Radio; import com.extjs.gxt.ui.client.widget.form.RadioGroup; import com.extjs.gxt.ui.client.widget.layout.ColumnData; import com.extjs.gxt.ui.client.widget.layout.ColumnLayout; import com.extjs.gxt.ui.client.widget.layout.FitLayout; import com.extjs.gxt.ui.client.widget.layout.FormData; import com.extjs.gxt.ui.client.widget.layout.FormLayout; import com.extjs.gxt.ui.client.widget.layout.HBoxLayout; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.Widget; import com.ptc.gxt.framework.client.EntryPointFactory; import com.ptc.gxt.framework.client.FormMode; import com.ptc.gxt.framework.client.FormType; import com.ptc.gxt.framework.client.FrameworkEntryPoint; import com.ptc.gxt.framework.client.TaskFactory; import com.ptc.gxt.framework.client.dependency.DependencyComponent; import com.ptc.gxt.framework.client.dependency.DependencyProcessor; import com.ptc.gxt.framework.client.entrypoint.CoreTaskFactory; import com.ptc.gxt.framework.client.event.ConfirmSelectListener; import com.ptc.gxt.framework.client.event.FrameworkEvents; import com.ptc.gxt.framework.client.ie.IEAtt; import com.ptc.gxt.framework.client.ie.IEElement; import com.ptc.gxt.framework.client.ie.IEGroup; import com.ptc.gxt.framework.client.ie.MsgLevel; import com.ptc.gxt.framework.client.ie.ParamMode; import com.ptc.gxt.framework.client.ie.Task; import com.ptc.gxt.framework.client.table.MergedModType; import com.ptc.gxt.framework.client.table.ReturnGroupHandler; import com.ptc.gxt.framework.client.table.TableGroupReturnGroupListener; import com.ptc.gxt.framework.client.widget.tools.DebugToolsMenu; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Defines the page. * The CATFormLayout is wrapped by StandardForm. * It defines a header, a body and footer information. * It operates in several modes, defined by the FormMode enum. * It produces several different types of form, defined by the FormType enum. * It provides standard buttons and actions associated to those buttons to support data acquisition, server update and cancel operations. * It provides for interaction with the launching window (refresh / forward to new urls) * It provides for standard actions when the action task completes (close on success, reset when apply button used, keep page when errors occur) * It provides validation services between fields via a DependencyProcessor tied to the page. * It provides for an initial data task, an action task and optionally, a cancel task. * * It provides for data transfer between in-browser pages via ReturnGroupListener. */ //public class CATFormLayout implements EntryPoint { public class CATFormLayout { private static final Boolean TRUE = true; private static final Boolean FALSE = false; /** *Identifier for use in retrieving the Apply button via getDialog().getButtonById(APPLY); */ public static final String APPLY = "apply"; private Header hdr; private String formName = "-No Form Name Specified-"; private String formLabel = ""; private String helpID = ""; private String helpURL = Registry.get("helpurl"); private ContentPanel container; private EntryPointFactory entryPoint; private String application = "FormLayout"; private Dialog dialog; private Viewport viewport = StandardComponents.getCATViewport(this); private HorizontalPanel buttonPanel; private CATFormPanel bodyPanel; private LayoutContainer hiddenPanel; private WindowMode forcedWindowAction; private ParentMode forcedParentAction; private int width = -1; private int height = -1; private int minWidth = -1; private int minHeight = -1; private int minCWidth = -1; private int minCHeight = -1; // private int formDataWidth = 250; private String oid; private String webAppURL; private FormType formType = FormType.FORM; private FormMode mode; private Map divePanel = new FastMap(); private Component focusField; private boolean hasFormBeenApplied; private boolean clearOnApply; private Task initTask; private IEGroup initTaskGroup; private Task actionTask; private IEGroup actionTaskGroup; private Task cancelTask; private IEGroup cancelTaskGroup; private IEGroup deferGroup; private IEElement initElement; private IEGroup initGroup; private ReturnGroupHandler returnGroupHandler; private DataReturnMode returnMode; // Any objects that need to be shared into inner classes // or get defined in the registry get declared here. // Use the name of the object as the registry entry // This should help with code completion private String messageBox; private String containerOid; private Button finishButton; private Button applyButton; private Button cancelButton; // private Button closeButton; private MessageBox waitBox; private boolean masked; private Map busyMap = new FastMap(); private Map hiddenParamMap; private int sbh = XDOM.getScrollBarWidth() + 12; private int sbw = XDOM.getScrollBarWidth() + 12; private StandardForm page; private List customHiddenFields; private Field currentFocusField; private LayoutContainer statusarea; private LayoutContainer footer; private Button tools; private boolean forceClose; private SelectionListener okSelectionListener; private SelectionListener applySelectionListener; private SelectionListener cancelSelectionListener; private SubmitMode submitMode = SubmitMode.OK; private String taskDataKey = "obid"; private Boolean dirty = false; private IEGroup dependencyjson; private boolean dependencySetup; private DependencyProcessor dependency; private String dependencyDescriptor; private String createdObjectOID; /** * When true, only dirty fields and hidden fields will be sent to edit tasks */ private boolean sendOnlyDirtyOnEdit; /** * The field that will display the widget's field label in the status area */ // private CATLabelField statusAreaLabel; /** * The field that will display the widget's tooltip in the status area */ private CATLabelField statusAreaMessage; /** * The Red Ball image for display between statusAreaLabel and statusAreaMessage */ private Image statusAreaImage; /** * The adapter to wrap statusAreaImage with to insure it displays properly */ private AdapterField statusAreaImgAdapter; /** * This creates a standard form layout for use by a page. It provides a standardized header with a title and a help icon, an OK / Apply / Cancel button set * and a CATFormPanel into which the developer will add the specific Field sub-classes to create the desired form. Once this method is run, the developer * should do a getBodyPanel() and start adding Field objects to the page. * * @param app The application name for this page * @return The form, already set up to take the specific fields. */ public static CATFormLayout newForm(String app) { return newForm(app, FormType.FORM); } /** * Creates a new CATFormLayout for the given EntryPointFactory enum entry. * @param app The EntryPointFactory enum entry for the StandardForm used with this CATFormLayout * @return The new CATFormLayout object */ public static CATFormLayout newForm(EntryPointFactory app) { return newForm(app, FormType.FORM); } /** * Creates a new CATFormLayout for the given app name and sets the form type as indicated. * @param app The name of the app that will resolve via the newEntryPoint method to a StandardForm * @param formtype A FormType of DIALOG, FORM or EMBEDDED * @return The new CATFormLayout object. */ public static CATFormLayout newForm(String app, FormType formtype) { EntryPointFactory epf = FrameworkEntryPoint.getEntryPointFactory(app); if (epf == null) { throw new RuntimeException("No EntryPoint found for application: " + app); } CATFormLayout newform = new CATFormLayout(); newform.setup(epf, formtype); return newform; } /** * Creates a new Form using an EntryPointFactory handle and FormType. * @param app The EntryPointFactory enum value for the desired page. * @param formtype The type of dialog being created (DIALOG, FORM, or EMBEDDED). * @return A CATFormLayout ready for use by setupLayout. */ public static CATFormLayout newForm(EntryPointFactory app, FormType formtype) { EntryPointFactory epf = app; if (epf == null) { throw new RuntimeException("No EntryPoint found for application: " + app); } CATFormLayout newform = new CATFormLayout(); newform.setup(epf, formtype); return newform; } // @Override /** * Creates the basic shell of the page * @param epf An EntryPointFactory enum value. * @param formtype The form type to be used for the form (@see FormType). */ public void setup(EntryPointFactory epf, FormType formtype) { Log.debug("FormLayout :onModuleLoad: start"); Log.debug("FormLayout : webappurl: " + Registry.get("webappurl")); Log.debug("Launching URL: " + com.google.gwt.user.client.Window.Location.getHref()); Log.debug("oid for this UI page: " + oid); Log.debug("containerOid for this UI page: " + containerOid); setEntryPoint(epf); setFormType(formtype); setMode(epf.getMode()); setFormName(epf.getName()); setHelpID(epf.getHelpID()); setApplication(epf.getName()); setMinHeight(epf.getHeight()); setMinWidth(epf.getWidth()); setWebAppURL((String) Registry.get("webappurl")); //TODO: Don't think this line is doing anything - nobody seems to set the registry entry it's looking for //setHiddenParamMap((Map) Registry.get(app + "_paramMap")); // For Creating Basic Container which is used to add all components.. container = new ContentPanel(); Log.trace("CATFormLayout container isMonitorWindowResize: " + container.isMonitorWindowResize()); container.setLayout(new FitLayout()); if (FormType.EMBEDDED != formType) { container.addStyleName("cat-background"); container.setId("background"); getViewport().removeStyleName("cat-background"); } CATFormPanel fp = getBodyPanel(); fp.setMethod(FormPanel.Method.POST); fp.setLabelAlign(LabelAlign.RIGHT); fp.setLabelWidth(150); fp.setHeaderVisible(false); fp.getHeader().setText(""); } /** * Creates a task with a taskoutgroupname generated from the taskuri. It will never re-use an existing instance of a task with a matching taskuri. * * @param task The TaskFactory enum entry of the task to be run * @return The configured task (also available via initFormTask) */ public Task setupInitTask(TaskFactory task) { return setupInitTask(task, null, false); } /** * Creates a task with a taskoutgroupname generated from the taskuri. When the useCache is true, it will re-use an already configured task with the same * uri. * * @param task The TaskFactory enum entry of the task to be run * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via initFormTask) */ public Task setupInitTask(TaskFactory task, boolean useCache) { return setupInitTask(task, null, useCache); } /** * Creates a task with a taskoutgroupname as specified (null means it will generate it from the taskuri). When the useCache is true, it will re-use an * already configured task with the same uri. * * @param task The TaskFactory enum entry of the task to be run * @param group The name of the group that will be returned by the task * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via initFormTask) */ public Task setupInitTask(TaskFactory task, String group, boolean useCache) { String taskuri = task.getTaskUri(); if (taskuri == null) { StandardComponents.alert(this, MsgLevel.FATAL, "Initializer task uri was not specified"); return null; } initTask = Task.newTask(task, group, useCache); String workgroup = group; if (group == null || group.isEmpty()) { workgroup = getInitTask().getTaskGroupOutName(); } initTaskGroup = getInitTask().getGroup(workgroup); final DefaultInitTaskListener initTaskListener = new DefaultInitTaskListener(); getInitTaskGroup().addStoreListener(initTaskListener); getInitTaskGroup().addListener(Store.Sort, new Listener() { @Override public void handleEvent(BaseEvent be) { initTaskListener.setSuppressClearBusy(true); Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { initTaskListener.setSuppressClearBusy(false); } }); } }); return getInitTask(); } /** * Invokes the page */ public void invoke() { // StandardComponents.removeLoadingMarker(); // Make sure we're not invoking a page that can't do anything. If we going to be modifying the DB, we need to either have an action task and / or a return group handler defined. if (getMode() == FormMode.CREATE || getMode() == FormMode.EDIT || getMode() == FormMode.DELETE) { if (getReturnGroupHandler() == null && (actionTask == null || actionTask.isNullTask())) { throw new RuntimeException("A page " + getPage().getClass().getName() + " in mode CREATE, EDIT or DELETE must have either an action task or a return group handler defined. This page has neither!"); } } if (dependency != null) { dependency.setProcessing(false); } // Reset the form in case we're executing this multiple times reset(); // Set up hardcoded data for the dialog (e.g., a fixed pulldown list or starting value) getPage().setupFixedData(); MergedModType mt = MergedModType.NONE; if (mode == FormMode.CREATE) { mt = MergedModType.ADD; } else if (mode == FormMode.EDIT) { mt = MergedModType.EDIT; } Field field = getBodyPanel().getField(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME); if (field == null) { addHiddenField(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME, mt.name()); } else { field.setValue(mt.name()); } setDefaultFocusField(); if (formType != FormType.DIALOG) { getViewport().layout(false); getViewport().show(); } else { getDialog().layout(true); getDialog().show(); } // Defer the rest of the work until after the form successfully paints (hopefully) // Two levels of defer to try to give all the formatting time to settle out so we don't get a Frankenscreen visible to the user Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { // Mask the form setBusy("FORM", "Please Wait...", "Initializing Form..."); Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { beginFormOperation(); } }); } }); } /** * Kicks off the init task. * Displays a message if the tasks are suppressed (debug tool for checking the layout of the page without waiting for data). * Turns on the dependency handling * Requests the form size adjustments to get the layouts to "settle" */ private void beginFormOperation() { Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { runInitTask(); String rt = (String) Registry.get("runtasks"); if (rt != null && !rt.equalsIgnoreCase("true")) { StandardComponents.alert(getSelf(), MsgLevel.WARN, "Tasks are not running - just letting you check form layouts"); } activateDependencyHandler(); initialFormAdjustment(); } }); } /** * Adjusts the form size down one pixel in height to help get the form to settle */ private void initialFormAdjustment() { // This is to force a resize to get the page to properly layout (some things just don't work right during pre-render layout) Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { jiggleForm(false); finalFormAdjustment(); } }); } /** * Adjusts the form back to its requested size, sets the default focus and clears the busy mask. */ private void finalFormAdjustment() { // This restores the original requested size and causes one more adjustment - hopefully it will be more reliable this way Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { jiggleForm(true); setDefaultFocus(); clearBusy("FORM"); } }); } private void activateDependencyHandler() { // Call the dependency processor Log.debug("Calling the DependencyProcessor"); // Restart the processor if the page is being reused if (dependency != null) { Log.debug("Restarting the dependency processor on the page"); dependency.processPage(getBodyPanel(), false); validate(); } else { // If we have already attempted to process the dependency file and it's still null, we won't get a better answer on the second attempt, so skip the task. if (!dependencySetup) { // If we are the top-level page, entrypointclassname should point to our classname - if so, use whatever descriptor is present if (getPage().getClass().getName().equals(Registry.get("entrypointclassname"))) { Log.debug("Opening the dependency descriptor for the top-level page: " + getPage().getClass().getName()); dependency = DependencyProcessor.newDependencyProcessor(getPage(), getDependencyDescriptor()); dependencySetup = true; } else { // we are not the top level page, run the task to get the dependency descriptor setBusy("DH", "Please Wait...", "Initializing Form..."); Task dependencyTask; if (Registry.get("windchill") != null) { dependencyTask = Task.newTask(CoreTaskFactory.GET_DEPENDENCY_DESCRIPTOR); } else { dependencyTask = Task.newJspTask(CoreTaskFactory.GET_DEPENDENCY_DESCRIPTOR_SERVLET, "getDependencyDescriptor", false); } dependencyTask.addTaskParam("pageclass", getPage().getClass().getName()); dependencyjson = dependencyTask.getGroup("dependencyjson"); dependencyjson.addStoreListener(new StoreListener() { @Override public void storeDataChanged(StoreEvent se) { // Retrieve the task results and use them to open the descriptor returned in the data IEElement depel; String depjson; if (dependencyjson.getElementCount() > 0) { depel = dependencyjson.getElementAt(0); depjson = depel.getValue("dependencyjson"); dependencyDescriptor = depjson; } Log.debug("Opening the dependency descriptor for the dialog: " + getPage().getClass().getName()); dependency = DependencyProcessor.newDependencyProcessor(getPage(), getDependencyDescriptor()); dependencySetup = true; clearBusy("DH"); validate(); } }); dependencyTask.invoke(); } } } } /** * Adjusts the size of the form either up or down one pixel in height. * This seems to be needed to get all of the interactions in the layouts to settle out and actually do what the developer intends. */ private void jiggleForm(boolean up) { final int upsize = 1; final int downsize = -1; int adjustSize = downsize; if (up) { adjustSize = upsize; } int theheight = Window.getClientHeight(); int thewidth = Window.getClientWidth(); if (formType == FormType.FORM) { if (getEntryPoint().getHeight() != -1 && getEntryPoint().getWidth() != -1) { StandardComponents.setWindowSize(getEntryPoint().getHeight() - adjustSize, getEntryPoint().getWidth()); } } if (formType != FormType.DIALOG) { getViewport().setSize(getViewport().getWidth(), getViewport().getHeight() - adjustSize); } else { getBodyPanel().setLayoutData(new FormData("98% 98%")); } getContainer().layout(); Log.debug("Resized window to " + theheight + ":" + thewidth); } /** * @return the dirty */ public Boolean isDirty() { return dirty; } /** * @param dirty the dirty to set */ public void setDirty(Boolean dirty) { this.dirty = dirty; } /** * Runs the page's Init Task. Only the hidden fields are sent as parameters. */ public void runInitTask() { // This setBusy call MUST appear after the show calls or you can wind up with multiple copies of the mask setBusy("INITTASK", "Please wait...", "Initializing Form..."); createTaskParameters(getInitTask(), true); if (!initTask.invoke()) { // If we're using a StartElement with field data, use it to set the form field values instead of the task results if (getInitElement() != null) { setFormFieldValues(initGroup); } clearBusy("INITTASK"); } } /** * @return the startElement * @deprecated Use getInitElement instead */ @Deprecated public IEElement getStartElement() { return initElement; } /** * @return the startElement */ public IEElement getInitElement() { return initElement; } /** * @param initElement the startElement to set. This will be treated like a single element being returned from an initTask in the default group. */ public void setInitElement(IEElement initElement) { this.initElement = initElement; initGroup = new IEGroup("initGroup"); initGroup.addElement(initElement); setInitGroup(initGroup); } /** * @param startElement the startElement to set. This will be treated like a single element being returned from an initTask in the default group. * @deprecated Use setInitElement(IEElement) instead. */ @Deprecated public void setStartElement(IEElement startElement) { setInitElement(startElement); } /** * @param initGroup the initGroup to set. This will be treated like it is the default group being returned from an initTask. * */ public void setInitGroup(IEGroup initGroup) { this.initGroup = initGroup; // if (initElement != null && getInitTask() != null && !initTask.getUri().equals(CoreTaskFactory.NULL.getTaskUri())) { // //throw new RuntimeException("Error: you cannot specify an init task and then use an initElement to initialize the page"); // } if (getInitTask() == null) { initTask = Task.newTask(CoreTaskFactory.NULL); } getInitTask().addGroup(initGroup); getInitTask().setTaskGroupOutName(initGroup.getName()); } /** * @return the initTask */ public Task getInitTask() { return initTask; } /** * @return the actionTask */ public Task getActionTask() { return actionTask; } /** * @return the cancelTask */ public Task getCancelTask() { return cancelTask; } /** * @return the submitMode */ public SubmitMode getSubmitMode() { return submitMode; } /** * @param submitMode the submitMode to set */ public void setSubmitMode(SubmitMode submitMode) { this.submitMode = submitMode; } /** * @return the initTaskGroup */ public IEGroup getInitTaskGroup() { return initTaskGroup; } /** * @return the actionTaskGroup */ public IEGroup getActionTaskGroup() { return actionTaskGroup; } /** * @return the cancelTaskGroup */ public IEGroup getCancelTaskGroup() { return cancelTaskGroup; } /** * @return the webAppURL */ public String getWebAppURL() { return webAppURL; } /** * The default task listener for the init task */ @SuppressWarnings("ProtectedInnerClass") public class DefaultInitTaskListener extends StoreListener { private boolean suppressClearBusy = false; /** * Indicates if the busy indicator should be suppressed for this page * @param suppress True to suppress, false to allow the indicator to be used. */ public void setSuppressClearBusy(boolean suppress) { suppressClearBusy = suppress; } @Override public void storeDataChanged(StoreEvent se) { //TODO: BEH: Perhaps all these data changed can be handled by the dependencyhandler, especially this type // form.getWaitBox().close(); Log.trace("InitializerTask.storeDataChanged: Enter"); if (assertGroupHasData(getInitTaskGroup(), getTaskDataKey(), "Initialization data could not be retrieved from task: " + getInitTask().getUri())) { setFormFieldValues(getInitTaskGroup()); } StandardComponents.alert(FormMode.ASSERT_EXIST, getInitTaskGroup()); isValid(); if (!suppressClearBusy) { clearBusy("INITTASK"); } suppressClearBusy = false; } } /** * Takes the values from the first element in the group and processes them into matching field names on the form. Only Field objects will be picked up. Only those * with names will be processed. If the group has a FormMap, the mappings are honored during processing. Events should not fire from the setValue calls - a * later, final validation of all fields on the form happens right after the form is shown. * * @param group The group to be processed */ protected void setFormFieldValues(IEGroup group) { // Check to make sure we have decided whether or not the dependency file has been found and read before putting values on the page, which will eventually start triggering through the DH. if (!dependencySetup) { deferGroup = group; // Defer this processing until later Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { setFormFieldValues(deferGroup); } }); return; } if (group == null) { StandardComponents.alert(this, MsgLevel.FATAL, "setFormField values was given a non-existent group"); return; } if (group.getElementCount() == 0) { StandardComponents.alert(this, MsgLevel.FATAL, "setFormField values was given an empty group"); return; } // Get the group's individual form to group map Map formToGroupMap = group.getFormToGroupMap(); if (formToGroupMap.isEmpty()) { // Group didn't have one - use the init task's map formToGroupMap = getInitTask().getFormToTaskMap(); } setFormFieldValues(group.getElementAt(0), formToGroupMap); } /** * Takes the values in the element and processes them into matching field names on the form. Only Field objects will be picked up. Only those * with names will be processed. If the group has a FormMap, the mappings are honored during processing. Events should not fire from the setValue calls - a * later, final validation of all fields on the form happens right after the form is shown. * * @param el The IEElement to be processed * @param formToGroupMap A map of form names to group attribute names that will be used during processing */ protected void setFormFieldValues(IEElement el, Map formToGroupMap) { boolean showMissingData = Log.isDebugEnabled(); StringBuilder missingData = new StringBuilder(); List> fields = getBodyPanel().getFields(); for (Field field : fields) { // for each item in the group // loop through all the fields on the form until a match is found // Don't process a field without a name String fieldName = field.getName(); String safeFieldName = fieldName; if (fieldName == null || fieldName.isEmpty()) { Log.debug("Field skipped because it has no name: " + field.getClass().getName()); continue; } if (fieldName.contains(".")) { Log.debug("Replacing dots with underscores for field param name: " + fieldName); safeFieldName = safeFieldName.replace(".", "_"); } // Pull the attribute from the element corresponding to the field name // Check the formMap to see if we need to pull a different attribute for this field String mappedAttName = formToGroupMap.get(safeFieldName); if (mappedAttName == null) { mappedAttName = safeFieldName; } // Now check for the mapped attribute name in the element that was passed to // populate the form Object obj = el.getValue(mappedAttName); Object pulldownlistgroup = el.getValue(mappedAttName + "_legalvalues"); if (pulldownlistgroup != null && !(pulldownlistgroup instanceof IEGroup)) { pulldownlistgroup = null; } if (field instanceof ComboBox) { ((ComboBox) field).setStore((IEGroup) pulldownlistgroup); } // if (fieldName.equals(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME)) { // if (obj instanceof MergedModType) { // obj = ((MergedModType) obj).toString(); // } // } String val = ""; if (obj != null) { try { // Disable validation for this field because it might depend on the values // of other fields which "have not" been set yet. //StandardComponents.setFieldValidation(field, false); field.setFireChangeEventOnSetValue(false); if (obj instanceof String) { val = (String) obj; Log.debug("Setting field '" + fieldName + "' to attribute '" + mappedAttName + "' - val: '" + val + "'"); if (field instanceof RadioGroup) { List> radios = ((RadioGroup) field).getAll(); for (Field fieldRadio : radios) { Radio radio = (Radio) fieldRadio; if (val.equals(radio.getValueAttribute())) { // radio.setFireChangeEventOnSetValue(false); radio.setValue(Boolean.TRUE); // radio.setFireChangeEventOnSetValue(true); // field.setFireChangeEventOnSetValue(false); ((RadioGroup) field).setValue(radio); ((RadioGroup) field).setOriginalValue(radio); // field.setFireChangeEventOnSetValue(true); } } } else if (field instanceof CATMultiField) { // field.setFireChangeEventOnSetValue(false); ((CATMultiField) field).setValue(val); ((CATMultiField) field).setOriginalValue(val); ((CATMultiField) field).getTextField().setOriginalValue(val); // field.setFireChangeEventOnSetValue(true); } else if (field instanceof LabelField) { // field.setFireChangeEventOnSetValue(false); ((LabelField) field).setText(val); ((LabelField) field).setValue(val); ((LabelField) field).setOriginalValue(val); // field.setFireChangeEventOnSetValue(true); } else if (field instanceof NumberField) { // field.setFireChangeEventOnSetValue(false); field.clear(); if (val == null || val.isEmpty()) { field.setValue(null); field.setOriginalValue(null); } else { field.setValue(field.getPropertyEditor().convertStringValue(val)); field.setOriginalValue(field.getValue()); } // field.setFireChangeEventOnSetValue(true); } else if (field instanceof Radio) { continue; } else if (field instanceof CATComboBox) { CATComboBox box = ((CATComboBox) field); if (box.getStore() == null) { throw new RuntimeException("ComboBox: " + box.getName() + " is not bound to a Group"); } if (box.getValueField() == null || box.getValueField().isEmpty()) { throw new RuntimeException("ComboBox: " + box.getName() + " does not have a valuefield set up"); } if (box.getDisplayField() == null || box.getDisplayField().isEmpty()) { throw new RuntimeException("ComboBox: " + box.getName() + " does not have a displayfield set up"); } // Try matching the raw value with something in the store if (box.getValue() == null) { Log.debug("Using the value from the backing store: " + val); box.setValueFromStore(val); } // Try creating a custom hidden item in the store and then match the raw value against the store if (box.getValue() == null) { Log.debug("Using the hardcoded value: " + val); box.addNewPulldownListEntry(val, val, false); // make sure that if box.getValue() is null then both value and originalValue // set to the same thing. In gxt, if I setValueFromStore("") and then // call getValue() then getValue() does some logic and returns null. box.setValueFromStore(val); final IEElement originalValue = new IEElement(); originalValue.set(box.getValueField(), box.getRawValue()); box.setOriginalValue(originalValue); Log.trace("CATFormLayout --> val = " + val + ", getValue() = " + box.getValue() + ", raw value = " + box.getRawValue() + ", originalValue = " + box.getOriginalValue()); } box.setData("rawValue", val); box.setData("forcedValue", "y"); if (box.getValue() == null) { Log.debug("Combo values: raw: " + ((CATComboBox) field).getRawValue() + " value: " + box.getValue()); // throw new RuntimeException("Failed to set value on combo box: " + box.getName()); } // box.getValue is not null then it hasn't been set yet. if (box.getValue() != null) { box.setOriginalValue(box.getValue()); } // Log.debug("Combo values: raw: " + ((CATComboBox) field).getRawValue() + " internal: " + box.getValue().get(box.getValueField()) + " display: " + box.getValue().get(box.getDisplayField())); } else if (field instanceof DateField) { if (val.isEmpty()) { val = null; } DateTimePropertyEditor editor = ((DateField) field).getPropertyEditor(); Date dt = null; if (val != null) { dt = editor.convertStringValue(val); } field.setValue(dt); field.setOriginalValue(dt); } else { if (val == null || val.isEmpty()) { field.setValue(null); field.setOriginalValue(null); } else { field.setValue(val); field.setOriginalValue(val); } } } else if (obj instanceof IEGroup) { // look for an adapter field containing a table // if it is found set the group found in this attribute // to the group for the table in the matching adapter field if (field instanceof CATAdapterField) { IEGroup g = (IEGroup) obj; CATAdapterField af = (CATAdapterField) field; if (af.getTable() != null) { IEGroup work = (IEGroup) af.getTable().getGrid().getStore(); work.setFiresEvents(false); work.removeAll(); work.add(g.deepClone().getElementList()); work.setFiresEvents(true); work.signalDataChanged(); } } } } catch (ClassCastException cce) { Log.error("ClassCastException: " + cce); Log.error("Field: " + field.getClass().getName() + " val: " + val); Log.error("Couldn't use setValue - trying setRawValue"); field.setRawValue(val); } finally { // StandardComponents.setFieldValidation(field, true); field.setFireChangeEventOnSetValue(true); } } else { if (showMissingData) { boolean fieldIsHidden = StandardComponents.isFieldHidden(field); if (!fieldIsHidden) { if (missingData.length() > 0) { missingData.append(", "); } if (fieldName.equals(mappedAttName)) { missingData.append(fieldName); } else { missingData.append(mappedAttName).append(" (mapped from ").append(fieldName).append(")"); } } } Log.warn("Field '" + mappedAttName + "' was not found"); } } // Now that all of the values are loaded, we can safely perform validation. DependencyComponent.clearValues(); validate(); } /** * Sets the field name to look for in the task data to confirm the operation was successful and to use as the object ID for any follow-on processing (defaults to 'obid'). * @param setting The field name */ public void setTaskDataKey(String setting) { taskDataKey = setting; if (setting == null) { StandardComponents.alert(FormMode.LIST, MsgLevel.ERROR, "setTaskDataKey was given a null argument"); taskDataKey = "obid"; } } /** * Retrieves the field name used as the object ID for task follow-on operations * @return */ public String getTaskDataKey() { return taskDataKey; } /** * Creates a task with a taskoutgroupname generated from the taskuri. This method will never re-use an existing instance of a task. * * @param task The TaskFactory enum entry of the task to be run * @return The configured task (also available via actionTask) */ public Task setupActionTask(TaskFactory task) { return setupActionTask(task, null, false); } /** * Creates a task with a taskoutgroupname generated from the taskuri. When the useCache is true, it will re-use an already configured task with the same * uri. * * @param task The TaskFactory enum entry of the task to be run * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via actionTask) */ public Task setupActionTask(TaskFactory task, boolean useCache) { return setupActionTask(task, null, useCache); } /** * Creates a task with a taskoutgroupname as specified (null means it will generate it from the taskuri). When the useCache is true, it will re-use an * already configured task with the same uri. * * @param task The TaskFactory enum entry of the task to be run * @param group The name of the group that will be returned by the task * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via actionTask) */ public Task setupActionTask(TaskFactory task, String group, boolean useCache) { mode = getMode(); String taskuri = task.getTaskUri(); if (taskuri == null) { throw new RuntimeException("StandardForm::setupActionTask - action task uri not specified"); } actionTask = Task.newTask(task, group, useCache); actionTaskGroup = getActionTask().getGroup(getActionTask().getTaskGroupOutName()); if (mode == FormMode.CREATE || mode == FormMode.EDIT || mode == FormMode.DELETE) { getActionTaskGroup().addStoreListener(new StoreListener() { @Override public void storeDataChanged(StoreEvent se) { //TODO: Can this be placed after postActionTaskProcessing for maximum mask coverage? clearBusy("ACTIONTASK"); postActionTaskProcessing(true); } }); } return getActionTask(); } /** * Sets the component that should receive the focus when the page is ready for input * @param c The Component that will receive the focus. Should be an input type Component */ public void setFocusField(Component c) { focusField = c; if (c instanceof Field) { currentFocusField = (Field) c; } } /** * Computes the Focus field by finding the field with the lowest tabindex or the first editable field on the page if no tabindex values are found */ public void setDefaultFocusField() { if (focusField == null) { Field firstEditable = null; Field lowestTabOrder = null; int minTabOrder = 999; // If the page forced a focusField, just leave if (focusField != null) { return; } // Scan through the fields looking for the field with the lowest Tab Order, or the first editable field and make that the focusField for (Field f : getBodyPanel().getFields()) { if (f.isEnabled() && !f.isReadOnly() && !(f instanceof HiddenField) && !(f instanceof CATHiddenField) && !(f instanceof LabelField)) { int to = f.getTabIndex(); if (to < minTabOrder) { minTabOrder = to; lowestTabOrder = f; Log.debug("Lowest Tab Index Field: " + lowestTabOrder.getId() + "(" + lowestTabOrder.getName() + ")"); } if (firstEditable == null) { firstEditable = f; Log.debug("First Editable Field: " + f.getId() + "(" + f.getName() + ")"); } if (lowestTabOrder != null) { focusField = lowestTabOrder; } else { focusField = firstEditable; } } } if (focusField != null) { Log.debug("Focus Field: " + ((Field) focusField).getId() + "(" + ((Field) focusField).getName() + ")"); } else { Log.debug("Focus Field: No available editable field found - no focus field set"); } } } /** * Gives focus to the field specified by setFocusField(). */ public void setDefaultFocus() { if (formType == FormType.FORM || formType == FormType.EMBEDDED) { getViewport().show(); if (focusField != null) { focusField.focus(); } } else if (formType == FormType.DIALOG) { getDialog().show(); if (focusField != null) { getDialog().setFocusWidget(focusField); getDialog().focus(); } } } /** * A method to return 'this' for event handlers (their 'this' is the handler itself). * @return The CATFormLayout object for the page. */ public CATFormLayout getSelf() { return this; } /** * Sets whether or not to close, ignoring any page dirty status. * @param forceClose True to force the page to close regardless of any dirty setting encountered. */ public void setForceClose(boolean forceClose) { this.forceClose = forceClose; } /** * @return the forceClose */ public boolean isForceClose() { return forceClose; } /** * Returns the EntryPointFactory used by this layout. * @return The EntryPointFactory object for this layout. */ public EntryPointFactory getEntryPoint() { return entryPoint; } /** * @param entryPoint the entryPoint to set */ public void setEntryPoint(EntryPointFactory entryPoint) { this.entryPoint = entryPoint; } /** * @return the width */ public int getWidth() { return width; } /** * @param width the width to set */ public void setWidth(int width) { this.width = width; } /** * @return the height */ public int getHeight() { return height; } /** * @param height the height to set */ public void setHeight(int height) { this.height = height; } /** * Adds a hidden field to the Form for transmission to the server during init/action tasks * * @param key The name of the field * @param value The value for the field */ public void addParam(String key, String value) { getParamMap().put(key, value); } /** * Returns a particular value from the paramMap * * @param key The name of the hidden param desired * @return The value of the hidden param */ public String getHiddenParam(String key) { return hiddenParamMap.get(key); } /** * Returns a particular value from the paramMap * * @param key The name of the entry desired * @return The value of the map entry * @deprecated Use getHiddenParam instead */ @Deprecated public String getParam(String key) { return hiddenParamMap.get(key); } /** * Sets the hidden param map - the list of hidden fields to create in the form for use in task parameters for the init and / or action tasks for the form * * @param paramMap the paramMap to set */ @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter") public void setHiddenParamMap(Map paramMap) { this.hiddenParamMap = paramMap; } /** * Sets the hidden param map - the list of hidden fields to create in the form for use in task parameters for the init and / or action tasks for the form * * @param paramMap the paramMap to set * @deprecated Use {@link #setHiddenParamMap}(Map<String, String> paramMap) instead */ @Deprecated @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter") public void setParamMap(Map paramMap) { this.hiddenParamMap = paramMap; } private void reportWindowSizes() { Log.trace("---------------------------------------------------------------"); Log.trace("----- window actuals:" + com.google.gwt.user.client.Window.getClientWidth() + "," + com.google.gwt.user.client.Window.getClientHeight()); Log.trace("----- window minimums: " + minWidth + "," + minHeight); if (container.isRendered()) { Log.trace("----- container actuals: " + container.getWidth() + "," + container.getHeight()); Log.trace("----- container minimums: " + minCWidth + "," + minCHeight); } if (getViewport().isRendered()) { Log.trace("----- viewport actuals: " + getViewport().getWidth() + "," + getViewport().getHeight()); } if (bodyPanel.isRendered()) { Log.trace("----- Body actuals: " + bodyPanel.getWidth() + "," + bodyPanel.getHeight()); } } /** * Prepares the layout for use by running all the standard behaviors not related to the page-defined data. * This is run just before the invocation of the page. */ public void finalizeLayout() { // Set up the Enter / Escape key button presses of OK / Cancel if (formType == FormType.DIALOG) { new KeyNav(getDialog()) { // Have the Enter key press the OK button @Override public void onEnter(ComponentEvent ce) { // If the form is not valid, don't click the button if (isValid()) { // If an Apply button is present, Enter will invoke it instead of OK. Button btn = getDialog().getButtonById(APPLY); if (btn != null && (!btn.isVisible() || !btn.isEnabled())) { btn = getDialog().getButtonById(Dialog.OK); } if (btn != null) { btn.fireEvent(Events.Select); } } else { Info.display("ERROR", "The form data is invalid. Correct the errors, then press OK"); } stopEventConditional(ce); } // Have the Escape key press the Cancel button @Override public void onEsc(ComponentEvent ce) { getDialog().getButtonById(Dialog.CANCEL).fireEvent(Events.Select); ce.stopEvent(); } }; } else if (formType == FormType.FORM) { new KeyNav(bodyPanel) { @Override public void onEnter(ComponentEvent ce) { stopEventConditional(ce); if (finishButton.isEnabled()) { finishButton.fireEvent(Events.Select); } } @Override public void onEsc(ComponentEvent ce) { //TODO: BEH: Add test for dirty form fields - if any found, prompt user to make sure they want to cancel cancelButton.fireEvent(Events.Select); } }; } else if (formType == FormType.EMBEDDED) { new KeyNav(bodyPanel) { //Prevent the audible "ding" which occurs for no apparent reason @Override public void onEnter(ComponentEvent ce) { stopEventConditional(ce); } }; } // This KeyNav picks up a Ctrl-Alt-D key press and toggles the debug button on and off new KeyNav(bodyPanel) { @Override public void onKeyPress(ComponentEvent ce) { super.onKeyPress(ce); if (ce.isControlKey() && ce.isAltKey()) { int code = ce.getKeyCode(); if (code == 68) { toggleDebug(); } } } }; // If formLabel is still blank, then there was no call to setFormLabel from the calling screen - use the entry point's default label if (formLabel.isEmpty()) { setFormLabel(getEntryPoint().getLabel()); } container.setStyleName("cat-top-container"); bodyPanel.setBorders(false); if (FormType.EMBEDDED != formType) { container.setLayout(new FormLayout()); container.add(bodyPanel, new FormData("100% 100%")); getViewport().add(container); getViewport().setScrollMode(Scroll.AUTO); container.setScrollMode(Scroll.NONE); } else { getViewport().setLayout(new FitLayout()); container.setLayout(new FormLayout()); container.add(bodyPanel, new FormData("100% 100%")); getViewport().add(container); getViewport().setScrollMode(Scroll.NONE); container.setScrollMode(Scroll.AUTO); } getViewport().layout(true); if (formType == FormType.FORM) { if (getEntryPoint().getHeight() != -1 && getEntryPoint().getWidth() != -1) { StandardComponents.setWindowSize(getEntryPoint().getHeight(), getEntryPoint().getWidth()); } Window.enableScrolling(false); getViewport().layout(true); RootPanel.get().add(getViewport()); container.getHeader().setTextStyle("cat-header-text"); } else if (formType == FormType.DIALOG) { getDialog().setSize(getEntryPoint().getWidth(), getEntryPoint().getHeight()); getDialog().setLayout(new FitLayout()); getDialog().add(container); container.getHeader().setTextStyle("cat-header-text"); } else if (FormType.EMBEDDED == formType) { RootPanel.get().add(getViewport()); } if (container.isRendered() && getViewport().isRendered()) { if (container.getWidth() < getViewport().getWidth()) { container.setWidth(getViewport().getWidth() - sbw); } if (container.getHeight() < getViewport().getHeight()) { container.setHeight(getViewport().getHeight() - sbh); } } reportWindowSizes(); setupFormValidation(); } /** * @return the page */ public StandardForm getPage() { return page; } /** * @param page the page to set */ public void setPage(StandardForm page) { this.page = page; } /** * Returns the string value used to invoke this layout (corresponds to the name value of EntryPointFactory) * @return The string value for the page being run. */ public String getApplication() { return application; } /** * Sets the string value used to invoke this layout (the name from the EntryPointFactory) * @param application The String to be used to invoke this layout. */ public void setApplication(String application) { this.application = application; } private void stopEventConditional(ComponentEvent ce) { if (!"TEXTAREA".equals(ce.getTarget().getTagName())) { ce.stopEvent(); } } /** * Returns the top level container that holds the form. This is the one and only child of the Viewport for the page * @return */ public ContentPanel getContainer() { return container; } /** * Returns the map object containing all the name value pairs to be put into hidden fields on the page. * @return A Map of string keys and string values. */ @SuppressWarnings("ReturnOfCollectionOrArrayField") protected Map getHiddenParamMap() { if (hiddenParamMap == null) { hiddenParamMap = new FastMap(); } return hiddenParamMap; } /** * Returns the map object containing all the name / value pairs to be put into hidden fields on the page * @return A map of string keys and string values holding the name / value pairs to be put into hidden fields on the page * @deprecated Use getHiddenParamMap instead */ @Deprecated @SuppressWarnings("ReturnOfCollectionOrArrayField") protected Map getParamMap() { if (hiddenParamMap == null) { hiddenParamMap = new FastMap(); } return hiddenParamMap; } /** * Creates the layout area with the page title and help icon * @param label The label to be used as the page title * @param helpID The help url to use under the help icon. The icon will not appear if helpUrl is null or empty */ public void createHeaderPanel(String label, final String helpID) { /* * // Set the Window title only if we are a FORM if (formType == FormType.FORM) { Window.setTitle(entryPoint.getLabel()); } else { * getDialog().setHeading(entryPoint.getLabel()); } // Vertical panel for adding Header and Help icon .. headerPanel = new LayoutContainer(); * ColumnLayout layout = new ColumnLayout(); headerPanel.setLayout(layout); headerPanel.setStyleName("cat-dialog-hdr"); * headerPanel.setId("cat-dialog-hdr"); * * // The left side sub-panel HorizontalPanel westpanel = new HorizontalPanel(); westpanel.setStyleName("cat-header-west"); hdrLabel = * StandardComponents.getCATHeaderLabel(label); hdrLabel.setStyleName("cat-header-label"); westpanel.add(hdrLabel); headerPanel.add(westpanel); * * // The right side sub-panel HorizontalPanel eastpanel = new HorizontalPanel(); eastpanel.setStyleName("cat-header-east"); ThemeSelector selector = * new ThemeSelector(); eastpanel.add(selector); String helpurl = webAppURL + "-WHC/index.jspx?id=" + helpID + "&action=show"; HTML rightWidget = * StandardComponents.getHelpIcon(helpurl); rightWidget.setStyleName("cat-helpicon"); eastpanel.add(rightWidget); headerPanel.add(eastpanel); * */ if (Window.getTitle().isEmpty()) { hdr = container.getHeader(); } else { hdr = getDialog().getHeader(); } // Set the Window title only if we are a FORM if (formType == FormType.FORM) { if (Window.getTitle().isEmpty()) { Window.setTitle(entryPoint.getLabel()); } } else if (formType == FormType.DIALOG) { // For Dialogs, use the Dialog's header and shut off the header we created hdr = getDialog().getHeader(); hdr.setVisible(true); } // Set the Window title only if we are a FORM // if (formType == FormType.FORM) { // Window.setTitle(getEntryPoint().getLabel()); // } else if (formType == FormType.DIALOG) { // getDialog().setHeading(getEntryPoint().getLabel()); // } //// container.layout(); // Header hdr = container.getHeader(); // // For Dialogs, use the Dialog's header and shut off the header we created // if (formType == FormType.DIALOG) { // hdr.setVisible(false); // hdr = getDialog().getHeader(); // } hdr.setText(getEntryPoint().getLabel()); // For Embedded forms, turn off the header if (formType == FormType.EMBEDDED) { hdr.setVisible(false); } // Only include the help button if a help ID was set or if the URL does not include a {0} string in it (meaning a fixed landing page) boolean showHelp = false; if (helpURL != null && !helpURL.isEmpty()) { if (helpURL.contains("{0}")) { if (helpID != null && !helpID.isEmpty()) { showHelp = true; } } else { showHelp = true; } } if (showHelp) { Button helpButton = new Button(); helpButton.setSize(StandardComponents.ICON_WIDTH, StandardComponents.WIDGET_HEIGHT); helpButton.addStyleName("helpbutton"); helpButton.addSelectionListener(new SelectionListener() { @Override public void componentSelected(ComponentEvent ce) { String url = helpURL; if (url.contains("{0}")) { url = url.replace("{0}", helpID); } Window.open(url, "_blank", ""); } }); hdr.addTool(helpButton); } } /** * Sets the object ID of the primary object's container. For create, this is where the object will be created. For edit, this is where the object is stored. * @param containerOid The object ID of the primary object's container or the container that launched this screen */ public void setContainerOid(String containerOid) { this.containerOid = containerOid; } /** * Creates the standard footer which includes the status area and the buttons to operate the page */ public void createFooter() { if (formType != FormType.DIALOG) { footer = new LayoutContainer(); footer.setLayout(new HBoxLayout()); getBodyPanel().setBottomComponent(footer); footer.setId("pageFooter"); } createButtonPanel(); createStatusArea(); if (formType != FormType.DIALOG) { footer.add(statusarea, new FormData("100% 26px")); footer.add(buttonPanel, new FormData("100% 26px")); } else { getDialog().getButtonBar().insert(statusarea, 0); } // Don't show the footer on EMBEDDED forms if (FormType.EMBEDDED == formType) { getFooter().setVisible(false); } } private void createButtonPanel() { final int buttonSpacing = 10; createButtons(); buttonPanel = new HorizontalPanel(); buttonPanel.setHeight("40px"); buttonPanel.addStyleName("cat-button-panel"); buttonPanel.setId("buttonPanel"); buttonPanel.setSpacing(buttonSpacing); buttonPanel.add(tools); buttonPanel.add(finishButton); buttonPanel.add(applyButton); buttonPanel.add(cancelButton); // Insert the debug button if (formType == FormType.DIALOG) { ButtonBar bar = getDialog().getButtonBar(); // The ButtonBar comes with OK/Cancel already - add in debug and apply bar.setHeight("28px"); // Since doing a add on the finish and cancel buttons will move them into the buttonPanel above, we need to move all buttons in the buttonPanel to the dialog's buttonBar. while (true) { if (buttonPanel.getItemCount() == 0) { break; } bar.add(buttonPanel.getItem(0)); } bar.insert(tools, 0); } assignButtonListeners(); } private void createButtons() { getFinishButton(); getApplyButton(); if (getMode() != FormMode.CREATE) { getApplyButton().setVisible(false); } getCancelButton(); DebugToolsMenu dtm = new DebugToolsMenu(); tools = dtm.getMenu(this); if (Log.isDebugEnabled()) { tools.setVisible(true); } else { tools.setVisible(false); } } private void assignButtonListeners() { if (FormType.EMBEDDED != formType) { disableActionButtons(); Button theOKButton = getFinishButton(); Button theApplyButton = getApplyButton(); Button theCancelButton = getCancelButton(); // For action type forms, set up the handlers on the buttons if (mode == FormMode.CREATE || mode == FormMode.EDIT || mode == FormMode.MULTIEDIT || mode == FormMode.DELETE) { if (getOkSelectionListener() == null) { // Use the DefaultOKSelectionListener setOkSelectionListener(new DefaultOKApplySelectionListener(SubmitMode.OK)); } getFinishButton().addSelectionListener(getOkSelectionListener()); if (getApplySelectionListener() == null) { // Use the DefaultOKSelectionListener setApplySelectionListener(new DefaultOKApplySelectionListener(SubmitMode.APPLY)); } theApplyButton.addSelectionListener(getApplySelectionListener()); // Set the cancelSelectionListener in case the developer registers a cancel task if (getCancelSelectionListener() == null) { setCancelSelectionListener(new DefaultCancelSelectionListener()); } theCancelButton.addSelectionListener(getCancelSelectionListener()); } else if (mode == FormMode.LIST || mode == FormMode.TREE) { // For LIST or TREE mode pages, hide the OK and APPLY buttons, change the CANCEL button to show "Close" theCancelButton = getCancelButton(); theCancelButton.setText("Close"); theApplyButton.hide(); theOKButton.hide(); if (getCancelSelectionListener() == null) { setCancelSelectionListener(new DefaultCancelSelectionListener()); } theCancelButton.addSelectionListener(getCancelSelectionListener()); ConfirmSelectListener cancelConfirmListener = new ConfirmSelectListener() { @Override public void handleEvent(BaseEvent be) { if (isDirty() && !isForceClose()) { MessageBox.confirm("Form Modified", "Do you want to discard the changes you've made?.", new Listener() { @Override public void handleEvent(MessageBoxEvent be) { if (StandardComponents.wasDialogYesOrOKClicked(be)) { confirm(); } } }); } else { confirm(); } } }; theCancelButton.addListener(FrameworkEvents.Confirm, cancelConfirmListener); } } } private LayoutContainer createStatusArea() { final int defaultSize = 50; statusarea = new LayoutContainer(); statusarea.setId("FormStatusArea"); statusarea.setLayout(new ColumnLayout()); statusarea.setWidth(defaultSize); statusarea.setHeight("25px;"); if (formType != FormType.DIALOG) { // FORM and EMBEDDED types HorizontalPanel btnpanel = getButtonPanel(); btnpanel.setId("buttonPanel"); } else { // DIALOG type ButtonBar bar = getDialog().getButtonBar(); bar.setId("buttonBar"); getDialog().setButtonAlign(Style.HorizontalAlignment.LEFT); bar.setEnableOverflow(false); } configureStatusArea(); statusarea.layout(true); statusarea.show(); return statusarea; } /** * Adds one button into the page's button bar. * The new button will be added just behind the debug button in all cases. * You shall not ever violate the OK/Apply/Cancel affinity. * So if you need to add multiple buttons, add them in right to left order. * addButton(3); addButton(2); addButton(1) will result in Debug;1;2;3;OK;Apply;Cancel * @param btn The Button to be added to the page's button bar. */ public void addButtonToBar(Button btn) { if (btn == null) { throw new RuntimeException("addButtonToBar: btn parameter is null"); } if (formType != FormType.DIALOG) { // FORM and EMBEDDED types HorizontalPanel btnpanel = getButtonPanel(); btnpanel.insert(btn, 1); } else { // DIALOG type ButtonBar bar = getDialog().getButtonBar(); bar.insert(btn, 1); } // updatePageButtonTabIndexes(); } /** * Update the tab indexes of the buttons in the button area */ // private void updatePageButtonTabIndexes() { // Container buttons; // if (formType != FormType.DIALOG) { // buttons = getButtonPanel(); // } else { // buttons = getDialog().getButtonBar(); // } // int ti = PAGE_BUTTON_STARTING_TABINDEX; // List btnlist = buttons.getItems(); // for (Component c : btnlist) { // if (c instanceof Button) { // c.setTabIndex(ti); // ti++; // } // } // } /** * @return the okSelectionListener */ public SelectionListener getOkSelectionListener() { return okSelectionListener; } /** * @param okSelectionListener the okSelectionListener to set */ public void setOkSelectionListener(SelectionListener okSelectionListener) { this.okSelectionListener = okSelectionListener; } /** * @return the applySelectionListener */ public SelectionListener getApplySelectionListener() { return applySelectionListener; } /** * @param applySelectionListener the applySelectionListener to set */ public void setApplySelectionListener(SelectionListener applySelectionListener) { this.applySelectionListener = applySelectionListener; } /** * @return the cancelSelectionListener */ public SelectionListener getCancelSelectionListener() { return cancelSelectionListener; } /** * @param cancelSelectionListener the cancelSelectionListener to set */ public void setCancelSelectionListener(SelectionListener cancelSelectionListener) { this.cancelSelectionListener = cancelSelectionListener; } private class DefaultOKApplySelectionListener extends SelectionListener { SubmitMode submitSetting = null; public DefaultOKApplySelectionListener(SubmitMode setting) { if (setting == null) { throw new RuntimeException("DefaultOKSelectionListenerII: No SubmitMode value supplied"); } submitSetting = setting; } @Override public void componentSelected(ComponentEvent ce) { setSubmitMode(submitSetting); ce.stopEvent(); Registry.register("forceClose", "true"); if (getSubmitMode() == SubmitMode.APPLY) { Log.debug("Form Apply Button clicked"); } else if (getSubmitMode() == SubmitMode.OK) { Log.debug("Form OK Button clicked"); } if (mode == FormMode.CREATE) { setBusy("ACTIONTASK", "Please wait...", "Creating..."); } else if (mode == FormMode.EDIT) { setBusy("ACTIONTASK", "Please wait...", "Updating..."); } else if (mode == FormMode.DELETE) { setBusy("ACTIONTASK", "Please wait...", "Deleting..."); } disableActionButtons(); createTaskParameters(getActionTask()); if (!actionTask.invoke()) { clearBusy("ACTIONTASK"); postActionTaskProcessing(false); } } } /** * Default Cancel Button Handler */ private class DefaultCancelSelectionListener extends SelectionListener { @Override public void componentSelected(ComponentEvent ce) { Object src = ce.getSource(); Log.debug("cancel button ::: Form Type:" + getFormType() + "Form Name:" + getFormName()); Log.debug("cancel button:" + src.getClass().getName()); Log.debug("StandardComponents.cancelButton.addSelectionListener: Clicked"); Log.debug("close dialog"); if (!isForceClose() && isDirty()) { if (isDirty() && getCancelTask() != null) { runCancelTask(); } } if (getFormType() == FormType.DIALOG) { getDialog().hide(); } else { Log.debug("close app"); StandardComponents.closeApplication(); } } } /** * Executes the Cancel Task for the page */ public void runCancelTask() { setBusy("CANCELTASK", "Please Wait", "Performing Server Cleanup for Cancel"); Log.debug("in cancel code"); if (isDirty()) { Log.debug("dirty: " + isDirty()); createTaskParameters(getCancelTask(), false); if (!cancelTask.invoke()) { clearBusy("CANCELTASK"); } } } /** * This method processes the Action Task results. * It is either called by the completion of the Action Task or by the componentSelected event handler if no task is run * It processes any ReturnGroupHandler data return, then * @param ranTask */ private void postActionTaskProcessing(boolean ranTask) { createdObjectOID = ""; String folderOID = ""; IEElement md; Log.trace("formActionTask.storeDataChanged: Enter"); // Confirm that the if (ranTask && assertGroupHasData(getActionTaskGroup(), getTaskDataKey(), "Did not find an object in the task results - does it already exist?")) { // If we get here, we know the group is ok, there is at least one element, and obid has a value so we can skip the checks, reducing the lines of code needed md = getActionTaskGroup().getElementAt(0); // get the obid of the newly created object createdObjectOID = (String) md.getValue("obid"); // Generate a folderOID by using either the returned parentFolder attribute in the data, then the openerOID, then the containerOID folderOID = (String) md.getValue("parentFolder"); if (folderOID == null || folderOID.isEmpty()) { // If they haven't supplied a specific parent, use the openerOid, then the containerOid as the target. folderOID = Registry.get("openerOid"); if (folderOID == null || folderOID.isEmpty()) { folderOID = Registry.get("containerOid"); } } } if (returnGroupHandler != null) { if (returnMode == null && !actionTask.isNullTask()) { // Default returnMode and the action task was not the NULL task, return the task group returnGroupHandler.sendReturnGroup(getActionTaskGroup()); } else if (returnMode == null && getActionTask().isNullTask()) { // Default returnMode and the action task WAS the NULL task, return the form data IEGroup rg = createReturnGroup(false); returnGroupHandler.sendReturnGroup(rg); } else if (returnMode == DataReturnMode.TASK) { // Page wants the action task data returned returnGroupHandler.sendReturnGroup(getActionTaskGroup()); } else if (returnMode == DataReturnMode.FORM) { // Page wants the form data returned IEGroup rg = createReturnGroup(false); returnGroupHandler.sendReturnGroup(rg); } } if (folderOID != null && folderOID.contains("wt.folder.SubFolder")) { handleFolderCompletion(getPage(), getActionTaskGroup(), folderOID, getSubmitMode()); } else { if (folderOID != null && mode == FormMode.DELETE) { handleCompletion(getPage(), getActionTaskGroup(), folderOID, getSubmitMode()); } else { handleCompletion(getPage(), getActionTaskGroup(), createdObjectOID, getSubmitMode()); } } if (getSubmitMode() == SubmitMode.APPLY) { handleApply(); } Log.trace("formActionTask.storeDataChanged: Exit"); } /** * Deals with the Apply Button behavior (leave all the fields alone so the user can make a single change and Apply again) * or reset all the fields to their original values so the page starts over. */ public void handleApply() { // if this is an APPLY, then reset the field values so dirty will be based on the last apply // the OK and APPLY buttons can't be enabled after an APPLY until a field is dirty // if clearOnApply is set to true, then all the fields' values will be reset to original values if created successfully // if clearOnApply is set to false, then all the fields' values will be reset to latest values if (clearOnApply) { // If clearOnApply and the task was NOT successful, then we purposefully don't do a reset. // However, we also do NOT want to do a resetOriginalValues()! if (createdObjectOID != null && !createdObjectOID.isEmpty()) { reset(); // reset() kills the hidden fields so we must set them up again // (similar to what invoke() does after IT calls reset()) setupHiddenFields(); } } else { resetOriginalValues(); } if (formType == FormType.FORM || formType == FormType.EMBEDDED) { if (focusField != null) { focusField.focus(); } } else if (formType == FormType.DIALOG) { if (focusField != null) { getDialog().setFocusWidget(focusField); getDialog().focus(); } } validate(); } /** * sets clearOnApply. If clearOnApply is true then all the fields' values will be reset to original values if created successfully when apply button clicked * if clearOnApply is set to false, then all the fields' values will be reset to latest values * * @param flag */ public void setClearOnApply(boolean flag) { clearOnApply = flag; } /** * @return the returnGroupHandler */ public ReturnGroupHandler getReturnGroupHandler() { return returnGroupHandler; } /** * @param returnGroupHandler the returnGroupHandler to set */ public void setReturnGroupHandler(ReturnGroupHandler returnGroupHandler) { this.returnGroupHandler = returnGroupHandler; } /** * Sets the source of data for a ReturnGroupHandler - FORM or TASK * @param setting A DataReturnMode enum value */ public void setReturnMode(DataReturnMode setting) { returnMode = setting; if (setting == null) { StandardComponents.alert(FormMode.LIST, MsgLevel.ERROR, "setReturnMode was given a null argument"); returnMode = DataReturnMode.FORM; } } /** * Returns the source of data for a ReturnGroupHandler - FORM or TASK * @return A DataReturnMode enum value */ public DataReturnMode getReturnMode() { return returnMode; } /** * creates a group out of all fields in the form, including the hidden ones into the specified task. Data structure objects (IEGroup and TreeStore) will be added directly to the group * * @param hiddenOnly When true, only the fields in the hidden panel will be added (used for the initialization task) * @return An IEGroup with a single element, containing the values of all fields scraped from the form */ public IEGroup createReturnGroup(boolean hiddenOnly) { //TODO: BEH: Why isn't this code used ultimately to generate the task params? This data could be captured and then used to generate the task params. Map formToGroupMap = getActionTask().getFormToTaskMap(); IEGroup group = new IEGroup("results"); IEElement el = new IEElement(); // Map taskParamMap = task.getParamMap(); List> fields = getBodyPanel().getFields(); String pid; for (Field field : fields) { // Don't generate a param for a field without a name String name = field.getName(); if (name == null || name.isEmpty()) { Log.debug("Skipping a field because it has no name. Class: " + field.getClass().getName() + " Value: " + field.getRawValue()); continue; } if (name.equals(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME)) { String val = (String) field.getValue(); if (val == null) { val = ""; } el.addAtt(new IEAtt(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME, val)); continue; } String safeName = name; if (name.contains(".")) { Log.debug("Replacing dots with underscores for field param name: " + name); safeName = safeName.replace(".", "_"); } // Pull the attribute from the element corresponding to the field name // Check the formMap to see if we need to pull a different attribute for this field String mapname = formToGroupMap.get(safeName); if (mapname == null) { mapname = safeName; } // Check to see if we are doing only the fields in the hidden panel. Continue out of the loop //if we are and the field is not parented by the panel with the id 'hidden' if (hiddenOnly) { if (!StandardComponents.isFieldHidden(field)) { Log.debug("Skipping field: " + name + " because it is not in the hidden panel"); continue; } // Widget parent = field.getParent(); // if (parent instanceof LayoutContainer) { // LayoutContainer lc = (LayoutContainer) parent; // pid = lc.getId(); // if (pid != null && !pid.equals("hidden")) { // Log.debug("Skipping field: " + name + " because it is not in the hidden panel"); // continue; // } // } } try { if (field instanceof ComboBox) { // For combo boxes, the value in the map can be colon-separated. So it could be 'a', 'a:b' or ':b'. // 'a' indicates that the internal value of the combo box should go to the attribute called a. The display value is not collected. // 'a:b' indicates that the internal value of the combo box should go to a and the display value should go to b. //':b' indicates that the display value of the combo box should go to b. The internal value is not collected. ComboBox cb = (ComboBox) field; ModelData md = cb.getValue(); String value = ""; String dvalue = ""; if (md != null) { value = md.get(cb.getValueField()); dvalue = md.get(cb.getDisplayField()); } String[] combomap = mapname.split(":"); if (combomap.length == 1) { el.addAtt(new IEAtt(combomap[0], value)); } else { if (combomap[0] == null || combomap[0].isEmpty()) { el.addAtt(new IEAtt(combomap[1], dvalue)); } else { el.addAtt(new IEAtt(combomap[0], value)); el.addAtt(new IEAtt(combomap[1], dvalue)); } } } else if (field instanceof AdapterField) { Object result = null; AdapterField af = (AdapterField) field; String widget = field.getData("type"); if (widget != null) { if (widget.equals("tree")) { Object x = af.getData("widget"); if (x != null && (x instanceof StandardTree)) { StandardTree stdTree = (StandardTree) af.getData("widget"); result = stdTree.getTreeStore(); } else { Log.error("type was set to tree, but the widget was either null or was not a TreeGrid - AdapterField: " + field.getName()); } } else if (widget.equals("table")) { Object x = af.getData("widget"); if (x != null && (x instanceof StandardTable)) { StandardTable stdTable = (StandardTable) af.getData("widget"); IEGroup g = ((IEGroup) stdTable.getGrid().getStore()).deepClone(); result = g; } else { Log.error("type was set to table, but the widget was either null or was not a Grid - AdapterField: " + field.getName()); } } else { Log.error("An ApapterField type was not valid: " + widget + " AdapterField: " + field.getName()); } } else { Log.error("An AdapterField type was null: " + field.getName()); } el.addAtt(new IEAtt(mapname, result)); } else { String val = StandardComponents.getFieldValue(field); Log.trace("Adding Att to return group - " + mapname + "=" + val); el.addAtt(new IEAtt(mapname, val)); } } catch (Exception e) { Log.error(" field has no value set, successfully caught exception " + e.toString()); } } group.addElement(el); return group; } /** * Method to handle the completion of the primary functions of a UI page - create, edit, editmulti. Consults the Action maps to see what to do with the * window, the parent window and whether to use a popup to display anything. * * @param page The StandardForm launching this method * @param group The group with the data from the server operation * @param obid The object ID of the object to which we will forward * @param submitMode Indicates whether this is an OK or an Apply press (OK will dismiss the window, but Apply will force a full revalidation and leave all * fields in place) */ public void handleCompletion(StandardForm page, IEGroup group, String obid, SubmitMode submitMode) { // TODO: This needs to be moved to a Windchill-specific CompletionHandler class //truncate the obid in all cases - the properties pages often puke on the ufids. String theobid = StandardComponents.getShortOid(obid); MsgLevel level = group.getSuccess(); // // If we ran a server side task, we still need to check to see if we have a return group handler so we can interact with a parent table or tree // if (getReturnGroupHandler() != null && returnMode==DataReturnMode.TASK) { // getReturnGroupHandler().sendReturnGroup(group); // } WindowMode windowAction; if (forcedWindowAction == null) { windowAction = StandardComponents.getWindowAction(page.form.getMode(), level, submitMode); } else { windowAction = forcedWindowAction; } ParentMode parentAction; if (forcedParentAction == null) { parentAction = StandardComponents.getParentAction(page.form.getMode(), level); } else { parentAction = forcedParentAction; } if (page.formType == FormType.FORM) { if (parentAction == ParentMode.FORWARD) { if (theobid == null || theobid.isEmpty()) { throw new RuntimeException("Cannot forward to the new object - the obid parameter was not supplied"); } //If the obid is a Version Reference (starts with VR), and the returning oid is equal to the opening oid, we need to refresh //as the browser will ignore a request to forward to the same URL if (theobid.startsWith("VR") && theobid.equals(Registry.get("oid"))) { StandardComponents.refreshParent(); } String url = group.getElementAt(0).getValue("detailsUrl"); if (url != null && !url.isEmpty()) { Log.info("Forward parent to URL - " + url); StandardComponents.forwardParent(url); } else { //R10: http://acm.corp.cat.com/acm/app/#ptc1/tcomp/infoPage?oid=OR%3Awt.part.WTPart%3A1505992&u8=1 Log.info("Forward parent to R10 URL -" + "/app/#ptc1/tcomp/infoPage?oid=" + theobid + "&u8=1"); StandardComponents.forwardParent("/app/#ptc1/tcomp/infoPage?oid=" + theobid + "&u8=1"); } } else if (parentAction == ParentMode.TOFOLDER) { if (theobid == null || theobid.isEmpty()) { throw new RuntimeException("Cannot forward to parent folder - containerOid was not specified"); } Log.info("Forward parent to R10 Folder URL -" + "/app/#ptc1/tcomp/infoPage?oid=" + theobid + "&u8=1"); StandardComponents.forwardParent("/app/#ptc1/tcomp/infoPage?oid=" + theobid + "&u8=1"); } else if (parentAction == ParentMode.REFRESH) { StandardComponents.refreshParent(); } } // TODO: If/when a console object becomes available, feed all messages to that console // Produce a message box whenever the action map indicates POPUP_NEEDED PopupMode popupAction = StandardComponents.getPopupAction(page.form.getMode(), level); if (popupAction == PopupMode.NEEDED) { StandardComponents.alert(page, level, group.getMessagesAsHtml()); } else { // If we aren't doing a popup, we may need to close the window here if (windowAction == WindowMode.CLOSE) { if (page.formType == FormType.FORM) { StandardComponents.closeApplication(); } else if (page.formType == FormType.DIALOG) { page.form.getDialog().hide(); } } } } /** * Method to handle the completion of the primary functions of a UI page launched from a Folder panel - create, edit, editmulti. Consults the Action maps to * see what to do with the window, the parent window and whether to use a popup to display anything. * * @param page The StandardForm object calling this method * @param group The group with the data from the server operation * @param obid The name of the attribute in the group's data with the object's retrieval ID (needed to create parent forwarding URLs) * @param submitMode Indicates whether this is an OK or an Apply press (OK will dismiss the window, but Apply will force a full revalidation and leave all * fields in place) */ public void handleFolderCompletion(StandardForm page, IEGroup group, String obid, SubmitMode submitMode) { //TODO: This needs to be moved to a Windchill-specific CompletionHandler class MsgLevel level = group.getSuccess(); // // If we ran a server side task, we still need to check to see if we have a return group handler so we can interact with a parent table or tree // if (getReturnGroupHandler() != null && returnMode==DataReturnMode.TASK) { // getReturnGroupHandler().sendReturnGroup(group); // } WindowMode windowAction; if (forcedWindowAction == null) { windowAction = StandardComponents.getWindowAction(page.form.getMode(), level, submitMode); } else { windowAction = forcedWindowAction; } ParentMode parentAction = null; if (forcedParentAction == null) { parentAction = StandardComponents.getParentAction(page.form.getMode(), level); } else { parentAction = forcedParentAction; } // Only run these behaviors for a plain old popup if (page.formType == FormType.FORM) { if (obid == null || obid.isEmpty()) { throw new RuntimeException("Cannot refresh / forward to the parent folder - the 'obid' parameter was not supplied"); } // The obid value will have been generated using the first non-null value of a 'parentFolder' attribute in the data, the openerOid parameter, or the containerOid parameter String theOpener = Registry.get("openerOid"); if (obid.equals(theOpener)) { StandardComponents.refreshParent(); Log.info("Parent refreshed"); } else { StandardComponents.forwardParent("/app/#ptc1/tcomp/infoPage?oid=" + obid + "&u8=1"); Log.info("Forward parent to URL -" + "/app/#ptc1/tcomp/infoPage?oid=" + obid + "&u8=1"); } } // TODO: If/when a console object becomes available, feed all messages to that console // Produce a message box whenever the action map indicates POPUP_NEEDED PopupMode popupAction = StandardComponents.getPopupAction(page.form.getMode(), level); if (popupAction == PopupMode.NEEDED) { StandardComponents.alert(page, level, group.getMessagesAsHtml()); } else { // If we aren't doing a popup, we may need to close the window here if (windowAction == WindowMode.CLOSE) { if (page.formType == FormType.FORM) { StandardComponents.closeApplication(); } else { page.form.getDialog().hide(); } } } } /** * @return the windowAction */ public WindowMode getWindowAction() { return forcedWindowAction; } /** * @param windowAction the windowAction to set */ public void setWindowAction(WindowMode windowAction) { forcedWindowAction = windowAction; } /** * @return the parentAction */ public ParentMode getParentAction() { return forcedParentAction; } /** * @param parentAction the parentAction to set */ public void setParentAction(ParentMode parentAction) { forcedParentAction = parentAction; } /** * Makes sure the group has at least one record and the specified attribute contains a non-null, non-empty value * * @param group The IEGroup object containing the task result * @param attrname The name of the expected attribute * @param alertmsg The message desired (will create a default "No data returned from the server call" if this value is null or empty * @return true if all tests pass. Otherwise, closeApplication will be called so the routine will never return. */ public static boolean assertGroupHasData(IEGroup group, String attrname, String alertmsg) { // Generate a default message String thealertmsg = alertmsg; if (thealertmsg == null || thealertmsg.isEmpty()) { thealertmsg = "No data returned from the server call"; } // The group we got was null if (group == null) { Log.error("assertGroupHasData: The group specified is null"); StandardComponents.alert(FormMode.ASSERT_EXIST, MsgLevel.FATAL, thealertmsg); return false; } // The group we got had no elements if (group.getElementCount() == 0 && group.getMessages().isEmpty()) { Log.warn("assertGroupHasData: The group has no elements and there are no other messages - show the default message"); StandardComponents.alert(FormMode.ASSERT_EXIST, MsgLevel.ERROR, thealertmsg); return false; } if (group.getElementCount() == 0) { Log.debug("assertGroupHasData: The group has no elements but there are other messages present"); return false; } // The attribute we got was null or empty if (attrname == null || attrname.isEmpty()) { group.addMessage("FATAL|" + thealertmsg); group.addMessage("FATAL|No attribute name for validating the group data was supplied"); } else { // The value of the attribute in the first element of the group is null or empty IEElement el = group.getElementAt(0); String val = (String) el.getValue(attrname); if (val == null || val.isEmpty()) { group.addMessage("FATAL|" + thealertmsg); group.addMessage("FATAL|The attribute '" + attrname + "' did not appear in the group data"); } } StandardComponents.alert(FormMode.ASSERT_EXIST, group); return true; } /** * Makes sure the group has at least one record and the specified attribute contains a non-null, non-empty value * * @param group The IEGroup object containing the task result * @return true if all tests pass. Otherwise, closeApplication will be called so the routine will never return. */ public static boolean assertGroupHasNoErrors(IEGroup group) { // The group we got was null if (group == null) { Log.error("assertGroupHasNoErrors: The group specified is null"); StandardComponents.alert(FormMode.ASSERT_EXIST, MsgLevel.FATAL, "assertGroupHasNoErrors: The group specified is null"); return false; } StandardComponents.alert(FormMode.ASSERT_EXIST, group); return true; } /** * Makes sure the group has no records * * @param group The IEGroup object containing the task result * @param alertmsg The message desired (will create a default "The object specified already exists" if this value is null or empty) * @return true if all tests pass. Otherwise, closeApplication will be called so the routine will never return. */ public static boolean assertGroupHasNoData(IEGroup group, String alertmsg) { // Generate a default message String thealertmsg = alertmsg; if (thealertmsg == null || thealertmsg.isEmpty()) { thealertmsg = "The object specified already exists"; } // The group we got was null if (group == null) { Log.error("assertGroupHasData: The group specified is null"); StandardComponents.alert(FormMode.ASSERT_NOT_EXIST, MsgLevel.FATAL, thealertmsg); return false; } // The group already had FATAL errors if (group.getSuccess() == MsgLevel.FATAL) { StandardComponents.alert(FormMode.ASSERT_NOT_EXIST, MsgLevel.FATAL, group.getMessagesAsHtml()); return false; } if (group.getElementCount() == 0) { return true; } else { return false; } } /** * Creates a task with a taskoutgroupname generated from the taskuri. This method will never re-use an existing instance of a task. * * @param task The TaskFactory enum entry of the task to be run * @return The configured task (also available via actionTask) */ public Task setupCancelTask(TaskFactory task) { return setupCancelTask(task, null, false); } /** * Creates a task with a taskoutgroupname generated from the taskuri. When the useCache is true, it will re-use an already configured task with the same * uri. * * @param task The TaskFactory enum entry of the task to be run * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via actionTask) */ public Task setupCancelTask(TaskFactory task, boolean useCache) { return setupCancelTask(task, null, useCache); } /** * Creates a task with a taskoutgroupname as specified (null means it will generate it from the taskuri). When the useCache is true, it will re-use an * already configured task with the same uri. * * @param task The TaskFactory enum entry of the task to be run * @param group The name of the group that will be returned by the task * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via actionTask) */ public Task setupCancelTask(TaskFactory task, String group, boolean useCache) { String taskuri = task.getTaskUri(); if (taskuri == null) { StandardComponents.alert(this, MsgLevel.FATAL, "Cancel task uri was not specified"); return null; } cancelTask = Task.newTask(task, group, useCache); cancelTaskGroup = cancelTask.getGroup(cancelTask.getTaskGroupOutName()); getCancelTaskGroup().addStoreListener(new StoreListener() { @Override public void storeDataChanged(StoreEvent se) { Log.trace("InitializerTask.storeDataChanged: Enter"); if (assertGroupHasData(getCancelTaskGroup(), getTaskDataKey(), "Initialization data could not be retrieved from task: " + getCancelTask().getUri())) { setFormFieldValues(getCancelTaskGroup()); } StandardComponents.alert(FormMode.ASSERT_EXIST, getCancelTaskGroup()); isValid(); clearBusy("CANCELTASK"); } }); return cancelTask; } /** * Creates focus listeners on all named fields on the page so we can show the tooltip in the button bar when the current field has an error. */ public void setupFocusHandler() { for (Field f : getBodyPanel().getFields()) { //TODO: Take this out if validation doesn't work when they tab out of the field anyway f.setValidateOnBlur(false); // add focus listener Field work; if (f.getName() != null && !f.getName().isEmpty()) { work = f; if (f instanceof CATMultiField) { work = ((CATMultiField) f).getTextField(); } work.addListener(Events.Focus, new Listener() { @Override public void handleEvent(BaseEvent be) { if (statusarea == null) { return; } Field f = (Field) be.getSource(); currentFocusField = f; showErrorInStatusArea(f); } }); work.addListener(Events.Blur, new Listener() { @Override public void handleEvent(BaseEvent be) { if (statusarea == null) { return; } Field f = (Field) be.getSource(); // currentFocusField = null; // showErrorInStatusArea(f); } }); work.addListener(Events.Valid, new Listener() { @Override public void handleEvent(BaseEvent be) { if (statusarea == null) { return; } Field f = (Field) be.getSource(); if (f == currentFocusField) { showErrorInStatusArea(f); } } }); work.addListener(Events.Invalid, new Listener() { @Override public void handleEvent(BaseEvent be) { if (statusarea == null) { return; } Field f = (Field) be.getSource(); if (f == currentFocusField) { showErrorInStatusArea(f); } } }); } } } /** * @return the sendOnlyDirtyOnEdit */ public boolean isSendOnlyDirtyOnEdit() { return sendOnlyDirtyOnEdit; } /** * @param sendOnlyDirtyOnEdit the sendOnlyDirtyOnEdit to set */ public void setSendOnlyDirtyOnEdit(boolean sendOnlyDirtyOnEdit) { this.sendOnlyDirtyOnEdit = sendOnlyDirtyOnEdit; } /** * creates task parameters for all fields in the form, including the hidden ones into the specified task. The parameters will be generated according to the * paramMode setting on the task (name=value or field=name=value) * * @param task The task to which the parameters will be added. */ public void createTaskParameters(Task task) { createTaskParameters(task, false); } /** * creates task parameters for all fields in the form, including the hidden ones into the specified task. The parameters will be generated according to the * paramMode setting on the task (name=value or field=name=value) * * @param task The task to which the parameters will be added. * @param hiddenOnly When true, only the fields in the hidden panel will be added (used for the initialization task) */ public void createTaskParameters(Task task, boolean hiddenOnly) { // Why isn't createReturnGroup used to generate the needed data instead of having separate code? if (task == null) { return; } // If we are configured to only send down changed fields, then we must clear out all task // parameter here. Here is the scenario this deals with: // 1. Bring up the dialog // 2. Edit field A // 3. Click OK // 4. You get some error back from the task. // 5. Undo your edit to field A and edit field B instead. // 6. Click OK // 7. Without this change, field A will get sent to the task since it was // added as a task param in step #3 and nobody has cleared it out again. if (sendOnlyDirtyOnEdit) { task.resetParams(); } Map taskParamMap = task.getFormToTaskMap(); List> fields = getBodyPanel().getFields(); for (Field field : fields) { // Don't generate a param for a field without a name String name = field.getName(); if (name == null || name.isEmpty()) { Log.debug("Skipping a field because it has no name. Class: " + field.getClass().getName() + " Value: " + field.getRawValue()); continue; } String safeName = name; if (name.contains(".")) { Log.debug("Replacing dots with underscores for field param name: " + name); safeName = safeName.replace(".", "_"); } // Check to see if we are doing only the fields in the hidden panel. Continue out of the loop //if we are and the field is not parented by the panel with the id 'hidden' boolean fieldIsHidden = StandardComponents.isFieldHidden(field); if (hiddenOnly && !fieldIsHidden) { Log.debug("Skipping field: " + name + " because it is not in the hidden panel"); continue; } if (!fieldIsHidden) { // Hidden field are always sent if (getMode() == FormMode.EDIT || getMode() == FormMode.MULTIEDIT) { // This only applies to Edit or Multi Edit pages if (sendOnlyDirtyOnEdit && !field.isDirty()) { // We skip if we are sending only dirty fields and the field is not dirty Log.debug("Field skipped due to edit settings: fieldIsHidden: " + fieldIsHidden + " dirty: " + field.isDirty() + " sendOnlyDirtyOnEdit: " + isSendOnlyDirtyOnEdit() + " form mode: " + getMode()); continue; } } } // Check the taskParamMap to see if we need to put this field value into a different parameter name String mapname = taskParamMap.get(safeName); if (mapname == null) { mapname = safeName; } String val = StandardComponents.getFieldValue(field); if (task.getParamMode() == ParamMode.DIRECT) { task.addTaskParam(mapname, val); //if(!mapname.equals(safeName)) // task.addTaskParam(safeName, val);//add task parameters for both mapname and safeName as numerous tasks are associated to the same widget and parameter keys differ } else { task.addTaskParam("field", mapname + "=" + val); //if(!mapname.equals(safeName)) // task.addTaskParam("field", safeName + "=" + val);//add task parameters for both mapname and safeName as numerous tasks are associated to the same widget } } } /** * Calls showErrorInStatusArea using the last field to accept focus. */ public void showErrorForCurrentFocusField() { if (currentFocusField == null) { return; } showErrorInStatusArea(currentFocusField); } /** * Shows the field label, the red ball, and the tooltip when there is a tooltip assigned to the field. * The data is shown in the left side of the button area on forms and dialogs. * @param f */ public void showErrorInStatusArea(Field f) { if (f.getName() != null && !f.getName().isEmpty() && f.getToolTip() != null && f.getToolTip().isEnabled()) { //statusarea.setVisible(true); statusAreaImage.setVisible(true); statusAreaMessage.setVisible(true); String flabel = f.getFieldLabel(); String fmsg = f.getToolTip().getToolTipConfig().getText(); String ftip = fmsg; if (!fmsg.startsWith(flabel)) { // Don't display the label field if it matches the start of the message field ftip = flabel + ": " + fmsg; } statusAreaMessage.setValue("  " + ftip); statusAreaImage.setTitle((String) statusAreaMessage.getValue()); } else { //statusarea.setVisible(false); statusAreaImage.setVisible(false); statusAreaMessage.setVisible(false); } // getBodyPanel().layout(true); int newwidth = calculateStatusareaWidth(); statusarea.setSize(newwidth, -1); statusarea.add(statusAreaImgAdapter, new ColumnData(StandardComponents.ICON_WIDTH)); statusarea.add(statusAreaMessage, new ColumnData(newwidth - StandardComponents.ICON_WIDTH)); statusarea.layout(true); if (formType == FormType.DIALOG) { // Nothing right now } else { getButtonPanel().setPosition(newwidth, 0); } // if (footer != null) { // footer.layout(); // } } private void configureStatusArea() { final int saheight = 27; if (statusAreaImage == null) { statusAreaImage = new Image("/Windchill/com/cat/gwt/org.cat.Main/images/windchill/form/exclamation.gif"); } if (statusAreaImgAdapter == null) { statusAreaImgAdapter = new AdapterField(statusAreaImage); statusAreaImgAdapter.setSize(StandardComponents.ICON_WIDTH, StandardComponents.WIDGET_HEIGHT); statusAreaImgAdapter.setId("statusAreaImgAdapter"); } if (statusAreaMessage == null) { statusAreaMessage = CATLabelField.newCATLabelField(this, "", ""); statusAreaMessage.setLabelSeparator(""); statusAreaMessage.setHideLabel(true); statusAreaMessage.setId("statusAreaMessage"); statusAreaMessage.setSize(300, saheight); } } private int calculateStatusareaWidth() { final int defaultWidth = 300; int result = defaultWidth; if (statusarea.isRendered()) { if (formType == FormType.DIALOG) { result = getDialog().getOffsetWidth() - getButtonBarWidth(defaultWidth) - 10 * (getDialog().getButtonBar().getItemCount() - 1); } else { result = getViewport().getOffsetWidth() - getButtonPanel().getOffsetWidth(); } } if (result < 0) { result = 0; } return result; } private int getButtonBarWidth(int defaultWidth) { int first = -1; int last = -1; // Find the first and last visible buttons in the button bar ButtonBar bar = getDialog().getButtonBar(); for (int i = 1; i < bar.getItemCount(); i++) { // Only look at buttons if (bar.getItem(i) instanceof Button) { // Only look at visible buttons if (bar.getItem(i).isVisible()) { // If we haven't already set first, set it with this visible button's position if (first == -1) { first = i; } // Always set the last to this visible button's position last = i; } } } // blow up if we failed to find either the first or last visible button (should never happen unless someone turns off all the buttons) if (first == -1 || last == -1) { // throw new RuntimeException("Could not find the first and last buttons in the page's button bar!"); return defaultWidth; } // get the rightmost position by adding the left and width of the last visible button, then subtract the left of the leftmost visible button int thewidth = (bar.getItem(last).getAbsoluteLeft() + bar.getItem(last).getOffsetWidth()) - bar.getItem(first).getAbsoluteLeft(); return thewidth; } /** * Returns the oid value (used to dispatch the page). * For Create screens, this will be the container object into which the object should be stored. * For Other screens, this will be a pointer to the object itself. * @return The String value of the object id used to dispatch the page */ public String getOid() { return oid; } /** * Sets the primary object id for the page execution. * For create pages, this should be the container into which the new object will be stored. * For all other pages, this should be the object being manipulated. * @param oid The object identifier for the object being manipulated. */ public void setOid(String oid) { this.oid = oid; } /** * Creates the standard Cancel button for the page */ protected void createCancelButton() { // Cancel button cancelButton = StandardComponents.createCancelButton(); } /** * Creates the standard OK button for the page */ protected void createFinishButton() { // OK button finishButton = StandardComponents.createOKButton(); finishButton.setType("submit"); } private MessageBox getWaitBox() { return waitBox; } /** * Creates the standard Apply button for Create pages */ protected void createApplyButton() { // Apply button applyButton = StandardComponents.createApplyButton(); applyButton.setType("submit"); applyButton.setItemId(APPLY); // so onEnter() can find it } private void setWaitBox(MessageBox waitBox) { this.waitBox = waitBox; waitBox.setModal(true); } /** * Makes the webAppURL for the application available to the form. * @param webAppURL the string representation of the webAppURL (like http://acm.corp.cat.com/Windchill) */ public void setWebAppURL(String webAppURL) { this.webAppURL = webAppURL; } /** * Returns the Finish (OK) Button for the form * The button will be created if necessary * @return The OK Button for the form */ public Button getFinishButton() { if (formType != FormType.DIALOG) { if (finishButton == null) { createFinishButton(); } } else { finishButton = getDialog().getButtonById(Dialog.OK); } return finishButton; } /** * Returns the Finish (OK) Button for the form * The button will be created if necessary * @return The OK Button for the form */ public Button getApplyButton() { if (applyButton == null) { createApplyButton(); } return applyButton; } /** * Returns the Finish (OK) Button for the form * The button will be created if necessary * @return The OK Button for the form */ public Button getCancelButton() { if (formType != FormType.DIALOG) { if (cancelButton == null) { createCancelButton(); } } else { cancelButton = getDialog().getButtonById(Dialog.CANCEL); } return cancelButton; } /** * Retrieves the HorizontalPanel object that holds all buttons at the bottom of the page. * This is only valid for non-DIALOG pages. * Use getDialog().getButtonBar for the equivalent functionality for a DIALOG page. * @return */ public HorizontalPanel getButtonPanel() { return buttonPanel; } /** * Retrieves the actual form area of the page (where all the page-specific content resides). * @return The CATFormPanel that is the form area for the page. */ public CATFormPanel getBodyPanel() { if (bodyPanel == null) { bodyPanel = StandardComponents.newBodyPanel(this); hiddenPanel = StandardComponents.newHiddenPanel(bodyPanel); bodyPanel.add(hiddenPanel); } return bodyPanel; } /** * @return the customHiddenFields */ @SuppressWarnings("ReturnOfCollectionOrArrayField") public List getCustomHiddenFields() { if (customHiddenFields == null) { customHiddenFields = new ArrayList(); } return customHiddenFields; } /** * Retrieves the Viewport used for the overall non-DIALOG page. * This is the object directly attached to the page root. * It is only valid when formType is not DIALOG. * @return The Viewport for the page. */ public Viewport getViewport() { return viewport; } /** * * @return The LayoutContainer holding all of the HiddenFields created to hold the data from getFormParameters. */ private LayoutContainer getHiddenPanel() { return hiddenPanel; } /** * Returns the minimum height needed for the page. * @return An int with the minimum height in pixels. */ public int getMinHeight() { return minHeight; } /** * Set the minimum height for the page (in pixels). * @param minHeight An int with the minimum height setting in pixels. */ public void setMinHeight(int minHeight) { this.minHeight = minHeight; } /** * Returns the minimum width needed for the page. * @return An int with the minimum width in pixels. */ public int getMinWidth() { return minWidth; } /** * Set the minimum width for the page (in pixels). * @param minWidth An int with the minimum width setting in pixels. */ public void setMinWidth(int minWidth) { this.minWidth = minWidth; } /** * @return the formName */ public String getFormName() { return formName; } /** * @param formName the formName to set */ public void setFormName(String formName) { this.formName = formName; } /** * @return the formLabel */ public String getFormLabel() { return formLabel; } /** * @param formLabel the formLabel to set */ public void setFormLabel(String formLabel) { this.formLabel = formLabel; if (formType == FormType.DIALOG) { container.getHeader().setVisible(false); } // if (Window.getTitle().isEmpty()) { // hdr = container.getHeader(); // } else { // hdr = getDialog().getHeader(); // } // Set the Window title only if we are a FORM if (formType == FormType.FORM) { if (Window.getTitle().isEmpty()) { Window.setTitle(entryPoint.getLabel()); } } else if (formType == FormType.DIALOG) { // For Dialogs, use the Dialog's header and shut off the header we created hdr = getDialog().getHeader(); hdr.setText(formLabel); // hdr.setVisible(false); } } /** * @return the helpID */ public String getHelpID() { return helpID; } /** * @param helpID the helpID to set */ public void setHelpID(String helpID) { this.helpID = helpID; } /** * Sets the mode of the form. If null, it defaults to LIST * @param mode The desired FormMode enum for the Form */ public void setMode(FormMode mode) { FormMode work = mode; if (work == null) { work = FormMode.LIST; } Registry.register("pageMode", work.toString()); this.mode = work; } /** * Returns the Mode of the form (CREATE, EDIT, DELETE, LIST, etc) * @return The FormMode enum value for this page. */ public FormMode getMode() { return mode; } /** * Creates new Hidden Fields for every entry found in the hiddenParamMap */ public void setupHiddenFields() { FormData fd = new FormData("100%"); fd.setWidth(getWidth()); Iterator ix = hiddenParamMap.keySet().iterator(); while (ix.hasNext()) { String key = ix.next(); String val = hiddenParamMap.get(key); addHiddenField(key, val, true); } for (HiddenField hf : getCustomHiddenFields()) { getHiddenPanel().add(hf); getBodyPanel().addHiddenField(hf); } } /** * Adds a new hidden field to the bodypanel. * @param fieldname The name of the Hidden field to be added * @param val The value for the newly created Hidden field. */ public void addHiddenField(String fieldname, String val) { addHiddenField(fieldname, val, false); } /** * Adds a new hidden field to the hidden panel (when formParam is true) or to the bodyPanel (when formParam is false). * @param fieldname The name of the Hidden field to be added * @param val The value for the newly created Hidden field. * @param formParam set to true to add to the form parameter hidden container. Otherwise, it will be added to the bodyPanel like any other field. */ private void addHiddenField(String fieldname, String val, boolean formParam) { // Field to hold object's oid on the page CATHiddenField hidden = CATHiddenField.newCATHiddenField(bodyPanel.getForm(), fieldname, fieldname); hidden.setValue(val); hidden.setAutoHeight(true); hidden.setAutoWidth(true); if (formParam) { hiddenPanel.add(hidden); } else { getBodyPanel().add(hidden); } getBodyPanel().addHiddenField(hidden); } /** * Empties all fields on the page that are in the FormPanel and that have names. Removes the hidden fields from the hidden panel (they will be rebuilt shortly). If they are also ComboBox, StandardTable or StandardTree objects, then their stores will be empties. */ public void reset() { // bodyPanel.reset(); getHiddenPanel().removeAll(); for (Field field : bodyPanel.getFields()) { if (!StandardComponents.isFieldHidden(field)) { String name = field.getName(); if (name != null && !name.isEmpty() && !name.equals("null")) { // Disable validation for this field during the reset. // Some validations depend on other field values and, // at this point in time, some of the fields are reset // but others are not. field.setFireChangeEventOnSetValue(false); field.reset(); field.setFireChangeEventOnSetValue(true); // Also reset any DependencyComponent associated with this field. // Otherwise, it will have a stale "startVal" which will cause // incorrect behavior. StandardComponents.resetFieldDependencyComponent(field); } } } formValid(false); } /** * Restores the original values for the form, stored when the init data was loaded. */ public void resetOriginalValues() { hasFormBeenApplied = true; for (Field field : bodyPanel.getFields()) { String name = field.getName(); if (name != null && !name.isEmpty() && !name.equals("null")) { Object value = field.getValue(); field.setOriginalValue(value); } } // turn off the APPLY and OK buttons formValid(false); } /** * Initializes the form validate processing. * All fields are added to a dive panel so the overall form status can be determined as validations occur. */ public void setupFormValidation() { Field focusfield = null; Log.debug("In CATFormLayoutsetupFormValidation."); divePanel = new FastMap(); // divePanel.put("$$BUSY", TRUE); for (Field field : bodyPanel.getFields()) { // field.setMessageTarget("tooltip"); // if (field instanceof CATMultiField) { // ((CATMultiField) field).getTextField().setMessageTarget("tooltip"); // } // if (field instanceof CATPicker) { // ((CATPicker) field).getText().setMessageTarget("tooltip"); // ((CATPicker) field).getSearchButtonAdapter().setMessageTarget("none"); // } String name = field.getName(); if (name != null && !name.isEmpty() && !"null".equals(name)) { Log.debug("Field: " + field.getName() + " tabindex: " + field.getTabIndex()); // Automatically focus on the field with tab index 1 Widget parent = field.getParent(); // field.getParent() returns null for the CATTextField in a MultiField so the // following null tests are a temporary work-around until the root cause is identified // of why the CATTextField in a multi-field doesn't have a parent if (parent instanceof LayoutContainer || (parent == null && field instanceof CATTextField)) { if (parent != null) { LayoutContainer lc = (LayoutContainer) parent; if (!"hidden".equals(lc.getId())) { if (!(field instanceof LabelField) && !(field instanceof HiddenField) && !(field instanceof CATHiddenField)) { if (field.isVisible() && field.isEnabled()) { if (focusfield == null) { focusfield = field; } } } } } else if (parent == null && field instanceof CATTextField) { // CATTextField in a CATMultiField if (field.isVisible() && field.isEnabled()) { if (focusfield == null) { focusfield = field; } } } divePanel.put(name, TRUE); Field thefield = field; // Special handling for the CATMultiField - redirect the events to the inner TextField. if (field instanceof CATMultiField) { thefield = ((CATMultiField) field).getTextField(); } Log.trace("Field " + name + " added to divePanel"); // Attach listeners from the fields for when they fire valid / invalid during validation thefield.addListener(Events.Valid, new Listener() { @Override public void handleEvent(FieldEvent fe) { Object objsource = fe.getSource(); //Log.trace("Field.Valid listener - Field:" + objsource); if (!(objsource instanceof Field)) { return; } Field fld = (Field) objsource; String name = fld.getName(); Log.debug("Received VALID event for field: " + name); updateDivePanel(name, true); } }); thefield.addListener(Events.Invalid, new Listener() { @Override public void handleEvent(FieldEvent fe) { Object objsource = fe.getSource(); //Log.trace("Field.Invalid listener - Field: " + objsource); if (!(objsource instanceof Field)) { return; } Field fld = (Field) objsource; String name = fld.getName(); Log.debug("Received INVALID event for field: " + name); updateDivePanel(name, false); } }); // Log.debug("About to pre-validate field: " + field.getName()); //field.focus(); // BEH: 2013-02-18 - turned off this validation step. //thefield.validate(); } } } if (focusfield != null) { Log.debug("Focusing on the first editable widget: " + focusfield.getName()); //focusfield.focus(); focusfield.validate(); } } /** * Returns the overall validity of the form. * Every dive panel object is tested to see if all are true. If so, then the form is valid. * Any false value found means the entire form is invalid. * @return True when all entries in the dive panel are marked as true. False otherwise. */ public boolean isValid() { Iterator ix = divePanel.keySet().iterator(); while (ix.hasNext()) { String key = ix.next(); boolean b = divePanel.get(key).booleanValue(); if (!b) { formValid(false); return false; } } formValid(true); return true; } // private void clearDivePanel(){ // Iterator ix=divePanel.keySet().iterator(); // while(ix.hasNext()){ // String key=ix.next(); // divePanel.put(key,TRUE); // } // } /** * Forces validation on every field on the page. */ public void validate() { // If the dependency processor looks like it's going to run, then clear the dive panel and the start values used by the DH to avoid validation. // This should make it revalidate every field on the page. // TODO: Maybe this should only re-evaluate the false items? // if(getDependencyProcessor()!=null){ // clearDivePanel(); // DependencyComponent.clearValues(); // } List> fields = bodyPanel.getFields(); for (Field field : fields) { field.isValid(); } showErrorForCurrentFocusField(); } /** * The method to override to change how the form reacts to its overall validation status (typically, the finish button is enabled or disabled according to * the validity boolean passed to the routine). * * @param valid set to true if the overall form is valid, false otherwise */ public void formValid(boolean valid) { boolean theValid = valid; if (hasFormBeenApplied) { // if the form as been applied, first check to see that at least one field is dirty // before allowing the OK and APPLY buttons to be enabled boolean isDirty = false; for (Field field : bodyPanel.getFields()) { String name = field.getName(); if (name != null && !name.isEmpty() && !"null".equals(name)) { if (field.isDirty()) { isDirty = true; break; } } } if (!isDirty) { // if nothing is dirty and this is after an apply, do not turn on the OK and APPLY buttons theValid = false; } } if (formType == FormType.FORM) { if (finishButton != null) { finishButton.setEnabled(theValid); } } else if (formType == FormType.DIALOG) { getDialog().getButtonById(Dialog.OK).setEnabled(theValid); } if (applyButton != null) { if (applyButton != null) { applyButton.setEnabled(theValid); } } } /** * Disables the OK and / or Apply buttons on a page */ public void disableActionButtons() { if (formType == FormType.FORM) { if (finishButton != null) { finishButton.setEnabled(false); } } else if (formType == FormType.DIALOG) { getDialog().getButtonById(Dialog.OK).setEnabled(false); } Button ab = applyButton; if (ab != null) { ab.setEnabled(false); } } /** * @return the formType */ public FormType getFormType() { return formType; } private void updateDivePanel(String name, boolean valid) { divePanel.put(name, Boolean.valueOf(valid)); boolean status = isValid(); Log.info("Overall form validity: " + status); } /** * @param formType the formType to set */ public void setFormType(FormType formType) { this.formType = formType; } /** * Updates the task parameters for the specified task by scraping the current form. * @param task The Task that will get the parameters from the current form. */ protected void replaceTaskParameters(Task task) { Map pmap = task.getFormToTaskMap(); Iterator ix = pmap.keySet().iterator(); // Cycle through the parameters in the task's paramMap while (ix.hasNext()) { // Fname holds the name of the field to be scraped - pname holds the parameter name to generate String fname = ix.next(); String pname = pmap.get(fname); if (fname != null && !fname.isEmpty() && pname != null && !pname.isEmpty()) { Log.trace("Searching for a field value to substitute for param '" + fname + "' from field '" + pname + "'"); for (Field field : getBodyPanel().getFields()) { String name = field.getName(); if (name == null || name.isEmpty()) { continue; } if (name.equals(fname)) { // Found the matching Field name - substitute the value into the parameter map String realval = StandardComponents.getFieldValue(field); task.addTaskParam(pname, realval); Log.trace("Found the field value: " + realval); break; } } } } } /** * @return the dialog */ public Dialog getDialog() { if (dialog == null) { dialog = new CATDialog(); dialog.setData("form", this); dialog.setButtons(Dialog.OKCANCEL); dialog.setHideOnButtonClick(false); dialog.setClosable(false); dialog.setAnimCollapse(true); dialog.setModal(true); dialog.setBlinkModal(true); } return dialog; } /** * Sets up the Form / Dialog's busy mask * * @param message The string containing the desired mask text * @param progressText The string to be presented inside the progress bar */ public void setBusy(String message, String progressText) { setBusy("INTERNAL", message, progressText); } /** * Sets up the Form / Dialog's busy mask. * Each Asynchronous operation needs to make a set / clear busy pair of calls. * The first call will mask the form. * Subsequent calls will merely add their reason to the map of current busy reasons. * A RuntimeException is thrown if a second setBusy call with the same reason is encountered. * * @param reason The ID used to track multiple set/clear busy requests. * @param message The string containing the desired mask text * @param progressText The string to be presented inside the progress bar */ public void setBusy(String reason, String message, String progressText) { if (busyMap.containsKey(reason)) { throw new RuntimeException("setBusy called with a duplicate reason: " + reason + " busyMap: " + busyMap); } if (busyMap.isEmpty()) { if (getFormType() == FormType.FORM || getFormType() == FormType.EMBEDDED) { if (waitBox == null) { setWaitBox(MessageBox.wait("Progress", message, progressText)); } } else { if (!masked) { getDialog().mask(progressText); masked = true; } } } else { Log.debug("Form's busyMap was already established - left existing mask alone"); } updateDivePanel("$$BUSY", FALSE); busyMap.put(reason, reason); } /** * Tears down the Form / Dialog's busy mask */ public void clearBusy() { clearBusy("INTERNAL"); } /** * Tears down the Form / Dialog's busy mask * @param reason A string with a value to correspond to a previous setBusy call. */ public void clearBusy(String reason) { if (!busyMap.containsKey(reason)) { Log.warn("clearBusy called without a corresponding setBusy for reason: " + reason); } busyMap.remove(reason); if (busyMap.isEmpty()) { if (getFormType() == FormType.FORM || getFormType() == FormType.EMBEDDED) { if (getWaitBox() != null) { getWaitBox().close(); waitBox = null; } } else { if (getDialog() != null) { getDialog().unmask(); masked = false; } } updateDivePanel("$$BUSY", TRUE); } else { Log.debug("Form's busyMap still has entries: " + busyMap); } } /** * Forces the page validation status to false by adding a special flag to the dive panel's status map and setting it to FALSE */ public void forceDivePanelInvalid() { updateDivePanel("$$FORCE_VALID", FALSE); } /** * Whenever markDirty is used on this layout, it will clear the special flag so normal validation can resume. */ public void markDirty() { updateDivePanel("$$FORCE_VALID", TRUE); } /** * Returns the current validation status for a field name. This is used by the widgets when no change has been detected. * Instead of returning the value of validateValueLocal (which might give a false positive), it will use the divePanel status instead. * @param fieldName The name of the Field object on the page to be checked. * @return true if the Field specified by fieldName was last validated as VALID, false otherwise. */ public boolean isFieldValid(String fieldName) { if (fieldName == null || fieldName.isEmpty()) { throw new RuntimeException("A Framework widget has no name setting"); } Boolean status = divePanel.get(fieldName); if (status == null) { // throw new RuntimeException("A field name was not found in the dive panel: " + fieldName); return true; } return status; } /** * @return the footer */ public LayoutContainer getFooter() { return footer; } /** * @param footer the footer to set */ public void setFooter(LayoutContainer footer) { this.footer = footer; } /** * Toggles the Debug button on and off */ public void toggleDebug() { boolean newsetting = !tools.isVisible(); if (formType == FormType.EMBEDDED) { getFooter().setVisible(newsetting); } tools.setVisible(!tools.isVisible()); } /** * @return the dependencyDescriptor */ public String getDependencyDescriptor() { if (dependencyDescriptor == null) { dependencyDescriptor = getParamMap().get("dependencyjson"); } if (dependencyDescriptor == null) { dependencyDescriptor = ""; } Log.trace("Length of dependencyDescriptor: " + dependencyDescriptor.length()); return dependencyDescriptor; } /** * @param dependencyDescriptor the dependencyDescriptor to set */ public void setDependencyDescriptor(String dependencyDescriptor) { this.dependencyDescriptor = dependencyDescriptor; } /** * Returns the page's dependency processor * @return The DependencyProcessor for the page */ public DependencyProcessor getDependencyProcessor() { return dependency; } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 148624; to = 148531.Please, attach your messages.log to new issue! ----- /* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.ptc.gxt.framework.client.widget; import com.allen_sauer.gwt.log.client.Log; import com.extjs.gxt.ui.client.Registry; import com.extjs.gxt.ui.client.Style; import com.extjs.gxt.ui.client.Style.Scroll; import com.extjs.gxt.ui.client.core.FastMap; import com.extjs.gxt.ui.client.core.XDOM; import com.extjs.gxt.ui.client.data.ModelData; import com.extjs.gxt.ui.client.event.BaseEvent; import com.extjs.gxt.ui.client.event.ComponentEvent; import com.extjs.gxt.ui.client.event.Events; import com.extjs.gxt.ui.client.event.FieldEvent; import com.extjs.gxt.ui.client.event.Listener; import com.extjs.gxt.ui.client.event.MessageBoxEvent; import com.extjs.gxt.ui.client.event.SelectionListener; import com.extjs.gxt.ui.client.store.Store; import com.extjs.gxt.ui.client.store.StoreEvent; import com.extjs.gxt.ui.client.store.StoreListener; import com.extjs.gxt.ui.client.util.KeyNav; import com.extjs.gxt.ui.client.widget.Component; import com.extjs.gxt.ui.client.widget.ContentPanel; import com.extjs.gxt.ui.client.widget.Dialog; import com.extjs.gxt.ui.client.widget.Header; import com.extjs.gxt.ui.client.widget.HorizontalPanel; import com.extjs.gxt.ui.client.widget.Info; import com.extjs.gxt.ui.client.widget.LayoutContainer; import com.extjs.gxt.ui.client.widget.MessageBox; import com.extjs.gxt.ui.client.widget.Viewport; import com.extjs.gxt.ui.client.widget.button.Button; import com.extjs.gxt.ui.client.widget.button.ButtonBar; import com.extjs.gxt.ui.client.widget.form.AdapterField; import com.extjs.gxt.ui.client.widget.form.ComboBox; import com.extjs.gxt.ui.client.widget.form.DateField; import com.extjs.gxt.ui.client.widget.form.DateTimePropertyEditor; import com.extjs.gxt.ui.client.widget.form.Field; import com.extjs.gxt.ui.client.widget.form.FormPanel; import com.extjs.gxt.ui.client.widget.form.FormPanel.LabelAlign; import com.extjs.gxt.ui.client.widget.form.HiddenField; import com.extjs.gxt.ui.client.widget.form.LabelField; import com.extjs.gxt.ui.client.widget.form.NumberField; import com.extjs.gxt.ui.client.widget.form.Radio; import com.extjs.gxt.ui.client.widget.form.RadioGroup; import com.extjs.gxt.ui.client.widget.layout.ColumnData; import com.extjs.gxt.ui.client.widget.layout.ColumnLayout; import com.extjs.gxt.ui.client.widget.layout.FitLayout; import com.extjs.gxt.ui.client.widget.layout.FormData; import com.extjs.gxt.ui.client.widget.layout.FormLayout; import com.extjs.gxt.ui.client.widget.layout.HBoxLayout; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.Widget; import com.ptc.gxt.framework.client.EntryPointFactory; import com.ptc.gxt.framework.client.FormMode; import com.ptc.gxt.framework.client.FormType; import com.ptc.gxt.framework.client.FrameworkEntryPoint; import com.ptc.gxt.framework.client.TaskFactory; import com.ptc.gxt.framework.client.dependency.DependencyComponent; import com.ptc.gxt.framework.client.dependency.DependencyProcessor; import com.ptc.gxt.framework.client.entrypoint.CoreTaskFactory; import com.ptc.gxt.framework.client.event.ConfirmSelectListener; import com.ptc.gxt.framework.client.event.FrameworkEvents; import com.ptc.gxt.framework.client.ie.IEAtt; import com.ptc.gxt.framework.client.ie.IEElement; import com.ptc.gxt.framework.client.ie.IEGroup; import com.ptc.gxt.framework.client.ie.MsgLevel; import com.ptc.gxt.framework.client.ie.ParamMode; import com.ptc.gxt.framework.client.ie.Task; import com.ptc.gxt.framework.client.table.MergedModType; import com.ptc.gxt.framework.client.table.ReturnGroupHandler; import com.ptc.gxt.framework.client.table.TableGroupReturnGroupListener; import com.ptc.gxt.framework.client.widget.tools.DebugToolsMenu; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Defines the page. * The CATFormLayout is wrapped by StandardForm. * It defines a header, a body and footer information. * It operates in several modes, defined by the FormMode enum. * It produces several different types of form, defined by the FormType enum. * It provides standard buttons and actions associated to those buttons to support data acquisition, server update and cancel operations. * It provides for interaction with the launching window (refresh / forward to new urls) * It provides for standard actions when the action task completes (close on success, reset when apply button used, keep page when errors occur) * It provides validation services between fields via a DependencyProcessor tied to the page. * It provides for an initial data task, an action task and optionally, a cancel task. * * It provides for data transfer between in-browser pages via ReturnGroupListener. */ //public class CATFormLayout implements EntryPoint { public class CATFormLayout { private static final Boolean TRUE = true; private static final Boolean FALSE = false; /** *Identifier for use in retrieving the Apply button via getDialog().getButtonById(APPLY); */ public static final String APPLY = "apply"; private Header hdr; private String formName = "-No Form Name Specified-"; private String formLabel = ""; private String helpID = ""; private String helpURL = Registry.get("helpurl"); private ContentPanel container; private EntryPointFactory entryPoint; private String application = "FormLayout"; private Dialog dialog; private Viewport viewport = StandardComponents.getCATViewport(this); private HorizontalPanel buttonPanel; private CATFormPanel bodyPanel; private LayoutContainer hiddenPanel; private WindowMode forcedWindowAction; private ParentMode forcedParentAction; private int width = -1; private int height = -1; private int minWidth = -1; private int minHeight = -1; private int minCWidth = -1; private int minCHeight = -1; // private int formDataWidth = 250; private String oid; private String webAppURL; private FormType formType = FormType.FORM; private FormMode mode; private Map divePanel = new FastMap(); private Component focusField; private boolean hasFormBeenApplied; private boolean clearOnApply; private Task initTask; private IEGroup initTaskGroup; private Task actionTask; private IEGroup actionTaskGroup; private Task cancelTask; private IEGroup cancelTaskGroup; private IEGroup deferGroup; private IEElement initElement; private IEGroup initGroup; private ReturnGroupHandler returnGroupHandler; private DataReturnMode returnMode; // Any objects that need to be shared into inner classes // or get defined in the registry get declared here. // Use the name of the object as the registry entry // This should help with code completion private String messageBox; private String containerOid; private Button finishButton; private Button applyButton; private Button cancelButton; // private Button closeButton; private MessageBox waitBox; private boolean masked; private Map busyMap = new FastMap(); private Map hiddenParamMap; private int sbh = XDOM.getScrollBarWidth() + 12; private int sbw = XDOM.getScrollBarWidth() + 12; private StandardForm page; private List customHiddenFields; private Field currentFocusField; private LayoutContainer statusarea; private LayoutContainer footer; private Button tools; private boolean forceClose; private SelectionListener okSelectionListener; private SelectionListener applySelectionListener; private SelectionListener cancelSelectionListener; private SubmitMode submitMode = SubmitMode.OK; private String taskDataKey = "obid"; private Boolean dirty = false; private IEGroup dependencyjson; private boolean dependencySetup; private DependencyProcessor dependency; private String dependencyDescriptor; private String createdObjectOID; /** * When true, only dirty fields and hidden fields will be sent to edit tasks */ private boolean sendOnlyDirtyOnEdit; /** * The field that will display the widget's field label in the status area */ // private CATLabelField statusAreaLabel; /** * The field that will display the widget's tooltip in the status area */ private CATLabelField statusAreaMessage; /** * The Red Ball image for display between statusAreaLabel and statusAreaMessage */ private Image statusAreaImage; /** * The adapter to wrap statusAreaImage with to insure it displays properly */ private AdapterField statusAreaImgAdapter; /** * This creates a standard form layout for use by a page. It provides a standardized header with a title and a help icon, an OK / Apply / Cancel button set * and a CATFormPanel into which the developer will add the specific Field sub-classes to create the desired form. Once this method is run, the developer * should do a getBodyPanel() and start adding Field objects to the page. * * @param app The application name for this page * @return The form, already set up to take the specific fields. */ public static CATFormLayout newForm(String app) { return newForm(app, FormType.FORM); } /** * Creates a new CATFormLayout for the given EntryPointFactory enum entry. * @param app The EntryPointFactory enum entry for the StandardForm used with this CATFormLayout * @return The new CATFormLayout object */ public static CATFormLayout newForm(EntryPointFactory app) { return newForm(app, FormType.FORM); } /** * Creates a new CATFormLayout for the given app name and sets the form type as indicated. * @param app The name of the app that will resolve via the newEntryPoint method to a StandardForm * @param formtype A FormType of DIALOG, FORM or EMBEDDED * @return The new CATFormLayout object. */ public static CATFormLayout newForm(String app, FormType formtype) { EntryPointFactory epf = FrameworkEntryPoint.getEntryPointFactory(app); if (epf == null) { throw new RuntimeException("No EntryPoint found for application: " + app); } CATFormLayout newform = new CATFormLayout(); newform.setup(epf, formtype); return newform; } /** * Creates a new Form using an EntryPointFactory handle and FormType. * @param app The EntryPointFactory enum value for the desired page. * @param formtype The type of dialog being created (DIALOG, FORM, or EMBEDDED). * @return A CATFormLayout ready for use by setupLayout. */ public static CATFormLayout newForm(EntryPointFactory app, FormType formtype) { EntryPointFactory epf = app; if (epf == null) { throw new RuntimeException("No EntryPoint found for application: " + app); } CATFormLayout newform = new CATFormLayout(); newform.setup(epf, formtype); return newform; } // @Override /** * Creates the basic shell of the page * @param epf An EntryPointFactory enum value. * @param formtype The form type to be used for the form (@see FormType). */ public void setup(EntryPointFactory epf, FormType formtype) { Log.debug("FormLayout :onModuleLoad: start"); Log.debug("FormLayout : webappurl: " + Registry.get("webappurl")); Log.debug("Launching URL: " + com.google.gwt.user.client.Window.Location.getHref()); Log.debug("oid for this UI page: " + oid); Log.debug("containerOid for this UI page: " + containerOid); setEntryPoint(epf); setFormType(formtype); setMode(epf.getMode()); setFormName(epf.getName()); setHelpID(epf.getHelpID()); setApplication(epf.getName()); setMinHeight(epf.getHeight()); setMinWidth(epf.getWidth()); setWebAppURL((String) Registry.get("webappurl")); //TODO: Don't think this line is doing anything - nobody seems to set the registry entry it's looking for //setHiddenParamMap((Map) Registry.get(app + "_paramMap")); // For Creating Basic Container which is used to add all components.. container = new ContentPanel(); Log.trace("CATFormLayout container isMonitorWindowResize: " + container.isMonitorWindowResize()); container.setLayout(new FitLayout()); if (FormType.EMBEDDED != formType) { container.addStyleName("cat-background"); container.setId("background"); getViewport().removeStyleName("cat-background"); } CATFormPanel fp = getBodyPanel(); fp.setMethod(FormPanel.Method.POST); fp.setLabelAlign(LabelAlign.RIGHT); fp.setLabelWidth(150); fp.setHeaderVisible(false); fp.getHeader().setText(""); } /** * Creates a task with a taskoutgroupname generated from the taskuri. It will never re-use an existing instance of a task with a matching taskuri. * * @param task The TaskFactory enum entry of the task to be run * @return The configured task (also available via initFormTask) */ public Task setupInitTask(TaskFactory task) { return setupInitTask(task, null, false); } /** * Creates a task with a taskoutgroupname generated from the taskuri. When the useCache is true, it will re-use an already configured task with the same * uri. * * @param task The TaskFactory enum entry of the task to be run * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via initFormTask) */ public Task setupInitTask(TaskFactory task, boolean useCache) { return setupInitTask(task, null, useCache); } /** * Creates a task with a taskoutgroupname as specified (null means it will generate it from the taskuri). When the useCache is true, it will re-use an * already configured task with the same uri. * * @param task The TaskFactory enum entry of the task to be run * @param group The name of the group that will be returned by the task * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via initFormTask) */ public Task setupInitTask(TaskFactory task, String group, boolean useCache) { String taskuri = task.getTaskUri(); if (taskuri == null) { StandardComponents.alert(this, MsgLevel.FATAL, "Initializer task uri was not specified"); return null; } initTask = Task.newTask(task, group, useCache); String workgroup = group; if (group == null || group.isEmpty()) { workgroup = getInitTask().getTaskGroupOutName(); } initTaskGroup = getInitTask().getGroup(workgroup); final DefaultInitTaskListener initTaskListener = new DefaultInitTaskListener(); getInitTaskGroup().addStoreListener(initTaskListener); getInitTaskGroup().addListener(Store.Sort, new Listener() { @Override public void handleEvent(BaseEvent be) { initTaskListener.setSuppressClearBusy(true); Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { initTaskListener.setSuppressClearBusy(false); } }); } }); return getInitTask(); } /** * Invokes the page */ public void invoke() { // StandardComponents.removeLoadingMarker(); // Make sure we're not invoking a page that can't do anything. If we going to be modifying the DB, we need to either have an action task and / or a return group handler defined. if (getMode() == FormMode.CREATE || getMode() == FormMode.EDIT || getMode() == FormMode.DELETE) { if (getReturnGroupHandler() == null && (actionTask == null || actionTask.isNullTask())) { throw new RuntimeException("A page " + getPage().getClass().getName() + " in mode CREATE, EDIT or DELETE must have either an action task or a return group handler defined. This page has neither!"); } } if (dependency != null) { dependency.setProcessing(false); } // Reset the form in case we're executing this multiple times reset(); // Set up hardcoded data for the dialog (e.g., a fixed pulldown list or starting value) getPage().setupFixedData(); MergedModType mt = MergedModType.NONE; if (mode == FormMode.CREATE) { mt = MergedModType.ADD; } else if (mode == FormMode.EDIT) { mt = MergedModType.EDIT; } Field field = getBodyPanel().getField(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME); if (field == null) { addHiddenField(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME, mt.name()); } else { field.setValue(mt.name()); } setDefaultFocusField(); if (formType != FormType.DIALOG) { getViewport().layout(false); getViewport().show(); } else { getDialog().layout(true); getDialog().show(); } // Defer the rest of the work until after the form successfully paints (hopefully) // Two levels of defer to try to give all the formatting time to settle out so we don't get a Frankenscreen visible to the user Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { // Mask the form setBusy("FORM", "Please Wait...", "Initializing Form..."); Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { beginFormOperation(); } }); } }); } /** * Kicks off the init task. * Displays a message if the tasks are suppressed (debug tool for checking the layout of the page without waiting for data). * Turns on the dependency handling * Requests the form size adjustments to get the layouts to "settle" */ private void beginFormOperation() { Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { runInitTask(); String rt = (String) Registry.get("runtasks"); if (rt != null && !rt.equalsIgnoreCase("true")) { StandardComponents.alert(getSelf(), MsgLevel.WARN, "Tasks are not running - just letting you check form layouts"); } activateDependencyHandler(); initialFormAdjustment(); } }); } /** * Adjusts the form size down one pixel in height to help get the form to settle */ private void initialFormAdjustment() { // This is to force a resize to get the page to properly layout (some things just don't work right during pre-render layout) Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { jiggleForm(false); finalFormAdjustment(); } }); } /** * Adjusts the form back to its requested size, sets the default focus and clears the busy mask. */ private void finalFormAdjustment() { // This restores the original requested size and causes one more adjustment - hopefully it will be more reliable this way Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { jiggleForm(true); setDefaultFocus(); clearBusy("FORM"); } }); } private void activateDependencyHandler() { // Call the dependency processor Log.debug("Calling the DependencyProcessor"); // Restart the processor if the page is being reused if (dependency != null) { Log.debug("Restarting the dependency processor on the page"); dependency.processPage(getBodyPanel(), false); validate(); } else { // If we have already attempted to process the dependency file and it's still null, we won't get a better answer on the second attempt, so skip the task. if (!dependencySetup) { // If we are the top-level page, entrypointclassname should point to our classname - if so, use whatever descriptor is present if (getPage().getClass().getName().equals(Registry.get("entrypointclassname"))) { Log.debug("Opening the dependency descriptor for the top-level page: " + getPage().getClass().getName()); dependency = DependencyProcessor.newDependencyProcessor(getPage(), getDependencyDescriptor()); dependencySetup = true; } else { // we are not the top level page, run the task to get the dependency descriptor setBusy("DH", "Please Wait...", "Initializing Form..."); Task dependencyTask; if (Registry.get("windchill") != null) { dependencyTask = Task.newTask(CoreTaskFactory.GET_DEPENDENCY_DESCRIPTOR); } else { dependencyTask = Task.newJspTask(CoreTaskFactory.GET_DEPENDENCY_DESCRIPTOR_SERVLET, "getDependencyDescriptor", false); } dependencyTask.addTaskParam("pageclass", getPage().getClass().getName()); dependencyjson = dependencyTask.getGroup("dependencyjson"); dependencyjson.addStoreListener(new StoreListener() { @Override public void storeDataChanged(StoreEvent se) { // Retrieve the task results and use them to open the descriptor returned in the data IEElement depel; String depjson; if (dependencyjson.getElementCount() > 0) { depel = dependencyjson.getElementAt(0); depjson = depel.getValue("dependencyjson"); dependencyDescriptor = depjson; } Log.debug("Opening the dependency descriptor for the dialog: " + getPage().getClass().getName()); dependency = DependencyProcessor.newDependencyProcessor(getPage(), getDependencyDescriptor()); dependencySetup = true; clearBusy("DH"); validate(); } }); dependencyTask.invoke(); } } } } /** * Adjusts the size of the form either up or down one pixel in height. * This seems to be needed to get all of the interactions in the layouts to settle out and actually do what the developer intends. */ private void jiggleForm(boolean up) { final int upsize = 1; final int downsize = -1; int adjustSize = downsize; if (up) { adjustSize = upsize; } int theheight = Window.getClientHeight(); int thewidth = Window.getClientWidth(); if (formType == FormType.FORM) { if (getEntryPoint().getHeight() != -1 && getEntryPoint().getWidth() != -1) { StandardComponents.setWindowSize(getEntryPoint().getHeight() - adjustSize, getEntryPoint().getWidth()); } } if (formType != FormType.DIALOG) { getViewport().setSize(getViewport().getWidth(), getViewport().getHeight() - adjustSize); } else { getBodyPanel().setLayoutData(new FormData("98% 98%")); } getContainer().layout(); Log.debug("Resized window to " + theheight + ":" + thewidth); } /** * @return the dirty */ public Boolean isDirty() { return dirty; } /** * @param dirty the dirty to set */ public void setDirty(Boolean dirty) { this.dirty = dirty; } /** * Runs the page's Init Task. Only the hidden fields are sent as parameters. */ public void runInitTask() { // This setBusy call MUST appear after the show calls or you can wind up with multiple copies of the mask setBusy("INITTASK", "Please wait...", "Initializing Form..."); createTaskParameters(getInitTask(), true); if (!initTask.invoke()) { // If we're using a StartElement with field data, use it to set the form field values instead of the task results if (getInitElement() != null) { setFormFieldValues(initGroup); } clearBusy("INITTASK"); } } /** * @return the startElement * @deprecated Use getInitElement instead */ @Deprecated public IEElement getStartElement() { return initElement; } /** * @return the startElement */ public IEElement getInitElement() { return initElement; } /** * @param initElement the startElement to set. This will be treated like a single element being returned from an initTask in the default group. */ public void setInitElement(IEElement initElement) { this.initElement = initElement; initGroup = new IEGroup("initGroup"); initGroup.addElement(initElement); setInitGroup(initGroup); } /** * @param startElement the startElement to set. This will be treated like a single element being returned from an initTask in the default group. * @deprecated Use setInitElement(IEElement) instead. */ @Deprecated public void setStartElement(IEElement startElement) { setInitElement(startElement); } /** * @param initGroup the initGroup to set. This will be treated like it is the default group being returned from an initTask. * */ public void setInitGroup(IEGroup initGroup) { this.initGroup = initGroup; // if (initElement != null && getInitTask() != null && !initTask.getUri().equals(CoreTaskFactory.NULL.getTaskUri())) { // //throw new RuntimeException("Error: you cannot specify an init task and then use an initElement to initialize the page"); // } if (getInitTask() == null) { initTask = Task.newTask(CoreTaskFactory.NULL); } getInitTask().addGroup(initGroup); getInitTask().setTaskGroupOutName(initGroup.getName()); } /** * @return the initTask */ public Task getInitTask() { return initTask; } /** * @return the actionTask */ public Task getActionTask() { return actionTask; } /** * @return the cancelTask */ public Task getCancelTask() { return cancelTask; } /** * @return the submitMode */ public SubmitMode getSubmitMode() { return submitMode; } /** * @param submitMode the submitMode to set */ public void setSubmitMode(SubmitMode submitMode) { this.submitMode = submitMode; } /** * @return the initTaskGroup */ public IEGroup getInitTaskGroup() { return initTaskGroup; } /** * @return the actionTaskGroup */ public IEGroup getActionTaskGroup() { return actionTaskGroup; } /** * @return the cancelTaskGroup */ public IEGroup getCancelTaskGroup() { return cancelTaskGroup; } /** * @return the webAppURL */ public String getWebAppURL() { return webAppURL; } /** * The default task listener for the init task */ @SuppressWarnings("ProtectedInnerClass") public class DefaultInitTaskListener extends StoreListener { private boolean suppressClearBusy = false; /** * Indicates if the busy indicator should be suppressed for this page * @param suppress True to suppress, false to allow the indicator to be used. */ public void setSuppressClearBusy(boolean suppress) { suppressClearBusy = suppress; } @Override public void storeDataChanged(StoreEvent se) { //TODO: BEH: Perhaps all these data changed can be handled by the dependencyhandler, especially this type // form.getWaitBox().close(); Log.trace("InitializerTask.storeDataChanged: Enter"); if (assertGroupHasData(getInitTaskGroup(), getTaskDataKey(), "Initialization data could not be retrieved from task: " + getInitTask().getUri())) { setFormFieldValues(getInitTaskGroup()); } StandardComponents.alert(FormMode.ASSERT_EXIST, getInitTaskGroup()); isValid(); if (!suppressClearBusy) { clearBusy("INITTASK"); } suppressClearBusy = false; } } /** * Takes the values from the first element in the group and processes them into matching field names on the form. Only Field objects will be picked up. Only those * with names will be processed. If the group has a FormMap, the mappings are honored during processing. Events should not fire from the setValue calls - a * later, final validation of all fields on the form happens right after the form is shown. * * @param group The group to be processed */ protected void setFormFieldValues(IEGroup group) { // Check to make sure we have decided whether or not the dependency file has been found and read before putting values on the page, which will eventually start triggering through the DH. if (!dependencySetup) { deferGroup = group; // Defer this processing until later Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { setFormFieldValues(deferGroup); } }); return; } if (group == null) { StandardComponents.alert(this, MsgLevel.FATAL, "setFormField values was given a non-existent group"); return; } if (group.getElementCount() == 0) { StandardComponents.alert(this, MsgLevel.FATAL, "setFormField values was given an empty group"); return; } // Get the group's individual form to group map Map formToGroupMap = group.getFormToGroupMap(); if (formToGroupMap.isEmpty()) { // Group didn't have one - use the init task's map formToGroupMap = getInitTask().getFormToTaskMap(); } setFormFieldValues(group.getElementAt(0), formToGroupMap); } /** * Takes the values in the element and processes them into matching field names on the form. Only Field objects will be picked up. Only those * with names will be processed. If the group has a FormMap, the mappings are honored during processing. Events should not fire from the setValue calls - a * later, final validation of all fields on the form happens right after the form is shown. * * @param el The IEElement to be processed * @param formToGroupMap A map of form names to group attribute names that will be used during processing */ protected void setFormFieldValues(IEElement el, Map formToGroupMap) { boolean showMissingData = Log.isDebugEnabled(); StringBuilder missingData = new StringBuilder(); List> fields = getBodyPanel().getFields(); for (Field field : fields) { // for each item in the group // loop through all the fields on the form until a match is found // Don't process a field without a name String fieldName = field.getName(); String safeFieldName = fieldName; if (fieldName == null || fieldName.isEmpty()) { Log.debug("Field skipped because it has no name: " + field.getClass().getName()); continue; } if (fieldName.contains(".")) { Log.debug("Replacing dots with underscores for field param name: " + fieldName); safeFieldName = safeFieldName.replace(".", "_"); } // Pull the attribute from the element corresponding to the field name // Check the formMap to see if we need to pull a different attribute for this field String mappedAttName = formToGroupMap.get(safeFieldName); if (mappedAttName == null) { mappedAttName = safeFieldName; } // Now check for the mapped attribute name in the element that was passed to // populate the form Object obj = el.getValue(mappedAttName); Object pulldownlistgroup = el.getValue(mappedAttName + "_legalvalues"); if (pulldownlistgroup != null && !(pulldownlistgroup instanceof IEGroup)) { pulldownlistgroup = null; } if (field instanceof ComboBox) { ((ComboBox) field).setStore((IEGroup) pulldownlistgroup); } // if (fieldName.equals(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME)) { // if (obj instanceof MergedModType) { // obj = ((MergedModType) obj).toString(); // } // } String val = ""; if (obj != null) { try { // Disable validation for this field because it might depend on the values // of other fields which "have not" been set yet. //StandardComponents.setFieldValidation(field, false); field.setFireChangeEventOnSetValue(false); if (obj instanceof String) { val = (String) obj; Log.debug("Setting field '" + fieldName + "' to attribute '" + mappedAttName + "' - val: '" + val + "'"); if (field instanceof RadioGroup) { List> radios = ((RadioGroup) field).getAll(); for (Field fieldRadio : radios) { Radio radio = (Radio) fieldRadio; if (val.equals(radio.getValueAttribute())) { // radio.setFireChangeEventOnSetValue(false); radio.setValue(Boolean.TRUE); // radio.setFireChangeEventOnSetValue(true); // field.setFireChangeEventOnSetValue(false); ((RadioGroup) field).setValue(radio); ((RadioGroup) field).setOriginalValue(radio); // field.setFireChangeEventOnSetValue(true); } } } else if (field instanceof CATMultiField) { // field.setFireChangeEventOnSetValue(false); ((CATMultiField) field).setValue(val); ((CATMultiField) field).setOriginalValue(val); ((CATMultiField) field).getTextField().setOriginalValue(val); // field.setFireChangeEventOnSetValue(true); } else if (field instanceof LabelField) { // field.setFireChangeEventOnSetValue(false); ((LabelField) field).setText(val); ((LabelField) field).setValue(val); ((LabelField) field).setOriginalValue(val); // field.setFireChangeEventOnSetValue(true); } else if (field instanceof NumberField) { // field.setFireChangeEventOnSetValue(false); field.clear(); if (val == null || val.isEmpty()) { field.setValue(null); field.setOriginalValue(null); } else { field.setValue(field.getPropertyEditor().convertStringValue(val)); field.setOriginalValue(field.getValue()); } // field.setFireChangeEventOnSetValue(true); } else if (field instanceof Radio) { continue; } else if (field instanceof CATComboBox) { CATComboBox box = ((CATComboBox) field); if (box.getStore() == null) { throw new RuntimeException("ComboBox: " + box.getName() + " is not bound to a Group"); } if (box.getValueField() == null || box.getValueField().isEmpty()) { throw new RuntimeException("ComboBox: " + box.getName() + " does not have a valuefield set up"); } if (box.getDisplayField() == null || box.getDisplayField().isEmpty()) { throw new RuntimeException("ComboBox: " + box.getName() + " does not have a displayfield set up"); } // Try matching the raw value with something in the store if (box.getValue() == null) { Log.debug("Using the value from the backing store: " + val); box.setValueFromStore(val); } // Try creating a custom hidden item in the store and then match the raw value against the store if (box.getValue() == null) { Log.debug("Using the hardcoded value: " + val); box.addNewPulldownListEntry(val, val, false); // make sure that if box.getValue() is null then both value and originalValue // set to the same thing. In gxt, if I setValueFromStore("") and then // call getValue() then getValue() does some logic and returns null. box.setValueFromStore(val); final IEElement originalValue = new IEElement(); originalValue.set(box.getValueField(), box.getRawValue()); box.setOriginalValue(originalValue); Log.trace("CATFormLayout --> val = " + val + ", getValue() = " + box.getValue() + ", raw value = " + box.getRawValue() + ", originalValue = " + box.getOriginalValue()); } box.setData("rawValue", val); box.setData("forcedValue", "y"); if (box.getValue() == null) { Log.debug("Combo values: raw: " + ((CATComboBox) field).getRawValue() + " value: " + box.getValue()); // throw new RuntimeException("Failed to set value on combo box: " + box.getName()); } // box.getValue is not null then it hasn't been set yet. if (box.getValue() != null) { box.setOriginalValue(box.getValue()); } // Log.debug("Combo values: raw: " + ((CATComboBox) field).getRawValue() + " internal: " + box.getValue().get(box.getValueField()) + " display: " + box.getValue().get(box.getDisplayField())); } else if (field instanceof DateField) { if (val.isEmpty()) { val = null; } DateTimePropertyEditor editor = ((DateField) field).getPropertyEditor(); Date dt = null; if (val != null) { dt = editor.convertStringValue(val); } field.setValue(dt); field.setOriginalValue(dt); } else { if (val == null || val.isEmpty()) { field.setValue(null); field.setOriginalValue(null); } else { field.setValue(val); field.setOriginalValue(val); } } } else if (obj instanceof IEGroup) { // look for an adapter field containing a table // if it is found set the group found in this attribute // to the group for the table in the matching adapter field if (field instanceof CATAdapterField) { IEGroup g = (IEGroup) obj; CATAdapterField af = (CATAdapterField) field; if (af.getTable() != null) { IEGroup work = (IEGroup) af.getTable().getGrid().getStore(); work.setFiresEvents(false); work.removeAll(); work.add(g.deepClone().getElementList()); work.setFiresEvents(true); work.signalDataChanged(); } } } } catch (ClassCastException cce) { Log.error("ClassCastException: " + cce); Log.error("Field: " + field.getClass().getName() + " val: " + val); Log.error("Couldn't use setValue - trying setRawValue"); field.setRawValue(val); } finally { // StandardComponents.setFieldValidation(field, true); field.setFireChangeEventOnSetValue(true); } } else { if (showMissingData) { boolean fieldIsHidden = StandardComponents.isFieldHidden(field); if (!fieldIsHidden) { if (missingData.length() > 0) { missingData.append(", "); } if (fieldName.equals(mappedAttName)) { missingData.append(fieldName); } else { missingData.append(mappedAttName).append(" (mapped from ").append(fieldName).append(")"); } } } Log.warn("Field '" + mappedAttName + "' was not found"); } } // Now that all of the values are loaded, we can safely perform validation. DependencyComponent.clearValues(); validate(); } /** * Sets the field name to look for in the task data to confirm the operation was successful and to use as the object ID for any follow-on processing (defaults to 'obid'). * @param setting The field name */ public void setTaskDataKey(String setting) { taskDataKey = setting; if (setting == null) { StandardComponents.alert(FormMode.LIST, MsgLevel.ERROR, "setTaskDataKey was given a null argument"); taskDataKey = "obid"; } } /** * Retrieves the field name used as the object ID for task follow-on operations * @return */ public String getTaskDataKey() { return taskDataKey; } /** * Creates a task with a taskoutgroupname generated from the taskuri. This method will never re-use an existing instance of a task. * * @param task The TaskFactory enum entry of the task to be run * @return The configured task (also available via actionTask) */ public Task setupActionTask(TaskFactory task) { return setupActionTask(task, null, false); } /** * Creates a task with a taskoutgroupname generated from the taskuri. When the useCache is true, it will re-use an already configured task with the same * uri. * * @param task The TaskFactory enum entry of the task to be run * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via actionTask) */ public Task setupActionTask(TaskFactory task, boolean useCache) { return setupActionTask(task, null, useCache); } /** * Creates a task with a taskoutgroupname as specified (null means it will generate it from the taskuri). When the useCache is true, it will re-use an * already configured task with the same uri. * * @param task The TaskFactory enum entry of the task to be run * @param group The name of the group that will be returned by the task * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via actionTask) */ public Task setupActionTask(TaskFactory task, String group, boolean useCache) { mode = getMode(); String taskuri = task.getTaskUri(); if (taskuri == null) { throw new RuntimeException("StandardForm::setupActionTask - action task uri not specified"); } actionTask = Task.newTask(task, group, useCache); actionTaskGroup = getActionTask().getGroup(getActionTask().getTaskGroupOutName()); if (mode == FormMode.CREATE || mode == FormMode.EDIT || mode == FormMode.DELETE) { getActionTaskGroup().addStoreListener(new StoreListener() { @Override public void storeDataChanged(StoreEvent se) { //TODO: Can this be placed after postActionTaskProcessing for maximum mask coverage? clearBusy("ACTIONTASK"); postActionTaskProcessing(true); } }); } return getActionTask(); } /** * Sets the component that should receive the focus when the page is ready for input * @param c The Component that will receive the focus. Should be an input type Component */ public void setFocusField(Component c) { focusField = c; if (c instanceof Field) { currentFocusField = (Field) c; } } /** * Computes the Focus field by finding the field with the lowest tabindex or the first editable field on the page if no tabindex values are found */ public void setDefaultFocusField() { if (focusField == null) { Field firstEditable = null; Field lowestTabOrder = null; int minTabOrder = 999; // If the page forced a focusField, just leave if (focusField != null) { return; } // Scan through the fields looking for the field with the lowest Tab Order, or the first editable field and make that the focusField for (Field f : getBodyPanel().getFields()) { if (f.isEnabled() && !f.isReadOnly() && !(f instanceof HiddenField) && !(f instanceof CATHiddenField) && !(f instanceof LabelField)) { int to = f.getTabIndex(); if (to < minTabOrder) { minTabOrder = to; lowestTabOrder = f; Log.debug("Lowest Tab Index Field: " + lowestTabOrder.getId() + "(" + lowestTabOrder.getName() + ")"); } if (firstEditable == null) { firstEditable = f; Log.debug("First Editable Field: " + f.getId() + "(" + f.getName() + ")"); } if (lowestTabOrder != null) { focusField = lowestTabOrder; } else { focusField = firstEditable; } } } if (focusField != null) { Log.debug("Focus Field: " + ((Field) focusField).getId() + "(" + ((Field) focusField).getName() + ")"); } else { Log.debug("Focus Field: No available editable field found - no focus field set"); } } } /** * Gives focus to the field specified by setFocusField(). */ public void setDefaultFocus() { if (formType == FormType.FORM || formType == FormType.EMBEDDED) { getViewport().show(); if (focusField != null) { focusField.focus(); } } else if (formType == FormType.DIALOG) { getDialog().show(); if (focusField != null) { getDialog().setFocusWidget(focusField); getDialog().focus(); } } } /** * A method to return 'this' for event handlers (their 'this' is the handler itself). * @return The CATFormLayout object for the page. */ public CATFormLayout getSelf() { return this; } /** * Sets whether or not to close, ignoring any page dirty status. * @param forceClose True to force the page to close regardless of any dirty setting encountered. */ public void setForceClose(boolean forceClose) { this.forceClose = forceClose; } /** * @return the forceClose */ public boolean isForceClose() { return forceClose; } /** * Returns the EntryPointFactory used by this layout. * @return The EntryPointFactory object for this layout. */ public EntryPointFactory getEntryPoint() { return entryPoint; } /** * @param entryPoint the entryPoint to set */ public void setEntryPoint(EntryPointFactory entryPoint) { this.entryPoint = entryPoint; } /** * @return the width */ public int getWidth() { return width; } /** * @param width the width to set */ public void setWidth(int width) { this.width = width; } /** * @return the height */ public int getHeight() { return height; } /** * @param height the height to set */ public void setHeight(int height) { this.height = height; } /** * Adds a hidden field to the Form for transmission to the server during init/action tasks * * @param key The name of the field * @param value The value for the field */ public void addParam(String key, String value) { getParamMap().put(key, value); } /** * Returns a particular value from the paramMap * * @param key The name of the hidden param desired * @return The value of the hidden param */ public String getHiddenParam(String key) { return hiddenParamMap.get(key); } /** * Returns a particular value from the paramMap * * @param key The name of the entry desired * @return The value of the map entry * @deprecated Use getHiddenParam instead */ @Deprecated public String getParam(String key) { return hiddenParamMap.get(key); } /** * Sets the hidden param map - the list of hidden fields to create in the form for use in task parameters for the init and / or action tasks for the form * * @param paramMap the paramMap to set */ @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter") public void setHiddenParamMap(Map paramMap) { this.hiddenParamMap = paramMap; } /** * Sets the hidden param map - the list of hidden fields to create in the form for use in task parameters for the init and / or action tasks for the form * * @param paramMap the paramMap to set * @deprecated Use {@link #setHiddenParamMap}(Map<String, String> paramMap) instead */ @Deprecated @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter") public void setParamMap(Map paramMap) { this.hiddenParamMap = paramMap; } private void reportWindowSizes() { Log.trace("---------------------------------------------------------------"); Log.trace("----- window actuals:" + com.google.gwt.user.client.Window.getClientWidth() + "," + com.google.gwt.user.client.Window.getClientHeight()); Log.trace("----- window minimums: " + minWidth + "," + minHeight); if (container.isRendered()) { Log.trace("----- container actuals: " + container.getWidth() + "," + container.getHeight()); Log.trace("----- container minimums: " + minCWidth + "," + minCHeight); } if (getViewport().isRendered()) { Log.trace("----- viewport actuals: " + getViewport().getWidth() + "," + getViewport().getHeight()); } if (bodyPanel.isRendered()) { Log.trace("----- Body actuals: " + bodyPanel.getWidth() + "," + bodyPanel.getHeight()); } } /** * Prepares the layout for use by running all the standard behaviors not related to the page-defined data. * This is run just before the invocation of the page. */ public void finalizeLayout() { // Set up the Enter / Escape key button presses of OK / Cancel if (formType == FormType.DIALOG) { new KeyNav(getDialog()) { // Have the Enter key press the OK button @Override public void onEnter(ComponentEvent ce) { // If the form is not valid, don't click the button if (isValid()) { // If an Apply button is present, Enter will invoke it instead of OK. Button btn = getDialog().getButtonById(APPLY); if (btn != null && (!btn.isVisible() || !btn.isEnabled())) { btn = getDialog().getButtonById(Dialog.OK); } if (btn != null) { btn.fireEvent(Events.Select); } } else { Info.display("ERROR", "The form data is invalid. Correct the errors, then press OK"); } stopEventConditional(ce); } // Have the Escape key press the Cancel button @Override public void onEsc(ComponentEvent ce) { getDialog().getButtonById(Dialog.CANCEL).fireEvent(Events.Select); ce.stopEvent(); } }; } else if (formType == FormType.FORM) { new KeyNav(bodyPanel) { @Override public void onEnter(ComponentEvent ce) { stopEventConditional(ce); if (finishButton.isEnabled()) { finishButton.fireEvent(Events.Select); } } @Override public void onEsc(ComponentEvent ce) { //TODO: BEH: Add test for dirty form fields - if any found, prompt user to make sure they want to cancel cancelButton.fireEvent(Events.Select); } }; } else if (formType == FormType.EMBEDDED) { new KeyNav(bodyPanel) { //Prevent the audible "ding" which occurs for no apparent reason @Override public void onEnter(ComponentEvent ce) { stopEventConditional(ce); } }; } // This KeyNav picks up a Ctrl-Alt-D key press and toggles the debug button on and off new KeyNav(bodyPanel) { @Override public void onKeyPress(ComponentEvent ce) { super.onKeyPress(ce); if (ce.isControlKey() && ce.isAltKey()) { int code = ce.getKeyCode(); if (code == 68) { toggleDebug(); } } } }; // If formLabel is still blank, then there was no call to setFormLabel from the calling screen - use the entry point's default label if (formLabel.isEmpty()) { setFormLabel(getEntryPoint().getLabel()); } container.setStyleName("cat-top-container"); bodyPanel.setBorders(false); if (FormType.EMBEDDED != formType) { container.setLayout(new FormLayout()); container.add(bodyPanel, new FormData("100% 100%")); getViewport().add(container); getViewport().setScrollMode(Scroll.AUTO); container.setScrollMode(Scroll.NONE); } else { getViewport().setLayout(new FitLayout()); container.setLayout(new FormLayout()); container.add(bodyPanel, new FormData("100% 100%")); getViewport().add(container); getViewport().setScrollMode(Scroll.NONE); container.setScrollMode(Scroll.AUTO); } getViewport().layout(true); if (formType == FormType.FORM) { if (getEntryPoint().getHeight() != -1 && getEntryPoint().getWidth() != -1) { StandardComponents.setWindowSize(getEntryPoint().getHeight(), getEntryPoint().getWidth()); } Window.enableScrolling(false); getViewport().layout(true); RootPanel.get().add(getViewport()); container.getHeader().setTextStyle("cat-header-text"); } else if (formType == FormType.DIALOG) { getDialog().setSize(getEntryPoint().getWidth(), getEntryPoint().getHeight()); getDialog().setLayout(new FitLayout()); getDialog().add(container); container.getHeader().setTextStyle("cat-header-text"); } else if (FormType.EMBEDDED == formType) { RootPanel.get().add(getViewport()); } if (container.isRendered() && getViewport().isRendered()) { if (container.getWidth() < getViewport().getWidth()) { container.setWidth(getViewport().getWidth() - sbw); } if (container.getHeight() < getViewport().getHeight()) { container.setHeight(getViewport().getHeight() - sbh); } } reportWindowSizes(); setupFormValidation(); } /** * @return the page */ public StandardForm getPage() { return page; } /** * @param page the page to set */ public void setPage(StandardForm page) { this.page = page; } /** * Returns the string value used to invoke this layout (corresponds to the name value of EntryPointFactory) * @return The string value for the page being run. */ public String getApplication() { return application; } /** * Sets the string value used to invoke this layout (the name from the EntryPointFactory) * @param application The String to be used to invoke this layout. */ public void setApplication(String application) { this.application = application; } private void stopEventConditional(ComponentEvent ce) { if (!"TEXTAREA".equals(ce.getTarget().getTagName())) { ce.stopEvent(); } } /** * Returns the top level container that holds the form. This is the one and only child of the Viewport for the page * @return */ public ContentPanel getContainer() { return container; } /** * Returns the map object containing all the name value pairs to be put into hidden fields on the page. * @return A Map of string keys and string values. */ @SuppressWarnings("ReturnOfCollectionOrArrayField") protected Map getHiddenParamMap() { if (hiddenParamMap == null) { hiddenParamMap = new FastMap(); } return hiddenParamMap; } /** * Returns the map object containing all the name / value pairs to be put into hidden fields on the page * @return A map of string keys and string values holding the name / value pairs to be put into hidden fields on the page * @deprecated Use getHiddenParamMap instead */ @Deprecated @SuppressWarnings("ReturnOfCollectionOrArrayField") protected Map getParamMap() { if (hiddenParamMap == null) { hiddenParamMap = new FastMap(); } return hiddenParamMap; } /** * Creates the layout area with the page title and help icon * @param label The label to be used as the page title * @param helpID The help url to use under the help icon. The icon will not appear if helpUrl is null or empty */ public void createHeaderPanel(String label, final String helpID) { /* * // Set the Window title only if we are a FORM if (formType == FormType.FORM) { Window.setTitle(entryPoint.getLabel()); } else { * getDialog().setHeading(entryPoint.getLabel()); } // Vertical panel for adding Header and Help icon .. headerPanel = new LayoutContainer(); * ColumnLayout layout = new ColumnLayout(); headerPanel.setLayout(layout); headerPanel.setStyleName("cat-dialog-hdr"); * headerPanel.setId("cat-dialog-hdr"); * * // The left side sub-panel HorizontalPanel westpanel = new HorizontalPanel(); westpanel.setStyleName("cat-header-west"); hdrLabel = * StandardComponents.getCATHeaderLabel(label); hdrLabel.setStyleName("cat-header-label"); westpanel.add(hdrLabel); headerPanel.add(westpanel); * * // The right side sub-panel HorizontalPanel eastpanel = new HorizontalPanel(); eastpanel.setStyleName("cat-header-east"); ThemeSelector selector = * new ThemeSelector(); eastpanel.add(selector); String helpurl = webAppURL + "-WHC/index.jspx?id=" + helpID + "&action=show"; HTML rightWidget = * StandardComponents.getHelpIcon(helpurl); rightWidget.setStyleName("cat-helpicon"); eastpanel.add(rightWidget); headerPanel.add(eastpanel); * */ if (Window.getTitle().isEmpty()) { hdr = container.getHeader(); } else { hdr = getDialog().getHeader(); } // Set the Window title only if we are a FORM if (formType == FormType.FORM) { if (Window.getTitle().isEmpty()) { Window.setTitle(entryPoint.getLabel()); } } else if (formType == FormType.DIALOG) { // For Dialogs, use the Dialog's header and shut off the header we created hdr = getDialog().getHeader(); hdr.setVisible(true); } // Set the Window title only if we are a FORM // if (formType == FormType.FORM) { // Window.setTitle(getEntryPoint().getLabel()); // } else if (formType == FormType.DIALOG) { // getDialog().setHeading(getEntryPoint().getLabel()); // } //// container.layout(); // Header hdr = container.getHeader(); // // For Dialogs, use the Dialog's header and shut off the header we created // if (formType == FormType.DIALOG) { // hdr.setVisible(false); // hdr = getDialog().getHeader(); // } hdr.setText(getEntryPoint().getLabel()); // For Embedded forms, turn off the header if (formType == FormType.EMBEDDED) { hdr.setVisible(false); } // Only include the help button if a help ID was set or if the URL does not include a {0} string in it (meaning a fixed landing page) boolean showHelp = false; if (helpURL != null && !helpURL.isEmpty()) { if (helpURL.contains("{0}")) { if (helpID != null && !helpID.isEmpty()) { showHelp = true; } } else { showHelp = true; } } if (showHelp) { Button helpButton = new Button(); helpButton.setSize(StandardComponents.ICON_WIDTH, StandardComponents.WIDGET_HEIGHT); helpButton.addStyleName("helpbutton"); helpButton.addSelectionListener(new SelectionListener() { @Override public void componentSelected(ComponentEvent ce) { String url = helpURL; if (url.contains("{0}")) { url = url.replace("{0}", helpID); } Window.open(url, "_blank", ""); } }); hdr.addTool(helpButton); } } /** * Sets the object ID of the primary object's container. For create, this is where the object will be created. For edit, this is where the object is stored. * @param containerOid The object ID of the primary object's container or the container that launched this screen */ public void setContainerOid(String containerOid) { this.containerOid = containerOid; } /** * Creates the standard footer which includes the status area and the buttons to operate the page */ public void createFooter() { if (formType != FormType.DIALOG) { footer = new LayoutContainer(); footer.setLayout(new HBoxLayout()); getBodyPanel().setBottomComponent(footer); footer.setId("pageFooter"); } createButtonPanel(); createStatusArea(); if (formType != FormType.DIALOG) { footer.add(statusarea, new FormData("100% 26px")); footer.add(buttonPanel, new FormData("100% 26px")); } else { getDialog().getButtonBar().insert(statusarea, 0); } // Don't show the footer on EMBEDDED forms if (FormType.EMBEDDED == formType) { getFooter().setVisible(false); } } private void createButtonPanel() { final int buttonSpacing = 10; createButtons(); buttonPanel = new HorizontalPanel(); buttonPanel.setHeight("40px"); buttonPanel.addStyleName("cat-button-panel"); buttonPanel.setId("buttonPanel"); buttonPanel.setSpacing(buttonSpacing); buttonPanel.add(tools); buttonPanel.add(finishButton); buttonPanel.add(applyButton); buttonPanel.add(cancelButton); // Insert the debug button if (formType == FormType.DIALOG) { ButtonBar bar = getDialog().getButtonBar(); // The ButtonBar comes with OK/Cancel already - add in debug and apply bar.setHeight("28px"); // Since doing a add on the finish and cancel buttons will move them into the buttonPanel above, we need to move all buttons in the buttonPanel to the dialog's buttonBar. while (true) { if (buttonPanel.getItemCount() == 0) { break; } bar.add(buttonPanel.getItem(0)); } bar.insert(tools, 0); } assignButtonListeners(); } private void createButtons() { getFinishButton(); getApplyButton(); if (getMode() != FormMode.CREATE) { getApplyButton().setVisible(false); } getCancelButton(); DebugToolsMenu dtm = new DebugToolsMenu(); tools = dtm.getMenu(this); if (Log.isDebugEnabled()) { tools.setVisible(true); } else { tools.setVisible(false); } } private void assignButtonListeners() { if (FormType.EMBEDDED != formType) { disableActionButtons(); Button theOKButton = getFinishButton(); Button theApplyButton = getApplyButton(); Button theCancelButton = getCancelButton(); // For action type forms, set up the handlers on the buttons if (mode == FormMode.CREATE || mode == FormMode.EDIT || mode == FormMode.MULTIEDIT || mode == FormMode.DELETE) { if (getOkSelectionListener() == null) { // Use the DefaultOKSelectionListener setOkSelectionListener(new DefaultOKApplySelectionListener(SubmitMode.OK)); } getFinishButton().addSelectionListener(getOkSelectionListener()); if (getApplySelectionListener() == null) { // Use the DefaultOKSelectionListener setApplySelectionListener(new DefaultOKApplySelectionListener(SubmitMode.APPLY)); } theApplyButton.addSelectionListener(getApplySelectionListener()); // Set the cancelSelectionListener in case the developer registers a cancel task if (getCancelSelectionListener() == null) { setCancelSelectionListener(new DefaultCancelSelectionListener()); } theCancelButton.addSelectionListener(getCancelSelectionListener()); } else if (mode == FormMode.LIST || mode == FormMode.TREE) { // For LIST or TREE mode pages, hide the OK and APPLY buttons, change the CANCEL button to show "Close" theCancelButton = getCancelButton(); theCancelButton.setText("Close"); theApplyButton.hide(); theOKButton.hide(); if (getCancelSelectionListener() == null) { setCancelSelectionListener(new DefaultCancelSelectionListener()); } theCancelButton.addSelectionListener(getCancelSelectionListener()); ConfirmSelectListener cancelConfirmListener = new ConfirmSelectListener() { @Override public void handleEvent(BaseEvent be) { if (isDirty() && !isForceClose()) { MessageBox.confirm("Form Modified", "Do you want to discard the changes you've made?.", new Listener() { @Override public void handleEvent(MessageBoxEvent be) { if (StandardComponents.wasDialogYesOrOKClicked(be)) { confirm(); } } }); } else { confirm(); } } }; theCancelButton.addListener(FrameworkEvents.Confirm, cancelConfirmListener); } } } private LayoutContainer createStatusArea() { final int defaultSize = 50; statusarea = new LayoutContainer(); statusarea.setId("FormStatusArea"); statusarea.setLayout(new ColumnLayout()); statusarea.setWidth(defaultSize); statusarea.setHeight("25px;"); if (formType != FormType.DIALOG) { // FORM and EMBEDDED types HorizontalPanel btnpanel = getButtonPanel(); btnpanel.setId("buttonPanel"); } else { // DIALOG type ButtonBar bar = getDialog().getButtonBar(); bar.setId("buttonBar"); getDialog().setButtonAlign(Style.HorizontalAlignment.LEFT); bar.setEnableOverflow(false); } configureStatusArea(); statusarea.layout(true); statusarea.show(); return statusarea; } /** * Adds one button into the page's button bar. * The new button will be added just behind the debug button in all cases. * You shall not ever violate the OK/Apply/Cancel affinity. * So if you need to add multiple buttons, add them in right to left order. * addButton(3); addButton(2); addButton(1) will result in Debug;1;2;3;OK;Apply;Cancel * @param btn The Button to be added to the page's button bar. */ public void addButtonToBar(Button btn) { if (btn == null) { throw new RuntimeException("addButtonToBar: btn parameter is null"); } if (formType != FormType.DIALOG) { // FORM and EMBEDDED types HorizontalPanel btnpanel = getButtonPanel(); btnpanel.insert(btn, 1); } else { // DIALOG type ButtonBar bar = getDialog().getButtonBar(); bar.insert(btn, 1); } // updatePageButtonTabIndexes(); } /** * Update the tab indexes of the buttons in the button area */ // private void updatePageButtonTabIndexes() { // Container buttons; // if (formType != FormType.DIALOG) { // buttons = getButtonPanel(); // } else { // buttons = getDialog().getButtonBar(); // } // int ti = PAGE_BUTTON_STARTING_TABINDEX; // List btnlist = buttons.getItems(); // for (Component c : btnlist) { // if (c instanceof Button) { // c.setTabIndex(ti); // ti++; // } // } // } /** * @return the okSelectionListener */ public SelectionListener getOkSelectionListener() { return okSelectionListener; } /** * @param okSelectionListener the okSelectionListener to set */ public void setOkSelectionListener(SelectionListener okSelectionListener) { this.okSelectionListener = okSelectionListener; } /** * @return the applySelectionListener */ public SelectionListener getApplySelectionListener() { return applySelectionListener; } /** * @param applySelectionListener the applySelectionListener to set */ public void setApplySelectionListener(SelectionListener applySelectionListener) { this.applySelectionListener = applySelectionListener; } /** * @return the cancelSelectionListener */ public SelectionListener getCancelSelectionListener() { return cancelSelectionListener; } /** * @param cancelSelectionListener the cancelSelectionListener to set */ public void setCancelSelectionListener(SelectionListener cancelSelectionListener) { this.cancelSelectionListener = cancelSelectionListener; } private class DefaultOKApplySelectionListener extends SelectionListener { SubmitMode submitSetting = null; public DefaultOKApplySelectionListener(SubmitMode setting) { if (setting == null) { throw new RuntimeException("DefaultOKSelectionListenerII: No SubmitMode value supplied"); } submitSetting = setting; } @Override public void componentSelected(ComponentEvent ce) { setSubmitMode(submitSetting); ce.stopEvent(); Registry.register("forceClose", "true"); if (getSubmitMode() == SubmitMode.APPLY) { Log.debug("Form Apply Button clicked"); } else if (getSubmitMode() == SubmitMode.OK) { Log.debug("Form OK Button clicked"); } if (mode == FormMode.CREATE) { setBusy("ACTIONTASK", "Please wait...", "Creating..."); } else if (mode == FormMode.EDIT) { setBusy("ACTIONTASK", "Please wait...", "Updating..."); } else if (mode == FormMode.DELETE) { setBusy("ACTIONTASK", "Please wait...", "Deleting..."); } disableActionButtons(); createTaskParameters(getActionTask()); if (!actionTask.invoke()) { clearBusy("ACTIONTASK"); postActionTaskProcessing(false); } } } /** * Default Cancel Button Handler */ private class DefaultCancelSelectionListener extends SelectionListener { @Override public void componentSelected(ComponentEvent ce) { Object src = ce.getSource(); Log.debug("cancel button ::: Form Type:" + getFormType() + "Form Name:" + getFormName()); Log.debug("cancel button:" + src.getClass().getName()); Log.debug("StandardComponents.cancelButton.addSelectionListener: Clicked"); Log.debug("close dialog"); if (!isForceClose() && isDirty()) { if (isDirty() && getCancelTask() != null) { runCancelTask(); } } if (getFormType() == FormType.DIALOG) { getDialog().hide(); } else { Log.debug("close app"); StandardComponents.closeApplication(); } } } /** * Executes the Cancel Task for the page */ public void runCancelTask() { setBusy("CANCELTASK", "Please Wait", "Performing Server Cleanup for Cancel"); Log.debug("in cancel code"); if (isDirty()) { Log.debug("dirty: " + isDirty()); createTaskParameters(getCancelTask(), false); if (!cancelTask.invoke()) { clearBusy("CANCELTASK"); } } } /** * This method processes the Action Task results. * It is either called by the completion of the Action Task or by the componentSelected event handler if no task is run * It processes any ReturnGroupHandler data return, then * @param ranTask */ private void postActionTaskProcessing(boolean ranTask) { createdObjectOID = ""; String folderOID = ""; IEElement md; Log.trace("formActionTask.storeDataChanged: Enter"); // Confirm that the if (ranTask && assertGroupHasData(getActionTaskGroup(), getTaskDataKey(), "Did not find an object in the task results - does it already exist?")) { // If we get here, we know the group is ok, there is at least one element, and obid has a value so we can skip the checks, reducing the lines of code needed md = getActionTaskGroup().getElementAt(0); // get the obid of the newly created object createdObjectOID = (String) md.getValue("obid"); // Generate a folderOID by using either the returned parentFolder attribute in the data, then the openerOID, then the containerOID folderOID = (String) md.getValue("parentFolder"); if (folderOID == null || folderOID.isEmpty()) { // If they haven't supplied a specific parent, use the openerOid, then the containerOid as the target. folderOID = Registry.get("openerOid"); if (folderOID == null || folderOID.isEmpty()) { folderOID = Registry.get("containerOid"); } } } if (returnGroupHandler != null) { if (returnMode == null && !actionTask.isNullTask()) { // Default returnMode and the action task was not the NULL task, return the task group returnGroupHandler.sendReturnGroup(getActionTaskGroup()); } else if (returnMode == null && getActionTask().isNullTask()) { // Default returnMode and the action task WAS the NULL task, return the form data IEGroup rg = createReturnGroup(false); returnGroupHandler.sendReturnGroup(rg); } else if (returnMode == DataReturnMode.TASK) { // Page wants the action task data returned returnGroupHandler.sendReturnGroup(getActionTaskGroup()); } else if (returnMode == DataReturnMode.FORM) { // Page wants the form data returned IEGroup rg = createReturnGroup(false); returnGroupHandler.sendReturnGroup(rg); } } if (folderOID != null && folderOID.contains("wt.folder.SubFolder")) { handleFolderCompletion(getPage(), getActionTaskGroup(), folderOID, getSubmitMode()); } else { if (folderOID != null && mode == FormMode.DELETE) { handleCompletion(getPage(), getActionTaskGroup(), folderOID, getSubmitMode()); } else { handleCompletion(getPage(), getActionTaskGroup(), createdObjectOID, getSubmitMode()); } } if (getSubmitMode() == SubmitMode.APPLY) { handleApply(); } Log.trace("formActionTask.storeDataChanged: Exit"); } /** * Deals with the Apply Button behavior (leave all the fields alone so the user can make a single change and Apply again) * or reset all the fields to their original values so the page starts over. */ public void handleApply() { // if this is an APPLY, then reset the field values so dirty will be based on the last apply // the OK and APPLY buttons can't be enabled after an APPLY until a field is dirty // if clearOnApply is set to true, then all the fields' values will be reset to original values if created successfully // if clearOnApply is set to false, then all the fields' values will be reset to latest values if (clearOnApply) { // If clearOnApply and the task was NOT successful, then we purposefully don't do a reset. // However, we also do NOT want to do a resetOriginalValues()! if (createdObjectOID != null && !createdObjectOID.isEmpty()) { reset(); // reset() kills the hidden fields so we must set them up again // (similar to what invoke() does after IT calls reset()) setupHiddenFields(); } } else { resetOriginalValues(); } if (formType == FormType.FORM || formType == FormType.EMBEDDED) { if (focusField != null) { focusField.focus(); } } else if (formType == FormType.DIALOG) { if (focusField != null) { getDialog().setFocusWidget(focusField); getDialog().focus(); } } validate(); } /** * sets clearOnApply. If clearOnApply is true then all the fields' values will be reset to original values if created successfully when apply button clicked * if clearOnApply is set to false, then all the fields' values will be reset to latest values * * @param flag */ public void setClearOnApply(boolean flag) { clearOnApply = flag; } /** * @return the returnGroupHandler */ public ReturnGroupHandler getReturnGroupHandler() { return returnGroupHandler; } /** * @param returnGroupHandler the returnGroupHandler to set */ public void setReturnGroupHandler(ReturnGroupHandler returnGroupHandler) { this.returnGroupHandler = returnGroupHandler; } /** * Sets the source of data for a ReturnGroupHandler - FORM or TASK * @param setting A DataReturnMode enum value */ public void setReturnMode(DataReturnMode setting) { returnMode = setting; if (setting == null) { StandardComponents.alert(FormMode.LIST, MsgLevel.ERROR, "setReturnMode was given a null argument"); returnMode = DataReturnMode.FORM; } } /** * Returns the source of data for a ReturnGroupHandler - FORM or TASK * @return A DataReturnMode enum value */ public DataReturnMode getReturnMode() { return returnMode; } /** * creates a group out of all fields in the form, including the hidden ones into the specified task. Data structure objects (IEGroup and TreeStore) will be added directly to the group * * @param hiddenOnly When true, only the fields in the hidden panel will be added (used for the initialization task) * @return An IEGroup with a single element, containing the values of all fields scraped from the form */ public IEGroup createReturnGroup(boolean hiddenOnly) { //TODO: BEH: Why isn't this code used ultimately to generate the task params? This data could be captured and then used to generate the task params. Map formToGroupMap = getActionTask().getFormToTaskMap(); IEGroup group = new IEGroup("results"); IEElement el = new IEElement(); // Map taskParamMap = task.getParamMap(); List> fields = getBodyPanel().getFields(); String pid; for (Field field : fields) { // Don't generate a param for a field without a name String name = field.getName(); if (name == null || name.isEmpty()) { Log.debug("Skipping a field because it has no name. Class: " + field.getClass().getName() + " Value: " + field.getRawValue()); continue; } if (name.equals(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME)) { String val = (String) field.getValue(); if (val == null) { val = ""; } el.addAtt(new IEAtt(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME, val)); continue; } String safeName = name; if (name.contains(".")) { Log.debug("Replacing dots with underscores for field param name: " + name); safeName = safeName.replace(".", "_"); } // Pull the attribute from the element corresponding to the field name // Check the formMap to see if we need to pull a different attribute for this field String mapname = formToGroupMap.get(safeName); if (mapname == null) { mapname = safeName; } // Check to see if we are doing only the fields in the hidden panel. Continue out of the loop //if we are and the field is not parented by the panel with the id 'hidden' if (hiddenOnly) { if (!StandardComponents.isFieldHidden(field)) { Log.debug("Skipping field: " + name + " because it is not in the hidden panel"); continue; } // Widget parent = field.getParent(); // if (parent instanceof LayoutContainer) { // LayoutContainer lc = (LayoutContainer) parent; // pid = lc.getId(); // if (pid != null && !pid.equals("hidden")) { // Log.debug("Skipping field: " + name + " because it is not in the hidden panel"); // continue; // } // } } try { if (field instanceof ComboBox) { // For combo boxes, the value in the map can be colon-separated. So it could be 'a', 'a:b' or ':b'. // 'a' indicates that the internal value of the combo box should go to the attribute called a. The display value is not collected. // 'a:b' indicates that the internal value of the combo box should go to a and the display value should go to b. //':b' indicates that the display value of the combo box should go to b. The internal value is not collected. ComboBox cb = (ComboBox) field; ModelData md = cb.getValue(); String value = ""; String dvalue = ""; if (md != null) { value = md.get(cb.getValueField()); dvalue = md.get(cb.getDisplayField()); } String[] combomap = mapname.split(":"); if (combomap.length == 1) { el.addAtt(new IEAtt(combomap[0], value)); } else { if (combomap[0] == null || combomap[0].isEmpty()) { el.addAtt(new IEAtt(combomap[1], dvalue)); } else { el.addAtt(new IEAtt(combomap[0], value)); el.addAtt(new IEAtt(combomap[1], dvalue)); } } } else if (field instanceof AdapterField) { Object result = null; AdapterField af = (AdapterField) field; String widget = field.getData("type"); if (widget != null) { if (widget.equals("tree")) { Object x = af.getData("widget"); if (x != null && (x instanceof StandardTree)) { StandardTree stdTree = (StandardTree) af.getData("widget"); result = stdTree.getTreeStore(); } else { Log.error("type was set to tree, but the widget was either null or was not a TreeGrid - AdapterField: " + field.getName()); } } else if (widget.equals("table")) { Object x = af.getData("widget"); if (x != null && (x instanceof StandardTable)) { StandardTable stdTable = (StandardTable) af.getData("widget"); IEGroup g = ((IEGroup) stdTable.getGrid().getStore()).deepClone(); result = g; } else { Log.error("type was set to table, but the widget was either null or was not a Grid - AdapterField: " + field.getName()); } } else { Log.error("An ApapterField type was not valid: " + widget + " AdapterField: " + field.getName()); } } else { Log.error("An AdapterField type was null: " + field.getName()); } el.addAtt(new IEAtt(mapname, result)); } else { String val = StandardComponents.getFieldValue(field); Log.trace("Adding Att to return group - " + mapname + "=" + val); el.addAtt(new IEAtt(mapname, val)); } } catch (Exception e) { Log.error(" field has no value set, successfully caught exception " + e.toString()); } } group.addElement(el); return group; } /** * Method to handle the completion of the primary functions of a UI page - create, edit, editmulti. Consults the Action maps to see what to do with the * window, the parent window and whether to use a popup to display anything. * * @param page The StandardForm launching this method * @param group The group with the data from the server operation * @param obid The object ID of the object to which we will forward * @param submitMode Indicates whether this is an OK or an Apply press (OK will dismiss the window, but Apply will force a full revalidation and leave all * fields in place) */ public void handleCompletion(StandardForm page, IEGroup group, String obid, SubmitMode submitMode) { // TODO: This needs to be moved to a Windchill-specific CompletionHandler class //truncate the obid in all cases - the properties pages often puke on the ufids. String theobid = StandardComponents.getShortOid(obid); MsgLevel level = group.getSuccess(); // // If we ran a server side task, we still need to check to see if we have a return group handler so we can interact with a parent table or tree // if (getReturnGroupHandler() != null && returnMode==DataReturnMode.TASK) { // getReturnGroupHandler().sendReturnGroup(group); // } WindowMode windowAction; if (forcedWindowAction == null) { windowAction = StandardComponents.getWindowAction(page.form.getMode(), level, submitMode); } else { windowAction = forcedWindowAction; } ParentMode parentAction; if (forcedParentAction == null) { parentAction = StandardComponents.getParentAction(page.form.getMode(), level); } else { parentAction = forcedParentAction; } if (page.formType == FormType.FORM) { if (parentAction == ParentMode.FORWARD) { if (theobid == null || theobid.isEmpty()) { throw new RuntimeException("Cannot forward to the new object - the obid parameter was not supplied"); } //If the obid is a Version Reference (starts with VR), and the returning oid is equal to the opening oid, we need to refresh //as the browser will ignore a request to forward to the same URL if (theobid.startsWith("VR") && theobid.equals(Registry.get("oid"))) { StandardComponents.refreshParent(); } String url = group.getElementAt(0).getValue("detailsUrl"); if (url != null && !url.isEmpty()) { Log.info("Forward parent to URL - " + url); StandardComponents.forwardParent(url); } else { //R10: http://acm.corp.cat.com/acm/app/#ptc1/tcomp/infoPage?oid=OR%3Awt.part.WTPart%3A1505992&u8=1 Log.info("Forward parent to R10 URL -" + "/app/#ptc1/tcomp/infoPage?oid=" + theobid + "&u8=1"); StandardComponents.forwardParent("/app/#ptc1/tcomp/infoPage?oid=" + theobid + "&u8=1"); } } else if (parentAction == ParentMode.TOFOLDER) { if (theobid == null || theobid.isEmpty()) { throw new RuntimeException("Cannot forward to parent folder - containerOid was not specified"); } Log.info("Forward parent to R10 Folder URL -" + "/app/#ptc1/tcomp/infoPage?oid=" + theobid + "&u8=1"); StandardComponents.forwardParent("/app/#ptc1/tcomp/infoPage?oid=" + theobid + "&u8=1"); } else if (parentAction == ParentMode.REFRESH) { StandardComponents.refreshParent(); } } // TODO: If/when a console object becomes available, feed all messages to that console // Produce a message box whenever the action map indicates POPUP_NEEDED PopupMode popupAction = StandardComponents.getPopupAction(page.form.getMode(), level); if (popupAction == PopupMode.NEEDED) { StandardComponents.alert(page, level, group.getMessagesAsHtml()); } else { // If we aren't doing a popup, we may need to close the window here if (windowAction == WindowMode.CLOSE) { if (page.formType == FormType.FORM) { StandardComponents.closeApplication(); } else if (page.formType == FormType.DIALOG) { page.form.getDialog().hide(); } } } } /** * Method to handle the completion of the primary functions of a UI page launched from a Folder panel - create, edit, editmulti. Consults the Action maps to * see what to do with the window, the parent window and whether to use a popup to display anything. * * @param page The StandardForm object calling this method * @param group The group with the data from the server operation * @param obid The name of the attribute in the group's data with the object's retrieval ID (needed to create parent forwarding URLs) * @param submitMode Indicates whether this is an OK or an Apply press (OK will dismiss the window, but Apply will force a full revalidation and leave all * fields in place) */ public void handleFolderCompletion(StandardForm page, IEGroup group, String obid, SubmitMode submitMode) { //TODO: This needs to be moved to a Windchill-specific CompletionHandler class MsgLevel level = group.getSuccess(); // // If we ran a server side task, we still need to check to see if we have a return group handler so we can interact with a parent table or tree // if (getReturnGroupHandler() != null && returnMode==DataReturnMode.TASK) { // getReturnGroupHandler().sendReturnGroup(group); // } WindowMode windowAction; if (forcedWindowAction == null) { windowAction = StandardComponents.getWindowAction(page.form.getMode(), level, submitMode); } else { windowAction = forcedWindowAction; } ParentMode parentAction = null; if (forcedParentAction == null) { parentAction = StandardComponents.getParentAction(page.form.getMode(), level); } else { parentAction = forcedParentAction; } // Only run these behaviors for a plain old popup if (page.formType == FormType.FORM) { if (obid == null || obid.isEmpty()) { throw new RuntimeException("Cannot refresh / forward to the parent folder - the 'obid' parameter was not supplied"); } // The obid value will have been generated using the first non-null value of a 'parentFolder' attribute in the data, the openerOid parameter, or the containerOid parameter String theOpener = Registry.get("openerOid"); if (obid.equals(theOpener)) { StandardComponents.refreshParent(); Log.info("Parent refreshed"); } else { StandardComponents.forwardParent("/app/#ptc1/tcomp/infoPage?oid=" + obid + "&u8=1"); Log.info("Forward parent to URL -" + "/app/#ptc1/tcomp/infoPage?oid=" + obid + "&u8=1"); } } // TODO: If/when a console object becomes available, feed all messages to that console // Produce a message box whenever the action map indicates POPUP_NEEDED PopupMode popupAction = StandardComponents.getPopupAction(page.form.getMode(), level); if (popupAction == PopupMode.NEEDED) { StandardComponents.alert(page, level, group.getMessagesAsHtml()); } else { // If we aren't doing a popup, we may need to close the window here if (windowAction == WindowMode.CLOSE) { if (page.formType == FormType.FORM) { StandardComponents.closeApplication(); } else { page.form.getDialog().hide(); } } } } /** * @return the windowAction */ public WindowMode getWindowAction() { return forcedWindowAction; } /** * @param windowAction the windowAction to set */ public void setWindowAction(WindowMode windowAction) { forcedWindowAction = windowAction; } /** * @return the parentAction */ public ParentMode getParentAction() { return forcedParentAction; } /** * @param parentAction the parentAction to set */ public void setParentAction(ParentMode parentAction) { forcedParentAction = parentAction; } /** * Makes sure the group has at least one record and the specified attribute contains a non-null, non-empty value * * @param group The IEGroup object containing the task result * @param attrname The name of the expected attribute * @param alertmsg The message desired (will create a default "No data returned from the server call" if this value is null or empty * @return true if all tests pass. Otherwise, closeApplication will be called so the routine will never return. */ public static boolean assertGroupHasData(IEGroup group, String attrname, String alertmsg) { // Generate a default message String thealertmsg = alertmsg; if (thealertmsg == null || thealertmsg.isEmpty()) { thealertmsg = "No data returned from the server call"; } // The group we got was null if (group == null) { Log.error("assertGroupHasData: The group specified is null"); StandardComponents.alert(FormMode.ASSERT_EXIST, MsgLevel.FATAL, thealertmsg); return false; } // The group we got had no elements if (group.getElementCount() == 0 && group.getMessages().isEmpty()) { Log.warn("assertGroupHasData: The group has no elements and there are no other messages - show the default message"); StandardComponents.alert(FormMode.ASSERT_EXIST, MsgLevel.ERROR, thealertmsg); return false; } if (group.getElementCount() == 0) { Log.debug("assertGroupHasData: The group has no elements but there are other messages present"); return false; } // The attribute we got was null or empty if (attrname == null || attrname.isEmpty()) { group.addMessage("FATAL|" + thealertmsg); group.addMessage("FATAL|No attribute name for validating the group data was supplied"); } else { // The value of the attribute in the first element of the group is null or empty IEElement el = group.getElementAt(0); String val = (String) el.getValue(attrname); if (val == null || val.isEmpty()) { group.addMessage("FATAL|" + thealertmsg); group.addMessage("FATAL|The attribute '" + attrname + "' did not appear in the group data"); } } StandardComponents.alert(FormMode.ASSERT_EXIST, group); return true; } /** * Makes sure the group has at least one record and the specified attribute contains a non-null, non-empty value * * @param group The IEGroup object containing the task result * @return true if all tests pass. Otherwise, closeApplication will be called so the routine will never return. */ public static boolean assertGroupHasNoErrors(IEGroup group) { // The group we got was null if (group == null) { Log.error("assertGroupHasNoErrors: The group specified is null"); StandardComponents.alert(FormMode.ASSERT_EXIST, MsgLevel.FATAL, "assertGroupHasNoErrors: The group specified is null"); return false; } StandardComponents.alert(FormMode.ASSERT_EXIST, group); return true; } /** * Makes sure the group has no records * * @param group The IEGroup object containing the task result * @param alertmsg The message desired (will create a default "The object specified already exists" if this value is null or empty) * @return true if all tests pass. Otherwise, closeApplication will be called so the routine will never return. */ public static boolean assertGroupHasNoData(IEGroup group, String alertmsg) { // Generate a default message String thealertmsg = alertmsg; if (thealertmsg == null || thealertmsg.isEmpty()) { thealertmsg = "The object specified already exists"; } // The group we got was null if (group == null) { Log.error("assertGroupHasData: The group specified is null"); StandardComponents.alert(FormMode.ASSERT_NOT_EXIST, MsgLevel.FATAL, thealertmsg); return false; } // The group already had FATAL errors if (group.getSuccess() == MsgLevel.FATAL) { StandardComponents.alert(FormMode.ASSERT_NOT_EXIST, MsgLevel.FATAL, group.getMessagesAsHtml()); return false; } if (group.getElementCount() == 0) { return true; } else { return false; } } /** * Creates a task with a taskoutgroupname generated from the taskuri. This method will never re-use an existing instance of a task. * * @param task The TaskFactory enum entry of the task to be run * @return The configured task (also available via actionTask) */ public Task setupCancelTask(TaskFactory task) { return setupCancelTask(task, null, false); } /** * Creates a task with a taskoutgroupname generated from the taskuri. When the useCache is true, it will re-use an already configured task with the same * uri. * * @param task The TaskFactory enum entry of the task to be run * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via actionTask) */ public Task setupCancelTask(TaskFactory task, boolean useCache) { return setupCancelTask(task, null, useCache); } /** * Creates a task with a taskoutgroupname as specified (null means it will generate it from the taskuri). When the useCache is true, it will re-use an * already configured task with the same uri. * * @param task The TaskFactory enum entry of the task to be run * @param group The name of the group that will be returned by the task * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via actionTask) */ public Task setupCancelTask(TaskFactory task, String group, boolean useCache) { String taskuri = task.getTaskUri(); if (taskuri == null) { StandardComponents.alert(this, MsgLevel.FATAL, "Cancel task uri was not specified"); return null; } cancelTask = Task.newTask(task, group, useCache); cancelTaskGroup = cancelTask.getGroup(cancelTask.getTaskGroupOutName()); getCancelTaskGroup().addStoreListener(new StoreListener() { @Override public void storeDataChanged(StoreEvent se) { Log.trace("InitializerTask.storeDataChanged: Enter"); if (assertGroupHasData(getCancelTaskGroup(), getTaskDataKey(), "Initialization data could not be retrieved from task: " + getCancelTask().getUri())) { setFormFieldValues(getCancelTaskGroup()); } StandardComponents.alert(FormMode.ASSERT_EXIST, getCancelTaskGroup()); isValid(); clearBusy("CANCELTASK"); } }); return cancelTask; } /** * Creates focus listeners on all named fields on the page so we can show the tooltip in the button bar when the current field has an error. */ public void setupFocusHandler() { for (Field f : getBodyPanel().getFields()) { //TODO: Take this out if validation doesn't work when they tab out of the field anyway f.setValidateOnBlur(false); // add focus listener Field work; if (f.getName() != null && !f.getName().isEmpty()) { work = f; if (f instanceof CATMultiField) { work = ((CATMultiField) f).getTextField(); } work.addListener(Events.Focus, new Listener() { @Override public void handleEvent(BaseEvent be) { if (statusarea == null) { return; } Field f = (Field) be.getSource(); currentFocusField = f; showErrorInStatusArea(f); } }); work.addListener(Events.Blur, new Listener() { @Override public void handleEvent(BaseEvent be) { if (statusarea == null) { return; } Field f = (Field) be.getSource(); // currentFocusField = null; // showErrorInStatusArea(f); } }); work.addListener(Events.Valid, new Listener() { @Override public void handleEvent(BaseEvent be) { if (statusarea == null) { return; } Field f = (Field) be.getSource(); if (f == currentFocusField) { showErrorInStatusArea(f); } } }); work.addListener(Events.Invalid, new Listener() { @Override public void handleEvent(BaseEvent be) { if (statusarea == null) { return; } Field f = (Field) be.getSource(); if (f == currentFocusField) { showErrorInStatusArea(f); } } }); } } } /** * @return the sendOnlyDirtyOnEdit */ public boolean isSendOnlyDirtyOnEdit() { return sendOnlyDirtyOnEdit; } /** * @param sendOnlyDirtyOnEdit the sendOnlyDirtyOnEdit to set */ public void setSendOnlyDirtyOnEdit(boolean sendOnlyDirtyOnEdit) { this.sendOnlyDirtyOnEdit = sendOnlyDirtyOnEdit; } /** * creates task parameters for all fields in the form, including the hidden ones into the specified task. The parameters will be generated according to the * paramMode setting on the task (name=value or field=name=value) * * @param task The task to which the parameters will be added. */ public void createTaskParameters(Task task) { createTaskParameters(task, false); } /** * creates task parameters for all fields in the form, including the hidden ones into the specified task. The parameters will be generated according to the * paramMode setting on the task (name=value or field=name=value) * * @param task The task to which the parameters will be added. * @param hiddenOnly When true, only the fields in the hidden panel will be added (used for the initialization task) */ public void createTaskParameters(Task task, boolean hiddenOnly) { // Why isn't createReturnGroup used to generate the needed data instead of having separate code? if (task == null) { return; } // If we are configured to only send down changed fields, then we must clear out all task // parameter here. Here is the scenario this deals with: // 1. Bring up the dialog // 2. Edit field A // 3. Click OK // 4. You get some error back from the task. // 5. Undo your edit to field A and edit field B instead. // 6. Click OK // 7. Without this change, field A will get sent to the task since it was // added as a task param in step #3 and nobody has cleared it out again. if (sendOnlyDirtyOnEdit) { task.resetParams(); } Map taskParamMap = task.getFormToTaskMap(); List> fields = getBodyPanel().getFields(); for (Field field : fields) { // Don't generate a param for a field without a name String name = field.getName(); if (name == null || name.isEmpty()) { Log.debug("Skipping a field because it has no name. Class: " + field.getClass().getName() + " Value: " + field.getRawValue()); continue; } String safeName = name; if (name.contains(".")) { Log.debug("Replacing dots with underscores for field param name: " + name); safeName = safeName.replace(".", "_"); } // Check to see if we are doing only the fields in the hidden panel. Continue out of the loop //if we are and the field is not parented by the panel with the id 'hidden' boolean fieldIsHidden = StandardComponents.isFieldHidden(field); if (hiddenOnly && !fieldIsHidden) { Log.debug("Skipping field: " + name + " because it is not in the hidden panel"); continue; } if (!fieldIsHidden) { // Hidden field are always sent if (getMode() == FormMode.EDIT || getMode() == FormMode.MULTIEDIT) { // This only applies to Edit or Multi Edit pages if (sendOnlyDirtyOnEdit && !field.isDirty()) { // We skip if we are sending only dirty fields and the field is not dirty Log.debug("Field skipped due to edit settings: fieldIsHidden: " + fieldIsHidden + " dirty: " + field.isDirty() + " sendOnlyDirtyOnEdit: " + isSendOnlyDirtyOnEdit() + " form mode: " + getMode()); continue; } } } // Check the taskParamMap to see if we need to put this field value into a different parameter name String mapname = taskParamMap.get(safeName); if (mapname == null) { mapname = safeName; } String val = StandardComponents.getFieldValue(field); if (task.getParamMode() == ParamMode.DIRECT) { task.addTaskParam(mapname, val); //if(!mapname.equals(safeName)) // task.addTaskParam(safeName, val);//add task parameters for both mapname and safeName as numerous tasks are associated to the same widget and parameter keys differ } else { task.addTaskParam("field", mapname + "=" + val); //if(!mapname.equals(safeName)) // task.addTaskParam("field", safeName + "=" + val);//add task parameters for both mapname and safeName as numerous tasks are associated to the same widget } } } /** * Calls showErrorInStatusArea using the last field to accept focus. */ public void showErrorForCurrentFocusField() { if (currentFocusField == null) { return; } showErrorInStatusArea(currentFocusField); } /** * Shows the field label, the red ball, and the tooltip when there is a tooltip assigned to the field. * The data is shown in the left side of the button area on forms and dialogs. * @param f */ public void showErrorInStatusArea(Field f) { if (f.getName() != null && !f.getName().isEmpty() && f.getToolTip() != null && f.getToolTip().isEnabled()) { //statusarea.setVisible(true); statusAreaImage.setVisible(true); statusAreaMessage.setVisible(true); String flabel = f.getFieldLabel(); String fmsg = f.getToolTip().getToolTipConfig().getText(); String ftip = fmsg; if (!fmsg.startsWith(flabel)) { // Don't display the label field if it matches the start of the message field ftip = flabel + ": " + fmsg; } statusAreaMessage.setValue("  " + ftip); statusAreaImage.setTitle((String) statusAreaMessage.getValue()); } else { //statusarea.setVisible(false); statusAreaImage.setVisible(false); statusAreaMessage.setVisible(false); } // getBodyPanel().layout(true); int newwidth = calculateStatusareaWidth(); statusarea.setSize(newwidth, -1); statusarea.add(statusAreaImgAdapter, new ColumnData(StandardComponents.ICON_WIDTH)); statusarea.add(statusAreaMessage, new ColumnData(newwidth - StandardComponents.ICON_WIDTH)); statusarea.layout(true); if (formType == FormType.DIALOG) { // Nothing right now } else { getButtonPanel().setPosition(newwidth, 0); } // if (footer != null) { // footer.layout(); // } } private void configureStatusArea() { final int saheight = 27; if (statusAreaImage == null) { statusAreaImage = new Image("/Windchill/com/cat/gwt/org.cat.Main/images/windchill/form/exclamation.gif"); } if (statusAreaImgAdapter == null) { statusAreaImgAdapter = new AdapterField(statusAreaImage); statusAreaImgAdapter.setSize(StandardComponents.ICON_WIDTH, StandardComponents.WIDGET_HEIGHT); statusAreaImgAdapter.setId("statusAreaImgAdapter"); } if (statusAreaMessage == null) { statusAreaMessage = CATLabelField.newCATLabelField(this, "", ""); statusAreaMessage.setLabelSeparator(""); statusAreaMessage.setHideLabel(true); statusAreaMessage.setId("statusAreaMessage"); statusAreaMessage.setSize(300, saheight); } } private int calculateStatusareaWidth() { final int defaultWidth = 300; int result = defaultWidth; if (statusarea.isRendered()) { if (formType == FormType.DIALOG) { result = getDialog().getOffsetWidth() - getButtonBarWidth(defaultWidth) - 10 * (getDialog().getButtonBar().getItemCount() - 1); } else { result = getViewport().getOffsetWidth() - getButtonPanel().getOffsetWidth(); } } if (result < 0) { result = 0; } return result; } private int getButtonBarWidth(int defaultWidth) { int first = -1; int last = -1; // Find the first and last visible buttons in the button bar ButtonBar bar = getDialog().getButtonBar(); for (int i = 1; i < bar.getItemCount(); i++) { // Only look at buttons if (bar.getItem(i) instanceof Button) { // Only look at visible buttons if (bar.getItem(i).isVisible()) { // If we haven't already set first, set it with this visible button's position if (first == -1) { first = i; } // Always set the last to this visible button's position last = i; } } } // blow up if we failed to find either the first or last visible button (should never happen unless someone turns off all the buttons) if (first == -1 || last == -1) { // throw new RuntimeException("Could not find the first and last buttons in the page's button bar!"); return defaultWidth; } // get the rightmost position by adding the left and width of the last visible button, then subtract the left of the leftmost visible button int thewidth = (bar.getItem(last).getAbsoluteLeft() + bar.getItem(last).getOffsetWidth()) - bar.getItem(first).getAbsoluteLeft(); return thewidth; } /** * Returns the oid value (used to dispatch the page). * For Create screens, this will be the container object into which the object should be stored. * For Other screens, this will be a pointer to the object itself. * @return The String value of the object id used to dispatch the page */ public String getOid() { return oid; } /** * Sets the primary object id for the page execution. * For create pages, this should be the container into which the new object will be stored. * For all other pages, this should be the object being manipulated. * @param oid The object identifier for the object being manipulated. */ public void setOid(String oid) { this.oid = oid; } /** * Creates the standard Cancel button for the page */ protected void createCancelButton() { // Cancel button cancelButton = StandardComponents.createCancelButton(); } /** * Creates the standard OK button for the page */ protected void createFinishButton() { // OK button finishButton = StandardComponents.createOKButton(); finishButton.setType("submit"); } private MessageBox getWaitBox() { return waitBox; } /** * Creates the standard Apply button for Create pages */ protected void createApplyButton() { // Apply button applyButton = StandardComponents.createApplyButton(); applyButton.setType("submit"); applyButton.setItemId(APPLY); // so onEnter() can find it } private void setWaitBox(MessageBox waitBox) { this.waitBox = waitBox; waitBox.setModal(true); } /** * Makes the webAppURL for the application available to the form. * @param webAppURL the string representation of the webAppURL (like http://acm.corp.cat.com/Windchill) */ public void setWebAppURL(String webAppURL) { this.webAppURL = webAppURL; } /** * Returns the Finish (OK) Button for the form * The button will be created if necessary * @return The OK Button for the form */ public Button getFinishButton() { if (formType != FormType.DIALOG) { if (finishButton == null) { createFinishButton(); } } else { finishButton = getDialog().getButtonById(Dialog.OK); } return finishButton; } /** * Returns the Finish (OK) Button for the form * The button will be created if necessary * @return The OK Button for the form */ public Button getApplyButton() { if (applyButton == null) { createApplyButton(); } return applyButton; } /** * Returns the Finish (OK) Button for the form * The button will be created if necessary * @return The OK Button for the form */ public Button getCancelButton() { if (formType != FormType.DIALOG) { if (cancelButton == null) { createCancelButton(); } } else { cancelButton = getDialog().getButtonById(Dialog.CANCEL); } return cancelButton; } /** * Retrieves the HorizontalPanel object that holds all buttons at the bottom of the page. * This is only valid for non-DIALOG pages. * Use getDialog().getButtonBar for the equivalent functionality for a DIALOG page. * @return */ public HorizontalPanel getButtonPanel() { return buttonPanel; } /** * Retrieves the actual form area of the page (where all the page-specific content resides). * @return The CATFormPanel that is the form area for the page. */ public CATFormPanel getBodyPanel() { if (bodyPanel == null) { bodyPanel = StandardComponents.newBodyPanel(this); hiddenPanel = StandardComponents.newHiddenPanel(bodyPanel); bodyPanel.add(hiddenPanel); } return bodyPanel; } /** * @return the customHiddenFields */ @SuppressWarnings("ReturnOfCollectionOrArrayField") public List getCustomHiddenFields() { if (customHiddenFields == null) { customHiddenFields = new ArrayList(); } return customHiddenFields; } /** * Retrieves the Viewport used for the overall non-DIALOG page. * This is the object directly attached to the page root. * It is only valid when formType is not DIALOG. * @return The Viewport for the page. */ public Viewport getViewport() { return viewport; } /** * * @return The LayoutContainer holding all of the HiddenFields created to hold the data from getFormParameters. */ private LayoutContainer getHiddenPanel() { return hiddenPanel; } /** * Returns the minimum height needed for the page. * @return An int with the minimum height in pixels. */ public int getMinHeight() { return minHeight; } /** * Set the minimum height for the page (in pixels). * @param minHeight An int with the minimum height setting in pixels. */ public void setMinHeight(int minHeight) { this.minHeight = minHeight; } /** * Returns the minimum width needed for the page. * @return An int with the minimum width in pixels. */ public int getMinWidth() { return minWidth; } /** * Set the minimum width for the page (in pixels). * @param minWidth An int with the minimum width setting in pixels. */ public void setMinWidth(int minWidth) { this.minWidth = minWidth; } /** * @return the formName */ public String getFormName() { return formName; } /** * @param formName the formName to set */ public void setFormName(String formName) { this.formName = formName; } /** * @return the formLabel */ public String getFormLabel() { return formLabel; } /** * @param formLabel the formLabel to set */ public void setFormLabel(String formLabel) { this.formLabel = formLabel; if (formType == FormType.DIALOG) { container.getHeader().setVisible(false); } // if (Window.getTitle().isEmpty()) { // hdr = container.getHeader(); // } else { // hdr = getDialog().getHeader(); // } // Set the Window title only if we are a FORM if (formType == FormType.FORM) { if (Window.getTitle().isEmpty()) { Window.setTitle(entryPoint.getLabel()); } } else if (formType == FormType.DIALOG) { // For Dialogs, use the Dialog's header and shut off the header we created hdr = getDialog().getHeader(); hdr.setText(formLabel); // hdr.setVisible(false); } } /** * @return the helpID */ public String getHelpID() { return helpID; } /** * @param helpID the helpID to set */ public void setHelpID(String helpID) { this.helpID = helpID; } /** * Sets the mode of the form. If null, it defaults to LIST * @param mode The desired FormMode enum for the Form */ public void setMode(FormMode mode) { FormMode work = mode; if (work == null) { work = FormMode.LIST; } Registry.register("pageMode", work.toString()); this.mode = work; } /** * Returns the Mode of the form (CREATE, EDIT, DELETE, LIST, etc) * @return The FormMode enum value for this page. */ public FormMode getMode() { return mode; } /** * Creates new Hidden Fields for every entry found in the hiddenParamMap */ public void setupHiddenFields() { FormData fd = new FormData("100%"); fd.setWidth(getWidth()); Iterator ix = hiddenParamMap.keySet().iterator(); while (ix.hasNext()) { String key = ix.next(); String val = hiddenParamMap.get(key); addHiddenField(key, val, true); } for (HiddenField hf : getCustomHiddenFields()) { getHiddenPanel().add(hf); getBodyPanel().addHiddenField(hf); } } /** * Adds a new hidden field to the bodypanel. * @param fieldname The name of the Hidden field to be added * @param val The value for the newly created Hidden field. */ public void addHiddenField(String fieldname, String val) { addHiddenField(fieldname, val, false); } /** * Adds a new hidden field to the hidden panel (when formParam is true) or to the bodyPanel (when formParam is false). * @param fieldname The name of the Hidden field to be added * @param val The value for the newly created Hidden field. * @param formParam set to true to add to the form parameter hidden container. Otherwise, it will be added to the bodyPanel like any other field. */ private void addHiddenField(String fieldname, String val, boolean formParam) { // Field to hold object's oid on the page CATHiddenField hidden = CATHiddenField.newCATHiddenField(bodyPanel.getForm(), fieldname, fieldname); hidden.setValue(val); hidden.setAutoHeight(true); hidden.setAutoWidth(true); if (formParam) { hiddenPanel.add(hidden); } else { getBodyPanel().add(hidden); } getBodyPanel().addHiddenField(hidden); } /** * Empties all fields on the page that are in the FormPanel and that have names. Removes the hidden fields from the hidden panel (they will be rebuilt shortly). If they are also ComboBox, StandardTable or StandardTree objects, then their stores will be empties. */ public void reset() { // bodyPanel.reset(); getHiddenPanel().removeAll(); for (Field field : bodyPanel.getFields()) { if (!StandardComponents.isFieldHidden(field)) { String name = field.getName(); if (name != null && !name.isEmpty() && !name.equals("null")) { // Disable validation for this field during the reset. // Some validations depend on other field values and, // at this point in time, some of the fields are reset // but others are not. field.setFireChangeEventOnSetValue(false); field.reset(); field.setFireChangeEventOnSetValue(true); // Also reset any DependencyComponent associated with this field. // Otherwise, it will have a stale "startVal" which will cause // incorrect behavior. StandardComponents.resetFieldDependencyComponent(field); } } } formValid(false); } /** * Restores the original values for the form, stored when the init data was loaded. */ public void resetOriginalValues() { hasFormBeenApplied = true; for (Field field : bodyPanel.getFields()) { String name = field.getName(); if (name != null && !name.isEmpty() && !name.equals("null")) { Object value = field.getValue(); field.setOriginalValue(value); } } // turn off the APPLY and OK buttons formValid(false); } /** * Initializes the form validate processing. * All fields are added to a dive panel so the overall form status can be determined as validations occur. */ public void setupFormValidation() { Field focusfield = null; Log.debug("In CATFormLayoutsetupFormValidation."); divePanel = new FastMap(); // divePanel.put("$$BUSY", TRUE); for (Field field : bodyPanel.getFields()) { // field.setMessageTarget("tooltip"); // if (field instanceof CATMultiField) { // ((CATMultiField) field).getTextField().setMessageTarget("tooltip"); // } // if (field instanceof CATPicker) { // ((CATPicker) field).getText().setMessageTarget("tooltip"); // ((CATPicker) field).getSearchButtonAdapter().setMessageTarget("none"); // } String name = field.getName(); if (name != null && !name.isEmpty() && !"null".equals(name)) { Log.debug("Field: " + field.getName() + " tabindex: " + field.getTabIndex()); // Automatically focus on the field with tab index 1 Widget parent = field.getParent(); // field.getParent() returns null for the CATTextField in a MultiField so the // following null tests are a temporary work-around until the root cause is identified // of why the CATTextField in a multi-field doesn't have a parent if (parent instanceof LayoutContainer || (parent == null && field instanceof CATTextField)) { if (parent != null) { LayoutContainer lc = (LayoutContainer) parent; if (!"hidden".equals(lc.getId())) { if (!(field instanceof LabelField) && !(field instanceof HiddenField) && !(field instanceof CATHiddenField)) { if (field.isVisible() && field.isEnabled()) { if (focusfield == null) { focusfield = field; } } } } } else if (parent == null && field instanceof CATTextField) { // CATTextField in a CATMultiField if (field.isVisible() && field.isEnabled()) { if (focusfield == null) { focusfield = field; } } } divePanel.put(name, TRUE); Field thefield = field; // Special handling for the CATMultiField - redirect the events to the inner TextField. if (field instanceof CATMultiField) { thefield = ((CATMultiField) field).getTextField(); } Log.trace("Field " + name + " added to divePanel"); // Attach listeners from the fields for when they fire valid / invalid during validation thefield.addListener(Events.Valid, new Listener() { @Override public void handleEvent(FieldEvent fe) { Object objsource = fe.getSource(); //Log.trace("Field.Valid listener - Field:" + objsource); if (!(objsource instanceof Field)) { return; } Field fld = (Field) objsource; String name = fld.getName(); Log.debug("Received VALID event for field: " + name); updateDivePanel(name, true); } }); thefield.addListener(Events.Invalid, new Listener() { @Override public void handleEvent(FieldEvent fe) { Object objsource = fe.getSource(); //Log.trace("Field.Invalid listener - Field: " + objsource); if (!(objsource instanceof Field)) { return; } Field fld = (Field) objsource; String name = fld.getName(); Log.debug("Received INVALID event for field: " + name); updateDivePanel(name, false); } }); // Log.debug("About to pre-validate field: " + field.getName()); //field.focus(); // BEH: 2013-02-18 - turned off this validation step. //thefield.validate(); } } } if (focusfield != null) { Log.debug("Focusing on the first editable widget: " + focusfield.getName()); //focusfield.focus(); focusfield.validate(); } } /** * Returns the overall validity of the form. * Every dive panel object is tested to see if all are true. If so, then the form is valid. * Any false value found means the entire form is invalid. * @return True when all entries in the dive panel are marked as true. False otherwise. */ public boolean isValid() { Iterator ix = divePanel.keySet().iterator(); while (ix.hasNext()) { String key = ix.next(); boolean b = divePanel.get(key).booleanValue(); if (!b) { formValid(false); return false; } } formValid(true); return true; } // private void clearDivePanel(){ // Iterator ix=divePanel.keySet().iterator(); // while(ix.hasNext()){ // String key=ix.next(); // divePanel.put(key,TRUE); // } // } /** * Forces validation on every field on the page. */ public void validate() { // If the dependency processor looks like it's going to run, then clear the dive panel and the start values used by the DH to avoid validation. // This should make it revalidate every field on the page. // TODO: Maybe this should only re-evaluate the false items? // if(getDependencyProcessor()!=null){ // clearDivePanel(); // DependencyComponent.clearValues(); // } List> fields = bodyPanel.getFields(); for (Field field : fields) { field.isValid(); } showErrorForCurrentFocusField(); } /** * The method to override to change how the form reacts to its overall validation status (typically, the finish button is enabled or disabled according to * the validity boolean passed to the routine). * * @param valid set to true if the overall form is valid, false otherwise */ public void formValid(boolean valid) { boolean theValid = valid; if (hasFormBeenApplied) { // if the form as been applied, first check to see that at least one field is dirty // before allowing the OK and APPLY buttons to be enabled boolean isDirty = false; for (Field field : bodyPanel.getFields()) { String name = field.getName(); if (name != null && !name.isEmpty() && !"null".equals(name)) { if (field.isDirty()) { isDirty = true; break; } } } if (!isDirty) { // if nothing is dirty and this is after an apply, do not turn on the OK and APPLY buttons theValid = false; } } if (formType == FormType.FORM) { if (finishButton != null) { finishButton.setEnabled(theValid); } } else if (formType == FormType.DIALOG) { getDialog().getButtonById(Dialog.OK).setEnabled(theValid); } if (applyButton != null) { if (applyButton != null) { applyButton.setEnabled(theValid); } } } /** * Disables the OK and / or Apply buttons on a page */ public void disableActionButtons() { if (formType == FormType.FORM) { if (finishButton != null) { finishButton.setEnabled(false); } } else if (formType == FormType.DIALOG) { getDialog().getButtonById(Dialog.OK).setEnabled(false); } Button ab = applyButton; if (ab != null) { ab.setEnabled(false); } } /** * @return the formType */ public FormType getFormType() { return formType; } private void updateDivePanel(String name, boolean valid) { divePanel.put(name, Boolean.valueOf(valid)); boolean status = isValid(); Log.info("Overall form validity: " + status); } /** * @param formType the formType to set */ public void setFormType(FormType formType) { this.formType = formType; } /** * Updates the task parameters for the specified task by scraping the current form. * @param task The Task that will get the parameters from the current form. */ protected void replaceTaskParameters(Task task) { Map pmap = task.getFormToTaskMap(); Iterator ix = pmap.keySet().iterator(); // Cycle through the parameters in the task's paramMap while (ix.hasNext()) { // Fname holds the name of the field to be scraped - pname holds the parameter name to generate String fname = ix.next(); String pname = pmap.get(fname); if (fname != null && !fname.isEmpty() && pname != null && !pname.isEmpty()) { Log.trace("Searching for a field value to substitute for param '" + fname + "' from field '" + pname + "'"); for (Field field : getBodyPanel().getFields()) { String name = field.getName(); if (name == null || name.isEmpty()) { continue; } if (name.equals(fname)) { // Found the matching Field name - substitute the value into the parameter map String realval = StandardComponents.getFieldValue(field); task.addTaskParam(pname, realval); Log.trace("Found the field value: " + realval); break; } } } } } /** * @return the dialog */ public Dialog getDialog() { if (dialog == null) { dialog = new CATDialog(); dialog.setData("form", this); dialog.setButtons(Dialog.OKCANCEL); dialog.setHideOnButtonClick(false); dialog.setClosable(false); dialog.setAnimCollapse(true); dialog.setModal(true); dialog.setBlinkModal(true); } return dialog; } /** * Sets up the Form / Dialog's busy mask * * @param message The string containing the desired mask text * @param progressText The string to be presented inside the progress bar */ public void setBusy(String message, String progressText) { setBusy("INTERNAL", message, progressText); } /** * Sets up the Form / Dialog's busy mask. * Each Asynchronous operation needs to make a set / clear busy pair of calls. * The first call will mask the form. * Subsequent calls will merely add their reason to the map of current busy reasons. * A RuntimeException is thrown if a second setBusy call with the same reason is encountered. * * @param reason The ID used to track multiple set/clear busy requests. * @param message The string containing the desired mask text * @param progressText The string to be presented inside the progress bar */ public void setBusy(String reason, String message, String progressText) { if (busyMap.containsKey(reason)) { throw new RuntimeException("setBusy called with a duplicate reason: " + reason + " busyMap: " + busyMap); } if (busyMap.isEmpty()) { if (getFormType() == FormType.FORM || getFormType() == FormType.EMBEDDED) { if (waitBox == null) { setWaitBox(MessageBox.wait("Progress", message, progressText)); } } else { if (!masked) { getDialog().mask(progressText); masked = true; } } } else { Log.debug("Form's busyMap was already established - left existing mask alone"); } updateDivePanel("$$BUSY", FALSE); busyMap.put(reason, reason); } /** * Tears down the Form / Dialog's busy mask */ public void clearBusy() { clearBusy("INTERNAL"); } /** * Tears down the Form / Dialog's busy mask * @param reason A string with a value to correspond to a previous setBusy call. */ public void clearBusy(String reason) { if (!busyMap.containsKey(reason)) { Log.warn("clearBusy called without a corresponding setBusy for reason: " + reason); } busyMap.remove(reason); if (busyMap.isEmpty()) { if (getFormType() == FormType.FORM || getFormType() == FormType.EMBEDDED) { if (getWaitBox() != null) { getWaitBox().close(); waitBox = null; } } else { if (getDialog() != null) { getDialog().unmask(); masked = false; } } updateDivePanel("$$BUSY", TRUE); } else { Log.debug("Form's busyMap still has entries: " + busyMap); } } /** * Forces the page validation status to false by adding a special flag to the dive panel's status map and setting it to FALSE */ public void forceDivePanelInvalid() { updateDivePanel("$$FORCE_VALID", FALSE); } /** * Whenever markDirty is used on this layout, it will clear the special flag so normal validation can resume. */ public void markDirty() { updateDivePanel("$$FORCE_VALID", TRUE); } /** * Returns the current validation status for a field name. This is used by the widgets when no change has been detected. * Instead of returning the value of validateValueLocal (which might give a false positive), it will use the divePanel status instead. * @param fieldName The name of the Field object on the page to be checked. * @return true if the Field specified by fieldName was last validated as VALID, false otherwise. */ public boolean isFieldValid(String fieldName) { if (fieldName == null || fieldName.isEmpty()) { throw new RuntimeException("A Framework widget has no name setting"); } Boolean status = divePanel.get(fieldName); if (status == null) { // throw new RuntimeException("A field name was not found in the dive panel: " + fieldName); return true; } return status; } /** * @return the footer */ public LayoutContainer getFooter() { return footer; } /** * @param footer the footer to set */ public void setFooter(LayoutContainer footer) { this.footer = footer; } /** * Toggles the Debug button on and off */ public void toggleDebug() { boolean newsetting = !tools.isVisible(); if (formType == FormType.EMBEDDED) { getFooter().setVisible(newsetting); } tools.setVisible(!tools.isVisible()); } /** * @return the dependencyDescriptor */ public String getDependencyDescriptor() { if (dependencyDescriptor == null) { dependencyDescriptor = getParamMap().get("dependencyjson"); } if (dependencyDescriptor == null) { dependencyDescriptor = ""; } Log.trace("Length of dependencyDescriptor: " + dependencyDescriptor.length()); return dependencyDescriptor; } /** * @param dependencyDescriptor the dependencyDescriptor to set */ public void setDependencyDescriptor(String dependencyDescriptor) { this.dependencyDescriptor = dependencyDescriptor; } /** * Returns the page's dependency processor * @return The DependencyProcessor for the page */ public DependencyProcessor getDependencyProcessor() { return dependency; } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 148917; to = 148824.Please, attach your messages.log to new issue! ----- /* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.ptc.gxt.framework.client.widget; import com.allen_sauer.gwt.log.client.Log; import com.extjs.gxt.ui.client.Registry; import com.extjs.gxt.ui.client.Style; import com.extjs.gxt.ui.client.Style.Scroll; import com.extjs.gxt.ui.client.core.FastMap; import com.extjs.gxt.ui.client.core.XDOM; import com.extjs.gxt.ui.client.data.ModelData; import com.extjs.gxt.ui.client.event.BaseEvent; import com.extjs.gxt.ui.client.event.ComponentEvent; import com.extjs.gxt.ui.client.event.Events; import com.extjs.gxt.ui.client.event.FieldEvent; import com.extjs.gxt.ui.client.event.Listener; import com.extjs.gxt.ui.client.event.MessageBoxEvent; import com.extjs.gxt.ui.client.event.SelectionListener; import com.extjs.gxt.ui.client.store.Store; import com.extjs.gxt.ui.client.store.StoreEvent; import com.extjs.gxt.ui.client.store.StoreListener; import com.extjs.gxt.ui.client.util.KeyNav; import com.extjs.gxt.ui.client.widget.Component; import com.extjs.gxt.ui.client.widget.ContentPanel; import com.extjs.gxt.ui.client.widget.Dialog; import com.extjs.gxt.ui.client.widget.Header; import com.extjs.gxt.ui.client.widget.HorizontalPanel; import com.extjs.gxt.ui.client.widget.Info; import com.extjs.gxt.ui.client.widget.LayoutContainer; import com.extjs.gxt.ui.client.widget.MessageBox; import com.extjs.gxt.ui.client.widget.Viewport; import com.extjs.gxt.ui.client.widget.button.Button; import com.extjs.gxt.ui.client.widget.button.ButtonBar; import com.extjs.gxt.ui.client.widget.form.AdapterField; import com.extjs.gxt.ui.client.widget.form.ComboBox; import com.extjs.gxt.ui.client.widget.form.DateField; import com.extjs.gxt.ui.client.widget.form.DateTimePropertyEditor; import com.extjs.gxt.ui.client.widget.form.Field; import com.extjs.gxt.ui.client.widget.form.FormPanel; import com.extjs.gxt.ui.client.widget.form.FormPanel.LabelAlign; import com.extjs.gxt.ui.client.widget.form.HiddenField; import com.extjs.gxt.ui.client.widget.form.LabelField; import com.extjs.gxt.ui.client.widget.form.NumberField; import com.extjs.gxt.ui.client.widget.form.Radio; import com.extjs.gxt.ui.client.widget.form.RadioGroup; import com.extjs.gxt.ui.client.widget.layout.ColumnData; import com.extjs.gxt.ui.client.widget.layout.ColumnLayout; import com.extjs.gxt.ui.client.widget.layout.FitLayout; import com.extjs.gxt.ui.client.widget.layout.FormData; import com.extjs.gxt.ui.client.widget.layout.FormLayout; import com.extjs.gxt.ui.client.widget.layout.HBoxLayout; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.Widget; import com.ptc.gxt.framework.client.EntryPointFactory; import com.ptc.gxt.framework.client.FormMode; import com.ptc.gxt.framework.client.FormType; import com.ptc.gxt.framework.client.FrameworkEntryPoint; import com.ptc.gxt.framework.client.TaskFactory; import com.ptc.gxt.framework.client.dependency.DependencyComponent; import com.ptc.gxt.framework.client.dependency.DependencyProcessor; import com.ptc.gxt.framework.client.entrypoint.CoreTaskFactory; import com.ptc.gxt.framework.client.event.ConfirmSelectListener; import com.ptc.gxt.framework.client.event.FrameworkEvents; import com.ptc.gxt.framework.client.ie.IEAtt; import com.ptc.gxt.framework.client.ie.IEElement; import com.ptc.gxt.framework.client.ie.IEGroup; import com.ptc.gxt.framework.client.ie.MsgLevel; import com.ptc.gxt.framework.client.ie.ParamMode; import com.ptc.gxt.framework.client.ie.Task; import com.ptc.gxt.framework.client.table.MergedModType; import com.ptc.gxt.framework.client.table.ReturnGroupHandler; import com.ptc.gxt.framework.client.table.TableGroupReturnGroupListener; import com.ptc.gxt.framework.client.widget.tools.DebugToolsMenu; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Defines the page. * The CATFormLayout is wrapped by StandardForm. * It defines a header, a body and footer information. * It operates in several modes, defined by the FormMode enum. * It produces several different types of form, defined by the FormType enum. * It provides standard buttons and actions associated to those buttons to support data acquisition, server update and cancel operations. * It provides for interaction with the launching window (refresh / forward to new urls) * It provides for standard actions when the action task completes (close on success, reset when apply button used, keep page when errors occur) * It provides validation services between fields via a DependencyProcessor tied to the page. * It provides for an initial data task, an action task and optionally, a cancel task. * * It provides for data transfer between in-browser pages via ReturnGroupListener. */ //public class CATFormLayout implements EntryPoint { public class CATFormLayout { private static final Boolean TRUE = true; private static final Boolean FALSE = false; /** *Identifier for use in retrieving the Apply button via getDialog().getButtonById(APPLY); */ public static final String APPLY = "apply"; private Header hdr; private String formName = "-No Form Name Specified-"; private String formLabel = ""; private String helpID = ""; private String helpURL = Registry.get("helpurl"); private ContentPanel container; private EntryPointFactory entryPoint; private String application = "FormLayout"; private Dialog dialog; private Viewport viewport = StandardComponents.getCATViewport(this); private HorizontalPanel buttonPanel; private CATFormPanel bodyPanel; private LayoutContainer hiddenPanel; private WindowMode forcedWindowAction; private ParentMode forcedParentAction; private int width = -1; private int height = -1; private int minWidth = -1; private int minHeight = -1; private int minCWidth = -1; private int minCHeight = -1; // private int formDataWidth = 250; private String oid; private String webAppURL; private FormType formType = FormType.FORM; private FormMode mode; private Map divePanel = new FastMap(); private Component focusField; private boolean hasFormBeenApplied; private boolean clearOnApply; private Task initTask; private IEGroup initTaskGroup; private Task actionTask; private IEGroup actionTaskGroup; private Task cancelTask; private IEGroup cancelTaskGroup; private IEGroup deferGroup; private IEElement initElement; private IEGroup initGroup; private ReturnGroupHandler returnGroupHandler; private DataReturnMode returnMode; // Any objects that need to be shared into inner classes // or get defined in the registry get declared here. // Use the name of the object as the registry entry // This should help with code completion private String messageBox; private String containerOid; private Button finishButton; private Button applyButton; private Button cancelButton; // private Button closeButton; private MessageBox waitBox; private boolean masked; private Map busyMap = new FastMap(); private Map hiddenParamMap; private int sbh = XDOM.getScrollBarWidth() + 12; private int sbw = XDOM.getScrollBarWidth() + 12; private StandardForm page; private List customHiddenFields; private Field currentFocusField; private LayoutContainer statusarea; private LayoutContainer footer; private Button tools; private boolean forceClose; private SelectionListener okSelectionListener; private SelectionListener applySelectionListener; private SelectionListener cancelSelectionListener; private SubmitMode submitMode = SubmitMode.OK; private String taskDataKey = "obid"; private Boolean dirty = false; private IEGroup dependencyjson; private boolean dependencySetup; private DependencyProcessor dependency; private String dependencyDescriptor; private String createdObjectOID; /** * When true, only dirty fields and hidden fields will be sent to edit tasks */ private boolean sendOnlyDirtyOnEdit; /** * The field that will display the widget's field label in the status area */ // private CATLabelField statusAreaLabel; /** * The field that will display the widget's tooltip in the status area */ private CATLabelField statusAreaMessage; /** * The Red Ball image for display between statusAreaLabel and statusAreaMessage */ private Image statusAreaImage; /** * The adapter to wrap statusAreaImage with to insure it displays properly */ private AdapterField statusAreaImgAdapter; /** * This creates a standard form layout for use by a page. It provides a standardized header with a title and a help icon, an OK / Apply / Cancel button set * and a CATFormPanel into which the developer will add the specific Field sub-classes to create the desired form. Once this method is run, the developer * should do a getBodyPanel() and start adding Field objects to the page. * * @param app The application name for this page * @return The form, already set up to take the specific fields. */ public static CATFormLayout newForm(String app) { return newForm(app, FormType.FORM); } /** * Creates a new CATFormLayout for the given EntryPointFactory enum entry. * @param app The EntryPointFactory enum entry for the StandardForm used with this CATFormLayout * @return The new CATFormLayout object */ public static CATFormLayout newForm(EntryPointFactory app) { return newForm(app, FormType.FORM); } /** * Creates a new CATFormLayout for the given app name and sets the form type as indicated. * @param app The name of the app that will resolve via the newEntryPoint method to a StandardForm * @param formtype A FormType of DIALOG, FORM or EMBEDDED * @return The new CATFormLayout object. */ public static CATFormLayout newForm(String app, FormType formtype) { EntryPointFactory epf = FrameworkEntryPoint.getEntryPointFactory(app); if (epf == null) { throw new RuntimeException("No EntryPoint found for application: " + app); } CATFormLayout newform = new CATFormLayout(); newform.setup(epf, formtype); return newform; } /** * Creates a new Form using an EntryPointFactory handle and FormType. * @param app The EntryPointFactory enum value for the desired page. * @param formtype The type of dialog being created (DIALOG, FORM, or EMBEDDED). * @return A CATFormLayout ready for use by setupLayout. */ public static CATFormLayout newForm(EntryPointFactory app, FormType formtype) { EntryPointFactory epf = app; if (epf == null) { throw new RuntimeException("No EntryPoint found for application: " + app); } CATFormLayout newform = new CATFormLayout(); newform.setup(epf, formtype); return newform; } // @Override /** * Creates the basic shell of the page * @param epf An EntryPointFactory enum value. * @param formtype The form type to be used for the form (@see FormType). */ public void setup(EntryPointFactory epf, FormType formtype) { Log.debug("FormLayout :onModuleLoad: start"); Log.debug("FormLayout : webappurl: " + Registry.get("webappurl")); Log.debug("Launching URL: " + com.google.gwt.user.client.Window.Location.getHref()); Log.debug("oid for this UI page: " + oid); Log.debug("containerOid for this UI page: " + containerOid); setEntryPoint(epf); setFormType(formtype); setMode(epf.getMode()); setFormName(epf.getName()); setHelpID(epf.getHelpID()); setApplication(epf.getName()); setMinHeight(epf.getHeight()); setMinWidth(epf.getWidth()); setWebAppURL((String) Registry.get("webappurl")); //TODO: Don't think this line is doing anything - nobody seems to set the registry entry it's looking for //setHiddenParamMap((Map) Registry.get(app + "_paramMap")); // For Creating Basic Container which is used to add all components.. container = new ContentPanel(); Log.trace("CATFormLayout container isMonitorWindowResize: " + container.isMonitorWindowResize()); container.setLayout(new FitLayout()); if (FormType.EMBEDDED != formType) { container.addStyleName("cat-background"); container.setId("background"); getViewport().removeStyleName("cat-background"); } CATFormPanel fp = getBodyPanel(); fp.setMethod(FormPanel.Method.POST); fp.setLabelAlign(LabelAlign.RIGHT); fp.setLabelWidth(150); fp.setHeaderVisible(false); fp.getHeader().setText(""); } /** * Creates a task with a taskoutgroupname generated from the taskuri. It will never re-use an existing instance of a task with a matching taskuri. * * @param task The TaskFactory enum entry of the task to be run * @return The configured task (also available via initFormTask) */ public Task setupInitTask(TaskFactory task) { return setupInitTask(task, null, false); } /** * Creates a task with a taskoutgroupname generated from the taskuri. When the useCache is true, it will re-use an already configured task with the same * uri. * * @param task The TaskFactory enum entry of the task to be run * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via initFormTask) */ public Task setupInitTask(TaskFactory task, boolean useCache) { return setupInitTask(task, null, useCache); } /** * Creates a task with a taskoutgroupname as specified (null means it will generate it from the taskuri). When the useCache is true, it will re-use an * already configured task with the same uri. * * @param task The TaskFactory enum entry of the task to be run * @param group The name of the group that will be returned by the task * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via initFormTask) */ public Task setupInitTask(TaskFactory task, String group, boolean useCache) { String taskuri = task.getTaskUri(); if (taskuri == null) { StandardComponents.alert(this, MsgLevel.FATAL, "Initializer task uri was not specified"); return null; } initTask = Task.newTask(task, group, useCache); String workgroup = group; if (group == null || group.isEmpty()) { workgroup = getInitTask().getTaskGroupOutName(); } initTaskGroup = getInitTask().getGroup(workgroup); final DefaultInitTaskListener initTaskListener = new DefaultInitTaskListener(); getInitTaskGroup().addStoreListener(initTaskListener); getInitTaskGroup().addListener(Store.Sort, new Listener() { @Override public void handleEvent(BaseEvent be) { initTaskListener.setSuppressClearBusy(true); Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { initTaskListener.setSuppressClearBusy(false); } }); } }); return getInitTask(); } /** * Invokes the page */ public void invoke() { // StandardComponents.removeLoadingMarker(); // Make sure we're not invoking a page that can't do anything. If we going to be modifying the DB, we need to either have an action task and / or a return group handler defined. if (getMode() == FormMode.CREATE || getMode() == FormMode.EDIT || getMode() == FormMode.DELETE) { if (getReturnGroupHandler() == null && (actionTask == null || actionTask.isNullTask())) { throw new RuntimeException("A page " + getPage().getClass().getName() + " in mode CREATE, EDIT or DELETE must have either an action task or a return group handler defined. This page has neither!"); } } if (dependency != null) { dependency.setProcessing(false); } // Reset the form in case we're executing this multiple times reset(); // Set up hardcoded data for the dialog (e.g., a fixed pulldown list or starting value) getPage().setupFixedData(); MergedModType mt = MergedModType.NONE; if (mode == FormMode.CREATE) { mt = MergedModType.ADD; } else if (mode == FormMode.EDIT) { mt = MergedModType.EDIT; } Field field = getBodyPanel().getField(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME); if (field == null) { addHiddenField(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME, mt.name()); } else { field.setValue(mt.name()); } setDefaultFocusField(); if (formType != FormType.DIALOG) { getViewport().layout(false); getViewport().show(); } else { getDialog().layout(true); getDialog().show(); } // Defer the rest of the work until after the form successfully paints (hopefully) // Two levels of defer to try to give all the formatting time to settle out so we don't get a Frankenscreen visible to the user Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { // Mask the form setBusy("FORM", "Please Wait...", "Initializing Form..."); Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { beginFormOperation(); } }); } }); } /** * Kicks off the init task. * Displays a message if the tasks are suppressed (debug tool for checking the layout of the page without waiting for data). * Turns on the dependency handling * Requests the form size adjustments to get the layouts to "settle" */ private void beginFormOperation() { Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { runInitTask(); String rt = (String) Registry.get("runtasks"); if (rt != null && !rt.equalsIgnoreCase("true")) { StandardComponents.alert(getSelf(), MsgLevel.WARN, "Tasks are not running - just letting you check form layouts"); } activateDependencyHandler(); initialFormAdjustment(); } }); } /** * Adjusts the form size down one pixel in height to help get the form to settle */ private void initialFormAdjustment() { // This is to force a resize to get the page to properly layout (some things just don't work right during pre-render layout) Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { jiggleForm(false); finalFormAdjustment(); } }); } /** * Adjusts the form back to its requested size, sets the default focus and clears the busy mask. */ private void finalFormAdjustment() { // This restores the original requested size and causes one more adjustment - hopefully it will be more reliable this way Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { jiggleForm(true); setDefaultFocus(); clearBusy("FORM"); } }); } private void activateDependencyHandler() { // Call the dependency processor Log.debug("Calling the DependencyProcessor"); // Restart the processor if the page is being reused if (dependency != null) { Log.debug("Restarting the dependency processor on the page"); dependency.processPage(getBodyPanel(), false); validate(); } else { // If we have already attempted to process the dependency file and it's still null, we won't get a better answer on the second attempt, so skip the task. if (!dependencySetup) { // If we are the top-level page, entrypointclassname should point to our classname - if so, use whatever descriptor is present if (getPage().getClass().getName().equals(Registry.get("entrypointclassname"))) { Log.debug("Opening the dependency descriptor for the top-level page: " + getPage().getClass().getName()); dependency = DependencyProcessor.newDependencyProcessor(getPage(), getDependencyDescriptor()); dependencySetup = true; } else { // we are not the top level page, run the task to get the dependency descriptor setBusy("DH", "Please Wait...", "Initializing Form..."); Task dependencyTask; if (Registry.get("windchill") != null) { dependencyTask = Task.newTask(CoreTaskFactory.GET_DEPENDENCY_DESCRIPTOR); } else { dependencyTask = Task.newJspTask(CoreTaskFactory.GET_DEPENDENCY_DESCRIPTOR_SERVLET, "getDependencyDescriptor", false); } dependencyTask.addTaskParam("pageclass", getPage().getClass().getName()); dependencyjson = dependencyTask.getGroup("dependencyjson"); dependencyjson.addStoreListener(new StoreListener() { @Override public void storeDataChanged(StoreEvent se) { // Retrieve the task results and use them to open the descriptor returned in the data IEElement depel; String depjson; if (dependencyjson.getElementCount() > 0) { depel = dependencyjson.getElementAt(0); depjson = depel.getValue("dependencyjson"); dependencyDescriptor = depjson; } Log.debug("Opening the dependency descriptor for the dialog: " + getPage().getClass().getName()); dependency = DependencyProcessor.newDependencyProcessor(getPage(), getDependencyDescriptor()); dependencySetup = true; clearBusy("DH"); validate(); } }); dependencyTask.invoke(); } } } } /** * Adjusts the size of the form either up or down one pixel in height. * This seems to be needed to get all of the interactions in the layouts to settle out and actually do what the developer intends. */ private void jiggleForm(boolean up) { final int upsize = 1; final int downsize = -1; int adjustSize = downsize; if (up) { adjustSize = upsize; } int theheight = Window.getClientHeight(); int thewidth = Window.getClientWidth(); if (formType == FormType.FORM) { if (getEntryPoint().getHeight() != -1 && getEntryPoint().getWidth() != -1) { StandardComponents.setWindowSize(getEntryPoint().getHeight() - adjustSize, getEntryPoint().getWidth()); } } if (formType != FormType.DIALOG) { getViewport().setSize(getViewport().getWidth(), getViewport().getHeight() - adjustSize); } else { getBodyPanel().setLayoutData(new FormData("98% 98%")); } getContainer().layout(); Log.debug("Resized window to " + theheight + ":" + thewidth); } /** * @return the dirty */ public Boolean isDirty() { return dirty; } /** * @param dirty the dirty to set */ public void setDirty(Boolean dirty) { this.dirty = dirty; } /** * Runs the page's Init Task. Only the hidden fields are sent as parameters. */ public void runInitTask() { // This setBusy call MUST appear after the show calls or you can wind up with multiple copies of the mask setBusy("INITTASK", "Please wait...", "Initializing Form..."); createTaskParameters(getInitTask(), true); if (!initTask.invoke()) { // If we're using a StartElement with field data, use it to set the form field values instead of the task results if (getInitElement() != null) { setFormFieldValues(initGroup); } clearBusy("INITTASK"); } } /** * @return the startElement * @deprecated Use getInitElement instead */ @Deprecated public IEElement getStartElement() { return initElement; } /** * @return the startElement */ public IEElement getInitElement() { return initElement; } /** * @param initElement the startElement to set. This will be treated like a single element being returned from an initTask in the default group. */ public void setInitElement(IEElement initElement) { this.initElement = initElement; initGroup = new IEGroup("initGroup"); initGroup.addElement(initElement); setInitGroup(initGroup); } /** * @param startElement the startElement to set. This will be treated like a single element being returned from an initTask in the default group. * @deprecated Use setInitElement(IEElement) instead. */ @Deprecated public void setStartElement(IEElement startElement) { setInitElement(startElement); } /** * @param initGroup the initGroup to set. This will be treated like it is the default group being returned from an initTask. * */ public void setInitGroup(IEGroup initGroup) { this.initGroup = initGroup; // if (initElement != null && getInitTask() != null && !initTask.getUri().equals(CoreTaskFactory.NULL.getTaskUri())) { // //throw new RuntimeException("Error: you cannot specify an init task and then use an initElement to initialize the page"); // } if (getInitTask() == null) { initTask = Task.newTask(CoreTaskFactory.NULL); } getInitTask().addGroup(initGroup); getInitTask().setTaskGroupOutName(initGroup.getName()); } /** * @return the initTask */ public Task getInitTask() { return initTask; } /** * @return the actionTask */ public Task getActionTask() { return actionTask; } /** * @return the cancelTask */ public Task getCancelTask() { return cancelTask; } /** * @return the submitMode */ public SubmitMode getSubmitMode() { return submitMode; } /** * @param submitMode the submitMode to set */ public void setSubmitMode(SubmitMode submitMode) { this.submitMode = submitMode; } /** * @return the initTaskGroup */ public IEGroup getInitTaskGroup() { return initTaskGroup; } /** * @return the actionTaskGroup */ public IEGroup getActionTaskGroup() { return actionTaskGroup; } /** * @return the cancelTaskGroup */ public IEGroup getCancelTaskGroup() { return cancelTaskGroup; } /** * @return the webAppURL */ public String getWebAppURL() { return webAppURL; } /** * The default task listener for the init task */ @SuppressWarnings("ProtectedInnerClass") public class DefaultInitTaskListener extends StoreListener { private boolean suppressClearBusy = false; /** * Indicates if the busy indicator should be suppressed for this page * @param suppress True to suppress, false to allow the indicator to be used. */ public void setSuppressClearBusy(boolean suppress) { suppressClearBusy = suppress; } @Override public void storeDataChanged(StoreEvent se) { //TODO: BEH: Perhaps all these data changed can be handled by the dependencyhandler, especially this type // form.getWaitBox().close(); Log.trace("InitializerTask.storeDataChanged: Enter"); if (assertGroupHasData(getInitTaskGroup(), getTaskDataKey(), "Initialization data could not be retrieved from task: " + getInitTask().getUri())) { setFormFieldValues(getInitTaskGroup()); } StandardComponents.alert(FormMode.ASSERT_EXIST, getInitTaskGroup()); isValid(); if (!suppressClearBusy) { clearBusy("INITTASK"); } suppressClearBusy = false; } } /** * Takes the values from the first element in the group and processes them into matching field names on the form. Only Field objects will be picked up. Only those * with names will be processed. If the group has a FormMap, the mappings are honored during processing. Events should not fire from the setValue calls - a * later, final validation of all fields on the form happens right after the form is shown. * * @param group The group to be processed */ protected void setFormFieldValues(IEGroup group) { // Check to make sure we have decided whether or not the dependency file has been found and read before putting values on the page, which will eventually start triggering through the DH. if (!dependencySetup) { deferGroup = group; // Defer this processing until later Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { setFormFieldValues(deferGroup); } }); return; } if (group == null) { StandardComponents.alert(this, MsgLevel.FATAL, "setFormField values was given a non-existent group"); return; } if (group.getElementCount() == 0) { StandardComponents.alert(this, MsgLevel.FATAL, "setFormField values was given an empty group"); return; } // Get the group's individual form to group map Map formToGroupMap = group.getFormToGroupMap(); if (formToGroupMap.isEmpty()) { // Group didn't have one - use the init task's map formToGroupMap = getInitTask().getFormToTaskMap(); } setFormFieldValues(group.getElementAt(0), formToGroupMap); } /** * Takes the values in the element and processes them into matching field names on the form. Only Field objects will be picked up. Only those * with names will be processed. If the group has a FormMap, the mappings are honored during processing. Events should not fire from the setValue calls - a * later, final validation of all fields on the form happens right after the form is shown. * * @param el The IEElement to be processed * @param formToGroupMap A map of form names to group attribute names that will be used during processing */ protected void setFormFieldValues(IEElement el, Map formToGroupMap) { boolean showMissingData = Log.isDebugEnabled(); StringBuilder missingData = new StringBuilder(); List> fields = getBodyPanel().getFields(); for (Field field : fields) { // for each item in the group // loop through all the fields on the form until a match is found // Don't process a field without a name String fieldName = field.getName(); String safeFieldName = fieldName; if (fieldName == null || fieldName.isEmpty()) { Log.debug("Field skipped because it has no name: " + field.getClass().getName()); continue; } if (fieldName.contains(".")) { Log.debug("Replacing dots with underscores for field param name: " + fieldName); safeFieldName = safeFieldName.replace(".", "_"); } // Pull the attribute from the element corresponding to the field name // Check the formMap to see if we need to pull a different attribute for this field String mappedAttName = formToGroupMap.get(safeFieldName); if (mappedAttName == null) { mappedAttName = safeFieldName; } // Now check for the mapped attribute name in the element that was passed to // populate the form Object obj = el.getValue(mappedAttName); Object pulldownlistgroup = el.getValue(mappedAttName + "_legalvalues"); if (pulldownlistgroup != null && !(pulldownlistgroup instanceof IEGroup)) { pulldownlistgroup = null; } if (field instanceof ComboBox) { ((ComboBox) field).setStore((IEGroup) pulldownlistgroup); } // if (fieldName.equals(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME)) { // if (obj instanceof MergedModType) { // obj = ((MergedModType) obj).toString(); // } // } String val = ""; if (obj != null) { try { // Disable validation for this field because it might depend on the values // of other fields which "have not" been set yet. //StandardComponents.setFieldValidation(field, false); field.setFireChangeEventOnSetValue(false); if (obj instanceof String) { val = (String) obj; Log.debug("Setting field '" + fieldName + "' to attribute '" + mappedAttName + "' - val: '" + val + "'"); if (field instanceof RadioGroup) { List> radios = ((RadioGroup) field).getAll(); for (Field fieldRadio : radios) { Radio radio = (Radio) fieldRadio; if (val.equals(radio.getValueAttribute())) { // radio.setFireChangeEventOnSetValue(false); radio.setValue(Boolean.TRUE); // radio.setFireChangeEventOnSetValue(true); // field.setFireChangeEventOnSetValue(false); ((RadioGroup) field).setValue(radio); ((RadioGroup) field).setOriginalValue(radio); // field.setFireChangeEventOnSetValue(true); } } } else if (field instanceof CATMultiField) { // field.setFireChangeEventOnSetValue(false); ((CATMultiField) field).setValue(val); ((CATMultiField) field).setOriginalValue(val); ((CATMultiField) field).getTextField().setOriginalValue(val); // field.setFireChangeEventOnSetValue(true); } else if (field instanceof LabelField) { // field.setFireChangeEventOnSetValue(false); ((LabelField) field).setText(val); ((LabelField) field).setValue(val); ((LabelField) field).setOriginalValue(val); // field.setFireChangeEventOnSetValue(true); } else if (field instanceof NumberField) { // field.setFireChangeEventOnSetValue(false); field.clear(); if (val == null || val.isEmpty()) { field.setValue(null); field.setOriginalValue(null); } else { field.setValue(field.getPropertyEditor().convertStringValue(val)); field.setOriginalValue(field.getValue()); } // field.setFireChangeEventOnSetValue(true); } else if (field instanceof Radio) { continue; } else if (field instanceof CATComboBox) { CATComboBox box = ((CATComboBox) field); if (box.getStore() == null) { throw new RuntimeException("ComboBox: " + box.getName() + " is not bound to a Group"); } if (box.getValueField() == null || box.getValueField().isEmpty()) { throw new RuntimeException("ComboBox: " + box.getName() + " does not have a valuefield set up"); } if (box.getDisplayField() == null || box.getDisplayField().isEmpty()) { throw new RuntimeException("ComboBox: " + box.getName() + " does not have a displayfield set up"); } // Try matching the raw value with something in the store if (box.getValue() == null) { Log.debug("Using the value from the backing store: " + val); box.setValueFromStore(val); } // Try creating a custom hidden item in the store and then match the raw value against the store if (box.getValue() == null) { Log.debug("Using the hardcoded value: " + val); box.addNewPulldownListEntry(val, val, false); // make sure that if box.getValue() is null then both value and originalValue // set to the same thing. In gxt, if I setValueFromStore("") and then // call getValue() then getValue() does some logic and returns null. box.setValueFromStore(val); final IEElement originalValue = new IEElement(); originalValue.set(box.getValueField(), box.getRawValue()); box.setOriginalValue(originalValue); Log.trace("CATFormLayout --> val = " + val + ", getValue() = " + box.getValue() + ", raw value = " + box.getRawValue() + ", originalValue = " + box.getOriginalValue()); } box.setData("rawValue", val); box.setData("forcedValue", "y"); if (box.getValue() == null) { Log.debug("Combo values: raw: " + ((CATComboBox) field).getRawValue() + " value: " + box.getValue()); // throw new RuntimeException("Failed to set value on combo box: " + box.getName()); } // box.getValue is not null then it hasn't been set yet. if (box.getValue() != null) { box.setOriginalValue(box.getValue()); } // Log.debug("Combo values: raw: " + ((CATComboBox) field).getRawValue() + " internal: " + box.getValue().get(box.getValueField()) + " display: " + box.getValue().get(box.getDisplayField())); } else if (field instanceof DateField) { if (val.isEmpty()) { val = null; } DateTimePropertyEditor editor = ((DateField) field).getPropertyEditor(); Date dt = null; if (val != null) { dt = editor.convertStringValue(val); } field.setValue(dt); field.setOriginalValue(dt); } else { if (val == null || val.isEmpty()) { field.setValue(null); field.setOriginalValue(null); } else { field.setValue(val); field.setOriginalValue(val); } } } else if (obj instanceof IEGroup) { // look for an adapter field containing a table // if it is found set the group found in this attribute // to the group for the table in the matching adapter field if (field instanceof CATAdapterField) { IEGroup g = (IEGroup) obj; CATAdapterField af = (CATAdapterField) field; if (af.getTable() != null) { IEGroup work = (IEGroup) af.getTable().getGrid().getStore(); work.setFiresEvents(false); work.removeAll(); work.add(g.deepClone().getElementList()); work.setFiresEvents(true); work.signalDataChanged(); } } } } catch (ClassCastException cce) { Log.error("ClassCastException: " + cce); Log.error("Field: " + field.getClass().getName() + " val: " + val); Log.error("Couldn't use setValue - trying setRawValue"); field.setRawValue(val); } finally { // StandardComponents.setFieldValidation(field, true); field.setFireChangeEventOnSetValue(true); } } else { if (showMissingData) { boolean fieldIsHidden = StandardComponents.isFieldHidden(field); if (!fieldIsHidden) { if (missingData.length() > 0) { missingData.append(", "); } if (fieldName.equals(mappedAttName)) { missingData.append(fieldName); } else { missingData.append(mappedAttName).append(" (mapped from ").append(fieldName).append(")"); } } } Log.warn("Field '" + mappedAttName + "' was not found"); } } // Now that all of the values are loaded, we can safely perform validation. DependencyComponent.clearValues(); validate(); } /** * Sets the field name to look for in the task data to confirm the operation was successful and to use as the object ID for any follow-on processing (defaults to 'obid'). * @param setting The field name */ public void setTaskDataKey(String setting) { taskDataKey = setting; if (setting == null) { StandardComponents.alert(FormMode.LIST, MsgLevel.ERROR, "setTaskDataKey was given a null argument"); taskDataKey = "obid"; } } /** * Retrieves the field name used as the object ID for task follow-on operations * @return */ public String getTaskDataKey() { return taskDataKey; } /** * Creates a task with a taskoutgroupname generated from the taskuri. This method will never re-use an existing instance of a task. * * @param task The TaskFactory enum entry of the task to be run * @return The configured task (also available via actionTask) */ public Task setupActionTask(TaskFactory task) { return setupActionTask(task, null, false); } /** * Creates a task with a taskoutgroupname generated from the taskuri. When the useCache is true, it will re-use an already configured task with the same * uri. * * @param task The TaskFactory enum entry of the task to be run * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via actionTask) */ public Task setupActionTask(TaskFactory task, boolean useCache) { return setupActionTask(task, null, useCache); } /** * Creates a task with a taskoutgroupname as specified (null means it will generate it from the taskuri). When the useCache is true, it will re-use an * already configured task with the same uri. * * @param task The TaskFactory enum entry of the task to be run * @param group The name of the group that will be returned by the task * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via actionTask) */ public Task setupActionTask(TaskFactory task, String group, boolean useCache) { mode = getMode(); String taskuri = task.getTaskUri(); if (taskuri == null) { throw new RuntimeException("StandardForm::setupActionTask - action task uri not specified"); } actionTask = Task.newTask(task, group, useCache); actionTaskGroup = getActionTask().getGroup(getActionTask().getTaskGroupOutName()); if (mode == FormMode.CREATE || mode == FormMode.EDIT || mode == FormMode.DELETE) { getActionTaskGroup().addStoreListener(new StoreListener() { @Override public void storeDataChanged(StoreEvent se) { //TODO: Can this be placed after postActionTaskProcessing for maximum mask coverage? clearBusy("ACTIONTASK"); postActionTaskProcessing(true); } }); } return getActionTask(); } /** * Sets the component that should receive the focus when the page is ready for input * @param c The Component that will receive the focus. Should be an input type Component */ public void setFocusField(Component c) { focusField = c; if (c instanceof Field) { currentFocusField = (Field) c; } } /** * Computes the Focus field by finding the field with the lowest tabindex or the first editable field on the page if no tabindex values are found */ public void setDefaultFocusField() { if (focusField == null) { Field firstEditable = null; Field lowestTabOrder = null; int minTabOrder = 999; // If the page forced a focusField, just leave if (focusField != null) { return; } // Scan through the fields looking for the field with the lowest Tab Order, or the first editable field and make that the focusField for (Field f : getBodyPanel().getFields()) { if (f.isEnabled() && !f.isReadOnly() && !(f instanceof HiddenField) && !(f instanceof CATHiddenField) && !(f instanceof LabelField)) { int to = f.getTabIndex(); if (to < minTabOrder) { minTabOrder = to; lowestTabOrder = f; Log.debug("Lowest Tab Index Field: " + lowestTabOrder.getId() + "(" + lowestTabOrder.getName() + ")"); } if (firstEditable == null) { firstEditable = f; Log.debug("First Editable Field: " + f.getId() + "(" + f.getName() + ")"); } if (lowestTabOrder != null) { focusField = lowestTabOrder; } else { focusField = firstEditable; } } } if (focusField != null) { Log.debug("Focus Field: " + ((Field) focusField).getId() + "(" + ((Field) focusField).getName() + ")"); } else { Log.debug("Focus Field: No available editable field found - no focus field set"); } } } /** * Gives focus to the field specified by setFocusField(). */ public void setDefaultFocus() { if (formType == FormType.FORM || formType == FormType.EMBEDDED) { getViewport().show(); if (focusField != null) { focusField.focus(); } } else if (formType == FormType.DIALOG) { getDialog().show(); if (focusField != null) { getDialog().setFocusWidget(focusField); getDialog().focus(); } } } /** * A method to return 'this' for event handlers (their 'this' is the handler itself). * @return The CATFormLayout object for the page. */ public CATFormLayout getSelf() { return this; } /** * Sets whether or not to close, ignoring any page dirty status. * @param forceClose True to force the page to close regardless of any dirty setting encountered. */ public void setForceClose(boolean forceClose) { this.forceClose = forceClose; } /** * @return the forceClose */ public boolean isForceClose() { return forceClose; } /** * Returns the EntryPointFactory used by this layout. * @return The EntryPointFactory object for this layout. */ public EntryPointFactory getEntryPoint() { return entryPoint; } /** * @param entryPoint the entryPoint to set */ public void setEntryPoint(EntryPointFactory entryPoint) { this.entryPoint = entryPoint; } /** * @return the width */ public int getWidth() { return width; } /** * @param width the width to set */ public void setWidth(int width) { this.width = width; } /** * @return the height */ public int getHeight() { return height; } /** * @param height the height to set */ public void setHeight(int height) { this.height = height; } /** * Adds a hidden field to the Form for transmission to the server during init/action tasks * * @param key The name of the field * @param value The value for the field */ public void addParam(String key, String value) { getParamMap().put(key, value); } /** * Returns a particular value from the paramMap * * @param key The name of the hidden param desired * @return The value of the hidden param */ public String getHiddenParam(String key) { return hiddenParamMap.get(key); } /** * Returns a particular value from the paramMap * * @param key The name of the entry desired * @return The value of the map entry * @deprecated Use getHiddenParam instead */ @Deprecated public String getParam(String key) { return hiddenParamMap.get(key); } /** * Sets the hidden param map - the list of hidden fields to create in the form for use in task parameters for the init and / or action tasks for the form * * @param paramMap the paramMap to set */ @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter") public void setHiddenParamMap(Map paramMap) { this.hiddenParamMap = paramMap; } /** * Sets the hidden param map - the list of hidden fields to create in the form for use in task parameters for the init and / or action tasks for the form * * @param paramMap the paramMap to set * @deprecated Use {@link #setHiddenParamMap}(Map<String, String> paramMap) instead */ @Deprecated @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter") public void setParamMap(Map paramMap) { this.hiddenParamMap = paramMap; } private void reportWindowSizes() { Log.trace("---------------------------------------------------------------"); Log.trace("----- window actuals:" + com.google.gwt.user.client.Window.getClientWidth() + "," + com.google.gwt.user.client.Window.getClientHeight()); Log.trace("----- window minimums: " + minWidth + "," + minHeight); if (container.isRendered()) { Log.trace("----- container actuals: " + container.getWidth() + "," + container.getHeight()); Log.trace("----- container minimums: " + minCWidth + "," + minCHeight); } if (getViewport().isRendered()) { Log.trace("----- viewport actuals: " + getViewport().getWidth() + "," + getViewport().getHeight()); } if (bodyPanel.isRendered()) { Log.trace("----- Body actuals: " + bodyPanel.getWidth() + "," + bodyPanel.getHeight()); } } /** * Prepares the layout for use by running all the standard behaviors not related to the page-defined data. * This is run just before the invocation of the page. */ public void finalizeLayout() { // Set up the Enter / Escape key button presses of OK / Cancel if (formType == FormType.DIALOG) { new KeyNav(getDialog()) { // Have the Enter key press the OK button @Override public void onEnter(ComponentEvent ce) { // If the form is not valid, don't click the button if (isValid()) { // If an Apply button is present, Enter will invoke it instead of OK. Button btn = getDialog().getButtonById(APPLY); if (btn != null && (!btn.isVisible() || !btn.isEnabled())) { btn = getDialog().getButtonById(Dialog.OK); } if (btn != null) { btn.fireEvent(Events.Select); } } else { Info.display("ERROR", "The form data is invalid. Correct the errors, then press OK"); } stopEventConditional(ce); } // Have the Escape key press the Cancel button @Override public void onEsc(ComponentEvent ce) { getDialog().getButtonById(Dialog.CANCEL).fireEvent(Events.Select); ce.stopEvent(); } }; } else if (formType == FormType.FORM) { new KeyNav(bodyPanel) { @Override public void onEnter(ComponentEvent ce) { stopEventConditional(ce); if (finishButton.isEnabled()) { finishButton.fireEvent(Events.Select); } } @Override public void onEsc(ComponentEvent ce) { //TODO: BEH: Add test for dirty form fields - if any found, prompt user to make sure they want to cancel cancelButton.fireEvent(Events.Select); } }; } else if (formType == FormType.EMBEDDED) { new KeyNav(bodyPanel) { //Prevent the audible "ding" which occurs for no apparent reason @Override public void onEnter(ComponentEvent ce) { stopEventConditional(ce); } }; } // This KeyNav picks up a Ctrl-Alt-D key press and toggles the debug button on and off new KeyNav(bodyPanel) { @Override public void onKeyPress(ComponentEvent ce) { super.onKeyPress(ce); if (ce.isControlKey() && ce.isAltKey()) { int code = ce.getKeyCode(); if (code == 68) { toggleDebug(); } } } }; // If formLabel is still blank, then there was no call to setFormLabel from the calling screen - use the entry point's default label if (formLabel.isEmpty()) { setFormLabel(getEntryPoint().getLabel()); } container.setStyleName("cat-top-container"); bodyPanel.setBorders(false); if (FormType.EMBEDDED != formType) { container.setLayout(new FormLayout()); container.add(bodyPanel, new FormData("100% 100%")); getViewport().add(container); getViewport().setScrollMode(Scroll.AUTO); container.setScrollMode(Scroll.NONE); } else { getViewport().setLayout(new FitLayout()); container.setLayout(new FormLayout()); container.add(bodyPanel, new FormData("100% 100%")); getViewport().add(container); getViewport().setScrollMode(Scroll.NONE); container.setScrollMode(Scroll.AUTO); } getViewport().layout(true); if (formType == FormType.FORM) { if (getEntryPoint().getHeight() != -1 && getEntryPoint().getWidth() != -1) { StandardComponents.setWindowSize(getEntryPoint().getHeight(), getEntryPoint().getWidth()); } Window.enableScrolling(false); getViewport().layout(true); RootPanel.get().add(getViewport()); container.getHeader().setTextStyle("cat-header-text"); } else if (formType == FormType.DIALOG) { getDialog().setSize(getEntryPoint().getWidth(), getEntryPoint().getHeight()); getDialog().setLayout(new FitLayout()); getDialog().add(container); container.getHeader().setTextStyle("cat-header-text"); } else if (FormType.EMBEDDED == formType) { RootPanel.get().add(getViewport()); } if (container.isRendered() && getViewport().isRendered()) { if (container.getWidth() < getViewport().getWidth()) { container.setWidth(getViewport().getWidth() - sbw); } if (container.getHeight() < getViewport().getHeight()) { container.setHeight(getViewport().getHeight() - sbh); } } reportWindowSizes(); setupFormValidation(); } /** * @return the page */ public StandardForm getPage() { return page; } /** * @param page the page to set */ public void setPage(StandardForm page) { this.page = page; } /** * Returns the string value used to invoke this layout (corresponds to the name value of EntryPointFactory) * @return The string value for the page being run. */ public String getApplication() { return application; } /** * Sets the string value used to invoke this layout (the name from the EntryPointFactory) * @param application The String to be used to invoke this layout. */ public void setApplication(String application) { this.application = application; } private void stopEventConditional(ComponentEvent ce) { if (!"TEXTAREA".equals(ce.getTarget().getTagName())) { ce.stopEvent(); } } /** * Returns the top level container that holds the form. This is the one and only child of the Viewport for the page * @return */ public ContentPanel getContainer() { return container; } /** * Returns the map object containing all the name value pairs to be put into hidden fields on the page. * @return A Map of string keys and string values. */ @SuppressWarnings("ReturnOfCollectionOrArrayField") protected Map getHiddenParamMap() { if (hiddenParamMap == null) { hiddenParamMap = new FastMap(); } return hiddenParamMap; } /** * Returns the map object containing all the name / value pairs to be put into hidden fields on the page * @return A map of string keys and string values holding the name / value pairs to be put into hidden fields on the page * @deprecated Use getHiddenParamMap instead */ @Deprecated @SuppressWarnings("ReturnOfCollectionOrArrayField") protected Map getParamMap() { if (hiddenParamMap == null) { hiddenParamMap = new FastMap(); } return hiddenParamMap; } /** * Creates the layout area with the page title and help icon * @param label The label to be used as the page title * @param helpID The help url to use under the help icon. The icon will not appear if helpUrl is null or empty */ public void createHeaderPanel(String label, final String helpID) { /* * // Set the Window title only if we are a FORM if (formType == FormType.FORM) { Window.setTitle(entryPoint.getLabel()); } else { * getDialog().setHeading(entryPoint.getLabel()); } // Vertical panel for adding Header and Help icon .. headerPanel = new LayoutContainer(); * ColumnLayout layout = new ColumnLayout(); headerPanel.setLayout(layout); headerPanel.setStyleName("cat-dialog-hdr"); * headerPanel.setId("cat-dialog-hdr"); * * // The left side sub-panel HorizontalPanel westpanel = new HorizontalPanel(); westpanel.setStyleName("cat-header-west"); hdrLabel = * StandardComponents.getCATHeaderLabel(label); hdrLabel.setStyleName("cat-header-label"); westpanel.add(hdrLabel); headerPanel.add(westpanel); * * // The right side sub-panel HorizontalPanel eastpanel = new HorizontalPanel(); eastpanel.setStyleName("cat-header-east"); ThemeSelector selector = * new ThemeSelector(); eastpanel.add(selector); String helpurl = webAppURL + "-WHC/index.jspx?id=" + helpID + "&action=show"; HTML rightWidget = * StandardComponents.getHelpIcon(helpurl); rightWidget.setStyleName("cat-helpicon"); eastpanel.add(rightWidget); headerPanel.add(eastpanel); * */ if (Window.getTitle().isEmpty()) { hdr = container.getHeader(); } else { hdr = getDialog().getHeader(); } // Set the Window title only if we are a FORM if (formType == FormType.FORM) { if (Window.getTitle().isEmpty()) { Window.setTitle(entryPoint.getLabel()); } } else if (formType == FormType.DIALOG) { // For Dialogs, use the Dialog's header and shut off the header we created hdr = getDialog().getHeader(); hdr.setVisible(true); } // Set the Window title only if we are a FORM // if (formType == FormType.FORM) { // Window.setTitle(getEntryPoint().getLabel()); // } else if (formType == FormType.DIALOG) { // getDialog().setHeading(getEntryPoint().getLabel()); // } //// container.layout(); // Header hdr = container.getHeader(); // // For Dialogs, use the Dialog's header and shut off the header we created // if (formType == FormType.DIALOG) { // hdr.setVisible(false); // hdr = getDialog().getHeader(); // } hdr.setText(getEntryPoint().getLabel()); // For Embedded forms, turn off the header if (formType == FormType.EMBEDDED) { hdr.setVisible(false); } // Only include the help button if a help ID was set or if the URL does not include a {0} string in it (meaning a fixed landing page) boolean showHelp = false; if (helpURL != null && !helpURL.isEmpty()) { if (helpURL.contains("{0}")) { if (helpID != null && !helpID.isEmpty()) { showHelp = true; } } else { showHelp = true; } } if (showHelp) { Button helpButton = new Button(); helpButton.setSize(StandardComponents.ICON_WIDTH, StandardComponents.WIDGET_HEIGHT); helpButton.addStyleName("helpbutton"); helpButton.addSelectionListener(new SelectionListener() { @Override public void componentSelected(ComponentEvent ce) { String url = helpURL; if (url.contains("{0}")) { url = url.replace("{0}", helpID); } Window.open(url, "_blank", ""); } }); hdr.addTool(helpButton); } } /** * Sets the object ID of the primary object's container. For create, this is where the object will be created. For edit, this is where the object is stored. * @param containerOid The object ID of the primary object's container or the container that launched this screen */ public void setContainerOid(String containerOid) { this.containerOid = containerOid; } /** * Creates the standard footer which includes the status area and the buttons to operate the page */ public void createFooter() { if (formType != FormType.DIALOG) { footer = new LayoutContainer(); footer.setLayout(new HBoxLayout()); getBodyPanel().setBottomComponent(footer); footer.setId("pageFooter"); } createButtonPanel(); createStatusArea(); if (formType != FormType.DIALOG) { footer.add(statusarea, new FormData("100% 26px")); footer.add(buttonPanel, new FormData("100% 26px")); } else { getDialog().getButtonBar().insert(statusarea, 0); } // Don't show the footer on EMBEDDED forms if (FormType.EMBEDDED == formType) { getFooter().setVisible(false); } } private void createButtonPanel() { final int buttonSpacing = 10; createButtons(); buttonPanel = new HorizontalPanel(); buttonPanel.setHeight("40px"); buttonPanel.addStyleName("cat-button-panel"); buttonPanel.setId("buttonPanel"); buttonPanel.setSpacing(buttonSpacing); buttonPanel.add(tools); buttonPanel.add(finishButton); buttonPanel.add(applyButton); buttonPanel.add(cancelButton); // Insert the debug button if (formType == FormType.DIALOG) { ButtonBar bar = getDialog().getButtonBar(); // The ButtonBar comes with OK/Cancel already - add in debug and apply bar.setHeight("28px"); // Since doing a add on the finish and cancel buttons will move them into the buttonPanel above, we need to move all buttons in the buttonPanel to the dialog's buttonBar. while (true) { if (buttonPanel.getItemCount() == 0) { break; } bar.add(buttonPanel.getItem(0)); } bar.insert(tools, 0); } assignButtonListeners(); } private void createButtons() { getFinishButton(); getApplyButton(); if (getMode() != FormMode.CREATE) { getApplyButton().setVisible(false); } getCancelButton(); DebugToolsMenu dtm = new DebugToolsMenu(); tools = dtm.getMenu(this); if (Log.isDebugEnabled()) { tools.setVisible(true); } else { tools.setVisible(false); } } private void assignButtonListeners() { if (FormType.EMBEDDED != formType) { disableActionButtons(); Button theOKButton = getFinishButton(); Button theApplyButton = getApplyButton(); Button theCancelButton = getCancelButton(); // For action type forms, set up the handlers on the buttons if (mode == FormMode.CREATE || mode == FormMode.EDIT || mode == FormMode.MULTIEDIT || mode == FormMode.DELETE) { if (getOkSelectionListener() == null) { // Use the DefaultOKSelectionListener setOkSelectionListener(new DefaultOKApplySelectionListener(SubmitMode.OK)); } getFinishButton().addSelectionListener(getOkSelectionListener()); if (getApplySelectionListener() == null) { // Use the DefaultOKSelectionListener setApplySelectionListener(new DefaultOKApplySelectionListener(SubmitMode.APPLY)); } theApplyButton.addSelectionListener(getApplySelectionListener()); // Set the cancelSelectionListener in case the developer registers a cancel task if (getCancelSelectionListener() == null) { setCancelSelectionListener(new DefaultCancelSelectionListener()); } theCancelButton.addSelectionListener(getCancelSelectionListener()); } else if (mode == FormMode.LIST || mode == FormMode.TREE) { // For LIST or TREE mode pages, hide the OK and APPLY buttons, change the CANCEL button to show "Close" theCancelButton = getCancelButton(); theCancelButton.setText("Close"); theApplyButton.hide(); theOKButton.hide(); if (getCancelSelectionListener() == null) { setCancelSelectionListener(new DefaultCancelSelectionListener()); } theCancelButton.addSelectionListener(getCancelSelectionListener()); ConfirmSelectListener cancelConfirmListener = new ConfirmSelectListener() { @Override public void handleEvent(BaseEvent be) { if (isDirty() && !isForceClose()) { MessageBox.confirm("Form Modified", "Do you want to discard the changes you've made?.", new Listener() { @Override public void handleEvent(MessageBoxEvent be) { if (StandardComponents.wasDialogYesOrOKClicked(be)) { confirm(); } } }); } else { confirm(); } } }; theCancelButton.addListener(FrameworkEvents.Confirm, cancelConfirmListener); } } } private LayoutContainer createStatusArea() { final int defaultSize = 50; statusarea = new LayoutContainer(); statusarea.setId("FormStatusArea"); statusarea.setLayout(new ColumnLayout()); statusarea.setWidth(defaultSize); statusarea.setHeight("25px;"); if (formType != FormType.DIALOG) { // FORM and EMBEDDED types HorizontalPanel btnpanel = getButtonPanel(); btnpanel.setId("buttonPanel"); } else { // DIALOG type ButtonBar bar = getDialog().getButtonBar(); bar.setId("buttonBar"); getDialog().setButtonAlign(Style.HorizontalAlignment.LEFT); bar.setEnableOverflow(false); } configureStatusArea(); statusarea.layout(true); statusarea.show(); return statusarea; } /** * Adds one button into the page's button bar. * The new button will be added just behind the debug button in all cases. * You shall not ever violate the OK/Apply/Cancel affinity. * So if you need to add multiple buttons, add them in right to left order. * addButton(3); addButton(2); addButton(1) will result in Debug;1;2;3;OK;Apply;Cancel * @param btn The Button to be added to the page's button bar. */ public void addButtonToBar(Button btn) { if (btn == null) { throw new RuntimeException("addButtonToBar: btn parameter is null"); } if (formType != FormType.DIALOG) { // FORM and EMBEDDED types HorizontalPanel btnpanel = getButtonPanel(); btnpanel.insert(btn, 1); } else { // DIALOG type ButtonBar bar = getDialog().getButtonBar(); bar.insert(btn, 1); } // updatePageButtonTabIndexes(); } /** * Update the tab indexes of the buttons in the button area */ // private void updatePageButtonTabIndexes() { // Container buttons; // if (formType != FormType.DIALOG) { // buttons = getButtonPanel(); // } else { // buttons = getDialog().getButtonBar(); // } // int ti = PAGE_BUTTON_STARTING_TABINDEX; // List btnlist = buttons.getItems(); // for (Component c : btnlist) { // if (c instanceof Button) { // c.setTabIndex(ti); // ti++; // } // } // } /** * @return the okSelectionListener */ public SelectionListener getOkSelectionListener() { return okSelectionListener; } /** * @param okSelectionListener the okSelectionListener to set */ public void setOkSelectionListener(SelectionListener okSelectionListener) { this.okSelectionListener = okSelectionListener; } /** * @return the applySelectionListener */ public SelectionListener getApplySelectionListener() { return applySelectionListener; } /** * @param applySelectionListener the applySelectionListener to set */ public void setApplySelectionListener(SelectionListener applySelectionListener) { this.applySelectionListener = applySelectionListener; } /** * @return the cancelSelectionListener */ public SelectionListener getCancelSelectionListener() { return cancelSelectionListener; } /** * @param cancelSelectionListener the cancelSelectionListener to set */ public void setCancelSelectionListener(SelectionListener cancelSelectionListener) { this.cancelSelectionListener = cancelSelectionListener; } private class DefaultOKApplySelectionListener extends SelectionListener { SubmitMode submitSetting = null; public DefaultOKApplySelectionListener(SubmitMode setting) { if (setting == null) { throw new RuntimeException("DefaultOKSelectionListenerII: No SubmitMode value supplied"); } submitSetting = setting; } @Override public void componentSelected(ComponentEvent ce) { setSubmitMode(submitSetting); ce.stopEvent(); Registry.register("forceClose", "true"); if (getSubmitMode() == SubmitMode.APPLY) { Log.debug("Form Apply Button clicked"); } else if (getSubmitMode() == SubmitMode.OK) { Log.debug("Form OK Button clicked"); } if (mode == FormMode.CREATE) { setBusy("ACTIONTASK", "Please wait...", "Creating..."); } else if (mode == FormMode.EDIT) { setBusy("ACTIONTASK", "Please wait...", "Updating..."); } else if (mode == FormMode.DELETE) { setBusy("ACTIONTASK", "Please wait...", "Deleting..."); } disableActionButtons(); createTaskParameters(getActionTask()); if (!actionTask.invoke()) { clearBusy("ACTIONTASK"); postActionTaskProcessing(false); } } } /** * Default Cancel Button Handler */ private class DefaultCancelSelectionListener extends SelectionListener { @Override public void componentSelected(ComponentEvent ce) { Object src = ce.getSource(); Log.debug("cancel button ::: Form Type:" + getFormType() + "Form Name:" + getFormName()); Log.debug("cancel button:" + src.getClass().getName()); Log.debug("StandardComponents.cancelButton.addSelectionListener: Clicked"); Log.debug("close dialog"); if (!isForceClose() && isDirty()) { if (isDirty() && getCancelTask() != null) { runCancelTask(); } } if (getFormType() == FormType.DIALOG) { getDialog().hide(); } else { Log.debug("close app"); StandardComponents.closeApplication(); } } } /** * Executes the Cancel Task for the page */ public void runCancelTask() { setBusy("CANCELTASK", "Please Wait", "Performing Server Cleanup for Cancel"); Log.debug("in cancel code"); if (isDirty()) { Log.debug("dirty: " + isDirty()); createTaskParameters(getCancelTask(), false); if (!cancelTask.invoke()) { clearBusy("CANCELTASK"); } } } /** * This method processes the Action Task results. * It is either called by the completion of the Action Task or by the componentSelected event handler if no task is run * It processes any ReturnGroupHandler data return, then * @param ranTask */ private void postActionTaskProcessing(boolean ranTask) { createdObjectOID = ""; String folderOID = ""; IEElement md; Log.trace("formActionTask.storeDataChanged: Enter"); // Confirm that the if (ranTask && assertGroupHasData(getActionTaskGroup(), getTaskDataKey(), "Did not find an object in the task results - does it already exist?")) { // If we get here, we know the group is ok, there is at least one element, and obid has a value so we can skip the checks, reducing the lines of code needed md = getActionTaskGroup().getElementAt(0); // get the obid of the newly created object createdObjectOID = (String) md.getValue("obid"); // Generate a folderOID by using either the returned parentFolder attribute in the data, then the openerOID, then the containerOID folderOID = (String) md.getValue("parentFolder"); if (folderOID == null || folderOID.isEmpty()) { // If they haven't supplied a specific parent, use the openerOid, then the containerOid as the target. folderOID = Registry.get("openerOid"); if (folderOID == null || folderOID.isEmpty()) { folderOID = Registry.get("containerOid"); } } } if (returnGroupHandler != null) { if (returnMode == null && !actionTask.isNullTask()) { // Default returnMode and the action task was not the NULL task, return the task group returnGroupHandler.sendReturnGroup(getActionTaskGroup()); } else if (returnMode == null && getActionTask().isNullTask()) { // Default returnMode and the action task WAS the NULL task, return the form data IEGroup rg = createReturnGroup(false); returnGroupHandler.sendReturnGroup(rg); } else if (returnMode == DataReturnMode.TASK) { // Page wants the action task data returned returnGroupHandler.sendReturnGroup(getActionTaskGroup()); } else if (returnMode == DataReturnMode.FORM) { // Page wants the form data returned IEGroup rg = createReturnGroup(false); returnGroupHandler.sendReturnGroup(rg); } } if (folderOID != null && folderOID.contains("wt.folder.SubFolder")) { handleFolderCompletion(getPage(), getActionTaskGroup(), folderOID, getSubmitMode()); } else { if (folderOID != null && mode == FormMode.DELETE) { handleCompletion(getPage(), getActionTaskGroup(), folderOID, getSubmitMode()); } else { handleCompletion(getPage(), getActionTaskGroup(), createdObjectOID, getSubmitMode()); } } if (getSubmitMode() == SubmitMode.APPLY) { handleApply(); } Log.trace("formActionTask.storeDataChanged: Exit"); } /** * Deals with the Apply Button behavior (leave all the fields alone so the user can make a single change and Apply again) * or reset all the fields to their original values so the page starts over. */ public void handleApply() { // if this is an APPLY, then reset the field values so dirty will be based on the last apply // the OK and APPLY buttons can't be enabled after an APPLY until a field is dirty // if clearOnApply is set to true, then all the fields' values will be reset to original values if created successfully // if clearOnApply is set to false, then all the fields' values will be reset to latest values if (clearOnApply) { // If clearOnApply and the task was NOT successful, then we purposefully don't do a reset. // However, we also do NOT want to do a resetOriginalValues()! if (createdObjectOID != null && !createdObjectOID.isEmpty()) { reset(); // reset() kills the hidden fields so we must set them up again // (similar to what invoke() does after IT calls reset()) setupHiddenFields(); } } else { resetOriginalValues(); } if (formType == FormType.FORM || formType == FormType.EMBEDDED) { if (focusField != null) { focusField.focus(); } } else if (formType == FormType.DIALOG) { if (focusField != null) { getDialog().setFocusWidget(focusField); getDialog().focus(); } } validate(); } /** * sets clearOnApply. If clearOnApply is true then all the fields' values will be reset to original values if created successfully when apply button clicked * if clearOnApply is set to false, then all the fields' values will be reset to latest values * * @param flag */ public void setClearOnApply(boolean flag) { clearOnApply = flag; } /** * @return the returnGroupHandler */ public ReturnGroupHandler getReturnGroupHandler() { return returnGroupHandler; } /** * @param returnGroupHandler the returnGroupHandler to set */ public void setReturnGroupHandler(ReturnGroupHandler returnGroupHandler) { this.returnGroupHandler = returnGroupHandler; } /** * Sets the source of data for a ReturnGroupHandler - FORM or TASK * @param setting A DataReturnMode enum value */ public void setReturnMode(DataReturnMode setting) { returnMode = setting; if (setting == null) { StandardComponents.alert(FormMode.LIST, MsgLevel.ERROR, "setReturnMode was given a null argument"); returnMode = DataReturnMode.FORM; } } /** * Returns the source of data for a ReturnGroupHandler - FORM or TASK * @return A DataReturnMode enum value */ public DataReturnMode getReturnMode() { return returnMode; } /** * creates a group out of all fields in the form, including the hidden ones into the specified task. Data structure objects (IEGroup and TreeStore) will be added directly to the group * * @param hiddenOnly When true, only the fields in the hidden panel will be added (used for the initialization task) * @return An IEGroup with a single element, containing the values of all fields scraped from the form */ public IEGroup createReturnGroup(boolean hiddenOnly) { //TODO: BEH: Why isn't this code used ultimately to generate the task params? This data could be captured and then used to generate the task params. Map formToGroupMap = getActionTask().getFormToTaskMap(); IEGroup group = new IEGroup("results"); IEElement el = new IEElement(); // Map taskParamMap = task.getParamMap(); List> fields = getBodyPanel().getFields(); String pid; for (Field field : fields) { // Don't generate a param for a field without a name String name = field.getName(); if (name == null || name.isEmpty()) { Log.debug("Skipping a field because it has no name. Class: " + field.getClass().getName() + " Value: " + field.getRawValue()); continue; } if (name.equals(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME)) { String val = (String) field.getValue(); if (val == null) { val = ""; } el.addAtt(new IEAtt(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME, val)); continue; } String safeName = name; if (name.contains(".")) { Log.debug("Replacing dots with underscores for field param name: " + name); safeName = safeName.replace(".", "_"); } // Pull the attribute from the element corresponding to the field name // Check the formMap to see if we need to pull a different attribute for this field String mapname = formToGroupMap.get(safeName); if (mapname == null) { mapname = safeName; } // Check to see if we are doing only the fields in the hidden panel. Continue out of the loop //if we are and the field is not parented by the panel with the id 'hidden' if (hiddenOnly) { if (!StandardComponents.isFieldHidden(field)) { Log.debug("Skipping field: " + name + " because it is not in the hidden panel"); continue; } // Widget parent = field.getParent(); // if (parent instanceof LayoutContainer) { // LayoutContainer lc = (LayoutContainer) parent; // pid = lc.getId(); // if (pid != null && !pid.equals("hidden")) { // Log.debug("Skipping field: " + name + " because it is not in the hidden panel"); // continue; // } // } } try { if (field instanceof ComboBox) { // For combo boxes, the value in the map can be colon-separated. So it could be 'a', 'a:b' or ':b'. // 'a' indicates that the internal value of the combo box should go to the attribute called a. The display value is not collected. // 'a:b' indicates that the internal value of the combo box should go to a and the display value should go to b. //':b' indicates that the display value of the combo box should go to b. The internal value is not collected. ComboBox cb = (ComboBox) field; ModelData md = cb.getValue(); String value = ""; String dvalue = ""; if (md != null) { value = md.get(cb.getValueField()); dvalue = md.get(cb.getDisplayField()); } String[] combomap = mapname.split(":"); if (combomap.length == 1) { el.addAtt(new IEAtt(combomap[0], value)); } else { if (combomap[0] == null || combomap[0].isEmpty()) { el.addAtt(new IEAtt(combomap[1], dvalue)); } else { el.addAtt(new IEAtt(combomap[0], value)); el.addAtt(new IEAtt(combomap[1], dvalue)); } } } else if (field instanceof AdapterField) { Object result = null; AdapterField af = (AdapterField) field; String widget = field.getData("type"); if (widget != null) { if (widget.equals("tree")) { Object x = af.getData("widget"); if (x != null && (x instanceof StandardTree)) { StandardTree stdTree = (StandardTree) af.getData("widget"); result = stdTree.getTreeStore(); } else { Log.error("type was set to tree, but the widget was either null or was not a TreeGrid - AdapterField: " + field.getName()); } } else if (widget.equals("table")) { Object x = af.getData("widget"); if (x != null && (x instanceof StandardTable)) { StandardTable stdTable = (StandardTable) af.getData("widget"); IEGroup g = ((IEGroup) stdTable.getGrid().getStore()).deepClone(); result = g; } else { Log.error("type was set to table, but the widget was either null or was not a Grid - AdapterField: " + field.getName()); } } else { Log.error("An ApapterField type was not valid: " + widget + " AdapterField: " + field.getName()); } } else { Log.error("An AdapterField type was null: " + field.getName()); } el.addAtt(new IEAtt(mapname, result)); } else { String val = StandardComponents.getFieldValue(field); Log.trace("Adding Att to return group - " + mapname + "=" + val); el.addAtt(new IEAtt(mapname, val)); } } catch (Exception e) { Log.error(" field has no value set, successfully caught exception " + e.toString()); } } group.addElement(el); return group; } /** * Method to handle the completion of the primary functions of a UI page - create, edit, editmulti. Consults the Action maps to see what to do with the * window, the parent window and whether to use a popup to display anything. * * @param page The StandardForm launching this method * @param group The group with the data from the server operation * @param obid The object ID of the object to which we will forward * @param submitMode Indicates whether this is an OK or an Apply press (OK will dismiss the window, but Apply will force a full revalidation and leave all * fields in place) */ public void handleCompletion(StandardForm page, IEGroup group, String obid, SubmitMode submitMode) { // TODO: This needs to be moved to a Windchill-specific CompletionHandler class //truncate the obid in all cases - the properties pages often puke on the ufids. String theobid = StandardComponents.getShortOid(obid); MsgLevel level = group.getSuccess(); // // If we ran a server side task, we still need to check to see if we have a return group handler so we can interact with a parent table or tree // if (getReturnGroupHandler() != null && returnMode==DataReturnMode.TASK) { // getReturnGroupHandler().sendReturnGroup(group); // } WindowMode windowAction; if (forcedWindowAction == null) { windowAction = StandardComponents.getWindowAction(page.form.getMode(), level, submitMode); } else { windowAction = forcedWindowAction; } ParentMode parentAction; if (forcedParentAction == null) { parentAction = StandardComponents.getParentAction(page.form.getMode(), level); } else { parentAction = forcedParentAction; } if (page.formType == FormType.FORM) { if (parentAction == ParentMode.FORWARD) { if (theobid == null || theobid.isEmpty()) { throw new RuntimeException("Cannot forward to the new object - the obid parameter was not supplied"); } //If the obid is a Version Reference (starts with VR), and the returning oid is equal to the opening oid, we need to refresh //as the browser will ignore a request to forward to the same URL if (theobid.startsWith("VR") && theobid.equals(Registry.get("oid"))) { StandardComponents.refreshParent(); } String url = group.getElementAt(0).getValue("detailsUrl"); if (url != null && !url.isEmpty()) { Log.info("Forward parent to URL - " + url); StandardComponents.forwardParent(url); } else { //R10: http://acm.corp.cat.com/acm/app/#ptc1/tcomp/infoPage?oid=OR%3Awt.part.WTPart%3A1505992&u8=1 Log.info("Forward parent to R10 URL -" + "/app/#ptc1/tcomp/infoPage?oid=" + theobid + "&u8=1"); StandardComponents.forwardParent("/app/#ptc1/tcomp/infoPage?oid=" + theobid + "&u8=1"); } } else if (parentAction == ParentMode.TOFOLDER) { if (theobid == null || theobid.isEmpty()) { throw new RuntimeException("Cannot forward to parent folder - containerOid was not specified"); } Log.info("Forward parent to R10 Folder URL -" + "/app/#ptc1/tcomp/infoPage?oid=" + theobid + "&u8=1"); StandardComponents.forwardParent("/app/#ptc1/tcomp/infoPage?oid=" + theobid + "&u8=1"); } else if (parentAction == ParentMode.REFRESH) { StandardComponents.refreshParent(); } } // TODO: If/when a console object becomes available, feed all messages to that console // Produce a message box whenever the action map indicates POPUP_NEEDED PopupMode popupAction = StandardComponents.getPopupAction(page.form.getMode(), level); if (popupAction == PopupMode.NEEDED) { StandardComponents.alert(page, level, group.getMessagesAsHtml()); } else { // If we aren't doing a popup, we may need to close the window here if (windowAction == WindowMode.CLOSE) { if (page.formType == FormType.FORM) { StandardComponents.closeApplication(); } else if (page.formType == FormType.DIALOG) { page.form.getDialog().hide(); } } } } /** * Method to handle the completion of the primary functions of a UI page launched from a Folder panel - create, edit, editmulti. Consults the Action maps to * see what to do with the window, the parent window and whether to use a popup to display anything. * * @param page The StandardForm object calling this method * @param group The group with the data from the server operation * @param obid The name of the attribute in the group's data with the object's retrieval ID (needed to create parent forwarding URLs) * @param submitMode Indicates whether this is an OK or an Apply press (OK will dismiss the window, but Apply will force a full revalidation and leave all * fields in place) */ public void handleFolderCompletion(StandardForm page, IEGroup group, String obid, SubmitMode submitMode) { //TODO: This needs to be moved to a Windchill-specific CompletionHandler class MsgLevel level = group.getSuccess(); // // If we ran a server side task, we still need to check to see if we have a return group handler so we can interact with a parent table or tree // if (getReturnGroupHandler() != null && returnMode==DataReturnMode.TASK) { // getReturnGroupHandler().sendReturnGroup(group); // } WindowMode windowAction; if (forcedWindowAction == null) { windowAction = StandardComponents.getWindowAction(page.form.getMode(), level, submitMode); } else { windowAction = forcedWindowAction; } ParentMode parentAction = null; if (forcedParentAction == null) { parentAction = StandardComponents.getParentAction(page.form.getMode(), level); } else { parentAction = forcedParentAction; } // Only run these behaviors for a plain old popup if (page.formType == FormType.FORM) { if (obid == null || obid.isEmpty()) { throw new RuntimeException("Cannot refresh / forward to the parent folder - the 'obid' parameter was not supplied"); } // The obid value will have been generated using the first non-null value of a 'parentFolder' attribute in the data, the openerOid parameter, or the containerOid parameter String theOpener = Registry.get("openerOid"); if (obid.equals(theOpener)) { StandardComponents.refreshParent(); Log.info("Parent refreshed"); } else { StandardComponents.forwardParent("/app/#ptc1/tcomp/infoPage?oid=" + obid + "&u8=1"); Log.info("Forward parent to URL -" + "/app/#ptc1/tcomp/infoPage?oid=" + obid + "&u8=1"); } } // TODO: If/when a console object becomes available, feed all messages to that console // Produce a message box whenever the action map indicates POPUP_NEEDED PopupMode popupAction = StandardComponents.getPopupAction(page.form.getMode(), level); if (popupAction == PopupMode.NEEDED) { StandardComponents.alert(page, level, group.getMessagesAsHtml()); } else { // If we aren't doing a popup, we may need to close the window here if (windowAction == WindowMode.CLOSE) { if (page.formType == FormType.FORM) { StandardComponents.closeApplication(); } else { page.form.getDialog().hide(); } } } } /** * @return the windowAction */ public WindowMode getWindowAction() { return forcedWindowAction; } /** * @param windowAction the windowAction to set */ public void setWindowAction(WindowMode windowAction) { forcedWindowAction = windowAction; } /** * @return the parentAction */ public ParentMode getParentAction() { return forcedParentAction; } /** * @param parentAction the parentAction to set */ public void setParentAction(ParentMode parentAction) { forcedParentAction = parentAction; } /** * Makes sure the group has at least one record and the specified attribute contains a non-null, non-empty value * * @param group The IEGroup object containing the task result * @param attrname The name of the expected attribute * @param alertmsg The message desired (will create a default "No data returned from the server call" if this value is null or empty * @return true if all tests pass. Otherwise, closeApplication will be called so the routine will never return. */ public static boolean assertGroupHasData(IEGroup group, String attrname, String alertmsg) { // Generate a default message String thealertmsg = alertmsg; if (thealertmsg == null || thealertmsg.isEmpty()) { thealertmsg = "No data returned from the server call"; } // The group we got was null if (group == null) { Log.error("assertGroupHasData: The group specified is null"); StandardComponents.alert(FormMode.ASSERT_EXIST, MsgLevel.FATAL, thealertmsg); return false; } // The group we got had no elements if (group.getElementCount() == 0 && group.getMessages().isEmpty()) { Log.warn("assertGroupHasData: The group has no elements and there are no other messages - show the default message"); StandardComponents.alert(FormMode.ASSERT_EXIST, MsgLevel.ERROR, thealertmsg); return false; } if (group.getElementCount() == 0) { Log.debug("assertGroupHasData: The group has no elements but there are other messages present"); return false; } // The attribute we got was null or empty if (attrname == null || attrname.isEmpty()) { group.addMessage("FATAL|" + thealertmsg); group.addMessage("FATAL|No attribute name for validating the group data was supplied"); } else { // The value of the attribute in the first element of the group is null or empty IEElement el = group.getElementAt(0); String val = (String) el.getValue(attrname); if (val == null || val.isEmpty()) { group.addMessage("FATAL|" + thealertmsg); group.addMessage("FATAL|The attribute '" + attrname + "' did not appear in the group data"); } } StandardComponents.alert(FormMode.ASSERT_EXIST, group); return true; } /** * Makes sure the group has at least one record and the specified attribute contains a non-null, non-empty value * * @param group The IEGroup object containing the task result * @return true if all tests pass. Otherwise, closeApplication will be called so the routine will never return. */ public static boolean assertGroupHasNoErrors(IEGroup group) { // The group we got was null if (group == null) { Log.error("assertGroupHasNoErrors: The group specified is null"); StandardComponents.alert(FormMode.ASSERT_EXIST, MsgLevel.FATAL, "assertGroupHasNoErrors: The group specified is null"); return false; } StandardComponents.alert(FormMode.ASSERT_EXIST, group); return true; } /** * Makes sure the group has no records * * @param group The IEGroup object containing the task result * @param alertmsg The message desired (will create a default "The object specified already exists" if this value is null or empty) * @return true if all tests pass. Otherwise, closeApplication will be called so the routine will never return. */ public static boolean assertGroupHasNoData(IEGroup group, String alertmsg) { // Generate a default message String thealertmsg = alertmsg; if (thealertmsg == null || thealertmsg.isEmpty()) { thealertmsg = "The object specified already exists"; } // The group we got was null if (group == null) { Log.error("assertGroupHasData: The group specified is null"); StandardComponents.alert(FormMode.ASSERT_NOT_EXIST, MsgLevel.FATAL, thealertmsg); return false; } // The group already had FATAL errors if (group.getSuccess() == MsgLevel.FATAL) { StandardComponents.alert(FormMode.ASSERT_NOT_EXIST, MsgLevel.FATAL, group.getMessagesAsHtml()); return false; } if (group.getElementCount() == 0) { return true; } else { return false; } } /** * Creates a task with a taskoutgroupname generated from the taskuri. This method will never re-use an existing instance of a task. * * @param task The TaskFactory enum entry of the task to be run * @return The configured task (also available via actionTask) */ public Task setupCancelTask(TaskFactory task) { return setupCancelTask(task, null, false); } /** * Creates a task with a taskoutgroupname generated from the taskuri. When the useCache is true, it will re-use an already configured task with the same * uri. * * @param task The TaskFactory enum entry of the task to be run * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via actionTask) */ public Task setupCancelTask(TaskFactory task, boolean useCache) { return setupCancelTask(task, null, useCache); } /** * Creates a task with a taskoutgroupname as specified (null means it will generate it from the taskuri). When the useCache is true, it will re-use an * already configured task with the same uri. * * @param task The TaskFactory enum entry of the task to be run * @param group The name of the group that will be returned by the task * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via actionTask) */ public Task setupCancelTask(TaskFactory task, String group, boolean useCache) { String taskuri = task.getTaskUri(); if (taskuri == null) { StandardComponents.alert(this, MsgLevel.FATAL, "Cancel task uri was not specified"); return null; } cancelTask = Task.newTask(task, group, useCache); cancelTaskGroup = cancelTask.getGroup(cancelTask.getTaskGroupOutName()); getCancelTaskGroup().addStoreListener(new StoreListener() { @Override public void storeDataChanged(StoreEvent se) { Log.trace("InitializerTask.storeDataChanged: Enter"); if (assertGroupHasData(getCancelTaskGroup(), getTaskDataKey(), "Initialization data could not be retrieved from task: " + getCancelTask().getUri())) { setFormFieldValues(getCancelTaskGroup()); } StandardComponents.alert(FormMode.ASSERT_EXIST, getCancelTaskGroup()); isValid(); clearBusy("CANCELTASK"); } }); return cancelTask; } /** * Creates focus listeners on all named fields on the page so we can show the tooltip in the button bar when the current field has an error. */ public void setupFocusHandler() { for (Field f : getBodyPanel().getFields()) { //TODO: Take this out if validation doesn't work when they tab out of the field anyway f.setValidateOnBlur(false); // add focus listener Field work; if (f.getName() != null && !f.getName().isEmpty()) { work = f; if (f instanceof CATMultiField) { work = ((CATMultiField) f).getTextField(); } work.addListener(Events.Focus, new Listener() { @Override public void handleEvent(BaseEvent be) { if (statusarea == null) { return; } Field f = (Field) be.getSource(); currentFocusField = f; showErrorInStatusArea(f); } }); work.addListener(Events.Blur, new Listener() { @Override public void handleEvent(BaseEvent be) { if (statusarea == null) { return; } Field f = (Field) be.getSource(); // currentFocusField = null; // showErrorInStatusArea(f); } }); work.addListener(Events.Valid, new Listener() { @Override public void handleEvent(BaseEvent be) { if (statusarea == null) { return; } Field f = (Field) be.getSource(); if (f == currentFocusField) { showErrorInStatusArea(f); } } }); work.addListener(Events.Invalid, new Listener() { @Override public void handleEvent(BaseEvent be) { if (statusarea == null) { return; } Field f = (Field) be.getSource(); if (f == currentFocusField) { showErrorInStatusArea(f); } } }); } } } /** * @return the sendOnlyDirtyOnEdit */ public boolean isSendOnlyDirtyOnEdit() { return sendOnlyDirtyOnEdit; } /** * @param sendOnlyDirtyOnEdit the sendOnlyDirtyOnEdit to set */ public void setSendOnlyDirtyOnEdit(boolean sendOnlyDirtyOnEdit) { this.sendOnlyDirtyOnEdit = sendOnlyDirtyOnEdit; } /** * creates task parameters for all fields in the form, including the hidden ones into the specified task. The parameters will be generated according to the * paramMode setting on the task (name=value or field=name=value) * * @param task The task to which the parameters will be added. */ public void createTaskParameters(Task task) { createTaskParameters(task, false); } /** * creates task parameters for all fields in the form, including the hidden ones into the specified task. The parameters will be generated according to the * paramMode setting on the task (name=value or field=name=value) * * @param task The task to which the parameters will be added. * @param hiddenOnly When true, only the fields in the hidden panel will be added (used for the initialization task) */ public void createTaskParameters(Task task, boolean hiddenOnly) { // Why isn't createReturnGroup used to generate the needed data instead of having separate code? if (task == null) { return; } // If we are configured to only send down changed fields, then we must clear out all task // parameter here. Here is the scenario this deals with: // 1. Bring up the dialog // 2. Edit field A // 3. Click OK // 4. You get some error back from the task. // 5. Undo your edit to field A and edit field B instead. // 6. Click OK // 7. Without this change, field A will get sent to the task since it was // added as a task param in step #3 and nobody has cleared it out again. if (sendOnlyDirtyOnEdit) { task.resetParams(); } Map taskParamMap = task.getFormToTaskMap(); List> fields = getBodyPanel().getFields(); for (Field field : fields) { // Don't generate a param for a field without a name String name = field.getName(); if (name == null || name.isEmpty()) { Log.debug("Skipping a field because it has no name. Class: " + field.getClass().getName() + " Value: " + field.getRawValue()); continue; } String safeName = name; if (name.contains(".")) { Log.debug("Replacing dots with underscores for field param name: " + name); safeName = safeName.replace(".", "_"); } // Check to see if we are doing only the fields in the hidden panel. Continue out of the loop //if we are and the field is not parented by the panel with the id 'hidden' boolean fieldIsHidden = StandardComponents.isFieldHidden(field); if (hiddenOnly && !fieldIsHidden) { Log.debug("Skipping field: " + name + " because it is not in the hidden panel"); continue; } if (!fieldIsHidden) { // Hidden field are always sent if (getMode() == FormMode.EDIT || getMode() == FormMode.MULTIEDIT) { // This only applies to Edit or Multi Edit pages if (sendOnlyDirtyOnEdit && !field.isDirty()) { // We skip if we are sending only dirty fields and the field is not dirty Log.debug("Field skipped due to edit settings: fieldIsHidden: " + fieldIsHidden + " dirty: " + field.isDirty() + " sendOnlyDirtyOnEdit: " + isSendOnlyDirtyOnEdit() + " form mode: " + getMode()); continue; } } } // Check the taskParamMap to see if we need to put this field value into a different parameter name String mapname = taskParamMap.get(safeName); if (mapname == null) { mapname = safeName; } String val = StandardComponents.getFieldValue(field); if (task.getParamMode() == ParamMode.DIRECT) { task.addTaskParam(mapname, val); //if(!mapname.equals(safeName)) // task.addTaskParam(safeName, val);//add task parameters for both mapname and safeName as numerous tasks are associated to the same widget and parameter keys differ } else { task.addTaskParam("field", mapname + "=" + val); //if(!mapname.equals(safeName)) // task.addTaskParam("field", safeName + "=" + val);//add task parameters for both mapname and safeName as numerous tasks are associated to the same widget } } } /** * Calls showErrorInStatusArea using the last field to accept focus. */ public void showErrorForCurrentFocusField() { if (currentFocusField == null) { return; } showErrorInStatusArea(currentFocusField); } /** * Shows the field label, the red ball, and the tooltip when there is a tooltip assigned to the field. * The data is shown in the left side of the button area on forms and dialogs. * @param f */ public void showErrorInStatusArea(Field f) { if (f.getName() != null && !f.getName().isEmpty() && f.getToolTip() != null && f.getToolTip().isEnabled()) { //statusarea.setVisible(true); statusAreaImage.setVisible(true); statusAreaMessage.setVisible(true); String flabel = f.getFieldLabel(); String fmsg = f.getToolTip().getToolTipConfig().getText(); String ftip = fmsg; if (!fmsg.startsWith(flabel)) { // Don't display the label field if it matches the start of the message field ftip = flabel + ": " + fmsg; } statusAreaMessage.setValue("  " + ftip); statusAreaImage.setTitle((String) statusAreaMessage.getValue()); } else { //statusarea.setVisible(false); statusAreaImage.setVisible(false); statusAreaMessage.setVisible(false); } // getBodyPanel().layout(true); int newwidth = calculateStatusareaWidth(); statusarea.setSize(newwidth, -1); statusarea.add(statusAreaImgAdapter, new ColumnData(StandardComponents.ICON_WIDTH)); statusarea.add(statusAreaMessage, new ColumnData(newwidth - StandardComponents.ICON_WIDTH)); statusarea.layout(true); if (formType == FormType.DIALOG) { // Nothing right now } else { getButtonPanel().setPosition(newwidth, 0); } // if (footer != null) { // footer.layout(); // } } private void configureStatusArea() { final int saheight = 27; if (statusAreaImage == null) { statusAreaImage = new Image("/Windchill/com/cat/gwt/org.cat.Main/images/windchill/form/exclamation.gif"); } if (statusAreaImgAdapter == null) { statusAreaImgAdapter = new AdapterField(statusAreaImage); statusAreaImgAdapter.setSize(StandardComponents.ICON_WIDTH, StandardComponents.WIDGET_HEIGHT); statusAreaImgAdapter.setId("statusAreaImgAdapter"); } if (statusAreaMessage == null) { statusAreaMessage = CATLabelField.newCATLabelField(this, "", ""); statusAreaMessage.setLabelSeparator(""); statusAreaMessage.setHideLabel(true); statusAreaMessage.setId("statusAreaMessage"); statusAreaMessage.setSize(300, saheight); } } private int calculateStatusareaWidth() { final int defaultWidth = 300; int result = defaultWidth; if (statusarea.isRendered()) { if (formType == FormType.DIALOG) { result = getDialog().getOffsetWidth() - getButtonBarWidth(defaultWidth) - 10 * (getDialog().getButtonBar().getItemCount() - 1); } else { result = getViewport().getOffsetWidth() - getButtonPanel().getOffsetWidth(); } } if (result < 0) { result = 0; } return result; } private int getButtonBarWidth(int defaultWidth) { int first = -1; int last = -1; // Find the first and last visible buttons in the button bar ButtonBar bar = getDialog().getButtonBar(); for (int i = 1; i < bar.getItemCount(); i++) { // Only look at buttons if (bar.getItem(i) instanceof Button) { // Only look at visible buttons if (bar.getItem(i).isVisible()) { // If we haven't already set first, set it with this visible button's position if (first == -1) { first = i; } // Always set the last to this visible button's position last = i; } } } // blow up if we failed to find either the first or last visible button (should never happen unless someone turns off all the buttons) if (first == -1 || last == -1) { // throw new RuntimeException("Could not find the first and last buttons in the page's button bar!"); return defaultWidth; } // get the rightmost position by adding the left and width of the last visible button, then subtract the left of the leftmost visible button int thewidth = (bar.getItem(last).getAbsoluteLeft() + bar.getItem(last).getOffsetWidth()) - bar.getItem(first).getAbsoluteLeft(); return thewidth; } /** * Returns the oid value (used to dispatch the page). * For Create screens, this will be the container object into which the object should be stored. * For Other screens, this will be a pointer to the object itself. * @return The String value of the object id used to dispatch the page */ public String getOid() { return oid; } /** * Sets the primary object id for the page execution. * For create pages, this should be the container into which the new object will be stored. * For all other pages, this should be the object being manipulated. * @param oid The object identifier for the object being manipulated. */ public void setOid(String oid) { this.oid = oid; } /** * Creates the standard Cancel button for the page */ protected void createCancelButton() { // Cancel button cancelButton = StandardComponents.createCancelButton(); } /** * Creates the standard OK button for the page */ protected void createFinishButton() { // OK button finishButton = StandardComponents.createOKButton(); finishButton.setType("submit"); } private MessageBox getWaitBox() { return waitBox; } /** * Creates the standard Apply button for Create pages */ protected void createApplyButton() { // Apply button applyButton = StandardComponents.createApplyButton(); applyButton.setType("submit"); applyButton.setItemId(APPLY); // so onEnter() can find it } private void setWaitBox(MessageBox waitBox) { this.waitBox = waitBox; waitBox.setModal(true); } /** * Makes the webAppURL for the application available to the form. * @param webAppURL the string representation of the webAppURL (like http://acm.corp.cat.com/Windchill) */ public void setWebAppURL(String webAppURL) { this.webAppURL = webAppURL; } /** * Returns the Finish (OK) Button for the form * The button will be created if necessary * @return The OK Button for the form */ public Button getFinishButton() { if (formType != FormType.DIALOG) { if (finishButton == null) { createFinishButton(); } } else { finishButton = getDialog().getButtonById(Dialog.OK); } return finishButton; } /** * Returns the Finish (OK) Button for the form * The button will be created if necessary * @return The OK Button for the form */ public Button getApplyButton() { if (applyButton == null) { createApplyButton(); } return applyButton; } /** * Returns the Finish (OK) Button for the form * The button will be created if necessary * @return The OK Button for the form */ public Button getCancelButton() { if (formType != FormType.DIALOG) { if (cancelButton == null) { createCancelButton(); } } else { cancelButton = getDialog().getButtonById(Dialog.CANCEL); } return cancelButton; } /** * Retrieves the HorizontalPanel object that holds all buttons at the bottom of the page. * This is only valid for non-DIALOG pages. * Use getDialog().getButtonBar for the equivalent functionality for a DIALOG page. * @return */ public HorizontalPanel getButtonPanel() { return buttonPanel; } /** * Retrieves the actual form area of the page (where all the page-specific content resides). * @return The CATFormPanel that is the form area for the page. */ public CATFormPanel getBodyPanel() { if (bodyPanel == null) { bodyPanel = StandardComponents.newBodyPanel(this); hiddenPanel = StandardComponents.newHiddenPanel(bodyPanel); bodyPanel.add(hiddenPanel); } return bodyPanel; } /** * @return the customHiddenFields */ @SuppressWarnings("ReturnOfCollectionOrArrayField") public List getCustomHiddenFields() { if (customHiddenFields == null) { customHiddenFields = new ArrayList(); } return customHiddenFields; } /** * Retrieves the Viewport used for the overall non-DIALOG page. * This is the object directly attached to the page root. * It is only valid when formType is not DIALOG. * @return The Viewport for the page. */ public Viewport getViewport() { return viewport; } /** * * @return The LayoutContainer holding all of the HiddenFields created to hold the data from getFormParameters. */ private LayoutContainer getHiddenPanel() { return hiddenPanel; } /** * Returns the minimum height needed for the page. * @return An int with the minimum height in pixels. */ public int getMinHeight() { return minHeight; } /** * Set the minimum height for the page (in pixels). * @param minHeight An int with the minimum height setting in pixels. */ public void setMinHeight(int minHeight) { this.minHeight = minHeight; } /** * Returns the minimum width needed for the page. * @return An int with the minimum width in pixels. */ public int getMinWidth() { return minWidth; } /** * Set the minimum width for the page (in pixels). * @param minWidth An int with the minimum width setting in pixels. */ public void setMinWidth(int minWidth) { this.minWidth = minWidth; } /** * @return the formName */ public String getFormName() { return formName; } /** * @param formName the formName to set */ public void setFormName(String formName) { this.formName = formName; } /** * @return the formLabel */ public String getFormLabel() { return formLabel; } /** * @param formLabel the formLabel to set */ public void setFormLabel(String formLabel) { this.formLabel = formLabel; if (formType == FormType.DIALOG) { container.getHeader().setVisible(false); } // if (Window.getTitle().isEmpty()) { // hdr = container.getHeader(); // } else { // hdr = getDialog().getHeader(); // } // Set the Window title only if we are a FORM if (formType == FormType.FORM) { if (Window.getTitle().isEmpty()) { Window.setTitle(entryPoint.getLabel()); } } else if (formType == FormType.DIALOG) { // For Dialogs, use the Dialog's header and shut off the header we created hdr = getDialog().getHeader(); hdr.setText(formLabel); // hdr.setVisible(false); } } /** * @return the helpID */ public String getHelpID() { return helpID; } /** * @param helpID the helpID to set */ public void setHelpID(String helpID) { this.helpID = helpID; } /** * Sets the mode of the form. If null, it defaults to LIST * @param mode The desired FormMode enum for the Form */ public void setMode(FormMode mode) { FormMode work = mode; if (work == null) { work = FormMode.LIST; } Registry.register("pageMode", work.toString()); this.mode = work; } /** * Returns the Mode of the form (CREATE, EDIT, DELETE, LIST, etc) * @return The FormMode enum value for this page. */ public FormMode getMode() { return mode; } /** * Creates new Hidden Fields for every entry found in the hiddenParamMap */ public void setupHiddenFields() { FormData fd = new FormData("100%"); fd.setWidth(getWidth()); Iterator ix = hiddenParamMap.keySet().iterator(); while (ix.hasNext()) { String key = ix.next(); String val = hiddenParamMap.get(key); addHiddenField(key, val, true); } for (HiddenField hf : getCustomHiddenFields()) { getHiddenPanel().add(hf); getBodyPanel().addHiddenField(hf); } } /** * Adds a new hidden field to the bodypanel. * @param fieldname The name of the Hidden field to be added * @param val The value for the newly created Hidden field. */ public void addHiddenField(String fieldname, String val) { addHiddenField(fieldname, val, false); } /** * Adds a new hidden field to the hidden panel (when formParam is true) or to the bodyPanel (when formParam is false). * @param fieldname The name of the Hidden field to be added * @param val The value for the newly created Hidden field. * @param formParam set to true to add to the form parameter hidden container. Otherwise, it will be added to the bodyPanel like any other field. */ private void addHiddenField(String fieldname, String val, boolean formParam) { // Field to hold object's oid on the page CATHiddenField hidden = CATHiddenField.newCATHiddenField(bodyPanel.getForm(), fieldname, fieldname); hidden.setValue(val); hidden.setAutoHeight(true); hidden.setAutoWidth(true); if (formParam) { hiddenPanel.add(hidden); } else { getBodyPanel().add(hidden); } getBodyPanel().addHiddenField(hidden); } /** * Empties all fields on the page that are in the FormPanel and that have names. Removes the hidden fields from the hidden panel (they will be rebuilt shortly). If they are also ComboBox, StandardTable or StandardTree objects, then their stores will be empties. */ public void reset() { // bodyPanel.reset(); getHiddenPanel().removeAll(); for (Field field : bodyPanel.getFields()) { if (!StandardComponents.isFieldHidden(field)) { String name = field.getName(); if (name != null && !name.isEmpty() && !name.equals("null")) { // Disable validation for this field during the reset. // Some validations depend on other field values and, // at this point in time, some of the fields are reset // but others are not. field.setFireChangeEventOnSetValue(false); field.reset(); field.setFireChangeEventOnSetValue(true); // Also reset any DependencyComponent associated with this field. // Otherwise, it will have a stale "startVal" which will cause // incorrect behavior. StandardComponents.resetFieldDependencyComponent(field); } } } formValid(false); } /** * Restores the original values for the form, stored when the init data was loaded. */ public void resetOriginalValues() { hasFormBeenApplied = true; for (Field field : bodyPanel.getFields()) { String name = field.getName(); if (name != null && !name.isEmpty() && !name.equals("null")) { Object value = field.getValue(); field.setOriginalValue(value); } } // turn off the APPLY and OK buttons formValid(false); } /** * Initializes the form validate processing. * All fields are added to a dive panel so the overall form status can be determined as validations occur. */ public void setupFormValidation() { Field focusfield = null; Log.debug("In CATFormLayoutsetupFormValidation."); divePanel = new FastMap(); // divePanel.put("$$BUSY", TRUE); for (Field field : bodyPanel.getFields()) { // field.setMessageTarget("tooltip"); // if (field instanceof CATMultiField) { // ((CATMultiField) field).getTextField().setMessageTarget("tooltip"); // } // if (field instanceof CATPicker) { // ((CATPicker) field).getText().setMessageTarget("tooltip"); // ((CATPicker) field).getSearchButtonAdapter().setMessageTarget("none"); // } String name = field.getName(); if (name != null && !name.isEmpty() && !"null".equals(name)) { Log.debug("Field: " + field.getName() + " tabindex: " + field.getTabIndex()); // Automatically focus on the field with tab index 1 Widget parent = field.getParent(); // field.getParent() returns null for the CATTextField in a MultiField so the // following null tests are a temporary work-around until the root cause is identified // of why the CATTextField in a multi-field doesn't have a parent if (parent instanceof LayoutContainer || (parent == null && field instanceof CATTextField)) { if (parent != null) { LayoutContainer lc = (LayoutContainer) parent; if (!"hidden".equals(lc.getId())) { if (!(field instanceof LabelField) && !(field instanceof HiddenField) && !(field instanceof CATHiddenField)) { if (field.isVisible() && field.isEnabled()) { if (focusfield == null) { focusfield = field; } } } } } else if (parent == null && field instanceof CATTextField) { // CATTextField in a CATMultiField if (field.isVisible() && field.isEnabled()) { if (focusfield == null) { focusfield = field; } } } divePanel.put(name, TRUE); Field thefield = field; // Special handling for the CATMultiField - redirect the events to the inner TextField. if (field instanceof CATMultiField) { thefield = ((CATMultiField) field).getTextField(); } Log.trace("Field " + name + " added to divePanel"); // Attach listeners from the fields for when they fire valid / invalid during validation thefield.addListener(Events.Valid, new Listener() { @Override public void handleEvent(FieldEvent fe) { Object objsource = fe.getSource(); //Log.trace("Field.Valid listener - Field:" + objsource); if (!(objsource instanceof Field)) { return; } Field fld = (Field) objsource; String name = fld.getName(); Log.debug("Received VALID event for field: " + name); updateDivePanel(name, true); } }); thefield.addListener(Events.Invalid, new Listener() { @Override public void handleEvent(FieldEvent fe) { Object objsource = fe.getSource(); //Log.trace("Field.Invalid listener - Field: " + objsource); if (!(objsource instanceof Field)) { return; } Field fld = (Field) objsource; String name = fld.getName(); Log.debug("Received INVALID event for field: " + name); updateDivePanel(name, false); } }); // Log.debug("About to pre-validate field: " + field.getName()); //field.focus(); // BEH: 2013-02-18 - turned off this validation step. //thefield.validate(); } } } if (focusfield != null) { Log.debug("Focusing on the first editable widget: " + focusfield.getName()); //focusfield.focus(); focusfield.validate(); } } /** * Returns the overall validity of the form. * Every dive panel object is tested to see if all are true. If so, then the form is valid. * Any false value found means the entire form is invalid. * @return True when all entries in the dive panel are marked as true. False otherwise. */ public boolean isValid() { Iterator ix = divePanel.keySet().iterator(); while (ix.hasNext()) { String key = ix.next(); boolean b = divePanel.get(key).booleanValue(); if (!b) { formValid(false); return false; } } formValid(true); return true; } // private void clearDivePanel(){ // Iterator ix=divePanel.keySet().iterator(); // while(ix.hasNext()){ // String key=ix.next(); // divePanel.put(key,TRUE); // } // } /** * Forces validation on every field on the page. */ public void validate() { // If the dependency processor looks like it's going to run, then clear the dive panel and the start values used by the DH to avoid validation. // This should make it revalidate every field on the page. // TODO: Maybe this should only re-evaluate the false items? // if(getDependencyProcessor()!=null){ // clearDivePanel(); // DependencyComponent.clearValues(); // } List> fields = bodyPanel.getFields(); for (Field field : fields) { field.isValid(); } showErrorForCurrentFocusField(); } /** * The method to override to change how the form reacts to its overall validation status (typically, the finish button is enabled or disabled according to * the validity boolean passed to the routine). * * @param valid set to true if the overall form is valid, false otherwise */ public void formValid(boolean valid) { boolean theValid = valid; if (hasFormBeenApplied) { // if the form as been applied, first check to see that at least one field is dirty // before allowing the OK and APPLY buttons to be enabled boolean isDirty = false; for (Field field : bodyPanel.getFields()) { String name = field.getName(); if (name != null && !name.isEmpty() && !"null".equals(name)) { if (field.isDirty()) { isDirty = true; break; } } } if (!isDirty) { // if nothing is dirty and this is after an apply, do not turn on the OK and APPLY buttons theValid = false; } } if (formType == FormType.FORM) { if (finishButton != null) { finishButton.setEnabled(theValid); } } else if (formType == FormType.DIALOG) { getDialog().getButtonById(Dialog.OK).setEnabled(theValid); } if (applyButton != null) { if (applyButton != null) { applyButton.setEnabled(theValid); } } } /** * Disables the OK and / or Apply buttons on a page */ public void disableActionButtons() { if (formType == FormType.FORM) { if (finishButton != null) { finishButton.setEnabled(false); } } else if (formType == FormType.DIALOG) { getDialog().getButtonById(Dialog.OK).setEnabled(false); } Button ab = applyButton; if (ab != null) { ab.setEnabled(false); } } /** * @return the formType */ public FormType getFormType() { return formType; } private void updateDivePanel(String name, boolean valid) { divePanel.put(name, Boolean.valueOf(valid)); boolean status = isValid(); Log.info("Overall form validity: " + status); } /** * @param formType the formType to set */ public void setFormType(FormType formType) { this.formType = formType; } /** * Updates the task parameters for the specified task by scraping the current form. * @param task The Task that will get the parameters from the current form. */ protected void replaceTaskParameters(Task task) { Map pmap = task.getFormToTaskMap(); Iterator ix = pmap.keySet().iterator(); // Cycle through the parameters in the task's paramMap while (ix.hasNext()) { // Fname holds the name of the field to be scraped - pname holds the parameter name to generate String fname = ix.next(); String pname = pmap.get(fname); if (fname != null && !fname.isEmpty() && pname != null && !pname.isEmpty()) { Log.trace("Searching for a field value to substitute for param '" + fname + "' from field '" + pname + "'"); for (Field field : getBodyPanel().getFields()) { String name = field.getName(); if (name == null || name.isEmpty()) { continue; } if (name.equals(fname)) { // Found the matching Field name - substitute the value into the parameter map String realval = StandardComponents.getFieldValue(field); task.addTaskParam(pname, realval); Log.trace("Found the field value: " + realval); break; } } } } } /** * @return the dialog */ public Dialog getDialog() { if (dialog == null) { dialog = new CATDialog(); dialog.setData("form", this); dialog.setButtons(Dialog.OKCANCEL); dialog.setHideOnButtonClick(false); dialog.setClosable(false); dialog.setAnimCollapse(true); dialog.setModal(true); dialog.setBlinkModal(true); } return dialog; } /** * Sets up the Form / Dialog's busy mask * * @param message The string containing the desired mask text * @param progressText The string to be presented inside the progress bar */ public void setBusy(String message, String progressText) { setBusy("INTERNAL", message, progressText); } /** * Sets up the Form / Dialog's busy mask. * Each Asynchronous operation needs to make a set / clear busy pair of calls. * The first call will mask the form. * Subsequent calls will merely add their reason to the map of current busy reasons. * A RuntimeException is thrown if a second setBusy call with the same reason is encountered. * * @param reason The ID used to track multiple set/clear busy requests. * @param message The string containing the desired mask text * @param progressText The string to be presented inside the progress bar */ public void setBusy(String reason, String message, String progressText) { if (busyMap.containsKey(reason)) { throw new RuntimeException("setBusy called with a duplicate reason: " + reason + " busyMap: " + busyMap); } if (busyMap.isEmpty()) { if (getFormType() == FormType.FORM || getFormType() == FormType.EMBEDDED) { if (waitBox == null) { setWaitBox(MessageBox.wait("Progress", message, progressText)); } } else { if (!masked) { getDialog().mask(progressText); masked = true; } } } else { Log.debug("Form's busyMap was already established - left existing mask alone"); } updateDivePanel("$$BUSY", FALSE); busyMap.put(reason, reason); } /** * Tears down the Form / Dialog's busy mask */ public void clearBusy() { clearBusy("INTERNAL"); } /** * Tears down the Form / Dialog's busy mask * @param reason A string with a value to correspond to a previous setBusy call. */ public void clearBusy(String reason) { if (!busyMap.containsKey(reason)) { Log.warn("clearBusy called without a corresponding setBusy for reason: " + reason); } busyMap.remove(reason); if (busyMap.isEmpty()) { if (getFormType() == FormType.FORM || getFormType() == FormType.EMBEDDED) { if (getWaitBox() != null) { getWaitBox().close(); waitBox = null; } } else { if (getDialog() != null) { getDialog().unmask(); masked = false; } } updateDivePanel("$$BUSY", TRUE); } else { Log.debug("Form's busyMap still has entries: " + busyMap); } } /** * Forces the page validation status to false by adding a special flag to the dive panel's status map and setting it to FALSE */ public void forceDivePanelInvalid() { updateDivePanel("$$FORCE_VALID", FALSE); } /** * Whenever markDirty is used on this layout, it will clear the special flag so normal validation can resume. */ public void markDirty() { updateDivePanel("$$FORCE_VALID", TRUE); } /** * Returns the current validation status for a field name. This is used by the widgets when no change has been detected. * Instead of returning the value of validateValueLocal (which might give a false positive), it will use the divePanel status instead. * @param fieldName The name of the Field object on the page to be checked. * @return true if the Field specified by fieldName was last validated as VALID, false otherwise. */ public boolean isFieldValid(String fieldName) { if (fieldName == null || fieldName.isEmpty()) { throw new RuntimeException("A Framework widget has no name setting"); } Boolean status = divePanel.get(fieldName); if (status == null) { // throw new RuntimeException("A field name was not found in the dive panel: " + fieldName); return true; } return status; } /** * @return the footer */ public LayoutContainer getFooter() { return footer; } /** * @param footer the footer to set */ public void setFooter(LayoutContainer footer) { this.footer = footer; } /** * Toggles the Debug button on and off */ public void toggleDebug() { boolean newsetting = !tools.isVisible(); if (formType == FormType.EMBEDDED) { getFooter().setVisible(newsetting); } tools.setVisible(!tools.isVisible()); } /** * @return the dependencyDescriptor */ public String getDependencyDescriptor() { if (dependencyDescriptor == null) { dependencyDescriptor = getParamMap().get("dependencyjson"); } if (dependencyDescriptor == null) { dependencyDescriptor = ""; } Log.trace("Length of dependencyDescriptor: " + dependencyDescriptor.length()); return dependencyDescriptor; } /** * @param dependencyDescriptor the dependencyDescriptor to set */ public void setDependencyDescriptor(String dependencyDescriptor) { this.dependencyDescriptor = dependencyDescriptor; } /** * Returns the page's dependency processor * @return The DependencyProcessor for the page */ public DependencyProcessor getDependencyProcessor() { return dependency; } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 149396; to = 149303.Please, attach your messages.log to new issue! ----- /* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.ptc.gxt.framework.client.widget; import com.allen_sauer.gwt.log.client.Log; import com.extjs.gxt.ui.client.Registry; import com.extjs.gxt.ui.client.Style; import com.extjs.gxt.ui.client.Style.Scroll; import com.extjs.gxt.ui.client.core.FastMap; import com.extjs.gxt.ui.client.core.XDOM; import com.extjs.gxt.ui.client.data.ModelData; import com.extjs.gxt.ui.client.event.BaseEvent; import com.extjs.gxt.ui.client.event.ComponentEvent; import com.extjs.gxt.ui.client.event.Events; import com.extjs.gxt.ui.client.event.FieldEvent; import com.extjs.gxt.ui.client.event.Listener; import com.extjs.gxt.ui.client.event.MessageBoxEvent; import com.extjs.gxt.ui.client.event.SelectionListener; import com.extjs.gxt.ui.client.store.Store; import com.extjs.gxt.ui.client.store.StoreEvent; import com.extjs.gxt.ui.client.store.StoreListener; import com.extjs.gxt.ui.client.util.KeyNav; import com.extjs.gxt.ui.client.widget.Component; import com.extjs.gxt.ui.client.widget.ContentPanel; import com.extjs.gxt.ui.client.widget.Dialog; import com.extjs.gxt.ui.client.widget.Header; import com.extjs.gxt.ui.client.widget.HorizontalPanel; import com.extjs.gxt.ui.client.widget.Info; import com.extjs.gxt.ui.client.widget.LayoutContainer; import com.extjs.gxt.ui.client.widget.MessageBox; import com.extjs.gxt.ui.client.widget.Viewport; import com.extjs.gxt.ui.client.widget.button.Button; import com.extjs.gxt.ui.client.widget.button.ButtonBar; import com.extjs.gxt.ui.client.widget.form.AdapterField; import com.extjs.gxt.ui.client.widget.form.ComboBox; import com.extjs.gxt.ui.client.widget.form.DateField; import com.extjs.gxt.ui.client.widget.form.DateTimePropertyEditor; import com.extjs.gxt.ui.client.widget.form.Field; import com.extjs.gxt.ui.client.widget.form.FormPanel; import com.extjs.gxt.ui.client.widget.form.FormPanel.LabelAlign; import com.extjs.gxt.ui.client.widget.form.HiddenField; import com.extjs.gxt.ui.client.widget.form.LabelField; import com.extjs.gxt.ui.client.widget.form.NumberField; import com.extjs.gxt.ui.client.widget.form.Radio; import com.extjs.gxt.ui.client.widget.form.RadioGroup; import com.extjs.gxt.ui.client.widget.layout.ColumnData; import com.extjs.gxt.ui.client.widget.layout.ColumnLayout; import com.extjs.gxt.ui.client.widget.layout.FitLayout; import com.extjs.gxt.ui.client.widget.layout.FormData; import com.extjs.gxt.ui.client.widget.layout.FormLayout; import com.extjs.gxt.ui.client.widget.layout.HBoxLayout; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.Widget; import com.ptc.gxt.framework.client.EntryPointFactory; import com.ptc.gxt.framework.client.FormMode; import com.ptc.gxt.framework.client.FormType; import com.ptc.gxt.framework.client.FrameworkEntryPoint; import com.ptc.gxt.framework.client.TaskFactory; import com.ptc.gxt.framework.client.dependency.DependencyComponent; import com.ptc.gxt.framework.client.dependency.DependencyProcessor; import com.ptc.gxt.framework.client.entrypoint.CoreTaskFactory; import com.ptc.gxt.framework.client.event.ConfirmSelectListener; import com.ptc.gxt.framework.client.event.FrameworkEvents; import com.ptc.gxt.framework.client.ie.IEAtt; import com.ptc.gxt.framework.client.ie.IEElement; import com.ptc.gxt.framework.client.ie.IEGroup; import com.ptc.gxt.framework.client.ie.MsgLevel; import com.ptc.gxt.framework.client.ie.ParamMode; import com.ptc.gxt.framework.client.ie.Task; import com.ptc.gxt.framework.client.table.MergedModType; import com.ptc.gxt.framework.client.table.ReturnGroupHandler; import com.ptc.gxt.framework.client.table.TableGroupReturnGroupListener; import com.ptc.gxt.framework.client.widget.tools.DebugToolsMenu; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Defines the page. * The CATFormLayout is wrapped by StandardForm. * It defines a header, a body and footer information. * It operates in several modes, defined by the FormMode enum. * It produces several different types of form, defined by the FormType enum. * It provides standard buttons and actions associated to those buttons to support data acquisition, server update and cancel operations. * It provides for interaction with the launching window (refresh / forward to new urls) * It provides for standard actions when the action task completes (close on success, reset when apply button used, keep page when errors occur) * It provides validation services between fields via a DependencyProcessor tied to the page. * It provides for an initial data task, an action task and optionally, a cancel task. * * It provides for data transfer between in-browser pages via ReturnGroupListener. */ //public class CATFormLayout implements EntryPoint { public class CATFormLayout { private static final Boolean TRUE = true; private static final Boolean FALSE = false; /** *Identifier for use in retrieving the Apply button via getDialog().getButtonById(APPLY); */ public static final String APPLY = "apply"; private Header hdr; private String formName = "-No Form Name Specified-"; private String formLabel = ""; private String helpID = ""; private String helpURL = Registry.get("helpurl"); private ContentPanel container; private EntryPointFactory entryPoint; private String application = "FormLayout"; private Dialog dialog; private Viewport viewport = StandardComponents.getCATViewport(this); private HorizontalPanel buttonPanel; private CATFormPanel bodyPanel; private LayoutContainer hiddenPanel; private WindowMode forcedWindowAction; private ParentMode forcedParentAction; private int width = -1; private int height = -1; private int minWidth = -1; private int minHeight = -1; private int minCWidth = -1; private int minCHeight = -1; // private int formDataWidth = 250; private String oid; private String webAppURL; private FormType formType = FormType.FORM; private FormMode mode; private Map divePanel = new FastMap(); private Component focusField; private boolean hasFormBeenApplied; private boolean clearOnApply; private Task initTask; private IEGroup initTaskGroup; private Task actionTask; private IEGroup actionTaskGroup; private Task cancelTask; private IEGroup cancelTaskGroup; private IEGroup deferGroup; private IEElement initElement; private IEGroup initGroup; private ReturnGroupHandler returnGroupHandler; private DataReturnMode returnMode; // Any objects that need to be shared into inner classes // or get defined in the registry get declared here. // Use the name of the object as the registry entry // This should help with code completion private String messageBox; private String containerOid; private Button finishButton; private Button applyButton; private Button cancelButton; // private Button closeButton; private MessageBox waitBox; private boolean masked; private Map busyMap = new FastMap(); private Map hiddenParamMap; private int sbh = XDOM.getScrollBarWidth() + 12; private int sbw = XDOM.getScrollBarWidth() + 12; private StandardForm page; private List customHiddenFields; private Field currentFocusField; private LayoutContainer statusarea; private LayoutContainer footer; private Button tools; private boolean forceClose; private SelectionListener okSelectionListener; private SelectionListener applySelectionListener; private SelectionListener cancelSelectionListener; private SubmitMode submitMode = SubmitMode.OK; private String taskDataKey = "obid"; private Boolean dirty = false; private IEGroup dependencyjson; private boolean dependencySetup; private DependencyProcessor dependency; private String dependencyDescriptor; private String createdObjectOID; /** * When true, only dirty fields and hidden fields will be sent to edit tasks */ private boolean sendOnlyDirtyOnEdit; /** * The field that will display the widget's field label in the status area */ // private CATLabelField statusAreaLabel; /** * The field that will display the widget's tooltip in the status area */ private CATLabelField statusAreaMessage; /** * The Red Ball image for display between statusAreaLabel and statusAreaMessage */ private Image statusAreaImage; /** * The adapter to wrap statusAreaImage with to insure it displays properly */ private AdapterField statusAreaImgAdapter; /** * This creates a standard form layout for use by a page. It provides a standardized header with a title and a help icon, an OK / Apply / Cancel button set * and a CATFormPanel into which the developer will add the specific Field sub-classes to create the desired form. Once this method is run, the developer * should do a getBodyPanel() and start adding Field objects to the page. * * @param app The application name for this page * @return The form, already set up to take the specific fields. */ public static CATFormLayout newForm(String app) { return newForm(app, FormType.FORM); } /** * Creates a new CATFormLayout for the given EntryPointFactory enum entry. * @param app The EntryPointFactory enum entry for the StandardForm used with this CATFormLayout * @return The new CATFormLayout object */ public static CATFormLayout newForm(EntryPointFactory app) { return newForm(app, FormType.FORM); } /** * Creates a new CATFormLayout for the given app name and sets the form type as indicated. * @param app The name of the app that will resolve via the newEntryPoint method to a StandardForm * @param formtype A FormType of DIALOG, FORM or EMBEDDED * @return The new CATFormLayout object. */ public static CATFormLayout newForm(String app, FormType formtype) { EntryPointFactory epf = FrameworkEntryPoint.getEntryPointFactory(app); if (epf == null) { throw new RuntimeException("No EntryPoint found for application: " + app); } CATFormLayout newform = new CATFormLayout(); newform.setup(epf, formtype); return newform; } /** * Creates a new Form using an EntryPointFactory handle and FormType. * @param app The EntryPointFactory enum value for the desired page. * @param formtype The type of dialog being created (DIALOG, FORM, or EMBEDDED). * @return A CATFormLayout ready for use by setupLayout. */ public static CATFormLayout newForm(EntryPointFactory app, FormType formtype) { EntryPointFactory epf = app; if (epf == null) { throw new RuntimeException("No EntryPoint found for application: " + app); } CATFormLayout newform = new CATFormLayout(); newform.setup(epf, formtype); return newform; } // @Override /** * Creates the basic shell of the page * @param epf An EntryPointFactory enum value. * @param formtype The form type to be used for the form (@see FormType). */ public void setup(EntryPointFactory epf, FormType formtype) { Log.debug("FormLayout :onModuleLoad: start"); Log.debug("FormLayout : webappurl: " + Registry.get("webappurl")); Log.debug("Launching URL: " + com.google.gwt.user.client.Window.Location.getHref()); Log.debug("oid for this UI page: " + oid); Log.debug("containerOid for this UI page: " + containerOid); setEntryPoint(epf); setFormType(formtype); setMode(epf.getMode()); setFormName(epf.getName()); setHelpID(epf.getHelpID()); setApplication(epf.getName()); setMinHeight(epf.getHeight()); setMinWidth(epf.getWidth()); setWebAppURL((String) Registry.get("webappurl")); //TODO: Don't think this line is doing anything - nobody seems to set the registry entry it's looking for //setHiddenParamMap((Map) Registry.get(app + "_paramMap")); // For Creating Basic Container which is used to add all components.. container = new ContentPanel(); Log.trace("CATFormLayout container isMonitorWindowResize: " + container.isMonitorWindowResize()); container.setLayout(new FitLayout()); if (FormType.EMBEDDED != formType) { container.addStyleName("cat-background"); container.setId("background"); getViewport().removeStyleName("cat-background"); } CATFormPanel fp = getBodyPanel(); fp.setMethod(FormPanel.Method.POST); fp.setLabelAlign(LabelAlign.RIGHT); fp.setLabelWidth(150); fp.setHeaderVisible(false); fp.getHeader().setText(""); } /** * Creates a task with a taskoutgroupname generated from the taskuri. It will never re-use an existing instance of a task with a matching taskuri. * * @param task The TaskFactory enum entry of the task to be run * @return The configured task (also available via initFormTask) */ public Task setupInitTask(TaskFactory task) { return setupInitTask(task, null, false); } /** * Creates a task with a taskoutgroupname generated from the taskuri. When the useCache is true, it will re-use an already configured task with the same * uri. * * @param task The TaskFactory enum entry of the task to be run * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via initFormTask) */ public Task setupInitTask(TaskFactory task, boolean useCache) { return setupInitTask(task, null, useCache); } /** * Creates a task with a taskoutgroupname as specified (null means it will generate it from the taskuri). When the useCache is true, it will re-use an * already configured task with the same uri. * * @param task The TaskFactory enum entry of the task to be run * @param group The name of the group that will be returned by the task * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via initFormTask) */ public Task setupInitTask(TaskFactory task, String group, boolean useCache) { String taskuri = task.getTaskUri(); if (taskuri == null) { StandardComponents.alert(this, MsgLevel.FATAL, "Initializer task uri was not specified"); return null; } initTask = Task.newTask(task, group, useCache); String workgroup = group; if (group == null || group.isEmpty()) { workgroup = getInitTask().getTaskGroupOutName(); } initTaskGroup = getInitTask().getGroup(workgroup); final DefaultInitTaskListener initTaskListener = new DefaultInitTaskListener(); getInitTaskGroup().addStoreListener(initTaskListener); getInitTaskGroup().addListener(Store.Sort, new Listener() { @Override public void handleEvent(BaseEvent be) { initTaskListener.setSuppressClearBusy(true); Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { initTaskListener.setSuppressClearBusy(false); } }); } }); return getInitTask(); } /** * Invokes the page */ public void invoke() { // StandardComponents.removeLoadingMarker(); // Make sure we're not invoking a page that can't do anything. If we going to be modifying the DB, we need to either have an action task and / or a return group handler defined. if (getMode() == FormMode.CREATE || getMode() == FormMode.EDIT || getMode() == FormMode.DELETE) { if (getReturnGroupHandler() == null && (actionTask == null || actionTask.isNullTask())) { throw new RuntimeException("A page " + getPage().getClass().getName() + " in mode CREATE, EDIT or DELETE must have either an action task or a return group handler defined. This page has neither!"); } } if (dependency != null) { dependency.setProcessing(false); } // Reset the form in case we're executing this multiple times reset(); // Set up hardcoded data for the dialog (e.g., a fixed pulldown list or starting value) getPage().setupFixedData(); MergedModType mt = MergedModType.NONE; if (mode == FormMode.CREATE) { mt = MergedModType.ADD; } else if (mode == FormMode.EDIT) { mt = MergedModType.EDIT; } Field field = getBodyPanel().getField(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME); if (field == null) { addHiddenField(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME, mt.name()); } else { field.setValue(mt.name()); } setDefaultFocusField(); if (formType != FormType.DIALOG) { getViewport().layout(false); getViewport().show(); } else { getDialog().layout(true); getDialog().show(); } // Defer the rest of the work until after the form successfully paints (hopefully) // Two levels of defer to try to give all the formatting time to settle out so we don't get a Frankenscreen visible to the user Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { // Mask the form setBusy("FORM", "Please Wait...", "Initializing Form..."); Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { beginFormOperation(); } }); } }); } /** * Kicks off the init task. * Displays a message if the tasks are suppressed (debug tool for checking the layout of the page without waiting for data). * Turns on the dependency handling * Requests the form size adjustments to get the layouts to "settle" */ private void beginFormOperation() { Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { runInitTask(); String rt = (String) Registry.get("runtasks"); if (rt != null && !rt.equalsIgnoreCase("true")) { StandardComponents.alert(getSelf(), MsgLevel.WARN, "Tasks are not running - just letting you check form layouts"); } activateDependencyHandler(); initialFormAdjustment(); } }); } /** * Adjusts the form size down one pixel in height to help get the form to settle */ private void initialFormAdjustment() { // This is to force a resize to get the page to properly layout (some things just don't work right during pre-render layout) Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { jiggleForm(false); finalFormAdjustment(); } }); } /** * Adjusts the form back to its requested size, sets the default focus and clears the busy mask. */ private void finalFormAdjustment() { // This restores the original requested size and causes one more adjustment - hopefully it will be more reliable this way Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { jiggleForm(true); setDefaultFocus(); clearBusy("FORM"); } }); } private void activateDependencyHandler() { // Call the dependency processor Log.debug("Calling the DependencyProcessor"); // Restart the processor if the page is being reused if (dependency != null) { Log.debug("Restarting the dependency processor on the page"); dependency.processPage(getBodyPanel(), false); validate(); } else { // If we have already attempted to process the dependency file and it's still null, we won't get a better answer on the second attempt, so skip the task. if (!dependencySetup) { // If we are the top-level page, entrypointclassname should point to our classname - if so, use whatever descriptor is present if (getPage().getClass().getName().equals(Registry.get("entrypointclassname"))) { Log.debug("Opening the dependency descriptor for the top-level page: " + getPage().getClass().getName()); dependency = DependencyProcessor.newDependencyProcessor(getPage(), getDependencyDescriptor()); dependencySetup = true; } else { // we are not the top level page, run the task to get the dependency descriptor setBusy("DH", "Please Wait...", "Initializing Form..."); Task dependencyTask; if (Registry.get("windchill") != null) { dependencyTask = Task.newTask(CoreTaskFactory.GET_DEPENDENCY_DESCRIPTOR); } else { dependencyTask = Task.newJspTask(CoreTaskFactory.GET_DEPENDENCY_DESCRIPTOR_SERVLET, "getDependencyDescriptor", false); } dependencyTask.addTaskParam("pageclass", getPage().getClass().getName()); dependencyjson = dependencyTask.getGroup("dependencyjson"); dependencyjson.addStoreListener(new StoreListener() { @Override public void storeDataChanged(StoreEvent se) { // Retrieve the task results and use them to open the descriptor returned in the data IEElement depel; String depjson; if (dependencyjson.getElementCount() > 0) { depel = dependencyjson.getElementAt(0); depjson = depel.getValue("dependencyjson"); dependencyDescriptor = depjson; } Log.debug("Opening the dependency descriptor for the dialog: " + getPage().getClass().getName()); dependency = DependencyProcessor.newDependencyProcessor(getPage(), getDependencyDescriptor()); dependencySetup = true; clearBusy("DH"); validate(); } }); dependencyTask.invoke(); } } } } /** * Adjusts the size of the form either up or down one pixel in height. * This seems to be needed to get all of the interactions in the layouts to settle out and actually do what the developer intends. */ private void jiggleForm(boolean up) { final int upsize = 1; final int downsize = -1; int adjustSize = downsize; if (up) { adjustSize = upsize; } int theheight = Window.getClientHeight(); int thewidth = Window.getClientWidth(); if (formType == FormType.FORM) { if (getEntryPoint().getHeight() != -1 && getEntryPoint().getWidth() != -1) { StandardComponents.setWindowSize(getEntryPoint().getHeight() - adjustSize, getEntryPoint().getWidth()); } } if (formType != FormType.DIALOG) { getViewport().setSize(getViewport().getWidth(), getViewport().getHeight() - adjustSize); } else { getBodyPanel().setLayoutData(new FormData("98% 98%")); } getContainer().layout(); Log.debug("Resized window to " + theheight + ":" + thewidth); } /** * @return the dirty */ public Boolean isDirty() { return dirty; } /** * @param dirty the dirty to set */ public void setDirty(Boolean dirty) { this.dirty = dirty; } /** * Runs the page's Init Task. Only the hidden fields are sent as parameters. */ public void runInitTask() { // This setBusy call MUST appear after the show calls or you can wind up with multiple copies of the mask setBusy("INITTASK", "Please wait...", "Initializing Form..."); createTaskParameters(getInitTask(), true); if (!initTask.invoke()) { // If we're using a StartElement with field data, use it to set the form field values instead of the task results if (getInitElement() != null) { setFormFieldValues(initGroup); } clearBusy("INITTASK"); } } /** * @return the startElement * @deprecated Use getInitElement instead */ @Deprecated public IEElement getStartElement() { return initElement; } /** * @return the startElement */ public IEElement getInitElement() { return initElement; } /** * @param initElement the startElement to set. This will be treated like a single element being returned from an initTask in the default group. */ public void setInitElement(IEElement initElement) { this.initElement = initElement; initGroup = new IEGroup("initGroup"); initGroup.addElement(initElement); setInitGroup(initGroup); } /** * @param startElement the startElement to set. This will be treated like a single element being returned from an initTask in the default group. * @deprecated Use setInitElement(IEElement) instead. */ @Deprecated public void setStartElement(IEElement startElement) { setInitElement(startElement); } /** * @param initGroup the initGroup to set. This will be treated like it is the default group being returned from an initTask. * */ public void setInitGroup(IEGroup initGroup) { this.initGroup = initGroup; // if (initElement != null && getInitTask() != null && !initTask.getUri().equals(CoreTaskFactory.NULL.getTaskUri())) { // //throw new RuntimeException("Error: you cannot specify an init task and then use an initElement to initialize the page"); // } if (getInitTask() == null) { initTask = Task.newTask(CoreTaskFactory.NULL); } getInitTask().addGroup(initGroup); getInitTask().setTaskGroupOutName(initGroup.getName()); } /** * @return the initTask */ public Task getInitTask() { return initTask; } /** * @return the actionTask */ public Task getActionTask() { return actionTask; } /** * @return the cancelTask */ public Task getCancelTask() { return cancelTask; } /** * @return the submitMode */ public SubmitMode getSubmitMode() { return submitMode; } /** * @param submitMode the submitMode to set */ public void setSubmitMode(SubmitMode submitMode) { this.submitMode = submitMode; } /** * @return the initTaskGroup */ public IEGroup getInitTaskGroup() { return initTaskGroup; } /** * @return the actionTaskGroup */ public IEGroup getActionTaskGroup() { return actionTaskGroup; } /** * @return the cancelTaskGroup */ public IEGroup getCancelTaskGroup() { return cancelTaskGroup; } /** * @return the webAppURL */ public String getWebAppURL() { return webAppURL; } /** * The default task listener for the init task */ @SuppressWarnings("ProtectedInnerClass") public class DefaultInitTaskListener extends StoreListener { private boolean suppressClearBusy = false; /** * Indicates if the busy indicator should be suppressed for this page * @param suppress True to suppress, false to allow the indicator to be used. */ public void setSuppressClearBusy(boolean suppress) { suppressClearBusy = suppress; } @Override public void storeDataChanged(StoreEvent se) { //TODO: BEH: Perhaps all these data changed can be handled by the dependencyhandler, especially this type // form.getWaitBox().close(); Log.trace("InitializerTask.storeDataChanged: Enter"); if (assertGroupHasData(getInitTaskGroup(), getTaskDataKey(), "Initialization data could not be retrieved from task: " + getInitTask().getUri())) { setFormFieldValues(getInitTaskGroup()); } StandardComponents.alert(FormMode.ASSERT_EXIST, getInitTaskGroup()); isValid(); if (!suppressClearBusy) { clearBusy("INITTASK"); } suppressClearBusy = false; } } /** * Takes the values from the first element in the group and processes them into matching field names on the form. Only Field objects will be picked up. Only those * with names will be processed. If the group has a FormMap, the mappings are honored during processing. Events should not fire from the setValue calls - a * later, final validation of all fields on the form happens right after the form is shown. * * @param group The group to be processed */ protected void setFormFieldValues(IEGroup group) { // Check to make sure we have decided whether or not the dependency file has been found and read before putting values on the page, which will eventually start triggering through the DH. if (!dependencySetup) { deferGroup = group; // Defer this processing until later Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { setFormFieldValues(deferGroup); } }); return; } if (group == null) { StandardComponents.alert(this, MsgLevel.FATAL, "setFormField values was given a non-existent group"); return; } if (group.getElementCount() == 0) { StandardComponents.alert(this, MsgLevel.FATAL, "setFormField values was given an empty group"); return; } // Get the group's individual form to group map Map formToGroupMap = group.getFormToGroupMap(); if (formToGroupMap.isEmpty()) { // Group didn't have one - use the init task's map formToGroupMap = getInitTask().getFormToTaskMap(); } setFormFieldValues(group.getElementAt(0), formToGroupMap); } /** * Takes the values in the element and processes them into matching field names on the form. Only Field objects will be picked up. Only those * with names will be processed. If the group has a FormMap, the mappings are honored during processing. Events should not fire from the setValue calls - a * later, final validation of all fields on the form happens right after the form is shown. * * @param el The IEElement to be processed * @param formToGroupMap A map of form names to group attribute names that will be used during processing */ protected void setFormFieldValues(IEElement el, Map formToGroupMap) { boolean showMissingData = Log.isDebugEnabled(); StringBuilder missingData = new StringBuilder(); List> fields = getBodyPanel().getFields(); for (Field field : fields) { // for each item in the group // loop through all the fields on the form until a match is found // Don't process a field without a name String fieldName = field.getName(); String safeFieldName = fieldName; if (fieldName == null || fieldName.isEmpty()) { Log.debug("Field skipped because it has no name: " + field.getClass().getName()); continue; } if (fieldName.contains(".")) { Log.debug("Replacing dots with underscores for field param name: " + fieldName); safeFieldName = safeFieldName.replace(".", "_"); } // Pull the attribute from the element corresponding to the field name // Check the formMap to see if we need to pull a different attribute for this field String mappedAttName = formToGroupMap.get(safeFieldName); if (mappedAttName == null) { mappedAttName = safeFieldName; } // Now check for the mapped attribute name in the element that was passed to // populate the form Object obj = el.getValue(mappedAttName); Object pulldownlistgroup = el.getValue(mappedAttName + "_legalvalues"); if (pulldownlistgroup != null && !(pulldownlistgroup instanceof IEGroup)) { pulldownlistgroup = null; } if (field instanceof ComboBox) { ((ComboBox) field).setStore((IEGroup) pulldownlistgroup); } // if (fieldName.equals(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME)) { // if (obj instanceof MergedModType) { // obj = ((MergedModType) obj).toString(); // } // } String val = ""; if (obj != null) { try { // Disable validation for this field because it might depend on the values // of other fields which "have not" been set yet. //StandardComponents.setFieldValidation(field, false); field.setFireChangeEventOnSetValue(false); if (obj instanceof String) { val = (String) obj; Log.debug("Setting field '" + fieldName + "' to attribute '" + mappedAttName + "' - val: '" + val + "'"); if (field instanceof RadioGroup) { List> radios = ((RadioGroup) field).getAll(); for (Field fieldRadio : radios) { Radio radio = (Radio) fieldRadio; if (val.equals(radio.getValueAttribute())) { // radio.setFireChangeEventOnSetValue(false); radio.setValue(Boolean.TRUE); // radio.setFireChangeEventOnSetValue(true); // field.setFireChangeEventOnSetValue(false); ((RadioGroup) field).setValue(radio); ((RadioGroup) field).setOriginalValue(radio); // field.setFireChangeEventOnSetValue(true); } } } else if (field instanceof CATMultiField) { // field.setFireChangeEventOnSetValue(false); ((CATMultiField) field).setValue(val); ((CATMultiField) field).setOriginalValue(val); ((CATMultiField) field).getTextField().setOriginalValue(val); // field.setFireChangeEventOnSetValue(true); } else if (field instanceof LabelField) { // field.setFireChangeEventOnSetValue(false); ((LabelField) field).setText(val); ((LabelField) field).setValue(val); ((LabelField) field).setOriginalValue(val); // field.setFireChangeEventOnSetValue(true); } else if (field instanceof NumberField) { // field.setFireChangeEventOnSetValue(false); field.clear(); if (val == null || val.isEmpty()) { field.setValue(null); field.setOriginalValue(null); } else { field.setValue(field.getPropertyEditor().convertStringValue(val)); field.setOriginalValue(field.getValue()); } // field.setFireChangeEventOnSetValue(true); } else if (field instanceof Radio) { continue; } else if (field instanceof CATComboBox) { CATComboBox box = ((CATComboBox) field); if (box.getStore() == null) { throw new RuntimeException("ComboBox: " + box.getName() + " is not bound to a Group"); } if (box.getValueField() == null || box.getValueField().isEmpty()) { throw new RuntimeException("ComboBox: " + box.getName() + " does not have a valuefield set up"); } if (box.getDisplayField() == null || box.getDisplayField().isEmpty()) { throw new RuntimeException("ComboBox: " + box.getName() + " does not have a displayfield set up"); } // Try matching the raw value with something in the store if (box.getValue() == null) { Log.debug("Using the value from the backing store: " + val); box.setValueFromStore(val); } // Try creating a custom hidden item in the store and then match the raw value against the store if (box.getValue() == null) { Log.debug("Using the hardcoded value: " + val); box.addNewPulldownListEntry(val, val, false); // make sure that if box.getValue() is null then both value and originalValue // set to the same thing. In gxt, if I setValueFromStore("") and then // call getValue() then getValue() does some logic and returns null. box.setValueFromStore(val); final IEElement originalValue = new IEElement(); originalValue.set(box.getValueField(), box.getRawValue()); box.setOriginalValue(originalValue); Log.trace("CATFormLayout --> val = " + val + ", getValue() = " + box.getValue() + ", raw value = " + box.getRawValue() + ", originalValue = " + box.getOriginalValue()); } box.setData("rawValue", val); box.setData("forcedValue", "y"); if (box.getValue() == null) { Log.debug("Combo values: raw: " + ((CATComboBox) field).getRawValue() + " value: " + box.getValue()); // throw new RuntimeException("Failed to set value on combo box: " + box.getName()); } // box.getValue is not null then it hasn't been set yet. if (box.getValue() != null) { box.setOriginalValue(box.getValue()); } // Log.debug("Combo values: raw: " + ((CATComboBox) field).getRawValue() + " internal: " + box.getValue().get(box.getValueField()) + " display: " + box.getValue().get(box.getDisplayField())); } else if (field instanceof DateField) { if (val.isEmpty()) { val = null; } DateTimePropertyEditor editor = ((DateField) field).getPropertyEditor(); Date dt = null; if (val != null) { dt = editor.convertStringValue(val); } field.setValue(dt); field.setOriginalValue(dt); } else { if (val == null || val.isEmpty()) { field.setValue(null); field.setOriginalValue(null); } else { field.setValue(val); field.setOriginalValue(val); } } } else if (obj instanceof IEGroup) { // look for an adapter field containing a table // if it is found set the group found in this attribute // to the group for the table in the matching adapter field if (field instanceof CATAdapterField) { IEGroup g = (IEGroup) obj; CATAdapterField af = (CATAdapterField) field; if (af.getTable() != null) { IEGroup work = (IEGroup) af.getTable().getGrid().getStore(); work.setFiresEvents(false); work.removeAll(); work.add(g.deepClone().getElementList()); work.setFiresEvents(true); work.signalDataChanged(); } } } } catch (ClassCastException cce) { Log.error("ClassCastException: " + cce); Log.error("Field: " + field.getClass().getName() + " val: " + val); Log.error("Couldn't use setValue - trying setRawValue"); field.setRawValue(val); } finally { // StandardComponents.setFieldValidation(field, true); field.setFireChangeEventOnSetValue(true); } } else { if (showMissingData) { boolean fieldIsHidden = StandardComponents.isFieldHidden(field); if (!fieldIsHidden) { if (missingData.length() > 0) { missingData.append(", "); } if (fieldName.equals(mappedAttName)) { missingData.append(fieldName); } else { missingData.append(mappedAttName).append(" (mapped from ").append(fieldName).append(")"); } } } Log.warn("Field '" + mappedAttName + "' was not found"); } } // Now that all of the values are loaded, we can safely perform validation. DependencyComponent.clearValues(); validate(); } /** * Sets the field name to look for in the task data to confirm the operation was successful and to use as the object ID for any follow-on processing (defaults to 'obid'). * @param setting The field name */ public void setTaskDataKey(String setting) { taskDataKey = setting; if (setting == null) { StandardComponents.alert(FormMode.LIST, MsgLevel.ERROR, "setTaskDataKey was given a null argument"); taskDataKey = "obid"; } } /** * Retrieves the field name used as the object ID for task follow-on operations * @return */ public String getTaskDataKey() { return taskDataKey; } /** * Creates a task with a taskoutgroupname generated from the taskuri. This method will never re-use an existing instance of a task. * * @param task The TaskFactory enum entry of the task to be run * @return The configured task (also available via actionTask) */ public Task setupActionTask(TaskFactory task) { return setupActionTask(task, null, false); } /** * Creates a task with a taskoutgroupname generated from the taskuri. When the useCache is true, it will re-use an already configured task with the same * uri. * * @param task The TaskFactory enum entry of the task to be run * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via actionTask) */ public Task setupActionTask(TaskFactory task, boolean useCache) { return setupActionTask(task, null, useCache); } /** * Creates a task with a taskoutgroupname as specified (null means it will generate it from the taskuri). When the useCache is true, it will re-use an * already configured task with the same uri. * * @param task The TaskFactory enum entry of the task to be run * @param group The name of the group that will be returned by the task * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via actionTask) */ public Task setupActionTask(TaskFactory task, String group, boolean useCache) { mode = getMode(); String taskuri = task.getTaskUri(); if (taskuri == null) { throw new RuntimeException("StandardForm::setupActionTask - action task uri not specified"); } actionTask = Task.newTask(task, group, useCache); actionTaskGroup = getActionTask().getGroup(getActionTask().getTaskGroupOutName()); if (mode == FormMode.CREATE || mode == FormMode.EDIT || mode == FormMode.DELETE) { getActionTaskGroup().addStoreListener(new StoreListener() { @Override public void storeDataChanged(StoreEvent se) { //TODO: Can this be placed after postActionTaskProcessing for maximum mask coverage? clearBusy("ACTIONTASK"); postActionTaskProcessing(true); } }); } return getActionTask(); } /** * Sets the component that should receive the focus when the page is ready for input * @param c The Component that will receive the focus. Should be an input type Component */ public void setFocusField(Component c) { focusField = c; if (c instanceof Field) { currentFocusField = (Field) c; } } /** * Computes the Focus field by finding the field with the lowest tabindex or the first editable field on the page if no tabindex values are found */ public void setDefaultFocusField() { if (focusField == null) { Field firstEditable = null; Field lowestTabOrder = null; int minTabOrder = 999; // If the page forced a focusField, just leave if (focusField != null) { return; } // Scan through the fields looking for the field with the lowest Tab Order, or the first editable field and make that the focusField for (Field f : getBodyPanel().getFields()) { if (f.isEnabled() && !f.isReadOnly() && !(f instanceof HiddenField) && !(f instanceof CATHiddenField) && !(f instanceof LabelField)) { int to = f.getTabIndex(); if (to < minTabOrder) { minTabOrder = to; lowestTabOrder = f; Log.debug("Lowest Tab Index Field: " + lowestTabOrder.getId() + "(" + lowestTabOrder.getName() + ")"); } if (firstEditable == null) { firstEditable = f; Log.debug("First Editable Field: " + f.getId() + "(" + f.getName() + ")"); } if (lowestTabOrder != null) { focusField = lowestTabOrder; } else { focusField = firstEditable; } } } if (focusField != null) { Log.debug("Focus Field: " + ((Field) focusField).getId() + "(" + ((Field) focusField).getName() + ")"); } else { Log.debug("Focus Field: No available editable field found - no focus field set"); } } } /** * Gives focus to the field specified by setFocusField(). */ public void setDefaultFocus() { if (formType == FormType.FORM || formType == FormType.EMBEDDED) { getViewport().show(); if (focusField != null) { focusField.focus(); } } else if (formType == FormType.DIALOG) { getDialog().show(); if (focusField != null) { getDialog().setFocusWidget(focusField); getDialog().focus(); } } } /** * A method to return 'this' for event handlers (their 'this' is the handler itself). * @return The CATFormLayout object for the page. */ public CATFormLayout getSelf() { return this; } /** * Sets whether or not to close, ignoring any page dirty status. * @param forceClose True to force the page to close regardless of any dirty setting encountered. */ public void setForceClose(boolean forceClose) { this.forceClose = forceClose; } /** * @return the forceClose */ public boolean isForceClose() { return forceClose; } /** * Returns the EntryPointFactory used by this layout. * @return The EntryPointFactory object for this layout. */ public EntryPointFactory getEntryPoint() { return entryPoint; } /** * @param entryPoint the entryPoint to set */ public void setEntryPoint(EntryPointFactory entryPoint) { this.entryPoint = entryPoint; } /** * @return the width */ public int getWidth() { return width; } /** * @param width the width to set */ public void setWidth(int width) { this.width = width; } /** * @return the height */ public int getHeight() { return height; } /** * @param height the height to set */ public void setHeight(int height) { this.height = height; } /** * Adds a hidden field to the Form for transmission to the server during init/action tasks * * @param key The name of the field * @param value The value for the field */ public void addParam(String key, String value) { getParamMap().put(key, value); } /** * Returns a particular value from the paramMap * * @param key The name of the hidden param desired * @return The value of the hidden param */ public String getHiddenParam(String key) { return hiddenParamMap.get(key); } /** * Returns a particular value from the paramMap * * @param key The name of the entry desired * @return The value of the map entry * @deprecated Use getHiddenParam instead */ @Deprecated public String getParam(String key) { return hiddenParamMap.get(key); } /** * Sets the hidden param map - the list of hidden fields to create in the form for use in task parameters for the init and / or action tasks for the form * * @param paramMap the paramMap to set */ @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter") public void setHiddenParamMap(Map paramMap) { this.hiddenParamMap = paramMap; } /** * Sets the hidden param map - the list of hidden fields to create in the form for use in task parameters for the init and / or action tasks for the form * * @param paramMap the paramMap to set * @deprecated Use {@link #setHiddenParamMap}(Map<String, String> paramMap) instead */ @Deprecated @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter") public void setParamMap(Map paramMap) { this.hiddenParamMap = paramMap; } private void reportWindowSizes() { Log.trace("---------------------------------------------------------------"); Log.trace("----- window actuals:" + com.google.gwt.user.client.Window.getClientWidth() + "," + com.google.gwt.user.client.Window.getClientHeight()); Log.trace("----- window minimums: " + minWidth + "," + minHeight); if (container.isRendered()) { Log.trace("----- container actuals: " + container.getWidth() + "," + container.getHeight()); Log.trace("----- container minimums: " + minCWidth + "," + minCHeight); } if (getViewport().isRendered()) { Log.trace("----- viewport actuals: " + getViewport().getWidth() + "," + getViewport().getHeight()); } if (bodyPanel.isRendered()) { Log.trace("----- Body actuals: " + bodyPanel.getWidth() + "," + bodyPanel.getHeight()); } } /** * Prepares the layout for use by running all the standard behaviors not related to the page-defined data. * This is run just before the invocation of the page. */ public void finalizeLayout() { // Set up the Enter / Escape key button presses of OK / Cancel if (formType == FormType.DIALOG) { new KeyNav(getDialog()) { // Have the Enter key press the OK button @Override public void onEnter(ComponentEvent ce) { // If the form is not valid, don't click the button if (isValid()) { // If an Apply button is present, Enter will invoke it instead of OK. Button btn = getDialog().getButtonById(APPLY); if (btn != null && (!btn.isVisible() || !btn.isEnabled())) { btn = getDialog().getButtonById(Dialog.OK); } if (btn != null) { btn.fireEvent(Events.Select); } } else { Info.display("ERROR", "The form data is invalid. Correct the errors, then press OK"); } stopEventConditional(ce); } // Have the Escape key press the Cancel button @Override public void onEsc(ComponentEvent ce) { getDialog().getButtonById(Dialog.CANCEL).fireEvent(Events.Select); ce.stopEvent(); } }; } else if (formType == FormType.FORM) { new KeyNav(bodyPanel) { @Override public void onEnter(ComponentEvent ce) { stopEventConditional(ce); if (finishButton.isEnabled()) { finishButton.fireEvent(Events.Select); } } @Override public void onEsc(ComponentEvent ce) { //TODO: BEH: Add test for dirty form fields - if any found, prompt user to make sure they want to cancel cancelButton.fireEvent(Events.Select); } }; } else if (formType == FormType.EMBEDDED) { new KeyNav(bodyPanel) { //Prevent the audible "ding" which occurs for no apparent reason @Override public void onEnter(ComponentEvent ce) { stopEventConditional(ce); } }; } // This KeyNav picks up a Ctrl-Alt-D key press and toggles the debug button on and off new KeyNav(bodyPanel) { @Override public void onKeyPress(ComponentEvent ce) { super.onKeyPress(ce); if (ce.isControlKey() && ce.isAltKey()) { int code = ce.getKeyCode(); if (code == 68) { toggleDebug(); } } } }; // If formLabel is still blank, then there was no call to setFormLabel from the calling screen - use the entry point's default label if (formLabel.isEmpty()) { setFormLabel(getEntryPoint().getLabel()); } container.setStyleName("cat-top-container"); bodyPanel.setBorders(false); if (FormType.EMBEDDED != formType) { container.setLayout(new FormLayout()); container.add(bodyPanel, new FormData("100% 100%")); getViewport().add(container); getViewport().setScrollMode(Scroll.AUTO); container.setScrollMode(Scroll.NONE); } else { getViewport().setLayout(new FitLayout()); container.setLayout(new FormLayout()); container.add(bodyPanel, new FormData("100% 100%")); getViewport().add(container); getViewport().setScrollMode(Scroll.NONE); container.setScrollMode(Scroll.AUTO); } getViewport().layout(true); if (formType == FormType.FORM) { if (getEntryPoint().getHeight() != -1 && getEntryPoint().getWidth() != -1) { StandardComponents.setWindowSize(getEntryPoint().getHeight(), getEntryPoint().getWidth()); } Window.enableScrolling(false); getViewport().layout(true); RootPanel.get().add(getViewport()); container.getHeader().setTextStyle("cat-header-text"); } else if (formType == FormType.DIALOG) { getDialog().setSize(getEntryPoint().getWidth(), getEntryPoint().getHeight()); getDialog().setLayout(new FitLayout()); getDialog().add(container); container.getHeader().setTextStyle("cat-header-text"); } else if (FormType.EMBEDDED == formType) { RootPanel.get().add(getViewport()); } if (container.isRendered() && getViewport().isRendered()) { if (container.getWidth() < getViewport().getWidth()) { container.setWidth(getViewport().getWidth() - sbw); } if (container.getHeight() < getViewport().getHeight()) { container.setHeight(getViewport().getHeight() - sbh); } } reportWindowSizes(); setupFormValidation(); } /** * @return the page */ public StandardForm getPage() { return page; } /** * @param page the page to set */ public void setPage(StandardForm page) { this.page = page; } /** * Returns the string value used to invoke this layout (corresponds to the name value of EntryPointFactory) * @return The string value for the page being run. */ public String getApplication() { return application; } /** * Sets the string value used to invoke this layout (the name from the EntryPointFactory) * @param application The String to be used to invoke this layout. */ public void setApplication(String application) { this.application = application; } private void stopEventConditional(ComponentEvent ce) { if (!"TEXTAREA".equals(ce.getTarget().getTagName())) { ce.stopEvent(); } } /** * Returns the top level container that holds the form. This is the one and only child of the Viewport for the page * @return */ public ContentPanel getContainer() { return container; } /** * Returns the map object containing all the name value pairs to be put into hidden fields on the page. * @return A Map of string keys and string values. */ @SuppressWarnings("ReturnOfCollectionOrArrayField") protected Map getHiddenParamMap() { if (hiddenParamMap == null) { hiddenParamMap = new FastMap(); } return hiddenParamMap; } /** * Returns the map object containing all the name / value pairs to be put into hidden fields on the page * @return A map of string keys and string values holding the name / value pairs to be put into hidden fields on the page * @deprecated Use getHiddenParamMap instead */ @Deprecated @SuppressWarnings("ReturnOfCollectionOrArrayField") protected Map getParamMap() { if (hiddenParamMap == null) { hiddenParamMap = new FastMap(); } return hiddenParamMap; } /** * Creates the layout area with the page title and help icon * @param label The label to be used as the page title * @param helpID The help url to use under the help icon. The icon will not appear if helpUrl is null or empty */ public void createHeaderPanel(String label, final String helpID) { /* * // Set the Window title only if we are a FORM if (formType == FormType.FORM) { Window.setTitle(entryPoint.getLabel()); } else { * getDialog().setHeading(entryPoint.getLabel()); } // Vertical panel for adding Header and Help icon .. headerPanel = new LayoutContainer(); * ColumnLayout layout = new ColumnLayout(); headerPanel.setLayout(layout); headerPanel.setStyleName("cat-dialog-hdr"); * headerPanel.setId("cat-dialog-hdr"); * * // The left side sub-panel HorizontalPanel westpanel = new HorizontalPanel(); westpanel.setStyleName("cat-header-west"); hdrLabel = * StandardComponents.getCATHeaderLabel(label); hdrLabel.setStyleName("cat-header-label"); westpanel.add(hdrLabel); headerPanel.add(westpanel); * * // The right side sub-panel HorizontalPanel eastpanel = new HorizontalPanel(); eastpanel.setStyleName("cat-header-east"); ThemeSelector selector = * new ThemeSelector(); eastpanel.add(selector); String helpurl = webAppURL + "-WHC/index.jspx?id=" + helpID + "&action=show"; HTML rightWidget = * StandardComponents.getHelpIcon(helpurl); rightWidget.setStyleName("cat-helpicon"); eastpanel.add(rightWidget); headerPanel.add(eastpanel); * */ if (Window.getTitle().isEmpty()) { hdr = container.getHeader(); } else { hdr = getDialog().getHeader(); } // Set the Window title only if we are a FORM if (formType == FormType.FORM) { if (Window.getTitle().isEmpty()) { Window.setTitle(entryPoint.getLabel()); } } else if (formType == FormType.DIALOG) { // For Dialogs, use the Dialog's header and shut off the header we created hdr = getDialog().getHeader(); hdr.setVisible(true); } // Set the Window title only if we are a FORM // if (formType == FormType.FORM) { // Window.setTitle(getEntryPoint().getLabel()); // } else if (formType == FormType.DIALOG) { // getDialog().setHeading(getEntryPoint().getLabel()); // } //// container.layout(); // Header hdr = container.getHeader(); // // For Dialogs, use the Dialog's header and shut off the header we created // if (formType == FormType.DIALOG) { // hdr.setVisible(false); // hdr = getDialog().getHeader(); // } hdr.setText(getEntryPoint().getLabel()); // For Embedded forms, turn off the header if (formType == FormType.EMBEDDED) { hdr.setVisible(false); } // Only include the help button if a help ID was set or if the URL does not include a {0} string in it (meaning a fixed landing page) boolean showHelp = false; if (helpURL != null && !helpURL.isEmpty()) { if (helpURL.contains("{0}")) { if (helpID != null && !helpID.isEmpty()) { showHelp = true; } } else { showHelp = true; } } if (showHelp) { Button helpButton = new Button(); helpButton.setSize(StandardComponents.ICON_WIDTH, StandardComponents.WIDGET_HEIGHT); helpButton.addStyleName("helpbutton"); helpButton.addSelectionListener(new SelectionListener() { @Override public void componentSelected(ComponentEvent ce) { String url = helpURL; if (url.contains("{0}")) { url = url.replace("{0}", helpID); } Window.open(url, "_blank", ""); } }); hdr.addTool(helpButton); } } /** * Sets the object ID of the primary object's container. For create, this is where the object will be created. For edit, this is where the object is stored. * @param containerOid The object ID of the primary object's container or the container that launched this screen */ public void setContainerOid(String containerOid) { this.containerOid = containerOid; } /** * Creates the standard footer which includes the status area and the buttons to operate the page */ public void createFooter() { if (formType != FormType.DIALOG) { footer = new LayoutContainer(); footer.setLayout(new HBoxLayout()); getBodyPanel().setBottomComponent(footer); footer.setId("pageFooter"); } createButtonPanel(); createStatusArea(); if (formType != FormType.DIALOG) { footer.add(statusarea, new FormData("100% 26px")); footer.add(buttonPanel, new FormData("100% 26px")); } else { getDialog().getButtonBar().insert(statusarea, 0); } // Don't show the footer on EMBEDDED forms if (FormType.EMBEDDED == formType) { getFooter().setVisible(false); } } private void createButtonPanel() { final int buttonSpacing = 10; createButtons(); buttonPanel = new HorizontalPanel(); buttonPanel.setHeight("40px"); buttonPanel.addStyleName("cat-button-panel"); buttonPanel.setId("buttonPanel"); buttonPanel.setSpacing(buttonSpacing); buttonPanel.add(tools); buttonPanel.add(finishButton); buttonPanel.add(applyButton); buttonPanel.add(cancelButton); // Insert the debug button if (formType == FormType.DIALOG) { ButtonBar bar = getDialog().getButtonBar(); // The ButtonBar comes with OK/Cancel already - add in debug and apply bar.setHeight("28px"); // Since doing a add on the finish and cancel buttons will move them into the buttonPanel above, we need to move all buttons in the buttonPanel to the dialog's buttonBar. while (true) { if (buttonPanel.getItemCount() == 0) { break; } bar.add(buttonPanel.getItem(0)); } bar.insert(tools, 0); } assignButtonListeners(); } private void createButtons() { getFinishButton(); getApplyButton(); if (getMode() != FormMode.CREATE) { getApplyButton().setVisible(false); } getCancelButton(); DebugToolsMenu dtm = new DebugToolsMenu(); tools = dtm.getMenu(this); if (Log.isDebugEnabled()) { tools.setVisible(true); } else { tools.setVisible(false); } } private void assignButtonListeners() { if (FormType.EMBEDDED != formType) { disableActionButtons(); Button theOKButton = getFinishButton(); Button theApplyButton = getApplyButton(); Button theCancelButton = getCancelButton(); // For action type forms, set up the handlers on the buttons if (mode == FormMode.CREATE || mode == FormMode.EDIT || mode == FormMode.MULTIEDIT || mode == FormMode.DELETE) { if (getOkSelectionListener() == null) { // Use the DefaultOKSelectionListener setOkSelectionListener(new DefaultOKApplySelectionListener(SubmitMode.OK)); } getFinishButton().addSelectionListener(getOkSelectionListener()); if (getApplySelectionListener() == null) { // Use the DefaultOKSelectionListener setApplySelectionListener(new DefaultOKApplySelectionListener(SubmitMode.APPLY)); } theApplyButton.addSelectionListener(getApplySelectionListener()); // Set the cancelSelectionListener in case the developer registers a cancel task if (getCancelSelectionListener() == null) { setCancelSelectionListener(new DefaultCancelSelectionListener()); } theCancelButton.addSelectionListener(getCancelSelectionListener()); } else if (mode == FormMode.LIST || mode == FormMode.TREE) { // For LIST or TREE mode pages, hide the OK and APPLY buttons, change the CANCEL button to show "Close" theCancelButton = getCancelButton(); theCancelButton.setText("Close"); theApplyButton.hide(); theOKButton.hide(); if (getCancelSelectionListener() == null) { setCancelSelectionListener(new DefaultCancelSelectionListener()); } theCancelButton.addSelectionListener(getCancelSelectionListener()); ConfirmSelectListener cancelConfirmListener = new ConfirmSelectListener() { @Override public void handleEvent(BaseEvent be) { if (isDirty() && !isForceClose()) { MessageBox.confirm("Form Modified", "Do you want to discard the changes you've made?.", new Listener() { @Override public void handleEvent(MessageBoxEvent be) { if (StandardComponents.wasDialogYesOrOKClicked(be)) { confirm(); } } }); } else { confirm(); } } }; theCancelButton.addListener(FrameworkEvents.Confirm, cancelConfirmListener); } } } private LayoutContainer createStatusArea() { final int defaultSize = 50; statusarea = new LayoutContainer(); statusarea.setId("FormStatusArea"); statusarea.setLayout(new ColumnLayout()); statusarea.setWidth(defaultSize); statusarea.setHeight("25px;"); if (formType != FormType.DIALOG) { // FORM and EMBEDDED types HorizontalPanel btnpanel = getButtonPanel(); btnpanel.setId("buttonPanel"); } else { // DIALOG type ButtonBar bar = getDialog().getButtonBar(); bar.setId("buttonBar"); getDialog().setButtonAlign(Style.HorizontalAlignment.LEFT); bar.setEnableOverflow(false); } configureStatusArea(); statusarea.layout(true); statusarea.show(); return statusarea; } /** * Adds one button into the page's button bar. * The new button will be added just behind the debug button in all cases. * You shall not ever violate the OK/Apply/Cancel affinity. * So if you need to add multiple buttons, add them in right to left order. * addButton(3); addButton(2); addButton(1) will result in Debug;1;2;3;OK;Apply;Cancel * @param btn The Button to be added to the page's button bar. */ public void addButtonToBar(Button btn) { if (btn == null) { throw new RuntimeException("addButtonToBar: btn parameter is null"); } if (formType != FormType.DIALOG) { // FORM and EMBEDDED types HorizontalPanel btnpanel = getButtonPanel(); btnpanel.insert(btn, 1); } else { // DIALOG type ButtonBar bar = getDialog().getButtonBar(); bar.insert(btn, 1); } // updatePageButtonTabIndexes(); } /** * Update the tab indexes of the buttons in the button area */ // private void updatePageButtonTabIndexes() { // Container buttons; // if (formType != FormType.DIALOG) { // buttons = getButtonPanel(); // } else { // buttons = getDialog().getButtonBar(); // } // int ti = PAGE_BUTTON_STARTING_TABINDEX; // List btnlist = buttons.getItems(); // for (Component c : btnlist) { // if (c instanceof Button) { // c.setTabIndex(ti); // ti++; // } // } // } /** * @return the okSelectionListener */ public SelectionListener getOkSelectionListener() { return okSelectionListener; } /** * @param okSelectionListener the okSelectionListener to set */ public void setOkSelectionListener(SelectionListener okSelectionListener) { this.okSelectionListener = okSelectionListener; } /** * @return the applySelectionListener */ public SelectionListener getApplySelectionListener() { return applySelectionListener; } /** * @param applySelectionListener the applySelectionListener to set */ public void setApplySelectionListener(SelectionListener applySelectionListener) { this.applySelectionListener = applySelectionListener; } /** * @return the cancelSelectionListener */ public SelectionListener getCancelSelectionListener() { return cancelSelectionListener; } /** * @param cancelSelectionListener the cancelSelectionListener to set */ public void setCancelSelectionListener(SelectionListener cancelSelectionListener) { this.cancelSelectionListener = cancelSelectionListener; } private class DefaultOKApplySelectionListener extends SelectionListener { SubmitMode submitSetting = null; public DefaultOKApplySelectionListener(SubmitMode setting) { if (setting == null) { throw new RuntimeException("DefaultOKSelectionListenerII: No SubmitMode value supplied"); } submitSetting = setting; } @Override public void componentSelected(ComponentEvent ce) { setSubmitMode(submitSetting); ce.stopEvent(); Registry.register("forceClose", "true"); if (getSubmitMode() == SubmitMode.APPLY) { Log.debug("Form Apply Button clicked"); } else if (getSubmitMode() == SubmitMode.OK) { Log.debug("Form OK Button clicked"); } if (mode == FormMode.CREATE) { setBusy("ACTIONTASK", "Please wait...", "Creating..."); } else if (mode == FormMode.EDIT) { setBusy("ACTIONTASK", "Please wait...", "Updating..."); } else if (mode == FormMode.DELETE) { setBusy("ACTIONTASK", "Please wait...", "Deleting..."); } disableActionButtons(); createTaskParameters(getActionTask()); if (!actionTask.invoke()) { clearBusy("ACTIONTASK"); postActionTaskProcessing(false); } } } /** * Default Cancel Button Handler */ private class DefaultCancelSelectionListener extends SelectionListener { @Override public void componentSelected(ComponentEvent ce) { Object src = ce.getSource(); Log.debug("cancel button ::: Form Type:" + getFormType() + "Form Name:" + getFormName()); Log.debug("cancel button:" + src.getClass().getName()); Log.debug("StandardComponents.cancelButton.addSelectionListener: Clicked"); Log.debug("close dialog"); if (!isForceClose() && isDirty()) { if (isDirty() && getCancelTask() != null) { runCancelTask(); } } if (getFormType() == FormType.DIALOG) { getDialog().hide(); } else { Log.debug("close app"); StandardComponents.closeApplication(); } } } /** * Executes the Cancel Task for the page */ public void runCancelTask() { setBusy("CANCELTASK", "Please Wait", "Performing Server Cleanup for Cancel"); Log.debug("in cancel code"); if (isDirty()) { Log.debug("dirty: " + isDirty()); createTaskParameters(getCancelTask(), false); if (!cancelTask.invoke()) { clearBusy("CANCELTASK"); } } } /** * This method processes the Action Task results. * It is either called by the completion of the Action Task or by the componentSelected event handler if no task is run * It processes any ReturnGroupHandler data return, then * @param ranTask */ private void postActionTaskProcessing(boolean ranTask) { createdObjectOID = ""; String folderOID = ""; IEElement md; Log.trace("formActionTask.storeDataChanged: Enter"); // Confirm that the if (ranTask && assertGroupHasData(getActionTaskGroup(), getTaskDataKey(), "Did not find an object in the task results - does it already exist?")) { // If we get here, we know the group is ok, there is at least one element, and obid has a value so we can skip the checks, reducing the lines of code needed md = getActionTaskGroup().getElementAt(0); // get the obid of the newly created object createdObjectOID = (String) md.getValue("obid"); // Generate a folderOID by using either the returned parentFolder attribute in the data, then the openerOID, then the containerOID folderOID = (String) md.getValue("parentFolder"); if (folderOID == null || folderOID.isEmpty()) { // If they haven't supplied a specific parent, use the openerOid, then the containerOid as the target. folderOID = Registry.get("openerOid"); if (folderOID == null || folderOID.isEmpty()) { folderOID = Registry.get("containerOid"); } } } if (returnGroupHandler != null) { if (returnMode == null && !actionTask.isNullTask()) { // Default returnMode and the action task was not the NULL task, return the task group returnGroupHandler.sendReturnGroup(getActionTaskGroup()); } else if (returnMode == null && getActionTask().isNullTask()) { // Default returnMode and the action task WAS the NULL task, return the form data IEGroup rg = createReturnGroup(false); returnGroupHandler.sendReturnGroup(rg); } else if (returnMode == DataReturnMode.TASK) { // Page wants the action task data returned returnGroupHandler.sendReturnGroup(getActionTaskGroup()); } else if (returnMode == DataReturnMode.FORM) { // Page wants the form data returned IEGroup rg = createReturnGroup(false); returnGroupHandler.sendReturnGroup(rg); } } if (folderOID != null && folderOID.contains("wt.folder.SubFolder")) { handleFolderCompletion(getPage(), getActionTaskGroup(), folderOID, getSubmitMode()); } else { if (folderOID != null && mode == FormMode.DELETE) { handleCompletion(getPage(), getActionTaskGroup(), folderOID, getSubmitMode()); } else { handleCompletion(getPage(), getActionTaskGroup(), createdObjectOID, getSubmitMode()); } } if (getSubmitMode() == SubmitMode.APPLY) { handleApply(); } Log.trace("formActionTask.storeDataChanged: Exit"); } /** * Deals with the Apply Button behavior (leave all the fields alone so the user can make a single change and Apply again) * or reset all the fields to their original values so the page starts over. */ public void handleApply() { // if this is an APPLY, then reset the field values so dirty will be based on the last apply // the OK and APPLY buttons can't be enabled after an APPLY until a field is dirty // if clearOnApply is set to true, then all the fields' values will be reset to original values if created successfully // if clearOnApply is set to false, then all the fields' values will be reset to latest values if (clearOnApply) { // If clearOnApply and the task was NOT successful, then we purposefully don't do a reset. // However, we also do NOT want to do a resetOriginalValues()! if (createdObjectOID != null && !createdObjectOID.isEmpty()) { reset(); // reset() kills the hidden fields so we must set them up again // (similar to what invoke() does after IT calls reset()) setupHiddenFields(); } } else { resetOriginalValues(); } if (formType == FormType.FORM || formType == FormType.EMBEDDED) { if (focusField != null) { focusField.focus(); } } else if (formType == FormType.DIALOG) { if (focusField != null) { getDialog().setFocusWidget(focusField); getDialog().focus(); } } validate(); } /** * sets clearOnApply. If clearOnApply is true then all the fields' values will be reset to original values if created successfully when apply button clicked * if clearOnApply is set to false, then all the fields' values will be reset to latest values * * @param flag */ public void setClearOnApply(boolean flag) { clearOnApply = flag; } /** * @return the returnGroupHandler */ public ReturnGroupHandler getReturnGroupHandler() { return returnGroupHandler; } /** * @param returnGroupHandler the returnGroupHandler to set */ public void setReturnGroupHandler(ReturnGroupHandler returnGroupHandler) { this.returnGroupHandler = returnGroupHandler; } /** * Sets the source of data for a ReturnGroupHandler - FORM or TASK * @param setting A DataReturnMode enum value */ public void setReturnMode(DataReturnMode setting) { returnMode = setting; if (setting == null) { StandardComponents.alert(FormMode.LIST, MsgLevel.ERROR, "setReturnMode was given a null argument"); returnMode = DataReturnMode.FORM; } } /** * Returns the source of data for a ReturnGroupHandler - FORM or TASK * @return A DataReturnMode enum value */ public DataReturnMode getReturnMode() { return returnMode; } /** * creates a group out of all fields in the form, including the hidden ones into the specified task. Data structure objects (IEGroup and TreeStore) will be added directly to the group * * @param hiddenOnly When true, only the fields in the hidden panel will be added (used for the initialization task) * @return An IEGroup with a single element, containing the values of all fields scraped from the form */ public IEGroup createReturnGroup(boolean hiddenOnly) { //TODO: BEH: Why isn't this code used ultimately to generate the task params? This data could be captured and then used to generate the task params. Map formToGroupMap = getActionTask().getFormToTaskMap(); IEGroup group = new IEGroup("results"); IEElement el = new IEElement(); // Map taskParamMap = task.getParamMap(); List> fields = getBodyPanel().getFields(); String pid; for (Field field : fields) { // Don't generate a param for a field without a name String name = field.getName(); if (name == null || name.isEmpty()) { Log.debug("Skipping a field because it has no name. Class: " + field.getClass().getName() + " Value: " + field.getRawValue()); continue; } if (name.equals(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME)) { String val = (String) field.getValue(); if (val == null) { val = ""; } el.addAtt(new IEAtt(TableGroupReturnGroupListener.ELEMENT_STATUS_ATT_NAME, val)); continue; } String safeName = name; if (name.contains(".")) { Log.debug("Replacing dots with underscores for field param name: " + name); safeName = safeName.replace(".", "_"); } // Pull the attribute from the element corresponding to the field name // Check the formMap to see if we need to pull a different attribute for this field String mapname = formToGroupMap.get(safeName); if (mapname == null) { mapname = safeName; } // Check to see if we are doing only the fields in the hidden panel. Continue out of the loop //if we are and the field is not parented by the panel with the id 'hidden' if (hiddenOnly) { if (!StandardComponents.isFieldHidden(field)) { Log.debug("Skipping field: " + name + " because it is not in the hidden panel"); continue; } // Widget parent = field.getParent(); // if (parent instanceof LayoutContainer) { // LayoutContainer lc = (LayoutContainer) parent; // pid = lc.getId(); // if (pid != null && !pid.equals("hidden")) { // Log.debug("Skipping field: " + name + " because it is not in the hidden panel"); // continue; // } // } } try { if (field instanceof ComboBox) { // For combo boxes, the value in the map can be colon-separated. So it could be 'a', 'a:b' or ':b'. // 'a' indicates that the internal value of the combo box should go to the attribute called a. The display value is not collected. // 'a:b' indicates that the internal value of the combo box should go to a and the display value should go to b. //':b' indicates that the display value of the combo box should go to b. The internal value is not collected. ComboBox cb = (ComboBox) field; ModelData md = cb.getValue(); String value = ""; String dvalue = ""; if (md != null) { value = md.get(cb.getValueField()); dvalue = md.get(cb.getDisplayField()); } String[] combomap = mapname.split(":"); if (combomap.length == 1) { el.addAtt(new IEAtt(combomap[0], value)); } else { if (combomap[0] == null || combomap[0].isEmpty()) { el.addAtt(new IEAtt(combomap[1], dvalue)); } else { el.addAtt(new IEAtt(combomap[0], value)); el.addAtt(new IEAtt(combomap[1], dvalue)); } } } else if (field instanceof AdapterField) { Object result = null; AdapterField af = (AdapterField) field; String widget = field.getData("type"); if (widget != null) { if (widget.equals("tree")) { Object x = af.getData("widget"); if (x != null && (x instanceof StandardTree)) { StandardTree stdTree = (StandardTree) af.getData("widget"); result = stdTree.getTreeStore(); } else { Log.error("type was set to tree, but the widget was either null or was not a TreeGrid - AdapterField: " + field.getName()); } } else if (widget.equals("table")) { Object x = af.getData("widget"); if (x != null && (x instanceof StandardTable)) { StandardTable stdTable = (StandardTable) af.getData("widget"); IEGroup g = ((IEGroup) stdTable.getGrid().getStore()).deepClone(); result = g; } else { Log.error("type was set to table, but the widget was either null or was not a Grid - AdapterField: " + field.getName()); } } else { Log.error("An ApapterField type was not valid: " + widget + " AdapterField: " + field.getName()); } } else { Log.error("An AdapterField type was null: " + field.getName()); } el.addAtt(new IEAtt(mapname, result)); } else { String val = StandardComponents.getFieldValue(field); Log.trace("Adding Att to return group - " + mapname + "=" + val); el.addAtt(new IEAtt(mapname, val)); } } catch (Exception e) { Log.error(" field has no value set, successfully caught exception " + e.toString()); } } group.addElement(el); return group; } /** * Method to handle the completion of the primary functions of a UI page - create, edit, editmulti. Consults the Action maps to see what to do with the * window, the parent window and whether to use a popup to display anything. * * @param page The StandardForm launching this method * @param group The group with the data from the server operation * @param obid The object ID of the object to which we will forward * @param submitMode Indicates whether this is an OK or an Apply press (OK will dismiss the window, but Apply will force a full revalidation and leave all * fields in place) */ public void handleCompletion(StandardForm page, IEGroup group, String obid, SubmitMode submitMode) { // TODO: This needs to be moved to a Windchill-specific CompletionHandler class //truncate the obid in all cases - the properties pages often puke on the ufids. String theobid = StandardComponents.getShortOid(obid); MsgLevel level = group.getSuccess(); // // If we ran a server side task, we still need to check to see if we have a return group handler so we can interact with a parent table or tree // if (getReturnGroupHandler() != null && returnMode==DataReturnMode.TASK) { // getReturnGroupHandler().sendReturnGroup(group); // } WindowMode windowAction; if (forcedWindowAction == null) { windowAction = StandardComponents.getWindowAction(page.form.getMode(), level, submitMode); } else { windowAction = forcedWindowAction; } ParentMode parentAction; if (forcedParentAction == null) { parentAction = StandardComponents.getParentAction(page.form.getMode(), level); } else { parentAction = forcedParentAction; } if (page.formType == FormType.FORM) { if (parentAction == ParentMode.FORWARD) { if (theobid == null || theobid.isEmpty()) { throw new RuntimeException("Cannot forward to the new object - the obid parameter was not supplied"); } //If the obid is a Version Reference (starts with VR), and the returning oid is equal to the opening oid, we need to refresh //as the browser will ignore a request to forward to the same URL if (theobid.startsWith("VR") && theobid.equals(Registry.get("oid"))) { StandardComponents.refreshParent(); } String url = group.getElementAt(0).getValue("detailsUrl"); if (url != null && !url.isEmpty()) { Log.info("Forward parent to URL - " + url); StandardComponents.forwardParent(url); } else { //R10: http://acm.corp.cat.com/acm/app/#ptc1/tcomp/infoPage?oid=OR%3Awt.part.WTPart%3A1505992&u8=1 Log.info("Forward parent to R10 URL -" + "/app/#ptc1/tcomp/infoPage?oid=" + theobid + "&u8=1"); StandardComponents.forwardParent("/app/#ptc1/tcomp/infoPage?oid=" + theobid + "&u8=1"); } } else if (parentAction == ParentMode.TOFOLDER) { if (theobid == null || theobid.isEmpty()) { throw new RuntimeException("Cannot forward to parent folder - containerOid was not specified"); } Log.info("Forward parent to R10 Folder URL -" + "/app/#ptc1/tcomp/infoPage?oid=" + theobid + "&u8=1"); StandardComponents.forwardParent("/app/#ptc1/tcomp/infoPage?oid=" + theobid + "&u8=1"); } else if (parentAction == ParentMode.REFRESH) { StandardComponents.refreshParent(); } } // TODO: If/when a console object becomes available, feed all messages to that console // Produce a message box whenever the action map indicates POPUP_NEEDED PopupMode popupAction = StandardComponents.getPopupAction(page.form.getMode(), level); if (popupAction == PopupMode.NEEDED) { StandardComponents.alert(page, level, group.getMessagesAsHtml()); } else { // If we aren't doing a popup, we may need to close the window here if (windowAction == WindowMode.CLOSE) { if (page.formType == FormType.FORM) { StandardComponents.closeApplication(); } else if (page.formType == FormType.DIALOG) { page.form.getDialog().hide(); } } } } /** * Method to handle the completion of the primary functions of a UI page launched from a Folder panel - create, edit, editmulti. Consults the Action maps to * see what to do with the window, the parent window and whether to use a popup to display anything. * * @param page The StandardForm object calling this method * @param group The group with the data from the server operation * @param obid The name of the attribute in the group's data with the object's retrieval ID (needed to create parent forwarding URLs) * @param submitMode Indicates whether this is an OK or an Apply press (OK will dismiss the window, but Apply will force a full revalidation and leave all * fields in place) */ public void handleFolderCompletion(StandardForm page, IEGroup group, String obid, SubmitMode submitMode) { //TODO: This needs to be moved to a Windchill-specific CompletionHandler class MsgLevel level = group.getSuccess(); // // If we ran a server side task, we still need to check to see if we have a return group handler so we can interact with a parent table or tree // if (getReturnGroupHandler() != null && returnMode==DataReturnMode.TASK) { // getReturnGroupHandler().sendReturnGroup(group); // } WindowMode windowAction; if (forcedWindowAction == null) { windowAction = StandardComponents.getWindowAction(page.form.getMode(), level, submitMode); } else { windowAction = forcedWindowAction; } ParentMode parentAction = null; if (forcedParentAction == null) { parentAction = StandardComponents.getParentAction(page.form.getMode(), level); } else { parentAction = forcedParentAction; } // Only run these behaviors for a plain old popup if (page.formType == FormType.FORM) { if (obid == null || obid.isEmpty()) { throw new RuntimeException("Cannot refresh / forward to the parent folder - the 'obid' parameter was not supplied"); } // The obid value will have been generated using the first non-null value of a 'parentFolder' attribute in the data, the openerOid parameter, or the containerOid parameter String theOpener = Registry.get("openerOid"); if (obid.equals(theOpener)) { StandardComponents.refreshParent(); Log.info("Parent refreshed"); } else { StandardComponents.forwardParent("/app/#ptc1/tcomp/infoPage?oid=" + obid + "&u8=1"); Log.info("Forward parent to URL -" + "/app/#ptc1/tcomp/infoPage?oid=" + obid + "&u8=1"); } } // TODO: If/when a console object becomes available, feed all messages to that console // Produce a message box whenever the action map indicates POPUP_NEEDED PopupMode popupAction = StandardComponents.getPopupAction(page.form.getMode(), level); if (popupAction == PopupMode.NEEDED) { StandardComponents.alert(page, level, group.getMessagesAsHtml()); } else { // If we aren't doing a popup, we may need to close the window here if (windowAction == WindowMode.CLOSE) { if (page.formType == FormType.FORM) { StandardComponents.closeApplication(); } else { page.form.getDialog().hide(); } } } } /** * @return the windowAction */ public WindowMode getWindowAction() { return forcedWindowAction; } /** * @param windowAction the windowAction to set */ public void setWindowAction(WindowMode windowAction) { forcedWindowAction = windowAction; } /** * @return the parentAction */ public ParentMode getParentAction() { return forcedParentAction; } /** * @param parentAction the parentAction to set */ public void setParentAction(ParentMode parentAction) { forcedParentAction = parentAction; } /** * Makes sure the group has at least one record and the specified attribute contains a non-null, non-empty value * * @param group The IEGroup object containing the task result * @param attrname The name of the expected attribute * @param alertmsg The message desired (will create a default "No data returned from the server call" if this value is null or empty * @return true if all tests pass. Otherwise, closeApplication will be called so the routine will never return. */ public static boolean assertGroupHasData(IEGroup group, String attrname, String alertmsg) { // Generate a default message String thealertmsg = alertmsg; if (thealertmsg == null || thealertmsg.isEmpty()) { thealertmsg = "No data returned from the server call"; } // The group we got was null if (group == null) { Log.error("assertGroupHasData: The group specified is null"); StandardComponents.alert(FormMode.ASSERT_EXIST, MsgLevel.FATAL, thealertmsg); return false; } // The group we got had no elements if (group.getElementCount() == 0 && group.getMessages().isEmpty()) { Log.warn("assertGroupHasData: The group has no elements and there are no other messages - show the default message"); StandardComponents.alert(FormMode.ASSERT_EXIST, MsgLevel.ERROR, thealertmsg); return false; } if (group.getElementCount() == 0) { Log.debug("assertGroupHasData: The group has no elements but there are other messages present"); return false; } // The attribute we got was null or empty if (attrname == null || attrname.isEmpty()) { group.addMessage("FATAL|" + thealertmsg); group.addMessage("FATAL|No attribute name for validating the group data was supplied"); } else { // The value of the attribute in the first element of the group is null or empty IEElement el = group.getElementAt(0); String val = (String) el.getValue(attrname); if (val == null || val.isEmpty()) { group.addMessage("FATAL|" + thealertmsg); group.addMessage("FATAL|The attribute '" + attrname + "' did not appear in the group data"); } } StandardComponents.alert(FormMode.ASSERT_EXIST, group); return true; } /** * Makes sure the group has at least one record and the specified attribute contains a non-null, non-empty value * * @param group The IEGroup object containing the task result * @return true if all tests pass. Otherwise, closeApplication will be called so the routine will never return. */ public static boolean assertGroupHasNoErrors(IEGroup group) { // The group we got was null if (group == null) { Log.error("assertGroupHasNoErrors: The group specified is null"); StandardComponents.alert(FormMode.ASSERT_EXIST, MsgLevel.FATAL, "assertGroupHasNoErrors: The group specified is null"); return false; } StandardComponents.alert(FormMode.ASSERT_EXIST, group); return true; } /** * Makes sure the group has no records * * @param group The IEGroup object containing the task result * @param alertmsg The message desired (will create a default "The object specified already exists" if this value is null or empty) * @return true if all tests pass. Otherwise, closeApplication will be called so the routine will never return. */ public static boolean assertGroupHasNoData(IEGroup group, String alertmsg) { // Generate a default message String thealertmsg = alertmsg; if (thealertmsg == null || thealertmsg.isEmpty()) { thealertmsg = "The object specified already exists"; } // The group we got was null if (group == null) { Log.error("assertGroupHasData: The group specified is null"); StandardComponents.alert(FormMode.ASSERT_NOT_EXIST, MsgLevel.FATAL, thealertmsg); return false; } // The group already had FATAL errors if (group.getSuccess() == MsgLevel.FATAL) { StandardComponents.alert(FormMode.ASSERT_NOT_EXIST, MsgLevel.FATAL, group.getMessagesAsHtml()); return false; } if (group.getElementCount() == 0) { return true; } else { return false; } } /** * Creates a task with a taskoutgroupname generated from the taskuri. This method will never re-use an existing instance of a task. * * @param task The TaskFactory enum entry of the task to be run * @return The configured task (also available via actionTask) */ public Task setupCancelTask(TaskFactory task) { return setupCancelTask(task, null, false); } /** * Creates a task with a taskoutgroupname generated from the taskuri. When the useCache is true, it will re-use an already configured task with the same * uri. * * @param task The TaskFactory enum entry of the task to be run * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via actionTask) */ public Task setupCancelTask(TaskFactory task, boolean useCache) { return setupCancelTask(task, null, useCache); } /** * Creates a task with a taskoutgroupname as specified (null means it will generate it from the taskuri). When the useCache is true, it will re-use an * already configured task with the same uri. * * @param task The TaskFactory enum entry of the task to be run * @param group The name of the group that will be returned by the task * @param useCache true to re-use an already existing copy of the task, false to create a new task * @return The configured task (also available via actionTask) */ public Task setupCancelTask(TaskFactory task, String group, boolean useCache) { String taskuri = task.getTaskUri(); if (taskuri == null) { StandardComponents.alert(this, MsgLevel.FATAL, "Cancel task uri was not specified"); return null; } cancelTask = Task.newTask(task, group, useCache); cancelTaskGroup = cancelTask.getGroup(cancelTask.getTaskGroupOutName()); getCancelTaskGroup().addStoreListener(new StoreListener() { @Override public void storeDataChanged(StoreEvent se) { Log.trace("InitializerTask.storeDataChanged: Enter"); if (assertGroupHasData(getCancelTaskGroup(), getTaskDataKey(), "Initialization data could not be retrieved from task: " + getCancelTask().getUri())) { setFormFieldValues(getCancelTaskGroup()); } StandardComponents.alert(FormMode.ASSERT_EXIST, getCancelTaskGroup()); isValid(); clearBusy("CANCELTASK"); } }); return cancelTask; } /** * Creates focus listeners on all named fields on the page so we can show the tooltip in the button bar when the current field has an error. */ public void setupFocusHandler() { for (Field f : getBodyPanel().getFields()) { //TODO: Take this out if validation doesn't work when they tab out of the field anyway f.setValidateOnBlur(false); // add focus listener Field work; if (f.getName() != null && !f.getName().isEmpty()) { work = f; if (f instanceof CATMultiField) { work = ((CATMultiField) f).getTextField(); } work.addListener(Events.Focus, new Listener() { @Override public void handleEvent(BaseEvent be) { if (statusarea == null) { return; } Field f = (Field) be.getSource(); currentFocusField = f; showErrorInStatusArea(f); } }); work.addListener(Events.Blur, new Listener() { @Override public void handleEvent(BaseEvent be) { if (statusarea == null) { return; } Field f = (Field) be.getSource(); // currentFocusField = null; // showErrorInStatusArea(f); } }); work.addListener(Events.Valid, new Listener() { @Override public void handleEvent(BaseEvent be) { if (statusarea == null) { return; } Field f = (Field) be.getSource(); if (f == currentFocusField) { showErrorInStatusArea(f); } } }); work.addListener(Events.Invalid, new Listener() { @Override public void handleEvent(BaseEvent be) { if (statusarea == null) { return; } Field f = (Field) be.getSource(); if (f == currentFocusField) { showErrorInStatusArea(f); } } }); } } } /** * @return the sendOnlyDirtyOnEdit */ public boolean isSendOnlyDirtyOnEdit() { return sendOnlyDirtyOnEdit; } /** * @param sendOnlyDirtyOnEdit the sendOnlyDirtyOnEdit to set */ public void setSendOnlyDirtyOnEdit(boolean sendOnlyDirtyOnEdit) { this.sendOnlyDirtyOnEdit = sendOnlyDirtyOnEdit; } /** * creates task parameters for all fields in the form, including the hidden ones into the specified task. The parameters will be generated according to the * paramMode setting on the task (name=value or field=name=value) * * @param task The task to which the parameters will be added. */ public void createTaskParameters(Task task) { createTaskParameters(task, false); } /** * creates task parameters for all fields in the form, including the hidden ones into the specified task. The parameters will be generated according to the * paramMode setting on the task (name=value or field=name=value) * * @param task The task to which the parameters will be added. * @param hiddenOnly When true, only the fields in the hidden panel will be added (used for the initialization task) */ public void createTaskParameters(Task task, boolean hiddenOnly) { // Why isn't createReturnGroup used to generate the needed data instead of having separate code? if (task == null) { return; } // If we are configured to only send down changed fields, then we must clear out all task // parameter here. Here is the scenario this deals with: // 1. Bring up the dialog // 2. Edit field A // 3. Click OK // 4. You get some error back from the task. // 5. Undo your edit to field A and edit field B instead. // 6. Click OK // 7. Without this change, field A will get sent to the task since it was // added as a task param in step #3 and nobody has cleared it out again. if (sendOnlyDirtyOnEdit) { task.resetParams(); } Map taskParamMap = task.getFormToTaskMap(); List> fields = getBodyPanel().getFields(); for (Field field : fields) { // Don't generate a param for a field without a name String name = field.getName(); if (name == null || name.isEmpty()) { Log.debug("Skipping a field because it has no name. Class: " + field.getClass().getName() + " Value: " + field.getRawValue()); continue; } String safeName = name; if (name.contains(".")) { Log.debug("Replacing dots with underscores for field param name: " + name); safeName = safeName.replace(".", "_"); } // Check to see if we are doing only the fields in the hidden panel. Continue out of the loop //if we are and the field is not parented by the panel with the id 'hidden' boolean fieldIsHidden = StandardComponents.isFieldHidden(field); if (hiddenOnly && !fieldIsHidden) { Log.debug("Skipping field: " + name + " because it is not in the hidden panel"); continue; } if (!fieldIsHidden) { // Hidden field are always sent if (getMode() == FormMode.EDIT || getMode() == FormMode.MULTIEDIT) { // This only applies to Edit or Multi Edit pages if (sendOnlyDirtyOnEdit && !field.isDirty()) { // We skip if we are sending only dirty fields and the field is not dirty Log.debug("Field skipped due to edit settings: fieldIsHidden: " + fieldIsHidden + " dirty: " + field.isDirty() + " sendOnlyDirtyOnEdit: " + isSendOnlyDirtyOnEdit() + " form mode: " + getMode()); continue; } } } // Check the taskParamMap to see if we need to put this field value into a different parameter name String mapname = taskParamMap.get(safeName); if (mapname == null) { mapname = safeName; } String val = StandardComponents.getFieldValue(field); if (task.getParamMode() == ParamMode.DIRECT) { task.addTaskParam(mapname, val); //if(!mapname.equals(safeName)) // task.addTaskParam(safeName, val);//add task parameters for both mapname and safeName as numerous tasks are associated to the same widget and parameter keys differ } else { task.addTaskParam("field", mapname + "=" + val); //if(!mapname.equals(safeName)) // task.addTaskParam("field", safeName + "=" + val);//add task parameters for both mapname and safeName as numerous tasks are associated to the same widget } } } /** * Calls showErrorInStatusArea using the last field to accept focus. */ public void showErrorForCurrentFocusField() { if (currentFocusField == null) { return; } showErrorInStatusArea(currentFocusField); } /** * Shows the field label, the red ball, and the tooltip when there is a tooltip assigned to the field. * The data is shown in the left side of the button area on forms and dialogs. * @param f */ public void showErrorInStatusArea(Field f) { if (f.getName() != null && !f.getName().isEmpty() && f.getToolTip() != null && f.getToolTip().isEnabled()) { //statusarea.setVisible(true); statusAreaImage.setVisible(true); statusAreaMessage.setVisible(true); String flabel = f.getFieldLabel(); String fmsg = f.getToolTip().getToolTipConfig().getText(); String ftip = fmsg; if (!fmsg.startsWith(flabel)) { // Don't display the label field if it matches the start of the message field ftip = flabel + ": " + fmsg; } statusAreaMessage.setValue("  " + ftip); statusAreaImage.setTitle((String) statusAreaMessage.getValue()); } else { //statusarea.setVisible(false); statusAreaImage.setVisible(false); statusAreaMessage.setVisible(false); } // getBodyPanel().layout(true); int newwidth = calculateStatusareaWidth(); statusarea.setSize(newwidth, -1); statusarea.add(statusAreaImgAdapter, new ColumnData(StandardComponents.ICON_WIDTH)); statusarea.add(statusAreaMessage, new ColumnData(newwidth - StandardComponents.ICON_WIDTH)); statusarea.layout(true); if (formType == FormType.DIALOG) { // Nothing right now } else { getButtonPanel().setPosition(newwidth, 0); } // if (footer != null) { // footer.layout(); // } } private void configureStatusArea() { final int saheight = 27; if (statusAreaImage == null) { statusAreaImage = new Image("/Windchill/com/cat/gwt/org.cat.Main/images/windchill/form/exclamation.gif"); } if (statusAreaImgAdapter == null) { statusAreaImgAdapter = new AdapterField(statusAreaImage); statusAreaImgAdapter.setSize(StandardComponents.ICON_WIDTH, StandardComponents.WIDGET_HEIGHT); statusAreaImgAdapter.setId("statusAreaImgAdapter"); } if (statusAreaMessage == null) { statusAreaMessage = CATLabelField.newCATLabelField(this, "", ""); statusAreaMessage.setLabelSeparator(""); statusAreaMessage.setHideLabel(true); statusAreaMessage.setId("statusAreaMessage"); statusAreaMessage.setSize(300, saheight); } } private int calculateStatusareaWidth() { final int defaultWidth = 300; int result = defaultWidth; if (statusarea.isRendered()) { if (formType == FormType.DIALOG) { result = getDialog().getOffsetWidth() - getButtonBarWidth(defaultWidth) - 10 * (getDialog().getButtonBar().getItemCount() - 1); } else { result = getViewport().getOffsetWidth() - getButtonPanel().getOffsetWidth(); } } if (result < 0) { result = 0; } return result; } private int getButtonBarWidth(int defaultWidth) { int first = -1; int last = -1; // Find the first and last visible buttons in the button bar ButtonBar bar = getDialog().getButtonBar(); for (int i = 1; i < bar.getItemCount(); i++) { // Only look at buttons if (bar.getItem(i) instanceof Button) { // Only look at visible buttons if (bar.getItem(i).isVisible()) { // If we haven't already set first, set it with this visible button's position if (first == -1) { first = i; } // Always set the last to this visible button's position last = i; } } } // blow up if we failed to find either the first or last visible button (should never happen unless someone turns off all the buttons) if (first == -1 || last == -1) { // throw new RuntimeException("Could not find the first and last buttons in the page's button bar!"); return defaultWidth; } // get the rightmost position by adding the left and width of the last visible button, then subtract the left of the leftmost visible button int thewidth = (bar.getItem(last).getAbsoluteLeft() + bar.getItem(last).getOffsetWidth()) - bar.getItem(first).getAbsoluteLeft(); return thewidth; } /** * Returns the oid value (used to dispatch the page). * For Create screens, this will be the container object into which the object should be stored. * For Other screens, this will be a pointer to the object itself. * @return The String value of the object id used to dispatch the page */ public String getOid() { return oid; } /** * Sets the primary object id for the page execution. * For create pages, this should be the container into which the new object will be stored. * For all other pages, this should be the object being manipulated. * @param oid The object identifier for the object being manipulated. */ public void setOid(String oid) { this.oid = oid; } /** * Creates the standard Cancel button for the page */ protected void createCancelButton() { // Cancel button cancelButton = StandardComponents.createCancelButton(); } /** * Creates the standard OK button for the page */ protected void createFinishButton() { // OK button finishButton = StandardComponents.createOKButton(); finishButton.setType("submit"); } private MessageBox getWaitBox() { return waitBox; } /** * Creates the standard Apply button for Create pages */ protected void createApplyButton() { // Apply button applyButton = StandardComponents.createApplyButton(); applyButton.setType("submit"); applyButton.setItemId(APPLY); // so onEnter() can find it } private void setWaitBox(MessageBox waitBox) { this.waitBox = waitBox; waitBox.setModal(true); } /** * Makes the webAppURL for the application available to the form. * @param webAppURL the string representation of the webAppURL (like http://acm.corp.cat.com/Windchill) */ public void setWebAppURL(String webAppURL) { this.webAppURL = webAppURL; } /** * Returns the Finish (OK) Button for the form * The button will be created if necessary * @return The OK Button for the form */ public Button getFinishButton() { if (formType != FormType.DIALOG) { if (finishButton == null) { createFinishButton(); } } else { finishButton = getDialog().getButtonById(Dialog.OK); } return finishButton; } /** * Returns the Finish (OK) Button for the form * The button will be created if necessary * @return The OK Button for the form */ public Button getApplyButton() { if (applyButton == null) { createApplyButton(); } return applyButton; } /** * Returns the Finish (OK) Button for the form * The button will be created if necessary * @return The OK Button for the form */ public Button getCancelButton() { if (formType != FormType.DIALOG) { if (cancelButton == null) { createCancelButton(); } } else { cancelButton = getDialog().getButtonById(Dialog.CANCEL); } return cancelButton; } /** * Retrieves the HorizontalPanel object that holds all buttons at the bottom of the page. * This is only valid for non-DIALOG pages. * Use getDialog().getButtonBar for the equivalent functionality for a DIALOG page. * @return */ public HorizontalPanel getButtonPanel() { return buttonPanel; } /** * Retrieves the actual form area of the page (where all the page-specific content resides). * @return The CATFormPanel that is the form area for the page. */ public CATFormPanel getBodyPanel() { if (bodyPanel == null) { bodyPanel = StandardComponents.newBodyPanel(this); hiddenPanel = StandardComponents.newHiddenPanel(bodyPanel); bodyPanel.add(hiddenPanel); } return bodyPanel; } /** * @return the customHiddenFields */ @SuppressWarnings("ReturnOfCollectionOrArrayField") public List getCustomHiddenFields() { if (customHiddenFields == null) { customHiddenFields = new ArrayList(); } return customHiddenFields; } /** * Retrieves the Viewport used for the overall non-DIALOG page. * This is the object directly attached to the page root. * It is only valid when formType is not DIALOG. * @return The Viewport for the page. */ public Viewport getViewport() { return viewport; } /** * * @return The LayoutContainer holding all of the HiddenFields created to hold the data from getFormParameters. */ private LayoutContainer getHiddenPanel() { return hiddenPanel; } /** * Returns the minimum height needed for the page. * @return An int with the minimum height in pixels. */ public int getMinHeight() { return minHeight; } /** * Set the minimum height for the page (in pixels). * @param minHeight An int with the minimum height setting in pixels. */ public void setMinHeight(int minHeight) { this.minHeight = minHeight; } /** * Returns the minimum width needed for the page. * @return An int with the minimum width in pixels. */ public int getMinWidth() { return minWidth; } /** * Set the minimum width for the page (in pixels). * @param minWidth An int with the minimum width setting in pixels. */ public void setMinWidth(int minWidth) { this.minWidth = minWidth; } /** * @return the formName */ public String getFormName() { return formName; } /** * @param formName the formName to set */ public void setFormName(String formName) { this.formName = formName; } /** * @return the formLabel */ public String getFormLabel() { return formLabel; } /** * @param formLabel the formLabel to set */ public void setFormLabel(String formLabel) { this.formLabel = formLabel; if (formType == FormType.DIALOG) { container.getHeader().setVisible(false); } // if (Window.getTitle().isEmpty()) { // hdr = container.getHeader(); // } else { // hdr = getDialog().getHeader(); // } // Set the Window title only if we are a FORM if (formType == FormType.FORM) { if (Window.getTitle().isEmpty()) { Window.setTitle(entryPoint.getLabel()); } } else if (formType == FormType.DIALOG) { // For Dialogs, use the Dialog's header and shut off the header we created hdr = getDialog().getHeader(); hdr.setText(formLabel); // hdr.setVisible(false); } } /** * @return the helpID */ public String getHelpID() { return helpID; } /** * @param helpID the helpID to set */ public void setHelpID(String helpID) { this.helpID = helpID; } /** * Sets the mode of the form. If null, it defaults to LIST * @param mode The desired FormMode enum for the Form */ public void setMode(FormMode mode) { FormMode work = mode; if (work == null) { work = FormMode.LIST; } Registry.register("pageMode", work.toString()); this.mode = work; } /** * Returns the Mode of the form (CREATE, EDIT, DELETE, LIST, etc) * @return The FormMode enum value for this page. */ public FormMode getMode() { return mode; } /** * Creates new Hidden Fields for every entry found in the hiddenParamMap */ public void setupHiddenFields() { FormData fd = new FormData("100%"); fd.setWidth(getWidth()); Iterator ix = hiddenParamMap.keySet().iterator(); while (ix.hasNext()) { String key = ix.next(); String val = hiddenParamMap.get(key); addHiddenField(key, val, true); } for (HiddenField hf : getCustomHiddenFields()) { getHiddenPanel().add(hf); getBodyPanel().addHiddenField(hf); } } /** * Adds a new hidden field to the bodypanel. * @param fieldname The name of the Hidden field to be added * @param val The value for the newly created Hidden field. */ public void addHiddenField(String fieldname, String val) { addHiddenField(fieldname, val, false); } /** * Adds a new hidden field to the hidden panel (when formParam is true) or to the bodyPanel (when formParam is false). * @param fieldname The name of the Hidden field to be added * @param val The value for the newly created Hidden field. * @param formParam set to true to add to the form parameter hidden container. Otherwise, it will be added to the bodyPanel like any other field. */ private void addHiddenField(String fieldname, String val, boolean formParam) { // Field to hold object's oid on the page CATHiddenField hidden = CATHiddenField.newCATHiddenField(bodyPanel.getForm(), fieldname, fieldname); hidden.setValue(val); hidden.setAutoHeight(true); hidden.setAutoWidth(true); if (formParam) { hiddenPanel.add(hidden); } else { getBodyPanel().add(hidden); } getBodyPanel().addHiddenField(hidden); } /** * Empties all fields on the page that are in the FormPanel and that have names. Removes the hidden fields from the hidden panel (they will be rebuilt shortly). If they are also ComboBox, StandardTable or StandardTree objects, then their stores will be empties. */ public void reset() { // bodyPanel.reset(); getHiddenPanel().removeAll(); for (Field field : bodyPanel.getFields()) { if (!StandardComponents.isFieldHidden(field)) { String name = field.getName(); if (name != null && !name.isEmpty() && !name.equals("null")) { // Disable validation for this field during the reset. // Some validations depend on other field values and, // at this point in time, some of the fields are reset // but others are not. field.setFireChangeEventOnSetValue(false); field.reset(); field.setFireChangeEventOnSetValue(true); // Also reset any DependencyComponent associated with this field. // Otherwise, it will have a stale "startVal" which will cause // incorrect behavior. StandardComponents.resetFieldDependencyComponent(field); } } } formValid(false); } /** * Restores the original values for the form, stored when the init data was loaded. */ public void resetOriginalValues() { hasFormBeenApplied = true; for (Field field : bodyPanel.getFields()) { String name = field.getName(); if (name != null && !name.isEmpty() && !name.equals("null")) { Object value = field.getValue(); field.setOriginalValue(value); } } // turn off the APPLY and OK buttons formValid(false); } /** * Initializes the form validate processing. * All fields are added to a dive panel so the overall form status can be determined as validations occur. */ public void setupFormValidation() { Field focusfield = null; Log.debug("In CATFormLayoutsetupFormValidation."); divePanel = new FastMap(); // divePanel.put("$$BUSY", TRUE); for (Field field : bodyPanel.getFields()) { // field.setMessageTarget("tooltip"); // if (field instanceof CATMultiField) { // ((CATMultiField) field).getTextField().setMessageTarget("tooltip"); // } // if (field instanceof CATPicker) { // ((CATPicker) field).getText().setMessageTarget("tooltip"); // ((CATPicker) field).getSearchButtonAdapter().setMessageTarget("none"); // } String name = field.getName(); if (name != null && !name.isEmpty() && !"null".equals(name)) { Log.debug("Field: " + field.getName() + " tabindex: " + field.getTabIndex()); // Automatically focus on the field with tab index 1 Widget parent = field.getParent(); // field.getParent() returns null for the CATTextField in a MultiField so the // following null tests are a temporary work-around until the root cause is identified // of why the CATTextField in a multi-field doesn't have a parent if (parent instanceof LayoutContainer || (parent == null && field instanceof CATTextField)) { if (parent != null) { LayoutContainer lc = (LayoutContainer) parent; if (!"hidden".equals(lc.getId())) { if (!(field instanceof LabelField) && !(field instanceof HiddenField) && !(field instanceof CATHiddenField)) { if (field.isVisible() && field.isEnabled()) { if (focusfield == null) { focusfield = field; } } } } } else if (parent == null && field instanceof CATTextField) { // CATTextField in a CATMultiField if (field.isVisible() && field.isEnabled()) { if (focusfield == null) { focusfield = field; } } } divePanel.put(name, TRUE); Field thefield = field; // Special handling for the CATMultiField - redirect the events to the inner TextField. if (field instanceof CATMultiField) { thefield = ((CATMultiField) field).getTextField(); } Log.trace("Field " + name + " added to divePanel"); // Attach listeners from the fields for when they fire valid / invalid during validation thefield.addListener(Events.Valid, new Listener() { @Override public void handleEvent(FieldEvent fe) { Object objsource = fe.getSource(); //Log.trace("Field.Valid listener - Field:" + objsource); if (!(objsource instanceof Field)) { return; } Field fld = (Field) objsource; String name = fld.getName(); Log.debug("Received VALID event for field: " + name); updateDivePanel(name, true); } }); thefield.addListener(Events.Invalid, new Listener() { @Override public void handleEvent(FieldEvent fe) { Object objsource = fe.getSource(); //Log.trace("Field.Invalid listener - Field: " + objsource); if (!(objsource instanceof Field)) { return; } Field fld = (Field) objsource; String name = fld.getName(); Log.debug("Received INVALID event for field: " + name); updateDivePanel(name, false); } }); // Log.debug("About to pre-validate field: " + field.getName()); //field.focus(); // BEH: 2013-02-18 - turned off this validation step. //thefield.validate(); } } } if (focusfield != null) { Log.debug("Focusing on the first editable widget: " + focusfield.getName()); //focusfield.focus(); focusfield.validate(); } } /** * Returns the overall validity of the form. * Every dive panel object is tested to see if all are true. If so, then the form is valid. * Any false value found means the entire form is invalid. * @return True when all entries in the dive panel are marked as true. False otherwise. */ public boolean isValid() { Iterator ix = divePanel.keySet().iterator(); while (ix.hasNext()) { String key = ix.next(); boolean b = divePanel.get(key).booleanValue(); if (!b) { formValid(false); return false; } } formValid(true); return true; } // private void clearDivePanel(){ // Iterator ix=divePanel.keySet().iterator(); // while(ix.hasNext()){ // String key=ix.next(); // divePanel.put(key,TRUE); // } // } /** * Forces validation on every field on the page. */ public void validate() { // If the dependency processor looks like it's going to run, then clear the dive panel and the start values used by the DH to avoid validation. // This should make it revalidate every field on the page. // TODO: Maybe this should only re-evaluate the false items? // if(getDependencyProcessor()!=null){ // clearDivePanel(); // DependencyComponent.clearValues(); // } List> fields = bodyPanel.getFields(); for (Field field : fields) { field.isValid(); } showErrorForCurrentFocusField(); } /** * The method to override to change how the form reacts to its overall validation status (typically, the finish button is enabled or disabled according to * the validity boolean passed to the routine). * * @param valid set to true if the overall form is valid, false otherwise */ public void formValid(boolean valid) { boolean theValid = valid; if (hasFormBeenApplied) { // if the form as been applied, first check to see that at least one field is dirty // before allowing the OK and APPLY buttons to be enabled boolean isDirty = false; for (Field field : bodyPanel.getFields()) { String name = field.getName(); if (name != null && !name.isEmpty() && !"null".equals(name)) { if (field.isDirty()) { isDirty = true; break; } } } if (!isDirty) { // if nothing is dirty and this is after an apply, do not turn on the OK and APPLY buttons theValid = false; } } if (formType == FormType.FORM) { if (finishButton != null) { finishButton.setEnabled(theValid); } } else if (formType == FormType.DIALOG) { getDialog().getButtonById(Dialog.OK).setEnabled(theValid); } if (applyButton != null) { if (applyButton != null) { applyButton.setEnabled(theValid); } } } /** * Disables the OK and / or Apply buttons on a page */ public void disableActionButtons() { if (formType == FormType.FORM) { if (finishButton != null) { finishButton.setEnabled(false); } } else if (formType == FormType.DIALOG) { getDialog().getButtonById(Dialog.OK).setEnabled(false); } Button ab = applyButton; if (ab != null) { ab.setEnabled(false); } } /** * @return the formType */ public FormType getFormType() { return formType; } private void updateDivePanel(String name, boolean valid) { divePanel.put(name, Boolean.valueOf(valid)); boolean status = isValid(); Log.info("Overall form validity: " + status); } /** * @param formType the formType to set */ public void setFormType(FormType formType) { this.formType = formType; } /** * Updates the task parameters for the specified task by scraping the current form. * @param task The Task that will get the parameters from the current form. */ protected void replaceTaskParameters(Task task) { Map pmap = task.getFormToTaskMap(); Iterator ix = pmap.keySet().iterator(); // Cycle through the parameters in the task's paramMap while (ix.hasNext()) { // Fname holds the name of the field to be scraped - pname holds the parameter name to generate String fname = ix.next(); String pname = pmap.get(fname); if (fname != null && !fname.isEmpty() && pname != null && !pname.isEmpty()) { Log.trace("Searching for a field value to substitute for param '" + fname + "' from field '" + pname + "'"); for (Field field : getBodyPanel().getFields()) { String name = field.getName(); if (name == null || name.isEmpty()) { continue; } if (name.equals(fname)) { // Found the matching Field name - substitute the value into the parameter map String realval = StandardComponents.getFieldValue(field); task.addTaskParam(pname, realval); Log.trace("Found the field value: " + realval); break; } } } } } /** * @return the dialog */ public Dialog getDialog() { if (dialog == null) { dialog = new CATDialog(); dialog.setData("form", this); dialog.setButtons(Dialog.OKCANCEL); dialog.setHideOnButtonClick(false); dialog.setClosable(false); dialog.setAnimCollapse(true); dialog.setModal(true); dialog.setBlinkModal(true); } return dialog; } /** * Sets up the Form / Dialog's busy mask * * @param message The string containing the desired mask text * @param progressText The string to be presented inside the progress bar */ public void setBusy(String message, String progressText) { setBusy("INTERNAL", message, progressText); } /** * Sets up the Form / Dialog's busy mask. * Each Asynchronous operation needs to make a set / clear busy pair of calls. * The first call will mask the form. * Subsequent calls will merely add their reason to the map of current busy reasons. * A RuntimeException is thrown if a second setBusy call with the same reason is encountered. * * @param reason The ID used to track multiple set/clear busy requests. * @param message The string containing the desired mask text * @param progressText The string to be presented inside the progress bar */ public void setBusy(String reason, String message, String progressText) { if (busyMap.containsKey(reason)) { throw new RuntimeException("setBusy called with a duplicate reason: " + reason + " busyMap: " + busyMap); } if (busyMap.isEmpty()) { if (getFormType() == FormType.FORM || getFormType() == FormType.EMBEDDED) { if (waitBox == null) { setWaitBox(MessageBox.wait("Progress", message, progressText)); } } else { if (!masked) { getDialog().mask(progressText); masked = true; } } } else { Log.debug("Form's busyMap was already established - left existing mask alone"); } updateDivePanel("$$BUSY", FALSE); busyMap.put(reason, reason); } /** * Tears down the Form / Dialog's busy mask */ public void clearBusy() { clearBusy("INTERNAL"); } /** * Tears down the Form / Dialog's busy mask * @param reason A string with a value to correspond to a previous setBusy call. */ public void clearBusy(String reason) { if (!busyMap.containsKey(reason)) { Log.warn("clearBusy called without a corresponding setBusy for reason: " + reason); } busyMap.remove(reason); if (busyMap.isEmpty()) { if (getFormType() == FormType.FORM || getFormType() == FormType.EMBEDDED) { if (getWaitBox() != null) { getWaitBox().close(); waitBox = null; } } else { if (getDialog() != null) { getDialog().unmask(); masked = false; } } updateDivePanel("$$BUSY", TRUE); } else { Log.debug("Form's busyMap still has entries: " + busyMap); } } /** * Forces the page validation status to false by adding a special flag to the dive panel's status map and setting it to FALSE */ public void forceDivePanelInvalid() { updateDivePanel("$$FORCE_VALID", FALSE); } /** * Whenever markDirty is used on this layout, it will clear the special flag so normal validation can resume. */ public void markDirty() { updateDivePanel("$$FORCE_VALID", TRUE); } /** * Returns the current validation status for a field name. This is used by the widgets when no change has been detected. * Instead of returning the value of validateValueLocal (which might give a false positive), it will use the divePanel status instead. * @param fieldName The name of the Field object on the page to be checked. * @return true if the Field specified by fieldName was last validated as VALID, false otherwise. */ public boolean isFieldValid(String fieldName) { if (fieldName == null || fieldName.isEmpty()) { throw new RuntimeException("A Framework widget has no name setting"); } Boolean status = divePanel.get(fieldName); if (status == null) { // throw new RuntimeException("A field name was not found in the dive panel: " + fieldName); return true; } return status; } /** * @return the footer */ public LayoutContainer getFooter() { return footer; } /** * @param footer the footer to set */ public void setFooter(LayoutContainer footer) { this.footer = footer; } /** * Toggles the Debug button on and off */ public void toggleDebug() { boolean newsetting = !tools.isVisible(); if (formType == FormType.EMBEDDED) { getFooter().setVisible(newsetting); } tools.setVisible(!tools.isVisible()); } /** * @return the dependencyDescriptor */ public String getDependencyDescriptor() { if (dependencyDescriptor == null) { dependencyDescriptor = getParamMap().get("dependencyjson"); } if (dependencyDescriptor == null) { dependencyDescriptor = ""; } Log.trace("Length of dependencyDescriptor: " + dependencyDescriptor.length()); return dependencyDescriptor; } /** * @param dependencyDescriptor the dependencyDescriptor to set */ public void setDependencyDescriptor(String dependencyDescriptor) { this.dependencyDescriptor = dependencyDescriptor; } /** * Returns the page's dependency processor * @return The DependencyProcessor for the page */ public DependencyProcessor getDependencyProcessor() { return dependency; } } ----- INFO [org.netbeans.modules.java.source.save.CasualDiff]: Illegal values: from = 149647; to = 149554.Please, attach your messages.log to new issue! INFO [org.netbeans.modules.parsing.impl.TaskProcessor]: Task: class org.netbeans.modules.java.source.JavaSourceAccessor$CancelableTaskWrapper ignored cancel for 24,179 ms. INFO [org.netbeans.modules.parsing.impl.TaskProcessor]: Task: class org.netbeans.modules.java.source.JavaSourceAccessor$CancelableTaskWrapper ignored cancel for 6,795 ms. INFO [org.netbeans.modules.parsing.impl.TaskProcessor]: Task: class org.netbeans.modules.java.source.JavaSourceAccessor$CancelableTaskWrapper ignored cancel for 4,237 ms. WARNING [org.openide.filesystems.Ordering]: Not all children in / marked with the position attribute: [org-netbeans-modules-editor-java-JavaBracesMatcher.shadow], but some are: [org-netbeans-modules-editor-bracesmatching-LegacyEssMatcher.instance, org-netbeans-modules-editor-bracesmatching-DefaultMatcher.instance] INFO [org.netbeans.modules.parsing.impl.TaskProcessor]: Task: class org.netbeans.modules.java.source.JavaSourceAccessor$CancelableTaskWrapper ignored cancel for 3,235 ms. java.lang.ClassNotFoundException: java.lang.UnsupportedClassVersionError: com/timboudreau/adhoc/project/AdhocProjectToolsAction : Unsupported major.minor version 51.0 at org.openide.loaders.InstanceSupport.findClass(InstanceSupport.java:511) at org.openide.loaders.InstanceSupport.instanceClass(InstanceSupport.java:148) at org.openide.loaders.InstanceDataObject$Ser.instanceClass(InstanceDataObject.java:1338) at org.openide.loaders.InstanceSupport.instanceOf(InstanceSupport.java:182) at org.openide.loaders.InstanceDataObject$Ser.instanceOf(InstanceDataObject.java:1362) at org.openide.loaders.InstanceDataObject.instanceOf(InstanceDataObject.java:828) at org.openide.loaders.FolderLookup.instanceForCookie(FolderLookup.java:215) at org.openide.loaders.FolderInstance$HoldInstance.instanceCreate(FolderInstance.java:1150) at org.openide.loaders.FolderLookup.createInstance(FolderLookup.java:166) at org.openide.loaders.FolderInstance.defaultProcessObjectsFinal(FolderInstance.java:885) at org.openide.loaders.FolderInstance$1R.run(FolderInstance.java:730) at org.openide.loaders.FolderLookup.postCreationTask(FolderLookup.java:260) at org.openide.loaders.FolderInstance.processObjects(FolderInstance.java:770) at org.openide.loaders.FolderInstance$Listener.finished(FolderInstance.java:1052) at org.openide.loaders.FolderList.createBoth(FolderList.java:923) at org.openide.loaders.FolderList.getObjects(FolderList.java:608) at org.openide.loaders.FolderList.access$200(FolderList.java:77) at org.openide.loaders.FolderList$ListTask.computeResult(FolderList.java:1007) at org.openide.loaders.FolderList$ListTask.run(FolderList.java:983) at org.openide.util.RequestProcessor$Task.run(RequestProcessor.java:1432) at org.openide.util.RequestProcessor$Processor.doEvaluate(RequestProcessor.java:2096) at org.openide.util.RequestProcessor$Task.waitFinished(RequestProcessor.java:1666) at org.openide.loaders.FolderInstance.waitFinished(FolderInstance.java:332) at org.openide.loaders.FolderInstance.instanceCreate(FolderInstance.java:281) at org.openide.loaders.FolderLookup.instanceForCookie(FolderLookup.java:222) at org.openide.loaders.FolderInstance$HoldInstance.instanceCreate(FolderInstance.java:1150) at org.openide.loaders.FolderLookup.createInstance(FolderLookup.java:166) at org.openide.loaders.FolderInstance.defaultProcessObjectsFinal(FolderInstance.java:885) at org.openide.loaders.FolderInstance$1R.run(FolderInstance.java:730) at org.openide.loaders.FolderLookup.postCreationTask(FolderLookup.java:260) at org.openide.loaders.FolderInstance.processObjects(FolderInstance.java:770) at org.openide.loaders.FolderInstance$Listener.finished(FolderInstance.java:1052) at org.openide.loaders.FolderList.createBoth(FolderList.java:923) at org.openide.loaders.FolderList.getObjects(FolderList.java:608) at org.openide.loaders.FolderList.access$200(FolderList.java:77) at org.openide.loaders.FolderList$ListTask.computeResult(FolderList.java:1007) at org.openide.loaders.FolderList$ListTask.run(FolderList.java:983) at org.openide.util.RequestProcessor$Task.run(RequestProcessor.java:1432) at org.openide.util.RequestProcessor$Processor.run(RequestProcessor.java:2044) Caused by: java.lang.UnsupportedClassVersionError: com/timboudreau/adhoc/project/AdhocProjectToolsAction : Unsupported major.minor version 51.0 at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632) at java.lang.ClassLoader.defineClass(ClassLoader.java:616) at org.netbeans.JarClassLoader.doLoadClass(JarClassLoader.java:274) at org.netbeans.ProxyClassLoader.selfLoadClass(ProxyClassLoader.java:259) at org.netbeans.ProxyClassLoader.loadClass(ProxyClassLoader.java:187) at org.netbeans.ModuleManager$SystemClassLoader.loadClass(ModuleManager.java:720) at java.lang.ClassLoader.loadClass(ClassLoader.java:248) at org.openide.loaders.InstanceSupport.findClass(InstanceSupport.java:498) ... 38 more Caused by: From file: MultiFileObject@64928775[Actions/Window/com-timboudreau-adhoc-project-AdhocProjectToolsAction.instance] INFO [org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater]: Resolving dependencies took: 33 ms INFO [org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater]: Complete indexing of 0 binary roots took: 1 ms INFO [org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater]: Indexing of: D:\Users\bhulse\AppData\Roaming\NetBeans\7.3\jsstubs\reststubs.zip took: 71 ms (New or modified files: 0, Deleted files: 0) [Adding listeners took: 0 ms] INFO [org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater]: Indexing of: D:\Users\bhulse\AppData\Roaming\NetBeans\7.3\jsstubs\domstubs.zip took: 88 ms (New or modified files: 0, Deleted files: 0) [Adding listeners took: 0 ms] INFO [org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater]: Indexing of: D:\Users\bhulse\AppData\Roaming\NetBeans\7.3\jsstubs\corestubs.zip took: 69 ms (New or modified files: 0, Deleted files: 0) [Adding listeners took: 0 ms] INFO [org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater]: Complete indexing of 3 source roots took: 228 ms (New or modified files: 0, Deleted files: 0) [Adding listeners took: 0 ms] WARNING [org.netbeans.modules.options.keymap.LayersBridge]: Invalid shortcut: org.openide.loaders.BrokenDataShadow@70aa335b[MultiFileObject@6d330af4[Keymaps/NetBeans/D-BACK_QUOTE.shadow]] WARNING [org.netbeans.modules.options.keymap.LayersBridge]: Invalid shortcut: org.openide.loaders.BrokenDataShadow@70aa335b[MultiFileObject@6d330af4[Keymaps/NetBeans/D-BACK_QUOTE.shadow]] WARNING [org.netbeans.modules.editor.settings.storage.keybindings.KeyMapsStorage]: The keybinding 'A-MOUSE_WHEEL_UP' in Editors/Keybindings/NetBeans/Defaults/org-netbeans-modules-editor-keybindings.xml may not work correctly on Mac. Keybindings starting with Alt or Ctrl should be coded with latin capital letters 'O' or 'D' respectively. For details see org.openide.util.Utilities.stringToKey(). WARNING [org.netbeans.modules.editor.settings.storage.keybindings.KeyMapsStorage]: The keybinding 'A-MOUSE_WHEEL_DOWN' in Editors/Keybindings/NetBeans/Defaults/org-netbeans-modules-editor-keybindings.xml may not work correctly on Mac. Keybindings starting with Alt or Ctrl should be coded with latin capital letters 'O' or 'D' respectively. For details see org.openide.util.Utilities.stringToKey(). INFO [org.netbeans.modules.subversion]: no repository url found for not managed file D:\Users\bhulse\NetBeansProjects\FrameworkUI\hs_err_pid1528.log INFO [org.netbeans.modules.parsing.impl.TaskProcessor]: Task: class org.netbeans.modules.java.source.JavaSourceAccessor$CancelableTaskWrapper ignored cancel for 25,583 ms. INFO [org.netbeans.modules.parsing.impl.TaskProcessor]: Task: class org.netbeans.modules.java.source.JavaSourceAccessor$CancelableTaskWrapper ignored cancel for 206 ms. INFO [org.netbeans.modules.parsing.impl.TaskProcessor]: Task: class org.netbeans.modules.java.source.JavaSourceAccessor$CancelableTaskWrapper ignored cancel for 32,241 ms. WARNING [org.netbeans.core.TimableEventQueue]: too much time in AWT thread null INFO [org.netbeans.modules.parsing.impl.TaskProcessor]: Task: class org.netbeans.modules.java.source.JavaSourceAccessor$CancelableTaskWrapper ignored cancel for 12,623 ms. WARNING [org.netbeans.ProxyClassLoader]: Will not load class org.apache.commons.logging.impl.Log4JLogger arbitrarily from one of NetbinoxLoader delegating to org.apache.commons.logging_1.1.1 and ModuleCL@6edcb6b4[org.netbeans.libs.amazon] starting from SystemClassLoader[584 modules]; see http://wiki.netbeans.org/DevFaqModuleCCE WARNING [org.netbeans.ProxyClassLoader]: Will not load class org.apache.commons.logging.impl.Jdk14Logger arbitrarily from one of NetbinoxLoader delegating to org.apache.commons.logging_1.1.1 and ModuleCL@6edcb6b4[org.netbeans.libs.amazon] starting from SystemClassLoader[584 modules]; see http://wiki.netbeans.org/DevFaqModuleCCE INFO [org.netbeans.modules.parsing.impl.TaskProcessor]: Task: class org.netbeans.modules.java.source.JavaSourceAccessor$CancelableTaskWrapper ignored cancel for 26,170 ms. INFO [org.netbeans.modules.bugzilla.Bugzilla]: Bugzilla repository [https://netbeans.org/bugzilla] has version 4.0.9.