Results 1 to 5 of 5

Thread: GridInlineEditing using Widgets

  1. #1
    Ext GWT Premium Member
    Join Date
    Jun 2010
    Location
    Kyrksterra, Norway
    Posts
    71

    Default GridInlineEditing using Widgets

    I needed base Widget support within GridInlineEditing, and created this variant of the implementation:

    Code:
    /**
     * @param <M>
     *            the model type
     * @see com.sencha.gxt.widget.core.client.grid.editing.GridInlineEditing
     * @author Stig Runar Vangen
     */
    public class GridInlineWidgetEditing<M> extends AbstractGridEditing<M> implements GridWidgetEditing<M> {
    
    
        private static final Logger LOGGER = Logger.getLogger(GridInlineWidgetEditing.class.getName());
    
    
        private final Map<ColumnConfig<M, ?>, Widget> widgetEditorMap;
        private final GroupingHandlerRegistration fieldRegistration;
        private boolean ignoreScroll;
        private boolean ignoreNextEnter;
        private boolean focusOnComplete;
        private boolean rowUpdated;
    
    
        public GridInlineWidgetEditing(final Grid<M> editableGrid) {
            this.widgetEditorMap = new HashMap<ColumnConfig<M, ?>, Widget>();
            this.fieldRegistration = new GroupingHandlerRegistration();
            this.setEditableGrid(editableGrid);
        }
    
    
        @Override
        public final <N> void addEditor(final ColumnConfig<M, N> columnConfig, final Widget widget) {
            this.widgetEditorMap.put(columnConfig, widget);
        }
    
    
        public final Widget getWidget(final ColumnConfig<M, ?> columnConfig) {
            return this.widgetEditorMap.get(columnConfig);
        }
    
    
        @Override
        public final void startEditing(final GridCell cell) {
            if (this.getEditableGrid() != null && this.getEditableGrid().isAttached() && cell != null) {
                final ColumnConfig<M, ?> c = this.columnModel.getColumn(cell.getCol());
    
    
                final M value = this.getEditableGrid().getStore().get(cell.getRow());
    
    
                // Editable
                if (value != null && this.getWidget(c) != null) {
                    final BeforeStartEditEvent<M> ce = new BeforeStartEditEvent<M>(cell);
                    this.fireEvent(ce);
                    if (ce.isCancelled()) {
                        return;
                    }
    
    
                    if (this.getEditableGrid().getSelectionModel() instanceof CellSelectionModel) {
                        if (GXTLogConfiguration.loggingIsEnabled()) {
                            LOGGER.finest("startEditing selectCell");
                        }
    
    
                        ((CellSelectionModel<?>) this.getEditableGrid().getSelectionModel())
                                .selectCell(cell.getRow(), cell.getCol());
                    }
    
    
                    final Element elem = this.getEditableGrid().getView().getCell(cell.getRow(), cell.getCol());
                    elem.getFirstChildElement().getStyle().setVisibility(Style.Visibility.HIDDEN);
    
    
                    if (GXTLogConfiguration.loggingIsEnabled()) {
                        LOGGER.finest("startEditing call cancelEditing, ignoreScroll true, ensure visible");
                    }
    
    
                    this.cancelEditing();
                    this.ignoreScroll = true;
                    this.getEditableGrid().getView().ensureVisible(cell.getRow(), cell.getCol(), true);
    
    
                    this.doStartEditing(cell);
                }
            }
        }
    
    
        @Override
        public final void completeEditing() {
            if (GXTLogConfiguration.loggingIsEnabled()) {
                LOGGER.finest("completeEditing active is " + (this.activeCell == null ? "null" : "no null"));
            }
            if (this.activeCell != null) {
                if (GXTLogConfiguration.loggingIsEnabled()) {
                    LOGGER.finest("completeEditing");
                }
    
    
                final Element elem = this.getEditableGrid().getView().getCell(
                        this.activeCell.getRow(), this.activeCell.getCol());
                elem.getFirstChildElement().getStyle().setVisibility(Style.Visibility.VISIBLE);
    
    
                this.doCompleteEditing();
            }
        }
    
    
        @Override
        public final void cancelEditing() {
            this.ignoreScroll = false;
            if (GXTLogConfiguration.loggingIsEnabled()) {
                LOGGER.finest("cancelEditing active is " + (this.activeCell == null ? "null" : "no null"));
            }
            if (this.activeCell != null) {
                final Element elem = this.getEditableGrid().getView().getCell(
                        this.activeCell.getRow(), this.activeCell.getCol());
                elem.getFirstChildElement().getStyle().setVisibility(Style.Visibility.VISIBLE);
    
    
                final ColumnConfig<M, ?> c = this.columnModel.getColumn(this.activeCell.getCol());
                final Widget widget = this.getWidget(c);
                this.removeEditor(this.activeCell, widget);
    
    
                final GridCell gc = this.activeCell;
                this.activeCell = null;
                this.fireEvent(new CancelEditEvent<M>(gc));
    
    
                if (this.focusOnComplete) {
                    this.focusOnComplete = false;
                    this.focusGrid();
                }
            }
        }
    
    
        @SuppressWarnings("unchecked")
        protected final <N, O> void doStartEditing(final GridCell cell) {
            if (GXTLogConfiguration.loggingIsEnabled()) {
                LOGGER.finest("doStartEditing");
            }
    
    
            if (this.getEditableGrid() != null && this.getEditableGrid().isAttached() && cell != null) {
                final M value = this.getEditableGrid().getStore().get(cell.getRow());
    
    
                final ColumnConfig<M, N> column = this.columnModel.getColumn(cell.getCol());
                if (column != null && value != null) {
    
    
                    final Converter<N, O> converter = this.getConverter(column);
    
    
                    final ValueProvider<? super M, N> v = column.getValueProvider();
                    final N colValue = this.getEditableGrid().getStore().hasRecord(value) ?
                            this.getEditableGrid().getStore().getRecord(value).getValue(v) : v.getValue(value);
                    O convertedValue;
                    if (converter != null) {
                        convertedValue = converter.convertModelValue(colValue);
                    } else {
                        convertedValue = (O) colValue;
                    }
    
    
                    final Widget widget = this.getWidget(column);
                    if (widget != null) {
                        if (widget instanceof Field) {
                            ((Field<O>) widget).setErrorSupport(null);
                        }
    
    
                        this.activeCell = cell;
    
    
                        if (GXTLogConfiguration.loggingIsEnabled()) {
                            LOGGER.finest("doStartEditing convertedValue: " + convertedValue);
                        }
    
    
                        if (widget instanceof HasValue) {
                            ((HasValue<O>) widget).setValue(convertedValue);
                        }
    
    
                        if (widget instanceof TriggerField<?>) {
                            ((TriggerField<?>) widget).setMonitorTab(false);
                        }
    
    
                        if (widget instanceof CheckBox) {
                            ((CheckBox) widget).setBorders(true);
                        }
    
    
                        this.getEditableGrid().getView().getEditorParent().appendChild(widget.getElement());
                        ComponentHelper.setParent(this.getEditableGrid(), widget);
                        ComponentHelper.doAttach(widget);
    
    
                        if (widget instanceof Component) {
                            ((Component) widget).setWidth(column.getWidth());
                            ((Component) widget).getElement().makePositionable(true);
                        }
    
    
                        final Element row = this.getEditableGrid().getView().getRow(cell.getRow());
    
    
                        int left = 0;
                        for (int i = 0; i < cell.getCol(); i++) {
                            if (!this.columnModel.isHidden(i)) {
                                left += this.columnModel.getColumnWidth(i);
                            }
                        }
    
    
                        if (widget instanceof Component) {
                            ((Component) widget).getElement().setLeftTop(left,
                                    row.getAbsoluteTop() - this.getEditableGrid().getView().getBody().getAbsoluteTop());
                            ((Component) widget).show();
                        }
    
    
                        this.startMonitoring();
    
    
                        Scheduler.get().scheduleDeferred(new ScheduledCommand() {
                            @Override
                            public void execute() {
                                if (GXTLogConfiguration.loggingIsEnabled()) {
                                    LOGGER.finest("doStartEditing scheduleDeferred doFocus ");
                                }
    
    
                                // browsers select all when tabbing into a input and put cursor at location when clicking
                                // into an input with inline editing, the field is not visible at time of click so we select
                                // all. we ignore field.isSelectOnFocus as this only applies when clicking into a field
                                if (widget instanceof ValueBaseField<?>) {
                                    final ValueBaseField<?> vf = (ValueBaseField<?>) widget;
                                    vf.selectAll();
                                }
    
    
                                // EXTGWT-2856 calling doFocus before selectAll is causing blur to fire which ends the edit
                                // immediately after it starts
                                GridInlineWidgetEditing.this.doFocus(widget);
    
    
                                GridInlineWidgetEditing.this.ignoreScroll = false;
    
    
                                GridInlineWidgetEditing.this.fieldRegistration.removeHandler();
    
    
                                if (widget instanceof HasValueChangeHandlers) {
                                    GridInlineWidgetEditing.this.fieldRegistration.add(((HasValueChangeHandlers<O>) widget)
                                            .addValueChangeHandler(new ValueChangeHandler<O>() {
                                                @Override
                                                public void onValueChange(final ValueChangeEvent<O> event) {
                                                    if (GXTLogConfiguration.loggingIsEnabled()) {
                                                        LOGGER.finest("doStartEditing onValueChanged");
                                                    }
    
    
                                                    // if enter key cause value change we want to ignore the next enter key
                                                    // otherwise new edit will start by onEnter
                                                    GridInlineWidgetEditing.this.ignoreNextEnter = true;
    
    
                                                    final Timer t = new Timer() {
                                                        @Override
                                                        public void run() {
                                                            GridInlineWidgetEditing.this.ignoreNextEnter = false;
                                                        }
                                                    };
    
    
                                                    GridInlineWidgetEditing.this.completeEditing();
                                                    t.schedule(100);
                                                }
                                            }));
                                }
    
    
                                if (widget instanceof HasBlurHandlers) {
                                    GridInlineWidgetEditing.this.fieldRegistration.add(((HasBlurHandlers) widget)
                                            .addBlurHandler(new BlurHandler() {
                                                @Override
                                                public void onBlur(final BlurEvent event) {
                                                    if (GXTLogConfiguration.loggingIsEnabled()) {
                                                        LOGGER.finest("doStartEditing onBlur");
                                                    }
    
    
                                                    GridInlineWidgetEditing.this.ignoreNextEnter = true;
    
    
                                                    final Timer t = new Timer() {
                                                        @Override
                                                        public void run() {
                                                            GridInlineWidgetEditing.this.ignoreNextEnter = false;
                                                        }
                                                    };
    
    
                                                    if (GXTLogConfiguration.loggingIsEnabled()) {
                                                        LOGGER.finest("doStartEditing onBlur call cancelEditing ");
                                                    }
    
    
                                                    GridInlineWidgetEditing.this.cancelEditing();
                                                    t.schedule(100);
                                                }
                                            }));
                                }
    
    
                                GridInlineWidgetEditing.this.fireEvent(new StartEditEvent<M>(cell));
                            }
                        });
                    }
                }
            }
    
    
        }
    
    
        @SuppressWarnings("unchecked")
        protected final <N, O> void doCompleteEditing() {
            if (GXTLogConfiguration.loggingIsEnabled()) {
                LOGGER.finest("doCompleteEditing activeCell is " + (this.activeCell != null ? " is not null" : "is null"));
            }
    
    
            if (this.activeCell != null) {
                final ColumnConfig<M, N> c = this.columnModel.getColumn(this.activeCell.getCol());
    
    
                final Widget widget = this.getWidget(c);
                if (widget != null) {
    
    
                    final Converter<N, O> converter = this.getConverter(c);
    
    
                    O fieldValue = null;
    
    
                    if (widget instanceof ValueBaseField) {
                        fieldValue = ((ValueBaseField<O>) widget).getCurrentValue();
                    } else if (widget instanceof HasValue) {
                        fieldValue = ((HasValue<O>) widget).getValue();
                    } else if (widget instanceof Field) {
                        fieldValue = ((Field<O>) widget).getValue();
                    }
    
    
                    final N convertedValue;
                    if (converter != null) {
                        convertedValue = converter.convertFieldValue(fieldValue);
                    } else {
                        convertedValue = (N) fieldValue;
                    }
    
    
                    if (GXTLogConfiguration.loggingIsEnabled()) {
                        LOGGER.finest("Converted value: " + convertedValue);
                    }
    
    
                    this.removeEditor(this.activeCell, widget);
    
    
                    final ListStore<M> store = this.getEditableGrid().getStore();
                    final ListStore<M>.Record r = store.getRecord(store.get(this.activeCell.getRow()));
    
    
                    this.rowUpdated = true;
    
    
                    r.addChange(c.getValueProvider(), convertedValue);
                    this.fireEvent(new CompleteEditEvent<M>(this.activeCell));
    
    
                    if (this.focusOnComplete) {
                        this.focusOnComplete = false;
                        this.focusGrid();
                    }
                }
    
    
                this.activeCell = null;
            }
        }
    
    
        protected final void doFocus(final Widget widget) {
            try {
                if (widget instanceof Component) {
                    ((Component) widget).focus();
                } else if (widget instanceof Focusable) {
                    ((Focusable) widget).setFocus(true);
                } else {
                    FocusImpl.getFocusImplForWidget().focus(widget.getElement());
                }
            } catch (final Exception ex) {
                // IE throws exception if element not focusable
            }
        }
    
    
        @Override
        protected final SafeHtml getErrorHtml() {
            final SafeHtmlBuilder sb = new SafeHtmlBuilder();
            sb.appendHtmlConstant("<ul>");
            final ColumnConfig<M, ?> c = this.columnModel.getColumn(this.activeCell.getCol());
            final Widget widget = this.getWidget(c);
    
    
            this.getErrorMessage(widget, sb, c.getHeader().asString());
    
    
            sb.appendHtmlConstant("</ul>");
            return sb.toSafeHtml();
        }
    
    
        protected final void getErrorMessage(final Widget widget, final SafeHtmlBuilder sb, final String title) {
            boolean result = true;
    
    
            if (widget instanceof ValueBaseField) {
                final ValueBaseField<?> vfield = (ValueBaseField<?>) widget;
                result = vfield.isCurrentValid(true);
            }
    
    
            if (widget instanceof Field) {
                final Field<?> field = (Field<?>) widget;
                if (!result || !field.isValid(true)) {
                    sb.appendHtmlConstant("<li><b>");
                    sb.appendEscaped(title);
                    sb.appendHtmlConstant("</b>: ");
                    sb.appendEscaped(field.getErrors().get(0).getMessage());
                    sb.appendHtmlConstant("</li>");
                }
            }
        }
    
    
        @Override
        protected final boolean isValid() {
            if (this.activeCell == null) {
                return true;
            }
            final ColumnConfig<M, ?> c = this.columnModel.getColumn(this.activeCell.getCol());
            final Widget w = this.getWidget(c);
            if (w instanceof ValueBaseField<?>) {
                final ValueBaseField<?> f = (ValueBaseField<?>) w;
                if (!f.isCurrentValid(true)) {
                    return false;
                }
            } else if (w instanceof Field<?>) {
                final Field<?> f = (Field<?>) w;
                if (!f.isValid(true)) {
                    return false;
                }
            }
    
    
            return true;
        }
    
    
        @Override
        protected final void showTooltip(final SafeHtml content) {
            if (this.activeCell == null) {
                return;
            }
    
    
            final ColumnConfig<M, ?> c = this.columnModel.getColumn(this.activeCell.getCol());
            final Widget widget = this.getWidget(c);
    
    
            if (this.tooltip == null) {
                final ToolTipConfig config = new ToolTipConfig();
                config.setAutoHide(false);
                config.setMouseOffset(new int[] { 0, 0 });
                config.setAnchor(Side.LEFT);
                this.tooltip = new ToolTip(widget, config);
                this.tooltip.setMaxWidth(600);
            }
    
    
            this.tooltip.initTarget(widget);
    
    
            final ToolTipConfig config = this.tooltip.getToolTipConfig();
            config.setBodyHtml(content);
            this.tooltip.update(config);
            this.tooltip.enable();
            if (!this.tooltip.isAttached()) {
                this.tooltip.show();
            }
        }
    
    
        private void removeEditor(final GridCell cell, final Widget widget) {
            this.removeFieldBlurHandler();
    
    
            if ((GXT.isIE8() || GXT.isIE7() || GXT.isIE6()) && widget instanceof ValueBaseField<?>) {
                ((ValueBaseField<?>) widget).getCell().getInputElement(widget.getElement()).blur();
            }
    
    
            if (widget != null && widget.isAttached()) {
                if (widget instanceof Component) {
                    ((Component) widget).hide();
                }
                ComponentHelper.setParent(null, widget);
                ComponentHelper.doDetach(widget);
            }
        }
    
    
        private void removeFieldBlurHandler() {
            this.fieldRegistration.removeHandler();
        }
    }
    Code:
    /**
     * @param <M>
     *            the model type
     * @author Stig Runar Vangen
     */
    public interface GridWidgetEditing<M> {
    
    
        /**
         * Adds an editor for the given column.
         *
         * @param columnConfig
         *            the column config
         * @param field
         *            the field
         */
        <N> void addEditor(ColumnConfig<M, N> columnConfig, Widget widget);
    }
    This implementation supports all Widget types within the grid, not restricted to subclasses of Field only. I hope something like this ends up in GXT 3.1, so that I don't need this custom implementation.

  2. #2
    Sencha User
    Join Date
    Feb 2009
    Location
    Minnesota
    Posts
    2,737

    Default

    Thanks for the feedback - from what I can tell, the main purpose is to draw the widget regardless if whether or not it can accept a value, and if it can, then pass in and out that value as needed?

    We're currently consider several options - first, expanding addEditor to accept IsField instead of just Field, second allowing the grid editor to continue to render the same content as in the regular grid instead of running toString() on the value (only applies to row editing), and third accepting some non-editor widget to be rendered in any column, but not to interact with the data. We're open to implementing one or more of these options, depending on what makes sense, is internally consistent and un-surprising, and provides the required functionality.

    Your solution looks a little like the third option, but it has its quirks. Quick code review:
    API - addEditor - this method does not accept an editor, and does not necessarily do any editing at all, and cannot necessarily respond to errors. Requiring IsField might make more sense to still call it addEditor, otherwise we might consider something like addWidget.
    dead code - doCompleteEditing has this block:
    [CODE] if (widget instanceof ValueBaseField) {
    fieldValue = ((ValueBaseField<O>) widget).getCurrentValue();
    } else if (widget instanceof HasValue) {
    fieldValue = ((HasValue<O>) widget).getValue();
    } elseif (widget instanceof Field) {
    fieldValue = ((Field<O>) widget).getValue();
    }[/
    CODE]
    The bolded section never gets run, since Field implements HasValue. Since the code called on either one is the same, this last block probably isn't required.
    dead code - the rowUpdated, ignoreNextEnter, ignoreScroll fields are written, but never read. I'm somewhat concerned that there is a loss of functionality from this.
    api changes - showTooltip has config.setMouseOffset(new int[] { 0, 0 }) - This method no longer is valid in 3.1, as we're removing int[] methods which only has two args. Instead should read config.setMouseOffsetX(0); config.setMouseOffsetY(0);.

    Which version of GXT is this based off of? Have you taken a look at any of the 3.1 nightly builds yet? We're in the process of merging some rather large changes in, but each day we're getting closer to our 3.1 beta.

  3. #3
    Ext GWT Premium Member
    Join Date
    Jun 2010
    Location
    Kyrksterra, Norway
    Posts
    71

    Default

    This code is based on the GridInlineEditing from GXT 3.0.6. Any errors in that implementation is therefore inherited into this implementation.

    I would prefer that the interface receives grid cells as widgets, as you then will have full control over what goes into the grid. If any specific value interface should be used, I would prefer HasValue or something similar that is somewhat neutral. This restricts what each cell might show, but could be worked around. Using the IsField interface is way better than the solution today, as you might be able to work around to a solution some way using this interface as well, even though it will be more complicated.

    The reason why I've run into this problem is that I've got some widgets that are loaded asynchronously using code splitting. The widgets themselves are not available at the point of creation, and I therefore store and forward requests as needed until the widget code is fully loaded.

    We use a proxy widget as a placeholder for async widgets. This proxy widget extends SimpleContainer and implements HasValue and LeafValueEditor in order to be able to store values while the widget is loaded or forwarded upon creation or after being loaded. Upon load, the real widget is inserted into the container, and will appear in its correct place within the user interface. All registered handlers are also added so that all manual and request factory registrations are attached to the real widget.

  4. #4
    Sencha User
    Join Date
    Feb 2009
    Location
    Minnesota
    Posts
    2,737

    Default

    GXT 3.0.6's GridInlineEditing used rowUpdated (mouse tracking to deal with checkbox/radio changes), ignoreNextEnter (enter key used to blur the field could sometimes re-start editing again), and ignoreScroll (when the user starts scrolling, cancel current editing). I'm not sure which other implementation errors you might be referring to, as the other possible changes I mentioned (except the 3.1 api change of course) also seem unique to your code.

    Your usage points are good ones, and generalizing to IsField will get us most of that, since IsField is just IsWidget plus get/setValue and a few validation methods. I'll see about removing validation as well, but the AdapterField type would probably work pretty well for your use case as well - it also is a SimpleContainer with IsField (i.e. LeafValueEditor which includes TakesValue), and we can look into implementing HasValue as well.

  5. #5
    Ext GWT Premium Member
    Join Date
    Jun 2010
    Location
    Kyrksterra, Norway
    Posts
    71

    Default

    I've updated the code with support for GXT 3.1 with your comments on the code.

    Code:
    public class GridInlineWidgetEditing<M> extends AbstractGridEditing<M> implements GridWidgetEditing<M> {
    
    
        private static final Logger LOGGER = Logger.getLogger(GridInlineWidgetEditing.class.getName());
    
    
        protected Callback callback = new Callback() {
            @Override
            public boolean isSelectable(final GridCell cell) {
                if (GridInlineWidgetEditing.this.editableGrid != null) {
                    final ColumnModel<M> cm = GridInlineWidgetEditing.this.editableGrid.getColumnModel();
                    return !cm.isHidden(cell.getCol())
                            && GridInlineWidgetEditing.this.widgetEditorMap.containsKey(cm.getColumn(cell.getCol()));
                }
                return false;
            }
        };
    
    
        private final Map<ColumnConfig<M, ?>, Widget> widgetEditorMap;
        private final GroupingHandlerRegistration fieldRegistration;
        private boolean ignoreScroll;
        private boolean ignoreNextEnter;
        private boolean focusOnComplete;
        private boolean activeEdit;
        private boolean rowUpdated;
        private boolean revertInvalid;
    
    
        public GridInlineWidgetEditing(final Grid<M> editableGrid) {
            this.widgetEditorMap = new HashMap<ColumnConfig<M, ?>, Widget>();
            this.fieldRegistration = new GroupingHandlerRegistration();
            this.setEditableGrid(editableGrid);
        }
    
    
        @Override
        public final <N> void addEditor(final ColumnConfig<M, N> columnConfig, final Field<N> field) {
            this.widgetEditorMap.put(columnConfig, field);
        }
    
    
        @Override
        public final <N> void addEditor(final ColumnConfig<M, N> columnConfig, final IsWidget widget) {
            if (widget != null) {
                this.widgetEditorMap.put(columnConfig, widget.asWidget());
            }
        }
    
    
        public final Widget getWidget(final ColumnConfig<M, ?> columnConfig) {
            return this.widgetEditorMap.get(columnConfig);
        }
    
    
        /**
         * Returns {@code true} of the editor reverts the value to the start value on invalid.
         *
         * @return the revert invalid state
         */
        public final boolean isRevertInvalid() {
            return this.revertInvalid;
        }
    
    
        /**
         * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
         * validation fails (defaults to {@code false}).
         *
         * @param revertInvalid
         *            true to revert
         */
        public final void setRevertInvalid(final boolean revertInvalid) {
            this.revertInvalid = revertInvalid;
        }
    
    
        @Override
        public void startEditing(final GridCell cell) {
            if (this.getEditableGrid() != null && this.getEditableGrid().isAttached() && cell != null) {
                final ColumnConfig<M, ?> c = this.columnModel.getColumn(cell.getCol());
    
    
                final M value = this.getEditableGrid().getStore().get(cell.getRow());
    
    
                // Editable
                if (value != null && this.getWidget(c) != null) {
                    final BeforeStartEditEvent<M> ce = new BeforeStartEditEvent<M>(cell);
                    this.fireEvent(ce);
                    if (ce.isCancelled()) {
                        return;
                    }
    
    
                    if (this.getEditableGrid().getSelectionModel() instanceof CellSelectionModel) {
                        if (GXTLogConfiguration.loggingIsEnabled()) {
                            LOGGER.finest("startEditing selectCell");
                        }
    
    
                        ((CellSelectionModel<?>) this.getEditableGrid().getSelectionModel())
                                .selectCell(cell.getRow(), cell.getCol());
                    }
    
    
                    final Element elem = this.getEditableGrid().getView().getCell(cell.getRow(), cell.getCol());
                    elem.getFirstChildElement().getStyle().setVisibility(Style.Visibility.HIDDEN);
    
    
                    if (GXTLogConfiguration.loggingIsEnabled()) {
                        LOGGER.finest("startEditing call cancelEditing, ignoreScroll true, ensure visible");
                    }
    
    
                    this.cancelEditing();
                    this.ignoreScroll = true;
                    this.getEditableGrid().getView().ensureVisible(cell.getRow(), cell.getCol(), true);
    
    
                    this.doStartEditing(cell);
                }
            }
        }
    
    
        @Override
        public final void completeEditing() {
            if (GXTLogConfiguration.loggingIsEnabled()) {
                LOGGER.finest("completeEditing active is " + (this.activeCell == null ? "null" : "no null"));
            }
            if (this.activeCell != null) {
                if (GXTLogConfiguration.loggingIsEnabled()) {
                    LOGGER.finest("completeEditing");
                }
    
    
                final Element elem = this.getEditableGrid().getView().getCell(
                        this.activeCell.getRow(), this.activeCell.getCol());
                elem.getFirstChildElement().getStyle().setVisibility(Style.Visibility.VISIBLE);
    
    
                this.doCompleteEditing();
            }
    
    
            this.stopMonitoring();
        }
    
    
        @Override
        public final void cancelEditing() {
            this.ignoreScroll = false;
            if (GXTLogConfiguration.loggingIsEnabled()) {
                LOGGER.finest("cancelEditing active is " + (this.activeCell == null ? "null" : "no null"));
            }
            if (this.activeCell != null) {
                final Element elem = this.getEditableGrid().getView().getCell(
                        this.activeCell.getRow(), this.activeCell.getCol());
                elem.getFirstChildElement().getStyle().setVisibility(Style.Visibility.VISIBLE);
    
    
                final ColumnConfig<M, ?> c = this.columnModel.getColumn(this.activeCell.getCol());
                final Widget widget = this.getWidget(c);
                if (widget instanceof Field<?>) {
                    ((Field<?>) widget).clear();
                }
    
    
                this.removeEditor(this.activeCell, widget);
    
    
                final GridCell gc = this.activeCell;
                this.activeCell = null;
                this.fireEvent(new CancelEditEvent<M>(gc));
    
    
                if (this.focusOnComplete) {
                    this.focusOnComplete = false;
                    this.focusGrid();
    
    
                    // EXTGWT-2856 focus of grid not working after canceling an edit in IE.
                    // something is stealing focus and the only fix so far is to run focus call in a timer. deferred does
                    // not fix.
                    // need to find why focus is not staying on first call.
                    if (GXT.isIE()) {
                        final Timer t = new Timer() {
                            @Override
                            public void run() {
                                GridInlineWidgetEditing.this.focusGrid();
                            }
                        };
                        t.schedule(100);
                    }
                }
            }
    
    
            this.stopMonitoring();
        }
    
    
        @SuppressWarnings("unchecked")
        protected final <N, O> void doStartEditing(final GridCell cell) {
            if (GXTLogConfiguration.loggingIsEnabled()) {
                LOGGER.finest("doStartEditing");
            }
    
    
            if (this.getEditableGrid() != null && this.getEditableGrid().isAttached() && cell != null) {
                final M value = this.getEditableGrid().getStore().get(cell.getRow());
    
    
                final ColumnConfig<M, N> column = this.columnModel.getColumn(cell.getCol());
                if (column != null && value != null) {
                    final Converter<N, O> converter = this.getConverter(column);
                    final ValueProvider<? super M, N> v = column.getValueProvider();
                    final N colValue = this.getEditableGrid().getStore().hasRecord(value) ?
                            this.getEditableGrid().getStore().getRecord(value).getValue(v) : v.getValue(value);
    
    
                    O convertedValue;
                    if (converter != null) {
                        convertedValue = converter.convertModelValue(colValue);
                    } else {
                        convertedValue = (O) colValue;
                    }
    
    
                    final Widget widget = this.getWidget(column);
                    if (widget != null) {
                        if (widget instanceof Field) {
                            ((Field<O>) widget).setErrorSupport(null);
                        }
    
    
                        this.activeCell = cell;
    
    
                        if (GXTLogConfiguration.loggingIsEnabled()) {
                            LOGGER.finest("doStartEditing convertedValue: " + convertedValue);
                        }
    
    
                        if (widget instanceof HasValue) {
                            ((HasValue<O>) widget).setValue(convertedValue);
                        }
    
    
                        if (widget instanceof TriggerField<?>) {
                            ((TriggerField<?>) widget).setMonitorTab(false);
                        }
    
    
                        if (widget instanceof CheckBox) {
                            ((CheckBox) widget).setBorders(true);
                        }
    
    
                        this.getEditableGrid().getView().getEditorParent().appendChild(widget.getElement());
                        ComponentHelper.setParent(this.getEditableGrid(), widget);
                        ComponentHelper.doAttach(widget);
    
    
                        if (widget instanceof Component) {
                            ((Component) widget).setWidth(column.getWidth());
                            ((Component) widget).getElement().makePositionable(true);
                        }
    
    
                        final Element row = this.getEditableGrid().getView().getRow(cell.getRow());
    
    
                        int left = 0;
                        for (int i = 0; i < cell.getCol(); i++) {
                            if (!this.columnModel.isHidden(i)) {
                                left += this.columnModel.getColumnWidth(i);
                            }
                        }
    
    
                        if (widget instanceof Component) {
                            ((Component) widget).getElement().setLeftTop(left,
                                    row.getAbsoluteTop() - this.getEditableGrid().getView().getBody().getAbsoluteTop());
                            ((Component) widget).show();
                        }
    
    
                        this.startMonitoring();
    
    
                        Scheduler.get().scheduleDeferred(new ScheduledCommand() {
                            @Override
                            public void execute() {
                                if (GXTLogConfiguration.loggingIsEnabled()) {
                                    LOGGER.finest("doStartEditing scheduleDeferred doFocus ");
                                }
    
    
                                // Browsers select all when tabbing into a input and put cursor at location when clicking
                                // into an input with inline editing, the field is not visible at time of click so we select
                                // all. we ignore field.isSelectOnFocus as this only applies when clicking into a field
                                if (widget instanceof ValueBaseField<?>) {
                                    final ValueBaseField<?> vf = (ValueBaseField<?>) widget;
                                    vf.selectAll();
                                }
    
    
                                // EXTGWT-2856 calling doFocus before selectAll is causing blur to fire which ends the edit
                                // immediately after it starts
                                GridInlineWidgetEditing.this.doFocus(widget);
    
    
                                GridInlineWidgetEditing.this.ignoreScroll = false;
    
    
                                GridInlineWidgetEditing.this.fieldRegistration.removeHandler();
    
    
                                if (widget instanceof HasValueChangeHandlers) {
                                    GridInlineWidgetEditing.this.fieldRegistration.add(((HasValueChangeHandlers<O>) widget)
                                            .addValueChangeHandler(new ValueChangeHandler<O>() {
                                                @Override
                                                public void onValueChange(final ValueChangeEvent<O> event) {
                                                    if (GXTLogConfiguration.loggingIsEnabled()) {
                                                        LOGGER.finest("doStartEditing onValueChanged");
                                                    }
    
    
                                                    // If enter key cause value change we want to ignore the next enter key
                                                    // otherwise new edit will start by onEnter
                                                    GridInlineWidgetEditing.this.ignoreNextEnter = true;
    
    
                                                    final Timer t = new Timer() {
                                                        @Override
                                                        public void run() {
                                                            GridInlineWidgetEditing.this.ignoreNextEnter = false;
                                                        }
                                                    };
    
    
                                                    GridInlineWidgetEditing.this.completeEditing();
                                                    t.schedule(100);
                                                }
                                            }));
                                }
    
    
                                if (widget instanceof HasBlurHandlers) {
                                    GridInlineWidgetEditing.this.fieldRegistration.add(((HasBlurHandlers) widget)
                                            .addBlurHandler(new BlurHandler() {
                                                @Override
                                                public void onBlur(final BlurEvent event) {
                                                    if (GXTLogConfiguration.loggingIsEnabled()) {
                                                        LOGGER.finest("doStartEditing onBlur");
                                                    }
    
    
                                                    GridInlineWidgetEditing.this.ignoreNextEnter = true;
    
    
                                                    final Timer t = new Timer() {
                                                        @Override
                                                        public void run() {
                                                            GridInlineWidgetEditing.this.ignoreNextEnter = false;
                                                        }
                                                    };
    
    
                                                    if (GXTLogConfiguration.loggingIsEnabled()) {
                                                        LOGGER.finest("doStartEditing onBlur call cancelEditing ");
                                                    }
    
    
                                                    GridInlineWidgetEditing.this.cancelEditing();
                                                    t.schedule(100);
                                                }
                                            }));
                                }
    
    
                                GridInlineWidgetEditing.this.fireEvent(new StartEditEvent<M>(cell));
                            }
                        });
                    }
                }
            }
    
    
        }
    
    
        @SuppressWarnings("unchecked")
        protected final <N, O> void doCompleteEditing() {
            if (GXTLogConfiguration.loggingIsEnabled()) {
                LOGGER.finest("doCompleteEditing activeCell is " + (this.activeCell != null ? " is not null" : "is null"));
            }
    
    
            if (this.activeCell != null) {
                final ColumnConfig<M, N> c = this.columnModel.getColumn(this.activeCell.getCol());
    
    
                final Widget widget = this.getWidget(c);
                if (widget != null) {
                    final Converter<N, O> converter = this.getConverter(c);
    
    
                    if ((widget instanceof Field<?>) && !((Field<?>) widget).isValid() && this.revertInvalid) {
                        this.cancelEditing();
                        return;
                    }
    
    
                    O fieldValue = null;
    
    
                    if (widget instanceof ValueBaseField) {
                        fieldValue = ((ValueBaseField<O>) widget).getCurrentValue();
                    } else if (widget instanceof HasValue) {
                        fieldValue = ((HasValue<O>) widget).getValue();
                    }
    
    
                    final N convertedValue;
                    if (converter != null) {
                        convertedValue = converter.convertFieldValue(fieldValue);
                    } else {
                        convertedValue = (N) fieldValue;
                    }
    
    
                    if (GXTLogConfiguration.loggingIsEnabled()) {
                        LOGGER.finest("Converted value: " + convertedValue);
                    }
    
    
                    this.removeEditor(this.activeCell, widget);
    
    
                    final ListStore<M> store = this.getEditableGrid().getStore();
                    final ListStore<M>.Record r = store.getRecord(store.get(this.activeCell.getRow()));
    
    
                    this.rowUpdated = true;
    
    
                    r.addChange(c.getValueProvider(), convertedValue);
                    this.fireEvent(new CompleteEditEvent<M>(this.activeCell));
    
    
                    if (this.focusOnComplete) {
                        this.focusOnComplete = false;
                        this.focusGrid();
                    }
                }
    
    
                this.activeCell = null;
            }
        }
    
    
        protected final void doFocus(final Widget widget) {
            try {
                if (widget instanceof Component) {
                    ((Component) widget).focus();
                } else if (widget instanceof Focusable) {
                    ((Focusable) widget).setFocus(true);
                } else {
                    FocusImpl.getFocusImplForWidget().focus(widget.getElement());
                }
            } catch (final Exception ex) {
                // IE throws exception if element not focusable
            }
        }
    
    
        @Override
        protected final KeyNav ensureInternalKeyNav() {
            if (this.keyNav == null) {
                this.keyNav = new GridEditingKeyNav();
            }
            return this.keyNav;
        }
    
    
        @Override
        protected final SafeHtml getErrorHtml() {
            final SafeHtmlBuilder sb = new SafeHtmlBuilder();
            sb.appendHtmlConstant("<ul>");
            final ColumnConfig<M, ?> c = this.columnModel.getColumn(this.activeCell.getCol());
            final Widget widget = this.getWidget(c);
    
    
            this.getErrorMessage(widget, sb, c.getHeader().asString());
    
    
            sb.appendHtmlConstant("</ul>");
            return sb.toSafeHtml();
        }
    
    
        protected final void getErrorMessage(final Widget widget, final SafeHtmlBuilder sb, final String title) {
            boolean result = true;
    
    
            if (widget instanceof ValueBaseField) {
                final ValueBaseField<?> vfield = (ValueBaseField<?>) widget;
                result = vfield.isCurrentValid(true);
            }
    
    
            if (widget instanceof Field) {
                final Field<?> field = (Field<?>) widget;
                if (!result || !field.isValid(true)) {
                    sb.appendHtmlConstant("<li><b>");
                    sb.appendEscaped(title);
                    sb.appendHtmlConstant("</b>: ");
                    sb.appendEscaped(field.getErrors().get(0).getMessage());
                    sb.appendHtmlConstant("</li>");
                }
            }
        }
    
    
        @Override
        protected final boolean isValid() {
            if (this.activeCell == null) {
                return true;
            }
            final ColumnConfig<M, ?> c = this.columnModel.getColumn(this.activeCell.getCol());
            final Widget w = this.getWidget(c);
            if (w instanceof ValueBaseField<?>) {
                final ValueBaseField<?> f = (ValueBaseField<?>) w;
                if (!f.isCurrentValid(true)) {
                    return false;
                }
            } else if (w instanceof Field<?>) {
                final Field<?> f = (Field<?>) w;
                if (!f.isValid(true)) {
                    return false;
                }
            }
    
    
            return true;
        }
    
    
        @Override
        protected final void onEnter(final NativeEvent evt) {
            if (GXTLogConfiguration.loggingIsEnabled()) {
                LOGGER.finest("onEnter");
            }
            if (this.ignoreNextEnter) {
                this.ignoreNextEnter = false;
                this.focusGrid();
                return;
            }
    
    
            // enter key with no value changed fired
            if (this.activeCell != null) {
                if (GXTLogConfiguration.loggingIsEnabled()) {
                    LOGGER.finest("onEnter activeCell not null (enter key no value change), cancel edit");
                }
    
    
                final ColumnConfig<M, ?> c = this.columnModel.getColumn(this.activeCell.getCol());
                final Widget widget = this.getWidget(c);
                if (widget instanceof TextArea) {
                    if (GXTLogConfiguration.loggingIsEnabled()) {
                        LOGGER.finest("onEnter editor type TextArea so ignoring");
                    }
                    return;
                }
    
    
                this.focusOnComplete = true;
                this.cancelEditing();
                return;
            }
    
    
            final GridSelectionModel<M> sm = this.getEditableGrid().getSelectionModel();
            if (sm instanceof CellSelectionModel) {
                final CellSelection<M> cell = ((CellSelectionModel<M>) sm).getSelectCell();
                if (cell != null) {
                    evt.preventDefault();
                    this.startEditing(new GridCell(cell.getRow(), cell.getCell()));
                }
            }
        }
    
    
        @Override
        protected final void onEsc(final NativeEvent evt) {
            if (this.activeCell != null) {
                this.focusOnComplete = true;
                super.onEsc(evt);
            }
        }
    
    
        @Override
        protected final void onMouseDown(final MouseDownEvent event) {
            // do we have an active edit at time of mouse down
            this.activeEdit = this.activeCell != null;
            this.rowUpdated = false;
        }
    
    
        @Override
        protected final void onMouseUp(final MouseUpEvent event) {
            // there was an active edit on mouse down and that edit has ended
            // we do not get a "click" event if the previous edit caused the row to be
            // updated one issue is that we will start the new edit even if clicks to
            // edit is 2 and the previous update updated the row
            if (this.activeEdit && this.rowUpdated && this.activeCell == null) {
                final Element target = event.getNativeEvent().getEventTarget().cast();
                this.startEditing(target);
            }
            this.activeEdit = false;
            this.rowUpdated = false;
        }
    
    
        @Override
        protected final void onScroll(final ScrollEvent event) {
            if (GXTLogConfiguration.loggingIsEnabled()) {
                LOGGER.finest("onScroll ignoreScroll " + (this.ignoreScroll ? "true" : "false calling cancelEditing"));
            }
            if (!this.ignoreScroll) {
                this.cancelEditing();
            }
        }
    
    
        protected final void onTab(final NativeEvent evt) {
            if (GXTLogConfiguration.loggingIsEnabled()) {
                LOGGER.finest("onTab");
            }
    
    
            // Keep active cell since we manually fire blur (finishEditing) which will call cancel edit
            // clearing active cell
            final GridCell active = this.activeCell;
    
    
            if (GXTLogConfiguration.loggingIsEnabled()) {
                LOGGER.finest("onTab activeCell is " + (this.activeCell == null ? "null" : "not null"));
            }
    
    
            if (this.activeCell != null) {
                final ColumnConfig<M, ?> c = this.columnModel.getColumn(this.activeCell.getCol());
    
    
                final Widget widget = this.getWidget(c);
    
    
                // We handle navigation programatically
                evt.preventDefault();
    
    
                // Since we are preventingDefault on tab key, the field will not blur on its
                // own, which means the value change event will not fire so we manually blur
                // the field, so we call finishEditing
                if (GXTLogConfiguration.loggingIsEnabled()) {
                    LOGGER.finest("onTab calling field.finishEditing()");
                }
                if (widget instanceof Field<?>) {
                    ((Field<?>) widget).finishEditing();
                }
            }
    
    
            if (active != null) {
                GridCell newCell = null;
    
    
                if (evt.getShiftKey()) {
                    newCell = this.getEditableGrid().walkCells(active.getRow(), active.getCol() - 1, -1, this.callback);
                } else {
                    newCell = this.getEditableGrid().walkCells(active.getRow(), active.getCol() + 1, 1, this.callback);
                }
                if (newCell != null) {
                    final GridCell c = newCell;
    
    
                    Scheduler.get().scheduleFinally(new ScheduledCommand() {
                        @Override
                        public void execute() {
                            if (GXTLogConfiguration.loggingIsEnabled()) {
                                LOGGER.finest("onTab scheduleFinally startEditing");
                            }
                            GridInlineWidgetEditing.this.startEditing(c);
                        }
                    });
                } else {
                    // When tabbing and no next cell to start edit, current edit is not ending
                    // the focusCell call is not causing field to blur and finish editing
                    if (this.isEditing()) {
                        this.completeEditing();
                    }
                    this.getEditableGrid().getView().focusCell(active.getRow(), active.getCol(), true);
                }
            }
        }
    
    
        @Override
        protected final void showTooltip(final SafeHtml content) {
            if (this.activeCell == null) {
                return;
            }
    
    
            final ColumnConfig<M, ?> c = this.columnModel.getColumn(this.activeCell.getCol());
            final Widget widget = this.getWidget(c);
    
    
            if (this.tooltip == null) {
                final ToolTipConfig config = new ToolTipConfig();
                config.setAutoHide(false);
                config.setMouseOffsetX(0);
                config.setMouseOffsetY(0);
                config.setAnchor(Side.LEFT);
                this.tooltip = new ToolTip(widget, config);
                this.tooltip.setMaxWidth(600);
            }
    
    
            this.tooltip.initTarget(widget);
    
    
            final ToolTipConfig config = this.tooltip.getToolTipConfig();
            config.setBodyHtml(content);
            this.tooltip.update(config);
            this.tooltip.enable();
            if (!this.tooltip.isAttached()) {
                this.tooltip.show();
            }
        }
    
    
        private void removeEditor(final GridCell cell, final Widget widget) {
            this.removeFieldBlurHandler();
    
    
            if ((GXT.isIE8() || GXT.isIE7() || GXT.isIE6()) && widget instanceof ValueBaseField<?>) {
                ((ValueBaseField<?>) widget).getCell().getInputElement(widget.getElement()).blur();
            }
    
    
            if (widget != null && widget.isAttached()) {
                if (widget instanceof Component) {
                    ((Component) widget).hide();
                }
                ComponentHelper.setParent(null, widget);
                ComponentHelper.doDetach(widget);
            }
        }
    
    
        private void removeFieldBlurHandler() {
            this.fieldRegistration.removeHandler();
        }
    
    
        protected class GridEditingKeyNav extends AbstractGridEditingKeyNav {
            @Override
            public final void onTab(final NativeEvent evt) {
                GridInlineWidgetEditing.this.onTab(evt);
            }
        }
    }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •