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

com.extjs.gxt.ui.client.widget.grid.RowEditor Maven / Gradle / Ivy

There is a newer version: 2.3.1-gwt22
Show newest version
/*
 * Sencha GXT 2.3.0 - Sencha for GWT
 * Copyright(c) 2007-2013, Sencha, Inc.
 * [email protected]
 * 
 * http://www.sencha.com/products/gxt/license/
 */
 package com.extjs.gxt.ui.client.widget.grid;

import java.util.Map;

import com.extjs.gxt.ui.client.GXT;
import com.extjs.gxt.ui.client.Style;
import com.extjs.gxt.ui.client.core.El;
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.ButtonEvent;
import com.extjs.gxt.ui.client.event.ColumnModelEvent;
import com.extjs.gxt.ui.client.event.ComponentEvent;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.GridEvent;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.event.RowEditorEvent;
import com.extjs.gxt.ui.client.event.SelectionListener;
import com.extjs.gxt.ui.client.store.Record;
import com.extjs.gxt.ui.client.util.KeyNav;
import com.extjs.gxt.ui.client.util.Margins;
import com.extjs.gxt.ui.client.util.Point;
import com.extjs.gxt.ui.client.widget.Component;
import com.extjs.gxt.ui.client.widget.ComponentHelper;
import com.extjs.gxt.ui.client.widget.ComponentManager;
import com.extjs.gxt.ui.client.widget.ComponentPlugin;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.extjs.gxt.ui.client.widget.Html;
import com.extjs.gxt.ui.client.widget.button.Button;
import com.extjs.gxt.ui.client.widget.form.Field;
import com.extjs.gxt.ui.client.widget.form.LabelField;
import com.extjs.gxt.ui.client.widget.form.TriggerField;
import com.extjs.gxt.ui.client.widget.grid.EditorGrid.ClicksToEdit;
import com.extjs.gxt.ui.client.widget.layout.HBoxLayout;
import com.extjs.gxt.ui.client.widget.layout.HBoxLayoutData;
import com.extjs.gxt.ui.client.widget.layout.MarginData;
import com.extjs.gxt.ui.client.widget.layout.TableLayout;
import com.extjs.gxt.ui.client.widget.tips.ToolTip;
import com.extjs.gxt.ui.client.widget.tips.ToolTipConfig;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Widget;

/**
 * This RowEditor should be used as a plugin to {@link Grid}. It displays an
 * editor for all cells in a row.
 * 
 * 
*
Events:
* *
BeforeEdit : RowEditorEvent(rowEditor, rowIndex)
*
Fires before row editing is triggered. Listeners can cancel the action * by calling {@link BaseEvent#setCancelled(boolean)}.
*
    *
  • rowEditor : this
  • *
  • rowIndex : the row index of the row about to be edited
  • *
*
* *
ValidateEdit : RowEditorEvent(rowEditor, rowIndex, changes)
*
Fires right before the model is updated. Listeners can cancel the action * by calling {@link BaseEvent#setCancelled(boolean)}.
*
    *
  • rowEditor : this
  • *
  • rowIndex : the row index of the row about to be edited
  • *
  • changes : a map of property name and new values
  • *
*
* *
AfterEdit : RowEditorEvent(rowEditor, rowIndex, changes)
*
Fires after a row has been edited.
*
    *
  • rowEditor : this
  • *
  • rowIndex : the row index of the row that was edited
  • *
  • changes : a map of property name and new values
  • *
*
*
* * @param the model type */ @SuppressWarnings("deprecation") public class RowEditor extends ContentPanel implements ComponentPlugin { public class RowEditorMessages { private String cancelText = GXT.MESSAGES.rowEditor_cancelText(); private String dirtyText = GXT.MESSAGES.rowEditor_dirtyText(); private String errorTipTitleText = GXT.MESSAGES.rowEditor_tipTitleText(); private String saveText = GXT.MESSAGES.rowEditor_saveText(); /** * Returns the buttons cancel text. * * @return the text */ public String getCancelText() { return cancelText; } /** * Returns the tool tip dirty text. * * @return the dirtyText */ public String getDirtyText() { return dirtyText; } /** * Returns the error tool tip title. * * @return the errorTipTitleText */ public String getErrorTipTitleText() { return errorTipTitleText; } /** * Returns the buttons save text. * * @return the text */ public String getSaveText() { return saveText; } /** * Sets the buttons cancel text * * @param cancelText the cancel text */ public void setCancelText(String cancelText) { this.cancelText = cancelText; } /** * Sets the tool tip dirty text. * * @param dirtyText the dirtyText to set */ public void setDirtyText(String dirtyText) { this.dirtyText = dirtyText; } /** * Sets the error tool tip title. * * @param errorTipTitleText the errorTipTitleText to set */ public void setErrorTipTitleText(String errorTipTitleText) { this.errorTipTitleText = errorTipTitleText; } /** * Sets the buttons save text * * @param saveText the save text */ public void setSaveText(String saveText) { this.saveText = saveText; } } protected ContentPanel btns; protected Grid grid; protected RowEditorMessages messages; protected boolean renderButtons = true; protected int rowIndex; protected Button saveBtn, cancelBtn; private boolean bound; private int buttonPad = 3; private ClicksToEdit clicksToEdit = ClicksToEdit.ONE; private boolean editing; private boolean errorSummary = true; private int frameWidth = 5; private boolean initialized; private boolean lastValid; private Listener> listener; private int monitorPoll = 200; private Timer monitorTimer; private boolean monitorValid = true; private Record record; private ToolTip tooltip; protected Html toolTipAlignWidget; public RowEditor() { super(); setFooter(true); setLayout(new HBoxLayout()); addStyleName("x-small-editor"); baseStyle = "x-row-editor"; messages = new RowEditorMessages(); } /** * Returns the clicks to edit. * * @return the clicks to edit */ public ClicksToEdit getClicksToEdit() { return clicksToEdit; } /** * Returns the roweditors's messages. * * @return the messages */ public RowEditorMessages getMessages() { return messages; } /** * Returns the interval in ms in that the roweditor is validated * * @return the interval in ms in that the roweditor is validated */ public int getMonitorPoll() { return monitorPoll; } @SuppressWarnings("unchecked") public void init(Component component) { grid = (Grid) component; grid.disableTextSelection(false); listener = new Listener>() { public void handleEvent(GridEvent be) { if (be.getType() == Events.RowDoubleClick) { onRowDblClick(be); } else if (be.getType() == Events.RowClick) { onRowClick(be); } else if (be.getType() == Events.OnKeyDown) { onGridKey(be); } else if (be.getType() == Events.ColumnResize || be.getType() == Events.Resize) { verifyLayout(false); } else if (be.getType() == Events.BodyScroll) { positionButtons(); } else if (be.getType() == Events.Detach) { stopEditing(false); } else if (be.getType() == Events.Reconfigure && initialized) { stopEditing(false); removeAll(); initialized = false; } } }; grid.addListener(Events.RowDoubleClick, listener); grid.addListener(Events.Resize, listener); grid.addListener(Events.RowClick, listener); grid.addListener(Events.OnKeyDown, listener); grid.addListener(Events.ColumnResize, listener); grid.addListener(Events.BodyScroll, listener); grid.addListener(Events.Detach, listener); grid.addListener(Events.Reconfigure, listener); grid.getColumnModel().addListener(Events.HiddenChange, new Listener() { public void handleEvent(ColumnModelEvent be) { verifyLayout(false); } }); grid.getColumnModel().addListener(Events.ColumnMove, new Listener() { public void handleEvent(ColumnModelEvent be) { if (initialized) { stopEditing(false); removeAll(); initialized = false; } } }); grid.getView().addListener(Events.Refresh, new Listener() { public void handleEvent(BaseEvent be) { stopEditing(false); } }); } /** * Returns true of the RowEditor is active and editing. * * @return true if the RowEditor is active */ public boolean isEditing() { return editing; } /** * Returns true if a tooltip with an error summary is shown. * * @return true if a tooltip with an error summary is shown */ public boolean isErrorSummary() { return errorSummary; } /** * Returns true if this roweditor is monitored. * * @return true if the roweditor is monitored */ public boolean isMonitorValid() { return monitorValid; } @Override public void onComponentEvent(ComponentEvent ce) { super.onComponentEvent(ce); if (ce.getEventTypeInt() == KeyNav.getKeyEvent().getEventCode()) { if (ce.getKeyCode() == KeyCodes.KEY_ENTER) { onEnter(ce); } else if (ce.getKeyCode() == KeyCodes.KEY_ESCAPE) { onEscape(ce); } else if (ce.getKeyCode() == KeyCodes.KEY_TAB) { onTab(ce); } } } /** * Sets the number of clicks to edit (defaults to ONE). * * @param clicksToEdit the clicks to edit */ public void setClicksToEdit(ClicksToEdit clicksToEdit) { this.clicksToEdit = clicksToEdit; } /** * True to show a tooltip with an error summary (defaults to true) * * @param errorSummary true to show an error summary. */ public void setErrorSummary(boolean errorSummary) { this.errorSummary = errorSummary; } /** * Sets the roweditors's messages. * * @param messages the messages */ public void setMessages(RowEditorMessages messages) { this.messages = messages; } /** * Sets the polling interval in ms in that the roweditor validation is done * (defaults to 200) * * @param monitorPoll the polling interval in ms in that validation is done */ public void setMonitorPoll(int monitorPoll) { this.monitorPoll = monitorPoll; } /** * True to monitor the valid status of this roweditor (defaults to true) * * @param monitorValid true to monitor this roweditor */ public void setMonitorValid(boolean monitorValid) { this.monitorValid = monitorValid; } /** * Start editing of a specific row. * * @param rowIndex the index of the row to edit. * @param doFocus true to focus the field */ @SuppressWarnings("unchecked") public void startEditing(int rowIndex, boolean doFocus) { if (disabled) { return; } if (editing && isDirty()) { showTooltip(getMessages().getDirtyText()); return; } hideTooltip(); M model = (M) grid.getStore().getAt(rowIndex); Record r = getRecord(model); RowEditorEvent ree = new RowEditorEvent(this, rowIndex); ree.setRecord(r); Element row = (Element) grid.getView().getRow(rowIndex); if (row == null || model == null || !fireEvent(Events.BeforeEdit, ree)) { return; } editing = true; record = r; this.rowIndex = rowIndex; if (!isRendered()) { render((Element) grid.getView().getEditorParent()); } ComponentHelper.doAttach(this); if (!initialized) { initFields(); } ColumnModel cm = grid.getColumnModel(); for (int i = 0, len = cm.getColumnCount(); i < len; i++) { Field f = (Field) getItem(i); if (GXT.isAriaEnabled()) { if (i == 0 && saveBtn != null) { saveBtn.getFocusSupport().setNextId(f.getId()); } f.getAriaSupport().setLabel(cm.getColumnHeader(i)); } String dIndex = cm.getDataIndex(i); CellEditor ed = cm.getEditor(i); Object val = ed != null ? ed.preProcessValue(record.get(dIndex)) : record.get(dIndex); f.updateOriginalValue(val); f.setValue(val); } if (cancelBtn != null) { cancelBtn.getFocusSupport().setPreviousId(getItem(getItemCount() - 1).getId()); } if (!isVisible()) { show(); } el().setXY(getPosition(row)); verifyLayout(true); if (doFocus) { deferFocus(null); } lastValid = false; el().scrollIntoView((Element) grid.getView().getEditorParent(), false, new int[] {renderButtons ? btns.getHeight() : 0, 0}); } /** * Stops editing. * * @param saveChanges true to save the changes. false to ignore them. */ public void stopEditing(boolean saveChanges) { if (disabled || !editing) { return; } editing = false; Map data = new FastMap(); boolean hasChange = false; ColumnModel cm = grid.getColumnModel(); for (int i = 0, len = cm.getColumnCount(); i < len; i++) { if (!cm.isHidden(i)) { Component c = getItem(i); if (c instanceof LabelField) { continue; } else if (c instanceof Field) { Field f = (Field) c; String dindex = cm.getDataIndex(i); Object oldValue = record.get(dindex); CellEditor ed = cm.getEditor(i); Object value = ed != null ? ed.postProcessValue(f.getValue()) : f.getValue(); if ((oldValue == null && value != null) || (oldValue != null && !oldValue.equals(value))) { data.put(dindex, value); hasChange = true; } } } } RowEditorEvent ree = new RowEditorEvent(this, rowIndex); ree.setRecord(record); ree.setChanges(data); if (!saveChanges || !isValid()) { fireEvent(Events.CancelEdit, ree); } else if (hasChange && fireEvent(Events.ValidateEdit, ree)) { record.beginEdit(); for (String k : data.keySet()) { record.set(k, data.get(k)); } record.endEdit(); fireEvent(Events.AfterEdit, ree); } hide(); } protected void afterRender() { super.afterRender(); positionButtons(); if (monitorValid) { startMonitoring(); } if (renderButtons) { btns.setWidth((getMinButtonWidth() * 2) + (frameWidth * 2) + (buttonPad * 4)); } } protected void bindHandler() { boolean valid = isValid(); if (!valid) { lastValid = false; if (errorSummary) { showTooltip(getErrorText()); } } else if (valid && !lastValid) { hideTooltip(); lastValid = true; } if (saveBtn != null) { saveBtn.setEnabled(valid); } if (!isVisible() && tooltip != null && tooltip.isEnabled()) { hideTooltip(); } } protected void createButtons() { btns = new ContentPanel() { protected void createStyles(String baseStyle) { baseStyle = "x-plain"; headerStyle = baseStyle + "-header"; headerTextStyle = baseStyle + "-header-text"; bwrapStyle = baseStyle + "-bwrap"; tbarStyle = baseStyle + "-tbar"; bodStyle = baseStyle + "-body"; bbarStyle = baseStyle + "-bbar"; footerStyle = baseStyle + "-footer"; collapseStyle = baseStyle + "-collapsed"; } }; btns.setHeaderVisible(false); btns.addStyleName("x-btns"); btns.setLayout(new TableLayout(2)); cancelBtn = new Button(getMessages().getCancelText(), new SelectionListener() { @Override public void componentSelected(ButtonEvent ce) { stopEditing(false); } }); cancelBtn.setMinWidth(getMinButtonWidth()); btns.add(cancelBtn); saveBtn = new Button(getMessages().getSaveText(), new SelectionListener() { @Override public void componentSelected(ButtonEvent ce) { stopEditing(true); } }); saveBtn.setMinWidth(getMinButtonWidth()); btns.add(saveBtn); cancelBtn.getFocusSupport().setNextId(saveBtn.getId()); saveBtn.getFocusSupport().setPreviousId(cancelBtn.getId()); btns.render(getElement("bwrap")); btns.layout(); btns.getElement().removeAttribute("tabindex"); btns.getFocusSupport().setIgnore(true); } protected void deferFocus(final int colIndex) { DeferredCommand.addCommand(new Command() { public void execute() { doFocus(colIndex); } }); } protected void deferFocus(final Point pt) { DeferredCommand.addCommand(new Command() { public void execute() { doFocus(pt); } }); } @Override protected void doAttachChildren() { super.doAttachChildren(); ComponentHelper.doAttach(btns); ComponentHelper.doAttach(toolTipAlignWidget); } @Override protected void doDetachChildren() { super.doDetachChildren(); ComponentHelper.doDetach(btns); ComponentHelper.doDetach(toolTipAlignWidget); } protected void doFocus(Point pt) { if (isVisible()) { int index = 0; if (pt != null) { index = getTargetColumnIndex(pt); } if (index >= 0) { doFocus(index); } } } protected void doFocus(int colIndex) { if (isVisible()) { ColumnModel cm = this.grid.getColumnModel(); for (int i = colIndex, len = cm.getColumnCount(); i < len && i >= 0; i++) { ColumnConfig c = cm.getColumn(i); if (!c.isHidden() && c.getEditor() != null) { c.getEditor().getField().focus(); break; } } } } protected void ensureVisible(CellEditor editor) { if (isVisible()) { grid.getView().ensureVisible(this.rowIndex, indexOf(editor), true); } } protected Component findField(Element elem) { El e = El.fly(elem).findParent(".x-row-editor-field", 3); if (e != null) { return ComponentManager.get().get(e.getId()); } return null; } protected String getErrorText() { StringBuffer sb = new StringBuffer(); sb.append("
    "); for (int i = 0; i < getItemCount(); i++) { Field f = (Field) getItem(i); if (!f.isValid(true)) { sb.append("
  • "); sb.append(grid.getColumnModel().getColumn(i).getHeaderHtml()); sb.append(": "); sb.append(f.getErrorMessage()); sb.append("
  • "); } } sb.append("
"); return sb.toString(); } protected Point getPosition(Element row) { return El.fly(row).getXY(); } protected Record getRecord(M model) { return grid.getStore().getRecord(model); } protected int getTargetColumnIndex(Point pt) { for (int i = 0, j = 0; i < grid.getColumnModel().getColumnCount(); i++) { ColumnConfig c = grid.getColumnModel().getColumn(i); if (!c.isHidden()) { if (El.fly(grid.getView().getHeaderCell(i)).getRegion().right >= pt.x) { return j; } j++; } } return -1; } protected void hideTooltip() { if (tooltip != null) { tooltip.hide(); tooltip.disable(); } } protected void initFields() { ColumnModel cm = grid.getColumnModel(); for (int i = 0, len = cm.getColumnCount(); i < len; i++) { ColumnConfig c = cm.getColumn(i); CellEditor ed = c.getEditor(); Field f = ed != null ? ed.getField() : new LabelField(); if (f instanceof TriggerField) { ((TriggerField) f).setMonitorTab(true); } f.setWidth(cm.getColumnWidth(i)); HBoxLayoutData ld = new HBoxLayoutData(); if (i == 0) { ld.setMargins(new Margins(0, 1, 2, 1)); } else if (i == len - 1) { ld.setMargins(new Margins(0, 0, 2, 1)); } else { ld.setMargins(new Margins(0, 1, 2, 2)); } f.setMessageTarget("tooltip"); f.addStyleName("x-row-editor-field"); // needed because we remove it from the celleditor clearParent(f); insert(f, i, ld); } initialized = true; } @SuppressWarnings("unchecked") protected boolean isDirty() { for (Component f : getItems()) { if (((Field) f).isDirty()) { return true; } } return false; } protected boolean isValid() { boolean valid = true; for (Component c : getItems()) { Field f = (Field) c; if (!f.isValid(true)) { return false; } } return valid; } protected void onEnter(ComponentEvent ce) { stopEditing(true); } protected void onEscape(ComponentEvent ce) { stopEditing(false); } protected void onGridKey(GridEvent e) { int kc = e.getKeyCode(); if ((kc == KeyCodes.KEY_ENTER || (kc == 113 && GXT.isWindows)) && !isVisible()) { M r = grid.getSelectionModel().getSelectedItem(); if (r != null) { int index = this.grid.store.indexOf(r); startEditing(index, true); e.cancelBubble(); } } } protected void onHide() { super.onHide(); stopMonitoring(); grid.getView().focusRow(rowIndex); record = null; ComponentHelper.doDetach(this); } @Override protected void onRender(Element target, int index) { super.onRender(target, index); el().makePositionable(true); sinkEvents(KeyNav.getKeyEvent().getEventCode()); swallowEvent(Events.OnKeyDown, el().dom, false); swallowEvent(Events.OnKeyUp, el().dom, false); swallowEvent(Events.OnKeyPress, el().dom, false); if (renderButtons) { createButtons(); ComponentHelper.setParent(this, btns); } toolTipAlignWidget = new Html(); toolTipAlignWidget.render(getElement("bwrap"), 0); } protected void onRowClick(GridEvent e) { if (clicksToEdit != ClicksToEdit.TWO) { startEditing(e.getRowIndex(), false); deferFocus(e.getColIndex()); } } protected void onRowDblClick(GridEvent e) { if (clicksToEdit == ClicksToEdit.TWO) { startEditing(e.getRowIndex(), false); deferFocus(e.getColIndex()); } } protected void onShow() { super.onShow(); if (monitorValid) { startMonitoring(); } } protected void onTab(ComponentEvent ce) { Element target = ce.getTarget(); Component c = findField(target); if (saveBtn != null && c != null && ce.isShiftKey() && indexOf(c) == 0) { ce.stopEvent(); saveBtn.focus(); return; } } protected void positionButtons() { if (isVisible()) { GridView view = grid.getView(); int scroll = view.getScrollState().x; int mainBodyWidth = view.scroller.getWidth(true); int h = el().getClientHeight(); if (btns != null) { int columnWidth = view.getTotalWidth(); int width = columnWidth < mainBodyWidth ? columnWidth : mainBodyWidth; int bw = btns.getWidth(true); this.btns.setPosition((width / 2) - (bw / 2) + scroll, h - 2); } if (toolTipAlignWidget != null) { toolTipAlignWidget.setStyleAttribute("position", "absolute"); toolTipAlignWidget.setSize(mainBodyWidth - (view.scroller.isScrollableY() ? XDOM.getScrollBarWidth() : 0), h); toolTipAlignWidget.setPosition(scroll, Style.DEFAULT); } } } protected void showTooltip(String msg) { if (tooltip == null) { ToolTipConfig config = new ToolTipConfig(); config.setAutoHide(false); config.setMouseOffset(new int[] {0, 0}); config.setTitle(getMessages().getErrorTipTitleText()); config.setAnchor("left"); tooltip = new ToolTip(toolTipAlignWidget, config); tooltip.setMaxWidth(600); } ToolTipConfig config = tooltip.getToolTipConfig(); config.setHtml(msg); tooltip.update(config); tooltip.enable(); if (!tooltip.isAttached()) { tooltip.show(); tooltip.el().updateZIndex(0); } } protected void startMonitoring() { if (!bound && monitorValid) { bound = true; if (monitorTimer == null) { monitorTimer = new Timer() { @Override public void run() { RowEditor.this.bindHandler(); } }; } monitorTimer.scheduleRepeating(monitorPoll); } } protected void stopMonitoring() { bound = false; if (monitorTimer != null) { monitorTimer.cancel(); } hideToolTip(); } protected void verifyLayout(boolean force) { if (initialized && (isVisible() || force)) { Element row = (Element) grid.getView().getRow(rowIndex); setSize(El.fly(row).getWidth(false), renderButtons ? btns.getHeight() : 0); syncSize(); ColumnModel cm = grid.getColumnModel(); for (int i = 0, len = cm.getColumnCount(); i < len; i++) { if (!cm.isHidden(i)) { Field f = (Field) getItem(i); f.show(); f.getElement().setAttribute("gxt-dindex", "" + cm.getDataIndex(i)); MarginData md = (MarginData) ComponentHelper.getLayoutData(f); f.setWidth(cm.getColumnWidth(i) - md.getMargins().left - md.getMargins().right); } else { getItem(i).hide(); } } layout(true); positionButtons(); } } private native void clearParent(Widget parent) /*-{ [email protected]::parent = null; }-*/; }