/****************************************************************************** * Copyright (C) 2008 Low Heng Sin * * This program is free software; you can redistribute it and/or modify it * * under the terms version 2 of the GNU General Public License as published * * by the Free Software Foundation. This program is distributed in the hope * * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * * with this program; if not, write to the Free Software Foundation, Inc., * * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * *****************************************************************************/ package org.adempiere.webui.adwindow; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import org.adempiere.util.GridRowCtx; import org.adempiere.webui.ClientInfo; import org.adempiere.webui.LayoutUtils; import org.adempiere.webui.component.Button; import org.adempiere.webui.component.Checkbox; import org.adempiere.webui.component.EditorBox; import org.adempiere.webui.component.NumberBox; import org.adempiere.webui.component.Textbox; import org.adempiere.webui.component.Urlbox; import org.adempiere.webui.editor.IEditorConfiguration; import org.adempiere.webui.editor.WButtonEditor; import org.adempiere.webui.editor.WEditor; import org.adempiere.webui.editor.WEditorPopupMenu; import org.adempiere.webui.editor.WImageEditor; import org.adempiere.webui.editor.WebEditorFactory; import org.adempiere.webui.event.ActionEvent; import org.adempiere.webui.event.ActionListener; import org.adempiere.webui.event.ContextMenuListener; import org.adempiere.webui.panel.HelpController; import org.adempiere.webui.session.SessionManager; import org.adempiere.webui.theme.ThemeManager; import org.adempiere.webui.util.GridTabDataBinder; import org.adempiere.webui.util.ZKUpdateUtil; import org.compiere.model.GridField; import org.compiere.model.GridTab; import org.compiere.model.MStyle; import org.compiere.model.MSysConfig; import org.compiere.util.DisplayType; import org.compiere.util.Env; import org.compiere.util.Msg; import org.compiere.util.Util; import org.zkoss.zk.au.out.AuScript; import org.zkoss.zk.ui.AbstractComponent; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Executions; import org.zkoss.zk.ui.HtmlBasedComponent; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.sys.ComponentCtrl; import org.zkoss.zk.ui.util.Clients; import org.zkoss.zul.Cell; import org.zkoss.zul.Grid; import org.zkoss.zul.Html; import org.zkoss.zul.Label; import org.zkoss.zul.Paging; import org.zkoss.zul.RendererCtrl; import org.zkoss.zul.Row; import org.zkoss.zul.RowRenderer; import org.zkoss.zul.RowRendererExt; import org.zkoss.zul.impl.XulElement; /** * Row renderer for GridTab grid. * @author hengsin * * @author Teo Sarca, teo.sarca@gmail.com *
  • BF [ 2996608 ] GridPanel is not displaying time * https://sourceforge.net/p/adempiere/zk-web-client/420/ */ public class GridTabRowRenderer implements RowRenderer, RowRendererExt, RendererCtrl, EventListener { /** Cell div component attribute to hold field column name value **/ protected static final String COLUMN_NAME_ATTR = "columnName"; /** Cell div component attribute to hold reference to editor component use to create display text for field **/ private static final String DISPLAY_COMPONENT_ATTR = "display.component"; /** Boolean execution attribute to indicate execution is handling the "Select" checkbox's ON_CHECK event **/ private static final String GRID_VIEW_ON_SELECT_ROW_ATTR = "gridView.onSelectRow"; /** Editor component attribute to store row index (absolute) **/ public static final String GRID_ROW_INDEX_ATTR = "grid.row.index"; //styles for grid cell private static final String CELL_DIV_STYLE = "height: 100%; cursor: pointer; "; private static final String CELL_DIV_STYLE_ALIGN_CENTER = CELL_DIV_STYLE + "text-align:center; "; private static final String CELL_DIV_STYLE_ALIGN_RIGHT = CELL_DIV_STYLE + "text-align:right; "; /** default max length for display text for field **/ private static final int MAX_TEXT_LENGTH_DEFAULT = 60; private GridTab gridTab; private int windowNo; /** Sync field editor changes to GridField **/ private GridTabDataBinder dataBinder; /** field editors **/ private Map editors = new LinkedHashMap(); /** readonly field editors to get display text for field value **/ private Map readOnlyEditors = new LinkedHashMap(); private Paging paging; /** internal listener for row event **/ private RowListener rowListener; /** Grid that own this renderer **/ private Grid grid = null; /** GridView that uses this renderer **/ private GridView gridPanel = null; /** current focus row **/ private Row currentRow; /** values of current row. updated in {@link #render(Row, Object[], int)}. **/ private Object[] currentValues; /** true if current row is in edit mode **/ private boolean editing = false; /** index of current row **/ private int currentRowIndex = -1; /** AD window content part that own this renderer **/ private AbstractADWindowContent m_windowPanel; /** internal listener for button ActionEvent **/ private ActionListener buttonListener; /** * Flag detect this view has customized column or not * value is set at {@link #render(Row, Object[], int)} */ private boolean isGridViewCustomized = false; /** DefaultFocusField */ private WEditor defaultFocusField = null; /** editor configuration for readonly field editor **/ private final static IEditorConfiguration readOnlyEditorConfiguration = new IEditorConfiguration() { @Override public Boolean getReadonly() { return Boolean.TRUE; } @Override public Boolean getMandatory() { return Boolean.FALSE; } }; /** * * @param gridTab * @param windowNo */ public GridTabRowRenderer(GridTab gridTab, int windowNo) { this.gridTab = gridTab; this.windowNo = windowNo; this.dataBinder = new GridTabDataBinder(gridTab); } /** * Get editor for GridField. * @param gridField * @return {@link WEditor} */ private WEditor getEditorCell(GridField gridField) { WEditor editor = editors.get(gridField); if (editor != null) { prepareFieldEditor(gridField, editor); editor.addValueChangeListener(dataBinder); gridField.removePropertyChangeListener(editor); gridField.addPropertyChangeListener(editor); editor.setValue(gridField.getValue()); } return editor; } /** * Setup field editor * @param gridField * @param editor */ private void prepareFieldEditor(GridField gridField, WEditor editor) { if (editor instanceof WButtonEditor) { if (buttonListener != null) { ((WButtonEditor)editor).addActionListener(buttonListener); } else { Object window = SessionManager.getAppDesktop().findWindow(windowNo); if (window != null && window instanceof ADWindow) { AbstractADWindowContent windowPanel = ((ADWindow)window).getADWindowContent(); ((WButtonEditor)editor).addActionListener(windowPanel); } } } //streach component to fill grid cell if (editor.getComponent() instanceof HtmlBasedComponent) { editor.fillHorizontal(); } } /** * @param field * @return column index for field, -1 if not found */ public int getColumnIndex(GridField field) { GridField[] fields = gridPanel.getFields(); for(int i = 0; i < fields.length; i++) { if (fields[i] == field) return i; } return -1; } /** * @param value * @return readonly checkbox component */ private Component createReadonlyCheckbox(Object value) { Checkbox checkBox = new Checkbox(); if (value != null && "true".equalsIgnoreCase(value.toString())) checkBox.setChecked(true); else checkBox.setChecked(false); checkBox.setDisabled(true); return checkBox; } /** * Create invisible component for GridField with IsHeading=Y. * To fill up space allocated for field component. * @return invisible text box component */ private Component createInvisibleComponent() { Textbox textBox = new Textbox(); textBox.setDisabled(true); textBox.setVisible(false); return textBox; } /** * Check existence of readonly editor and return display text * @param value * @param gridField * @param rowIndex * @return display text */ protected String getDisplayTextWithEditorCheck(Object value, GridField gridField, int rowIndex) { WEditor readOnlyEditor = readOnlyEditors.get(gridField); if (readOnlyEditor == null) { readOnlyEditor = WebEditorFactory.getEditor(gridField, true, readOnlyEditorConfiguration); if (readOnlyEditor != null) { readOnlyEditors.put(gridField, readOnlyEditor); } } return getDisplayText(value, gridField, rowIndex); } /** * call {@link #getDisplayText(Object, GridField, int, boolean)} with isForceGetValue = false * @param value * @param gridField * @param rowIndex * @return display text */ public String getDisplayText(Object value, GridField gridField, int rowIndex){ return getDisplayText(value, gridField, rowIndex, false); } /** * Get display text of a field. when field have isDisplay = false always return empty string, except isForceGetValue = true * @param value * @param gridField * @param rowIndex * @param isForceGetValue true to return text for field value even if IsDisplay return false. This is to allow Grid customization * to override IsDisplay result. * @return display text */ private String getDisplayText(Object value, GridField gridField, int rowIndex, boolean isForceGetValue) { if (value == null) return ""; GridRowCtx gridRowCtx = new GridRowCtx(Env.getCtx(), gridTab, rowIndex); if (rowIndex >= 0) { if (!isForceGetValue && !gridField.isDisplayed(gridRowCtx, true)) { return ""; } } if (gridField.isEncryptedField()) { return "********"; } else if (readOnlyEditors.get(gridField) != null) { WEditor editor = readOnlyEditors.get(gridField); return editor.getDisplayTextForGridView(gridRowCtx, value); } else return value.toString(); } /** * Get component to display value of a field.
    * When display type is boolean or button, return corresponding component.
    * Otherwise, use Label or Component from {@link WEditor#getDisplayComponent()} to display text from {@link #getDisplayText(Object, GridField, int, boolean)} * (As it is, only {@link Html} is supported for {@link WEditor#getDisplayComponent()}). * @param rowIndex * @param value * @param gridField * @param isForceGetValue true to return Component with value even if IsDisplay return false. This is to allow Grid customization * preference to override IsDisplay result. * @return {@link Component} */ private Component getDisplayComponent(int rowIndex, Object value, GridField gridField, boolean isForceGetValue) { Component component; if (gridField.getDisplayType() == DisplayType.YesNo) { component = createReadonlyCheckbox(value); } else if (gridField.isHeading()) { component = createInvisibleComponent(); } else if (gridField.getDisplayType() == DisplayType.Button) { // Each row renderer --- ctx per row wise GridRowCtx gridRowCtx = new GridRowCtx(Env.getCtx(), gridTab, rowIndex); WButtonEditor editor = new WButtonEditor(gridField, rowIndex); editor.setValue(gridTab.getValue(rowIndex, gridField.getColumnName())); editor.setReadWrite(gridField.isEditable(gridRowCtx, true,true)); editor.getComponent().setAttribute(GRID_ROW_INDEX_ATTR, rowIndex); editor.addActionListener(buttonListener); component = editor.getComponent(); } else { String text = getDisplayText(value, gridField, rowIndex, isForceGetValue); WEditor editor = getEditorCell(gridField); if (editor.getDisplayComponent() == null){ Label label = new Label(); setLabelText(text, label); component = label; }else{ component = editor.getDisplayComponent(); if (component instanceof Html){ ((Html)component).setContent(text); }else{ throw new UnsupportedOperationException("Only implemented for Html component."); } } } applyFieldStyle(gridField, rowIndex, (HtmlBasedComponent) component); return component; } /** * Apply AD_Style to field. * @param gridField * @param rowIndex * @param component */ private void applyFieldStyle(GridField gridField, int rowIndex, HtmlBasedComponent component) { int AD_Style_ID = gridField.getAD_FieldStyle_ID(); if (AD_Style_ID <= 0) return; GridRowCtx gridRowCtx = new GridRowCtx(Env.getCtx(), gridTab, rowIndex); MStyle style = MStyle.get(Env.getCtx(), AD_Style_ID); setComponentStyle(component, style.buildStyle(ThemeManager.getTheme(), gridRowCtx)); } /** * Set component's style, sclass or zclass property * @param component * @param style "@sclass=" for sclass for "@zclass=" for zclass. default to style if there's no prefix. */ protected void setComponentStyle(HtmlBasedComponent component, String style) { if (style != null && style.startsWith(MStyle.SCLASS_PREFIX)) { String sclass = style.substring(MStyle.SCLASS_PREFIX.length()); if (component instanceof EditorBox) ((EditorBox)component).getTextbox().setSclass(sclass); else component.setSclass(sclass); } else if (style != null && style.startsWith(MStyle.ZCLASS_PREFIX)) { String zclass = style.substring(MStyle.ZCLASS_PREFIX.length()); if (component instanceof EditorBox) ((EditorBox)component).getTextbox().setZclass(zclass); else component.setZclass(zclass); } else { if (component instanceof EditorBox) ((EditorBox)component).getTextbox().setStyle(style); else component.setStyle(style); } } /** * set label text, shorten text if length exceed define max length. * @param text * @param label */ private void setLabelText(String text, Label label) { String display = text; final int MAX_TEXT_LENGTH = MSysConfig.getIntValue(MSysConfig.MAX_TEXT_LENGTH_ON_GRID_VIEW,MAX_TEXT_LENGTH_DEFAULT,Env.getAD_Client_ID(Env.getCtx())); if (text != null && text.length() > MAX_TEXT_LENGTH) display = text.substring(0, MAX_TEXT_LENGTH - 3) + "..."; label.setValue(display); if (text != null && text.length() > MAX_TEXT_LENGTH) label.setTooltiptext(text); } /** * @return field editor list */ public List getEditors() { List editorList = new ArrayList(); if (!editors.isEmpty()) editorList.addAll(editors.values()); return editorList; } /** * @param paging */ public void setPaging(Paging paging) { this.paging = paging; } /** * Detach all editor and optionally set the current value of the editor as cell label. * @param updateCellLabel */ public void stopEditing(boolean updateCellLabel) { if (!editing) { return; } else { editing = false; } Row row = null; for (Entry entry : editors.entrySet()) { if (entry.getValue().getComponent().getParent() != null) { Component child = entry.getValue().getComponent(); Cell div = null; while (div == null && child != null) { Component parent = child.getParent(); if (parent instanceof Cell && parent.getParent() instanceof Row) div = (Cell)parent; else child = parent; } Component component = div!=null ? (Component) div.getAttribute(DISPLAY_COMPONENT_ATTR) : null; if (updateCellLabel) { if (component instanceof Label) { Label label = (Label)component; label.getChildren().clear(); String text = getDisplayText(entry.getValue().getValue(), entry.getValue().getGridField(), -1); setLabelText(text, label); } else if (component instanceof Checkbox) { Checkbox checkBox = (Checkbox)component; Object value = entry.getValue().getValue(); if (value != null && "true".equalsIgnoreCase(value.toString())) checkBox.setChecked(true); else checkBox.setChecked(false); } else if (component instanceof Html){ ((Html)component).setContent(getDisplayText(entry.getValue().getValue(), entry.getValue().getGridField(), -1)); } } if (row == null) row = ((Row)div.getParent()); entry.getValue().getComponent().detach(); entry.getKey().removePropertyChangeListener(entry.getValue()); entry.getValue().removeValuechangeListener(dataBinder); if (component.getParent() == null || component.getParent() != div) div.appendChild(component); else if (!component.isVisible()) { component.setVisible(true); } } } GridTableListModel model = (GridTableListModel) grid.getModel(); model.setEditing(false); } /** * Render data for row. * @param row * @param data Object[] values for row * @param index row index within current page (i.e if page size is 25, index is one of 0 to 24). */ @Override public void render(Row row, Object[] data, int index) throws Exception { //don't render if not visible int columnCount = 0; GridField[] gridPanelFields = null; GridField[] gridTabFields = null; if (gridPanel != null) { if (!gridPanel.isVisible()) { return; } else{ gridPanelFields = gridPanel.getFields(); columnCount = gridPanelFields.length; gridTabFields = gridTab.getFields(); isGridViewCustomized = gridTabFields.length != gridPanelFields.length; } gridPanel.autoHideEmptyColumns(); } if (grid == null) grid = (Grid) row.getParent().getParent(); if (rowListener == null) rowListener = new RowListener((Grid)row.getParent().getParent()); if (!isGridViewCustomized) { for(int i = 0; i < gridTabFields.length; i++) { if (gridPanelFields[i].getAD_Field_ID() != gridTabFields[i].getAD_Field_ID()) { isGridViewCustomized = true; break; } } } if (!isGridViewCustomized) { currentValues = data; } else { List dataList = new ArrayList(); for(GridField gridField : gridPanelFields) { for(int i = 0; i < gridTabFields.length; i++) { if (gridField.getAD_Field_ID() == gridTabFields[i].getAD_Field_ID()) { dataList.add(data[i]); break; } } } currentValues = dataList.toArray(new Object[0]); } Grid grid = (Grid) row.getParent().getParent(); org.zkoss.zul.Columns columns = grid.getColumns(); int rowIndex = index; if (paging != null && paging.getPageSize() > 0) { rowIndex = (paging.getActivePage() * paging.getPageSize()) + rowIndex; } Cell cell = new Cell(); cell.setTooltiptext(Msg.getMsg(Env.getCtx(), "Select")); Checkbox selection = new Checkbox(); selection.setAttribute(GRID_ROW_INDEX_ATTR, rowIndex); selection.setChecked(gridTab.isSelected(rowIndex)); cell.setStyle("border: none;"); selection.addEventListener(Events.ON_CHECK, this); if (!selection.isChecked()) { if (gridPanel.selectAll.isChecked()) { gridPanel.selectAll.setChecked(false); } } cell.appendChild(selection); row.appendChild(cell); if (isShowCurrentRowIndicatorColumn()) { cell = new Cell(); cell.addEventListener(Events.ON_CLICK, this); cell.setStyle("border: none;"); cell.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "EditRecord"))); if (ThemeManager.isUseFontIconForImage()) { Label indicatorLabel = new Label(); cell.appendChild(indicatorLabel); final Cell finalCell = cell; indicatorLabel.addEventListener(Events.ON_CLICK, evt->Events.postEvent(Events.ON_CLICK, finalCell, indicatorLabel.getSclass())); } cell.setValign("middle"); row.appendChild(cell); } Boolean isActive = null; int colIndex = -1; for (int i = 0; i < columnCount; i++) { if (editors.get(gridPanelFields[i]) == null) { WEditor editor = WebEditorFactory.getEditor(gridPanelFields[i], true); if (editor != null) { editors.put(gridPanelFields[i], editor); if (editor instanceof WButtonEditor) { ((WButtonEditor)editor).addActionListener(buttonListener); } //readonly for display text WEditor readOnlyEditor = WebEditorFactory.getEditor(gridPanelFields[i], true, readOnlyEditorConfiguration); if (readOnlyEditor != null) { readOnlyEditors.put(gridPanelFields[i], readOnlyEditor); } if (editor.getComponent() instanceof AbstractComponent) { editor.getComponent().setWidgetOverride("fieldHeader", HelpController.escapeJavascriptContent(gridPanelFields[i].getHeader())); editor.getComponent().setWidgetOverride("fieldDescription", HelpController.escapeJavascriptContent(gridPanelFields[i].getDescription())); editor.getComponent().setWidgetOverride("fieldHelp", HelpController.escapeJavascriptContent(gridPanelFields[i].getHelp())); editor.getComponent().setWidgetListener("onFocus", "zWatch.fire('onFieldTooltip', this, null, this.fieldHeader(), this.fieldDescription(), this.fieldHelp());"); ((AbstractComponent)editor.getComponent()).addCallback(ComponentCtrl.AFTER_PAGE_DETACHED, (t) -> {((AbstractComponent)t).setWidgetListener("onFocus", null);}); } // Default Focus if (defaultFocusField == null && gridPanelFields[i].isDefaultFocus()) defaultFocusField = editor; } } if ("IsActive".equals(gridPanelFields[i].getColumnName())) { isActive = Boolean.FALSE; if (currentValues[i] != null) { if ("true".equalsIgnoreCase(currentValues[i].toString())) { isActive = Boolean.TRUE; } } } // IDEMPIERE-2148: when has tab customize, ignore check properties isDisplayedGrid if ((!isGridViewCustomized && gridPanelFields[i].isDisplayedGrid()) || gridPanelFields[i].isToolbarOnlyButton()) { continue; } colIndex ++; Cell div = new Cell(); String divStyle = CELL_DIV_STYLE; org.zkoss.zul.Column column = (org.zkoss.zul.Column) columns.getChildren().get(colIndex); if (column.isVisible()) { Component component = getDisplayComponent(rowIndex, currentValues[i], gridPanelFields[i], isGridViewCustomized); div.appendChild(component); div.setAttribute(DISPLAY_COMPONENT_ATTR, component); if (gridPanelFields[i].isHeading()) { component.setVisible(false); } if (DisplayType.YesNo == gridPanelFields[i].getDisplayType() || DisplayType.Image == gridPanelFields[i].getDisplayType()) { divStyle = CELL_DIV_STYLE_ALIGN_CENTER; } else if (DisplayType.isNumeric(gridPanelFields[i].getDisplayType())) { divStyle = CELL_DIV_STYLE_ALIGN_RIGHT; } GridRowCtx ctx = new GridRowCtx(Env.getCtx(), gridTab, rowIndex); if (!gridPanelFields[i].isDisplayedGrid(ctx, true)){ // IDEMPIERE-2253 component.setVisible(false); } } div.setStyle(divStyle); ZKUpdateUtil.setWidth(div, "100%"); div.setAttribute(COLUMN_NAME_ATTR, gridPanelFields[i].getColumnName()); div.addEventListener(Events.ON_CLICK, rowListener); div.addEventListener(Events.ON_DOUBLE_CLICK, rowListener); row.appendChild(div); } if (rowIndex == gridTab.getCurrentRow()) { setCurrentRow(row); } row.setStyle("cursor:pointer"); row.addEventListener(Events.ON_CLICK, rowListener); row.addEventListener(Events.ON_OK, rowListener); row.setTooltiptext(Msg.getMsg(Env.getCtx(), "Row") + " " + (rowIndex+1)); if (isActive == null) { Object isActiveValue = gridTab.getValue(rowIndex, "IsActive"); if (isActiveValue != null) { if ("true".equalsIgnoreCase(isActiveValue.toString())) { isActive = Boolean.TRUE; } else { isActive = Boolean.FALSE; } } } if (isActive != null && !isActive.booleanValue()) { LayoutUtils.addSclass("grid-inactive-row", row); } //IDEMPIERE-4165 After adding a new row to the list (New or copy) repaint the grid when rendering the last row if (gridTab.isNew() && rowIndex == grid.getRows().getChildren().size()-1) { grid.invalidate(); } if (MSysConfig.getBooleanValue(MSysConfig.ZK_GRID_VIEW_USE_DEFER_RENDERING, false, Env.getAD_Client_ID(Env.getCtx()))) row.setRenderdefer(1); } /** * @param row */ public void setCurrentRow(Row row) { if (currentRow != null && currentRow.getParent() != null && currentRow != row && isShowCurrentRowIndicatorColumn()) { Cell cell = (Cell) currentRow.getChildren().get(1); if (cell != null) { cell.setSclass("row-indicator"); if (cell.getFirstChild() != null) ((Label)cell.getFirstChild()).setSclass(""); } } currentRow = row; Cell cell = (Cell) currentRow.getChildren().get(1); if (cell != null && isShowCurrentRowIndicatorColumn()) { if (ThemeManager.isUseFontIconForImage()) { Label indicatorLabel = (Label) cell.getFirstChild(); indicatorLabel.setSclass("row-indicator-selected z-icon-Edit"); } else cell.setSclass("row-indicator-selected"); } currentRowIndex = gridTab.getCurrentRow(); if (currentRowIndex == gridTab.getCurrentRow()) { if (editing) { stopEditing(false); editCurrentRow(); } } else { currentRowIndex = gridTab.getCurrentRow(); if (editing) { stopEditing(false); } } String script = "jq('#"+row.getUuid()+"').addClass('highlight').siblings().removeClass('highlight')"; Boolean isActive = null; Object isActiveValue = gridTab.getValue(currentRowIndex, "IsActive"); if (isActiveValue != null) { if ("true".equalsIgnoreCase(isActiveValue.toString())) { isActive = Boolean.TRUE; } else { isActive = Boolean.FALSE; } } if (isActive != null && !isActive.booleanValue()) { script = "jq('#"+row.getUuid()+"').addClass('grid-inactive-row').siblings().removeClass('highlight')"; } Clients.response(new AuScript(script)); } /** * @return current {@link Row} */ public Row getCurrentRow() { return currentRow; } /** * @return current row index ( absolute ) */ public int getCurrentRowIndex() { return currentRowIndex; } /** * Enter edit mode for current focus row. */ public void editCurrentRow() { if (ClientInfo.isMobile()) { if (!MSysConfig.getBooleanValue(MSysConfig.ZK_GRID_MOBILE_EDITABLE, false)) return; } if (currentRow != null && currentRow.getParent() != null && currentRow.isVisible() && grid != null && grid.isVisible() && grid.getParent() != null && grid.getParent().isVisible()) { GridField[] gridPanelFields = gridPanel.getFields(); int columnCount = gridPanelFields.length; org.zkoss.zul.Columns columns = grid.getColumns(); //skip selection and indicator column int colIndex = isShowCurrentRowIndicatorColumn() ? 1 : 0; for (int i = 0; i < columnCount; i++) { if ((!isGridViewCustomized && !gridPanelFields[i].isDisplayedGrid()) || gridPanelFields[i].isToolbarOnlyButton()) { continue; } colIndex ++; if (editors.get(gridPanelFields[i]) == null) editors.put(gridPanelFields[i], WebEditorFactory.getEditor(gridPanelFields[i], true)); org.zkoss.zul.Column column = (org.zkoss.zul.Column) columns.getChildren().get(colIndex); if (column.isVisible()) { Cell div = (Cell) currentRow.getChildren().get(colIndex); WEditor editor = getEditorCell(gridPanelFields[i]); if (div.getChildren().isEmpty() || !(div.getChildren().get(0) instanceof Button)) div.getChildren().clear(); else if (!div.getChildren().isEmpty()) { div.getChildren().get(0).setVisible(false); } div.appendChild(editor.getComponent()); WEditorPopupMenu popupMenu = editor.getPopupMenu(); if (popupMenu != null) { popupMenu.addMenuListener((ContextMenuListener)editor); div.appendChild(popupMenu); Component editorComponent = editor.getComponent(); if (editorComponent instanceof XulElement) popupMenu.addContextElement((XulElement) editorComponent); } Properties ctx = isDetailPane() ? new GridRowCtx(Env.getCtx(), gridTab) : gridPanelFields[i].getVO().ctx; //check context if (!gridPanelFields[i].isDisplayedGrid(ctx, true)){ // IDEMPIERE-2253 editor.getComponent().setVisible(false); } editor.setReadWrite(gridPanelFields[i].isEditableGrid(true)); } } editing = true; GridTableListModel model = (GridTableListModel) grid.getModel(); model.setEditing(true); } } /** * @return true if it is own by {@link DetailPane}. */ private boolean isDetailPane() { Component parent = grid.getParent(); while (parent != null) { if (parent instanceof DetailPane) { return true; } parent = parent.getParent(); } return false; } /** * @see RowRendererExt#getControls() */ @Override public int getControls() { return DETACH_ON_RENDER; } /** * @see RowRendererExt#newCell(Row) */ @Override public Component newCell(Row row) { return null; } /** * @see RowRendererExt#newRow(Grid) */ @Override public Row newRow(Grid grid) { return null; } /** * @see RendererCtrl#doCatch(Throwable) */ @Override public void doCatch(Throwable ex) throws Throwable { } /** * @see RendererCtrl#doFinally() */ @Override public void doFinally() { } /** * @see RendererCtrl#doTry() */ @Override public void doTry() { } /** * Set focus to first writable field editor (or default focus field editor if it is writable). * If no field editor is writable, set focus to first visible field editor. */ public void focusToFirstEditor() { if (currentRow != null && currentRow.getParent() != null) { WEditor toFocus = null; WEditor firstEditor = null; if (defaultFocusField != null && defaultFocusField.isVisible() && defaultFocusField.isReadWrite() && defaultFocusField.getComponent().getParent() != null && !(defaultFocusField instanceof WImageEditor)) { toFocus = defaultFocusField; } else { for (WEditor editor : getEditors()) { if (editor.isVisible() && editor.getComponent().getParent() != null) { if (editor.isReadWrite()) { toFocus = editor; break; } if (firstEditor == null) firstEditor = editor; } } } if (toFocus != null) { focusToEditor(toFocus); } else if (firstEditor != null) { focusToEditor(firstEditor); } } } /** * @param toFocus */ protected void focusToEditor(WEditor toFocus) { Component c = toFocus.getComponent(); if (c instanceof EditorBox) { c = ((EditorBox)c).getTextbox(); } else if (c instanceof NumberBox) { c = ((NumberBox)c).getDecimalbox(); } else if (c instanceof Urlbox) { c = ((Urlbox) c).getTextbox(); } ((HtmlBasedComponent)c).focus(); } /** * set focus to next writable editor from ref * @param ref */ public void focusToNextEditor(WEditor ref) { boolean found = false; for (WEditor editor : getEditors()) { if (editor == ref) { found = true; continue; } if (found) { if (editor.isVisible() && editor.isReadWrite()) { focusToEditor(editor); break; } } } } /** * Set {@link GridView} that own this renderer. * @param gridPanel */ public void setGridPanel(GridView gridPanel) { this.gridPanel = gridPanel; } /** * Internal listener for row event (ON_CLICK, ON_DOUBLE_CLICK and ON_OK). */ static class RowListener implements EventListener { private Grid _grid; public RowListener(Grid grid) { _grid = grid; } public void onEvent(Event event) throws Exception { if (Events.ON_CLICK.equals(event.getName())) { if (Executions.getCurrent().getAttribute(GRID_VIEW_ON_SELECT_ROW_ATTR) != null) return; Event evt = new Event(Events.ON_CLICK, _grid, event.getTarget()); Events.sendEvent(_grid, evt); evt.stopPropagation(); } else if (Events.ON_DOUBLE_CLICK.equals(event.getName())) { Event evt = new Event(Events.ON_DOUBLE_CLICK, _grid, _grid); Events.sendEvent(_grid, evt); } else if (Events.ON_OK.equals(event.getName())) { Event evt = new Event(Events.ON_OK, _grid, _grid); Events.sendEvent(_grid, evt); } } } /** * @return true if current row is in edit mode, false otherwise */ public boolean isEditing() { return editing; } /** * Set AD window content part that own this renderer. * {@link #buttonListener} need this to call {@link AbstractADWindowContent#actionPerformed(ActionEvent)}. * @param windowPanel */ public void setADWindowPanel(AbstractADWindowContent windowPanel) { if (this.m_windowPanel == windowPanel) return; this.m_windowPanel = windowPanel; buttonListener = new ActionListener() { public void actionPerformed(ActionEvent event) { WButtonEditor editor = (WButtonEditor) event.getSource(); Integer rowIndex = (Integer) editor.getComponent().getAttribute(GRID_ROW_INDEX_ATTR); if (rowIndex != null) { int newRowIndex = gridTab.navigate(rowIndex); if (newRowIndex == rowIndex) { m_windowPanel.actionPerformed(event); } } else { m_windowPanel.actionPerformed(event); } } }; } @Override public void onEvent(Event event) throws Exception { if (event.getTarget() instanceof Cell) { Cell cell = (Cell) event.getTarget(); if (cell.getSclass() != null && cell.getSclass().indexOf("row-indicator-selected") >= 0) Events.sendEvent(gridPanel, new Event(DetailPane.ON_EDIT_EVENT, gridPanel)); else if (event.getData() != null && event.getData().toString().indexOf("row-indicator-selected") >= 0) Events.sendEvent(gridPanel, new Event(DetailPane.ON_EDIT_EVENT, gridPanel)); else Events.sendEvent(event.getTarget().getParent(), event); } else if (event.getTarget() instanceof Checkbox) { Executions.getCurrent().setAttribute(GRID_VIEW_ON_SELECT_ROW_ATTR, Boolean.TRUE); Checkbox checkBox = (Checkbox) event.getTarget(); Events.sendEvent(gridPanel, new Event("onSelectRow", gridPanel, checkBox)); } } /** * @return {@link GridView#isShowCurrentRowIndicatorColumn} */ private boolean isShowCurrentRowIndicatorColumn() { return gridPanel != null && gridPanel.isShowCurrentRowIndicatorColumn(); } }