package com.eas.client.forms.components.model.grid; import com.eas.client.forms.IconCache; import com.bearsoft.gui.grid.columns.ConstrainedColumnModel; import com.bearsoft.gui.grid.constraints.LinearConstraint; import com.bearsoft.gui.grid.data.CachingTableModel; import com.bearsoft.gui.grid.data.CollapseExpandListener; import com.bearsoft.gui.grid.data.TableFront2TreedModel; import com.bearsoft.gui.grid.editing.InsettedTreeEditor; import com.bearsoft.gui.grid.header.ColumnNodesContainer; import com.bearsoft.gui.grid.header.GridColumnsNode; import com.bearsoft.gui.grid.header.HeaderSplitter; import com.bearsoft.gui.grid.header.MultiLevelHeader; import com.bearsoft.gui.grid.rendering.InsettedTreeRenderer; import com.bearsoft.gui.grid.rendering.TreeColumnLeadingComponent; import com.bearsoft.gui.grid.rows.ConstrainedRowSorter; import com.bearsoft.gui.grid.rows.TabularRowsSorter; import com.bearsoft.gui.grid.rows.TreedRowsSorter; import com.bearsoft.gui.grid.selection.ConstrainedListSelectionModel; import com.eas.client.events.SourcedEvent; import com.eas.client.forms.Forms; import com.eas.client.forms.HasComponentEvents; import com.eas.client.forms.HasJsName; import com.eas.client.forms.Widget; import com.eas.client.forms.components.model.ArrayModelWidget; import com.eas.client.forms.events.CellRenderEvent; import com.eas.client.forms.components.model.ModelComponentDecorator; import com.eas.client.forms.components.model.ModelWidget; import com.eas.client.forms.components.model.grid.columns.ModelColumn; import com.eas.client.forms.components.model.grid.columns.RadioServiceColumn; import com.eas.client.forms.components.model.grid.models.ArrayModel; import com.eas.client.forms.components.model.grid.models.ArrayTableModel; import com.eas.client.forms.components.model.grid.models.ArrayTreedModel; import com.eas.client.forms.events.rt.ControlEventsIProxy; import com.eas.client.forms.layouts.MarginLayout; import com.eas.design.Designable; import com.eas.design.Undesignable; import com.eas.gui.ScriptColor; import com.eas.script.AlreadyPublishedException; import com.eas.script.EventMethod; import com.eas.script.HasPublished; import com.eas.script.NoPublisherException; import com.eas.script.ScriptFunction; import com.eas.script.Scripts; import com.eas.util.ListenerRegistration; import java.awt.*; import java.awt.event.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.*; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.*; import javax.swing.border.MatteBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.*; import jdk.nashorn.api.scripting.AbstractJSObject; import jdk.nashorn.api.scripting.JSObject; import jdk.nashorn.api.scripting.ScriptUtils; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptObject; /** * * @author mg */ public class ModelGrid extends JPanel implements ColumnNodesContainer, ArrayModelWidget, TablesGridContainer, HasComponentEvents, Widget, HasPublished, HasJsName { protected JSObject published; public void injectPublished(JSObject aValue) { published = aValue; } protected void applyComponentPopupMenu() { if (tlTable != null) { tlTable.setComponentPopupMenu(getComponentPopupMenu()); } if (trTable != null) { trTable.setComponentPopupMenu(getComponentPopupMenu()); } if (blTable != null) { blTable.setComponentPopupMenu(getComponentPopupMenu()); } if (brTable != null) { brTable.setComponentPopupMenu(getComponentPopupMenu()); } } protected void applyGridColor() { if (getGridColor() != null) { if (tlTable != null) { tlTable.setGridColor(getGridColor()); } if (trTable != null) { trTable.setGridColor(getGridColor()); } if (blTable != null) { blTable.setGridColor(getGridColor()); } if (brTable != null) { brTable.setGridColor(getGridColor()); } } } protected void applyRowHeight() { if (tlTable != null) { tlTable.setRowHeight(getRowsHeight()); } if (trTable != null) { trTable.setRowHeight(getRowsHeight()); } if (blTable != null) { blTable.setRowHeight(getRowsHeight()); } if (brTable != null) { brTable.setRowHeight(getRowsHeight()); } } protected void applyShowHorizontalLines() { if (tlTable != null) { tlTable.setShowHorizontalLines(getShowHorizontalLines()); } if (trTable != null) { trTable.setShowHorizontalLines(getShowHorizontalLines()); } if (blTable != null) { blTable.setShowHorizontalLines(getShowHorizontalLines()); } if (brTable != null) { brTable.setShowHorizontalLines(getShowHorizontalLines()); } } protected void applyShowVerticalLines() { if (tlTable != null) { tlTable.setShowVerticalLines(getShowVerticalLines()); } if (trTable != null) { trTable.setShowVerticalLines(getShowVerticalLines()); } if (blTable != null) { blTable.setShowVerticalLines(getShowVerticalLines()); } if (brTable != null) { brTable.setShowVerticalLines(getShowVerticalLines()); } } protected void applyToolTipText() { if (tlTable != null) { tlTable.setToolTipText(getToolTipText()); } if (trTable != null) { trTable.setToolTipText(getToolTipText()); } if (blTable != null) { blTable.setToolTipText(getToolTipText()); } if (brTable != null) { brTable.setToolTipText(getToolTipText()); } } public int viewIndexByElement(JSObject anElement) { int modelIndex = modelIndexByElement(anElement); return modelIndex != -1 ? rowSorter.convertRowIndexToView(modelIndex) : -1; } /** * Returns index of a row in the model. Index is in model coordinates. Index * is 0-based. * * @param anElement Element to calculate index for. * @return Index if row. */ public int modelIndexByElement(JSObject anElement) { int idx = -1; if (deepModel instanceof TableFront2TreedModel<?>) { TableFront2TreedModel<JSObject> front = (TableFront2TreedModel<JSObject>) deepModel; idx = front.getIndexOf(anElement); } else if (deepModel instanceof ArrayTableModel) { ArrayTableModel tmodel = (ArrayTableModel) deepModel; idx = tmodel.elementToIndex(anElement); } return idx; } @Override public JSObject elementByViewIndex(int aViewIndex) { return elementByModelIndex(rowSorter.convertRowIndexToModel(aViewIndex)); } /** * Returns row for particular Index. Index is in model's coordinates. Index * is 0-based. * * @param aIdx Index the row is to be calculated for. * @return Row's index; */ @ScriptFunction @Override public JSObject elementByModelIndex(int aIdx) { JSObject element = null; if (deepModel instanceof TableFront2TreedModel<?>) { TableFront2TreedModel<JSObject> front = (TableFront2TreedModel<JSObject>) deepModel; element = front.getElementAt(aIdx); } else if (deepModel instanceof ArrayTableModel) { ArrayTableModel tmodel = (ArrayTableModel) deepModel; element = tmodel.indexToElement(aIdx); } return element; } protected void putAction(Action aAction) { if (aAction != null) { tlTable.getActionMap().put(aAction.getClass().getName(), aAction); trTable.getActionMap().put(aAction.getClass().getName(), aAction); blTable.getActionMap().put(aAction.getClass().getName(), aAction); brTable.getActionMap().put(aAction.getClass().getName(), aAction); } } private void configureTreedView() { if (rowsModel instanceof ArrayTreedModel && columnModel.getColumnCount() > 0) { TableColumn tCol = columnModel.getColumn(0); tCol.setCellRenderer(new InsettedTreeRenderer<>(tCol.getCellRenderer(), new TreeColumnLeadingComponent<>(deepModel, false, closedFolderIcon, openFolderIcon, leafIcon))); tCol.setCellEditor(new InsettedTreeEditor<>(tCol.getCellEditor(), new TreeColumnLeadingComponent<>(deepModel, true, closedFolderIcon, openFolderIcon, leafIcon))); } } private void applyOddRowsColor() { if (tlTable != null) { tlTable.setOddRowsColor(getOddRowsColor()); } if (trTable != null) { trTable.setOddRowsColor(getOddRowsColor()); } if (blTable != null) { blTable.setOddRowsColor(getOddRowsColor()); } if (brTable != null) { brTable.setOddRowsColor(getOddRowsColor()); } } private void applyShowOddRowsInOtherColor() { if (tlTable != null) { tlTable.setShowOddRowsInOtherColor(getShowOddRowsInOtherColor()); } if (trTable != null) { trTable.setShowOddRowsInOtherColor(getShowOddRowsInOtherColor()); } if (blTable != null) { blTable.setShowOddRowsInOtherColor(getShowOddRowsInOtherColor()); } if (brTable != null) { brTable.setShowOddRowsInOtherColor(getShowOddRowsInOtherColor()); } } private void applyEditable() { if (tlTable != null) { tlTable.setEditable(editable); } if (trTable != null) { trTable.setEditable(editable); } if (blTable != null) { blTable.setEditable(editable); } if (brTable != null) { brTable.setEditable(editable); } } private void applyEnabled() { if (tlTable != null) { tlTable.setEnabled(isEnabled()); } if (trTable != null) { trTable.setEnabled(isEnabled()); } if (blTable != null) { blTable.setEnabled(isEnabled()); } if (brTable != null) { brTable.setEnabled(isEnabled()); } } public JTable getTableByViewCell(int row, int column) { if (row >= 0 && row < tlTable.getRowCount() && column >= 0 && column < tlTable.getColumnCount()) { return tlTable; } else if (row >= 0 && row < trTable.getRowCount() && (column - tlTable.getColumnCount()) >= 0 && (column - tlTable.getColumnCount()) < trTable.getColumnCount()) { return trTable; } else if ((row - tlTable.getRowCount()) >= 0 && (row - tlTable.getRowCount()) < blTable.getRowCount() && column >= 0 && column < blTable.getColumnCount()) { return blTable; } else if ((row - tlTable.getRowCount()) >= 0 && (row - tlTable.getRowCount()) < brTable.getRowCount() && (column - tlTable.getColumnCount()) >= 0 && (column - tlTable.getColumnCount()) < brTable.getColumnCount()) { return brTable; } else { return null; } } @ScriptFunction @Designable(category = "model") public String getParentField() { return parentField; } @ScriptFunction public void setParentField(String aValue) { if (parentField == null ? aValue != null : !parentField.equals(aValue)) { boolean wasTree = isTreeConfigured(); parentField = aValue; boolean isTree = isTreeConfigured(); if (wasTree != isTree) { applyRows(); } } } @ScriptFunction @Designable(category = "model") public String getChildrenField() { return childrenField; } @ScriptFunction public void setChildrenField(String aValue) { if (childrenField == null ? aValue != null : !childrenField.equals(aValue)) { boolean wasTree = isTreeConfigured(); childrenField = aValue; boolean isTree = isTreeConfigured(); if (wasTree != isTree) { applyRows(); } } } public final boolean isTreeConfigured() { return parentField != null && !parentField.isEmpty() && childrenField != null && !childrenField.isEmpty(); } // public static final int CELLS_CACHE_SIZE = 65536;// 2^16 public static final Color FIXED_COLOR = new Color(154, 204, 255); public static Icon processIcon = IconCache.getIcon("16x16/process-indicator.gif"); protected TablesKeyboardManager tablesKeyboardManager = new TablesKeyboardManager(); protected TablesMousePropagator tablesMousePropagator = new TablesMousePropagator(); // design protected Icon openFolderIcon; protected Icon closedFolderIcon; protected Icon leafIcon; protected List<GridColumnsNode> header = new ArrayList<>(); protected int rowsHeight = 30; protected boolean showVerticalLines = true; protected boolean showHorizontalLines = true; protected boolean showOddRowsInOtherColor = true; protected boolean editable = true; protected boolean insertable = true; protected boolean deletable = true; protected Color oddRowsColor = new ScriptColor("#efefef"); protected Color gridColor; // data protected ArrayModel rowsModel; protected TableModel deepModel; protected CachingTableModel cachingModel; protected TabularRowsSorter<? extends TableModel> rowSorter; protected Set<JSObject> processedRows = new HashSet<>(); protected JSObject data; protected String field; protected String cursorProperty = "cursor"; protected JSObject boundToData; protected JSObject boundToCursor; // tree protected String parentField; protected String childrenField; protected ListenerRegistration collapseExpandListener; protected JSObject onCollapse; protected JSObject onExpand; protected JSObject onAfterRender; // rows and columns protected int frozenRows; protected int frozenColumns; // view protected TableColumnModel columnModel; protected JSObject generalOnRender; // selection protected ListSelectionModel rowsSelectionModel; protected ListSelectionModel columnsSelectionModel; protected GeneralSelectionChangesReflector generalSelectionChangesReflector; // focus protected TablesFocusPropagator focusPropagator = new TablesFocusPropagator(); // visual components protected LinearConstraint topRowsConstraint = new LinearConstraint(0, -1); protected LinearConstraint bottomRowsConstraint = new LinearConstraint(0, Integer.MAX_VALUE); protected JLabel progressIndicator; protected JScrollPane gridScroll; protected MultiLevelHeader lheader; protected MultiLevelHeader rheader; protected GridTable tlTable; protected GridTable trTable; protected GridTable blTable; protected GridTable brTable; protected JPanel tlPanel = new JPanel(new BorderLayout()); protected JPanel trPanel = new JPanel(new BorderLayout()); protected JPanel blPanel = new JPanel(new BorderLayout()); protected JPanel brPanel; // actions protected Action findSomethingAction; public ModelGrid() { super(); // columns configuration columnsSelectionModel = new DefaultListSelectionModel(); columnModel = new DefaultTableColumnModel(); columnModel.setSelectionModel(columnsSelectionModel); columnModel.setColumnSelectionAllowed(true); // rows configuration rowsSelectionModel = new DefaultListSelectionModel(); rowsSelectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); // selection generalSelectionChangesReflector = new GeneralSelectionChangesReflector(); rowsSelectionModel.addListSelectionListener(generalSelectionChangesReflector); // constrained layer models setup tlTable = new GridTable(null, this); trTable = new GridTable(null, this); blTable = new GridTable(tlTable, this); brTable = new GridTable(trTable, this); brPanel = new GridTableScrollablePanel(brTable); tlTable.setOpaque(false); trTable.setOpaque(false); blTable.setOpaque(false); brTable.setOpaque(false); tlTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); trTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); blTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); brTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); tlTable.setAutoCreateColumnsFromModel(false); trTable.setAutoCreateColumnsFromModel(false); blTable.setAutoCreateColumnsFromModel(false); brTable.setAutoCreateColumnsFromModel(false); // keyboard focus flow setup tlTable.addKeyListener(tablesKeyboardManager); trTable.addKeyListener(tablesKeyboardManager); blTable.addKeyListener(tablesKeyboardManager); brTable.addKeyListener(tablesKeyboardManager); // focus events setup tlTable.addFocusListener(focusPropagator); trTable.addFocusListener(focusPropagator); blTable.addFocusListener(focusPropagator); brTable.addFocusListener(focusPropagator); // mouse propagating setup tlTable.addMouseListener(tablesMousePropagator); trTable.addMouseListener(tablesMousePropagator); blTable.addMouseListener(tablesMousePropagator); brTable.addMouseListener(tablesMousePropagator); tlTable.addMouseWheelListener(tablesMousePropagator); trTable.addMouseWheelListener(tablesMousePropagator); blTable.addMouseWheelListener(tablesMousePropagator); brTable.addMouseWheelListener(tablesMousePropagator); tlTable.addMouseMotionListener(tablesMousePropagator); trTable.addMouseMotionListener(tablesMousePropagator); blTable.addMouseMotionListener(tablesMousePropagator); brTable.addMouseMotionListener(tablesMousePropagator); // grid columns setup. // left header setup lheader = new MultiLevelHeader(); lheader.setBackground(new JButton().getBackground()); lheader.setTable(tlTable); lheader.setColumnModel(tlTable.getColumnModel()); tlTable.getTableHeader().setResizingAllowed(true); lheader.setSlaveHeaders(tlTable.getTableHeader(), blTable.getTableHeader()); // right header setup rheader = new MultiLevelHeader(); rheader.setBackground(new JButton().getBackground()); rheader.setTable(trTable); rheader.setColumnModel(trTable.getColumnModel()); trTable.getTableHeader().setResizingAllowed(true); rheader.setSlaveHeaders(trTable.getTableHeader(), brTable.getTableHeader()); // Tables are enclosed in panels to avoid table's stupid efforts // to configure it's parent scroll pane. tlPanel.add(lheader, BorderLayout.NORTH); tlPanel.add(tlTable, BorderLayout.CENTER); trPanel.add(rheader, BorderLayout.NORTH); trPanel.add(trTable, BorderLayout.CENTER); blPanel.add(blTable, BorderLayout.CENTER); //brPanel.add(brTable, BorderLayout.CENTER); tlPanel.setOpaque(false); trPanel.setOpaque(false); blPanel.setOpaque(false); brPanel.setOpaque(false); progressIndicator = new JLabel(processIcon); progressIndicator.setOpaque(false); progressIndicator.setVisible(false); gridScroll = new JScrollPane(); gridScroll.setOpaque(false); gridScroll.setCorner(JScrollPane.UPPER_LEFT_CORNER, tlPanel); gridScroll.setCorner(JScrollPane.UPPER_RIGHT_CORNER, progressIndicator); gridScroll.setColumnHeaderView(trPanel); gridScroll.getColumnHeader().addChangeListener(new ColumnHeaderScroller(gridScroll.getColumnHeader(), gridScroll.getViewport())); gridScroll.setRowHeaderView(blPanel); gridScroll.getRowHeader().addChangeListener(new RowHeaderScroller(gridScroll.getRowHeader(), gridScroll.getViewport())); gridScroll.setViewportView(brPanel); gridScroll.getViewport().setOpaque(false); gridScroll.getColumnHeader().setOpaque(false); gridScroll.getRowHeader().setOpaque(false); setLayout(new BorderLayout()); add(gridScroll, BorderLayout.CENTER); // lheader.setNeightbour(rheader); rheader.setNeightbour(lheader); applyColumns(); applyRows(); configureActions(); applyEnabled(); applyFont(); applyEditable(); applyBackground(); applyForeground(); applyComponentPopupMenu(); applyGridColor(); applyRowHeight(); applyShowHorizontalLines(); applyShowVerticalLines(); applyToolTipText(); applyShowOddRowsInOtherColor(); applyOddRowsColor(); applyComponentOrientation(getComponentOrientation());// Swing allows only argumented call. setupStyle(); } protected final void setupStyle() { JTable tbl = new JTable(); gridColor = tbl.getGridColor(); setFont(tbl.getFont()); Color uiColor = tbl.getBackground(); setBackground(new Color(uiColor.getRed(), uiColor.getGreen(), uiColor.getBlue(), uiColor.getAlpha())); uiColor = tbl.getForeground(); setForeground(new Color(uiColor.getRed(), uiColor.getGreen(), uiColor.getBlue(), uiColor.getAlpha())); Object uiOpenedIcon = UIManager.get("Tree.openIcon"); Object uiClosedIcon = UIManager.get("Tree.closedIcon"); Object uiLeafIcon = UIManager.get("Tree.leafIcon"); if (uiOpenedIcon instanceof Icon) { openFolderIcon = (Icon) uiOpenedIcon; } else { openFolderIcon = IconCache.getIcon("16x16/folder-horizontal-open.png"); } if (uiClosedIcon instanceof Icon) { closedFolderIcon = (Icon) uiClosedIcon; } else { closedFolderIcon = IconCache.getIcon("16x16/folder-horizontal.png"); } if (uiLeafIcon instanceof Icon) { leafIcon = (Icon) uiLeafIcon; } else { leafIcon = IconCache.getIcon("16x16/status-offline.png"); } } protected boolean headerVisible = true; @ScriptFunction public boolean isHeaderVisible() { return headerVisible; } @ScriptFunction public void setHeaderVisible(boolean aValue) { headerVisible = aValue; } protected boolean draggableRows; @ScriptFunction protected boolean isDraggableRows() { return draggableRows; } @ScriptFunction public void setDraggableRows(boolean aValue) { draggableRows = aValue; } private static final String ON_RENDER_JSDOC = "" + "/**\n" + " * General render event handler.\n" + " * This hanler is called on each cell's rendering in the case when no render handler is provided for the specific column.\n" + " */"; @ScriptFunction(jsDoc = ON_RENDER_JSDOC) @EventMethod(eventClass = CellRenderEvent.class) @Undesignable @Override public JSObject getOnRender() { return generalOnRender; } @ScriptFunction public void setOnRender(JSObject aValue) { generalOnRender = aValue; if (rowsModel != null) { rowsModel.setGeneralOnRender(aValue); } } private static final String ON_AFTER_RENDER_JSDOC = "" + "/**\n" + " * After render event handler.\n" + " * This handler is called after cells' rendering is complete.\n" + " * It is called in asynchonous manner." + " */"; @ScriptFunction(jsDoc = ON_AFTER_RENDER_JSDOC) @EventMethod(eventClass = SourcedEvent.class) @Undesignable public JSObject getOnAfterRender() { return onAfterRender; } @ScriptFunction public void setOnAfterRender(JSObject aValue) { onAfterRender = aValue; } @ScriptFunction public void setShowHorizontalLines(boolean aValue) { showHorizontalLines = aValue; applyShowHorizontalLines(); } @ScriptFunction public void setShowVerticalLines(boolean aValue) { showVerticalLines = aValue; applyShowVerticalLines(); } private static final String ODD_ROW_COLOR_JSDOC = "" + "/**\n" + " * Odd rows color.\n" + " */"; @ScriptFunction(jsDoc = ODD_ROW_COLOR_JSDOC) @Designable(category = "appearance") public Color getOddRowsColor() { return oddRowsColor; } @ScriptFunction public void setOddRowsColor(Color aValue) { oddRowsColor = aValue; applyOddRowsColor(); } private static final String SHOW_HORIZONTAL_LINES_JSDOC = "" + "/**\n" + " * Determines if grid shows horizontal lines.\n" + " */"; @ScriptFunction(jsDoc = SHOW_HORIZONTAL_LINES_JSDOC) @Designable(category = "appearance") public boolean getShowHorizontalLines() { return showHorizontalLines; } private static final String SHOW_VERTICAL_LINES_JSDOC = "" + "/**\n" + " * Determines if grid shows vertical lines.\n" + " */"; @ScriptFunction(jsDoc = SHOW_VERTICAL_LINES_JSDOC) @Designable(category = "appearance") public boolean getShowVerticalLines() { return showVerticalLines; } private static final String SHOW_ODD_ROWS_IN_OTHER_COLOR_JSDOC = "" + "/**\n" + " * Determines if grid shows odd rows if other color.\n" + " */"; @ScriptFunction(jsDoc = SHOW_ODD_ROWS_IN_OTHER_COLOR_JSDOC) @Designable(category = "appearance") public boolean getShowOddRowsInOtherColor() { return showOddRowsInOtherColor; } @ScriptFunction public void setShowOddRowsInOtherColor(boolean aValue) { showOddRowsInOtherColor = aValue; applyShowOddRowsInOtherColor(); } private static final String GRID_COLOR_JSDOC = "" + "/**\n" + " * The color of the grid.\n" + " */"; @ScriptFunction(jsDoc = GRID_COLOR_JSDOC) @Designable(category = "appearance") public Color getGridColor() { return gridColor; } @ScriptFunction public void setGridColor(Color aValue) { gridColor = aValue; applyGridColor(); } private static final String ROWS_HEIGHT_JSDOC = "" + "/**\n" + " * The height of grid's rows.\n" + " */"; @ScriptFunction(jsDoc = ROWS_HEIGHT_JSDOC) @Designable(category = "appearance") public int getRowsHeight() { return rowsHeight; } @ScriptFunction public void setRowsHeight(int aValue) { if (aValue < 10) { aValue = 10; } if (aValue > 350) { aValue = 350; } rowsHeight = aValue; applyRowHeight(); } @ScriptFunction public void setEditable(boolean aValue) { editable = aValue; applyEditable(); } private static final String EDITABLE_JSDOC = "" + "/**\n" + " * Determines if gris cells are editable.\n" + " */"; @ScriptFunction(jsDoc = EDITABLE_JSDOC) @Designable(category = "editing") public boolean isEditable() { return editable; } @ScriptFunction public void setInsertable(boolean aValue) { insertable = aValue; } private static final String INSERTABLE_JSDOC = "" + "/**\n" + " * Determines if grid allows row insertion.\n" + " */"; @ScriptFunction(jsDoc = INSERTABLE_JSDOC) @Designable(category = "editing") public boolean isInsertable() { return insertable; } @ScriptFunction public void setDeletable(boolean aValue) { deletable = aValue; } private static final String DELETABLE_JSDOC = "" + "/**\n" + " * Determines if grid allows to delete rows.\n" + " */"; @ScriptFunction(jsDoc = DELETABLE_JSDOC) @Designable(category = "editing") public boolean isDeletable() { return deletable; } public GridTable getTopLeftTable() { return tlTable; } public GridTable getTopRightTable() { return trTable; } public GridTable getBottomLeftTable() { return blTable; } public GridTable getBottomRightTable() { return brTable; } public ListSelectionModel getRowsSelectionModel() { return rowsSelectionModel; } public ListSelectionModel getColumnsSelectionModel() { return columnsSelectionModel; } public List<JSObject> getSelected() throws Exception { List<JSObject> selectedRows = new ArrayList<>(); if (deepModel != null) {// design time is only the case for (int i = 0; i < deepModel.getRowCount(); i++) { if (rowsSelectionModel.isSelectedIndex(i)) { JSObject element = elementByViewIndex(i); selectedRows.add(element); } } } return selectedRows; } @Undesignable @ScriptFunction(name = "selected") public JSObject getJsSelected() throws Exception { List<JSObject> selectedRows = getSelected(); JSObject jsArray = Scripts.getSpace().makeArray(); JSObject jsPush = (JSObject) jsArray.getMember("push"); selectedRows.forEach((Object aItem) -> { jsPush.call(jsArray, new Object[]{aItem}); }); return jsArray; } @ScriptFunction @Override public JPopupMenu getComponentPopupMenu() { return super.getComponentPopupMenu(); } @ScriptFunction @Override public void setComponentPopupMenu(JPopupMenu aPopup) { super.setComponentPopupMenu(aPopup); applyComponentPopupMenu(); } @ScriptFunction @Override public void setBackground(Color aValue) { super.setBackground(aValue); applyBackground(); } public void applyBackground() { if (tlTable != null) { tlTable.setBackground(getBackground()); } if (trTable != null) { trTable.setBackground(getBackground()); } if (blTable != null) { blTable.setBackground(getBackground()); } if (brTable != null) { brTable.setBackground(getBackground()); } } @ScriptFunction(jsDoc = JS_NAME_DOC) @Override public String getName() { return super.getName(); } @ScriptFunction @Override public void setName(String name) { super.setName(name); } @ScriptFunction(jsDoc = GET_NEXT_FOCUSABLE_COMPONENT_JSDOC) @Override public JComponent getNextFocusableComponent() { return (JComponent) super.getNextFocusableComponent(); } @ScriptFunction @Override public void setNextFocusableComponent(JComponent aValue) { super.setNextFocusableComponent(aValue); } protected String errorMessage; @ScriptFunction(jsDoc = ERROR_JSDOC) @Override public String getError() { return errorMessage; } @ScriptFunction @Override public void setError(String aValue) { errorMessage = aValue; } @ScriptFunction @Override public Color getBackground() { return super.getBackground(); } @ScriptFunction @Override public void setForeground(Color aValue) { super.setForeground(aValue); applyForeground(); } public void applyForeground() { if (tlTable != null) { tlTable.setForeground(getForeground()); } if (trTable != null) { trTable.setForeground(getForeground()); } if (blTable != null) { blTable.setForeground(getForeground()); } if (brTable != null) { brTable.setForeground(getForeground()); } } @ScriptFunction @Override public Color getForeground() { return super.getForeground(); } @ScriptFunction @Override public void setFont(Font aValue) { super.setFont(aValue); applyFont(); } public void applyFont() { if (tlTable != null) { tlTable.setFont(getFont()); } if (trTable != null) { trTable.setFont(getFont()); } if (blTable != null) { blTable.setFont(getFont()); } if (brTable != null) { brTable.setFont(getFont()); } } @ScriptFunction @Override public Font getFont() { return super.getFont(); } @ScriptFunction(jsDoc = LEFT_JSDOC) @Override public int getLeft() { return super.getLocation().x; } @ScriptFunction @Override public void setLeft(int aValue) { if (super.getParent() != null && super.getParent().getLayout() instanceof MarginLayout) { MarginLayout.ajustLeft(this, aValue); } super.setLocation(aValue, getTop()); } @ScriptFunction(jsDoc = TOP_JSDOC) @Override public int getTop() { return super.getLocation().y; } @ScriptFunction @Override public void setTop(int aValue) { if (super.getParent() != null && super.getParent().getLayout() instanceof MarginLayout) { MarginLayout.ajustTop(this, aValue); } super.setLocation(getLeft(), aValue); } @ScriptFunction(jsDoc = WIDTH_JSDOC) @Override public int getWidth() { return super.getWidth(); } @ScriptFunction @Override public void setWidth(int aValue) { Widget.setWidth(this, aValue); } @ScriptFunction(jsDoc = HEIGHT_JSDOC) @Override public int getHeight() { return super.getHeight(); } @ScriptFunction @Override public void setHeight(int aValue) { Widget.setHeight(this, aValue); } @ScriptFunction(jsDoc = FOCUS_JSDOC) @Override public void focus() { if (!brTable.requestFocusInWindow()) { if (!trTable.requestFocusInWindow()) { if (!blTable.requestFocusInWindow()) { tlTable.requestFocusInWindow(); } } } } @ScriptFunction(jsDoc = VISIBLE_JSDOC) @Override public boolean getVisible() { return super.isVisible(); } @ScriptFunction @Override public void setVisible(boolean aValue) { super.setVisible(aValue); } @ScriptFunction(jsDoc = FOCUSABLE_JSDOC) @Override public boolean getFocusable() { return super.isFocusable(); } @ScriptFunction @Override public void setFocusable(boolean aValue) { super.setFocusable(aValue); } @ScriptFunction(jsDoc = ENABLED_JSDOC) @Override public boolean getEnabled() { return super.isEnabled(); } @ScriptFunction @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); applyEnabled(); } @ScriptFunction @Override public String getToolTipText() { return super.getToolTipText(); } @ScriptFunction @Override public void setToolTipText(String aValue) { if (aValue != null && aValue.isEmpty()) { aValue = null; } super.setToolTipText(aValue); applyToolTipText(); } @ScriptFunction(jsDoc = OPAQUE_TEXT_JSDOC) @Override public boolean getOpaque() { return super.isOpaque(); } @ScriptFunction @Override public void setOpaque(boolean aValue) { super.setOpaque(aValue); } @ScriptFunction @Designable(category = "appearance") public int getFrozenColumns() { return frozenColumns; } @ScriptFunction public void setFrozenColumns(int aValue) throws Exception { if (frozenColumns != aValue) { frozenColumns = aValue; applyColumns(); applyHeader(); applyFrozenDecor(); } } @ScriptFunction @Designable(category = "appearance") public int getFrozenRows() { return frozenRows; } @ScriptFunction public void setFrozenRows(int aValue) { if (frozenRows != aValue) { frozenRows = aValue; // lightweight apply topRowsConstraint.setMax(frozenRows - 1); bottomRowsConstraint.setMin(frozenRows); // heavy apply //applyRows(); applyFrozenDecor(); } } protected void unbindCursor() { if (boundToCursor != null) { Scripts.unlisten(boundToCursor); boundToCursor = null; } } protected void bindCursor(JSObject aModelData) { if (aModelData != null) { boundToCursor = Scripts.getSpace().listen(aModelData, cursorProperty, new AbstractJSObject() { @Override public Object call(Object thiz, Object... args) { ModelGrid.this.invalidate(); ModelGrid.this.repaint(); return null; } }); } } protected void bind() { if (data != null) { if (Scripts.isInitialized()) { Object modelData = field != null && !field.isEmpty() ? ModelWidget.getPathData(data, field) : data; if (rowsModel != null) { modelData = modelData instanceof ScriptObject ? ScriptUtils.wrap((ScriptObject) modelData) : modelData; if (modelData instanceof JSObject) { JSObject jsModelData = (JSObject) modelData; unbindCursor(); rowsModel.setData(jsModelData); bindCursor(jsModelData); } else { unbindCursor(); rowsModel.setData(null); } } if (field != null && !field.isEmpty()) { boundToData = Scripts.getSpace().listen(data, field, new AbstractJSObject() { @Override public Object call(Object thiz, Object... args) { Object newModelData = ModelWidget.getPathData(data, field); if (rowsModel != null) { newModelData = newModelData instanceof ScriptObject ? ScriptUtils.wrap((ScriptObject) newModelData) : newModelData; if (newModelData instanceof JSObject) { JSObject jsModelData = (JSObject) newModelData; unbindCursor(); rowsModel.setData(jsModelData); bindCursor(jsModelData); } else { unbindCursor(); rowsModel.setData(null); } } return null; } }); } } else { rowsModel.setData(data); } } } protected void unbind() { if (boundToData != null) { Scripts.unlisten(boundToData); boundToData = null; } rowsModel.setData(null); } @ScriptFunction @Designable(category = "model") public JSObject getData() { return data; } @ScriptFunction public void setData(JSObject aValue) { if (data != null ? !data.equals(aValue) : aValue != null) { unbind(); data = aValue; bind(); } } @ScriptFunction @Designable(category = "model") public String getField() { return field; } @ScriptFunction public void setField(String aValue) { if (field == null ? aValue != null : !field.equals(aValue)) { unbind(); field = aValue; bind(); } } public static final String CURSOR_FIELD_JSDOC = "" + "/**\n" + " * Determines wich property of ModelGrid's collection is responsible of \"current\" item. Its default value is \"cursor\".\n" + " */"; @ScriptFunction(jsDoc = CURSOR_FIELD_JSDOC) @Designable(category = "model") public String getCursorProperty() { return cursorProperty; } @ScriptFunction public void setCursorProperty(String aValue) { if (aValue != null && !cursorProperty.equals(aValue)) { unbind(); cursorProperty = aValue; bind(); } } private static final String REDRAW_JSDOC = "" + "/**\n" + " * Redraw the component.\n" + " */"; @ScriptFunction(jsDoc = REDRAW_JSDOC) @Override public void redraw() { ListSelectionModel rowsSelection = saveRowsSelection(); ListSelectionModel columnsSelection = saveColumnsSelection(); rowsModel.fireElementsChanged(); invalidate(); repaint(); restoreRowsSelection(rowsSelection); restoreColumnsSelection(columnsSelection); } protected void applyRows() { if (collapseExpandListener != null) { collapseExpandListener.remove(); } if (rowsModel != null) { rowsModel.setData(null); } Object oModelData = field != null && !field.isEmpty() ? ModelWidget.getPathData(data, field) : data; JSObject modelData = oModelData instanceof JSObject ? (JSObject) oModelData : null; if (isTreeConfigured()) { rowsModel = new ArrayTreedModel(columnModel, modelData, parentField, childrenField, generalOnRender); deepModel = new TableFront2TreedModel<>((ArrayTreedModel) rowsModel); collapseExpandListener = ((TableFront2TreedModel<JSObject>) deepModel).addCollapseExpandListener(new CollapseExpandListener<JSObject>() { @Override public void collapsed(JSObject aItem) { if (onCollapse != null) { onCollapse.call(published, new Object[]{new com.eas.client.forms.events.ItemEvent(ModelGrid.this, aItem).getPublished()}); } } @Override public void expanded(JSObject aItem) { if (onExpand != null) { onExpand.call(published, new Object[]{new com.eas.client.forms.events.ItemEvent(ModelGrid.this, aItem).getPublished()}); } } }); rowSorter = new TreedRowsSorter<>((TableFront2TreedModel<JSObject>) deepModel, rowsSelectionModel); } else { rowsModel = new ArrayTableModel(columnModel, modelData, generalOnRender); deepModel = (TableModel) rowsModel; rowSorter = new TabularRowsSorter<>((ArrayTableModel) deepModel, rowsSelectionModel); } cachingModel = new CachingTableModel(deepModel, CELLS_CACHE_SIZE); lheader.setRowSorter(rowSorter); rheader.setRowSorter(rowSorter); // rows constraints setup topRowsConstraint = new LinearConstraint(0, frozenRows - 1); bottomRowsConstraint = new LinearConstraint(frozenRows, Integer.MAX_VALUE); tlTable.setSelectionModel(new DefaultListSelectionModel()); trTable.setSelectionModel(new DefaultListSelectionModel()); blTable.setSelectionModel(new DefaultListSelectionModel()); brTable.setSelectionModel(new DefaultListSelectionModel()); tlTable.setRowSorter(null); trTable.setRowSorter(null); blTable.setRowSorter(null); brTable.setRowSorter(null); tlTable.setModel(new DefaultTableModel()); trTable.setModel(new DefaultTableModel()); blTable.setModel(new DefaultTableModel()); brTable.setModel(new DefaultTableModel()); tlTable.setRowSorter(new ConstrainedRowSorter<>(rowSorter, topRowsConstraint)); trTable.setRowSorter(new ConstrainedRowSorter<>(rowSorter, topRowsConstraint)); blTable.setRowSorter(new ConstrainedRowSorter<>(rowSorter, bottomRowsConstraint)); brTable.setRowSorter(new ConstrainedRowSorter<>(rowSorter, bottomRowsConstraint)); // tlTable.setModel(cachingModel); trTable.setModel(cachingModel); blTable.setModel(cachingModel); brTable.setModel(cachingModel); tlTable.setSelectionModel(new ConstrainedListSelectionModel(rowsSelectionModel, topRowsConstraint)); trTable.setSelectionModel(new ConstrainedListSelectionModel(rowsSelectionModel, topRowsConstraint)); blTable.setSelectionModel(new ConstrainedListSelectionModel(rowsSelectionModel, bottomRowsConstraint)); brTable.setSelectionModel(new ConstrainedListSelectionModel(rowsSelectionModel, bottomRowsConstraint)); configureTreedView(); } private static final String CHANGED_JS_DOC = "" + "/**\n" + " * Notifies the grid about data have been changed.\n" + " * @param aChanged Array of changed objects.\n" + " */"; @ScriptFunction(jsDoc = CHANGED_JS_DOC, params = "aChanged") public void changed(JSObject aChangedItems) { if (aChangedItems.hasMember("length") && JSType.toNumber(aChangedItems.getMember("length")) > 0) { rowsModel.fireElementsDataChanged(); } } private static final String ADDED_JS_DOC = "" + "/**\n" + " * Notifies the grid about some elements have been added to grid's rows array.\n" + " * @param aAdded Array of added objects.\n" + " */"; @ScriptFunction(jsDoc = ADDED_JS_DOC, params = "aAdded") public void added(JSObject aAddedItems) { rowsModel.fireElementsChanged(); } private static final String REMOVED_JS_DOC = "" + "/**\n" + " * Notifies the grid about some elements have been removed from grid's rows array.\n" + " * @param aRemoved Array of removed objects.\n" + " */"; @ScriptFunction(jsDoc = REMOVED_JS_DOC, params = "aRemoved") public void removed(JSObject aRemovedItems) { rowsModel.fireElementsChanged(); } @Undesignable public List<GridColumnsNode> getHeader() { return header; } public void setHeader(List<GridColumnsNode> aValue) throws Exception { if (header != aValue) { header = aValue; applyHeader(); } } protected void applyFrozenDecor() { tlPanel.setBorder(new MatteBorder(0, 0, frozenRows > 0 ? 1 : 0, frozenColumns > 0 ? 1 : 0, FIXED_COLOR)); trPanel.setBorder(new MatteBorder(0, 0, frozenRows > 0 ? 1 : 0, 0, FIXED_COLOR)); blPanel.setBorder(new MatteBorder(0, 0, 0, frozenColumns > 0 ? 1 : 0, FIXED_COLOR)); } protected void applyColumns() { // columns constraints setup LinearConstraint leftColsConstraint = new LinearConstraint(0, frozenColumns - 1); LinearConstraint rightColsConstraint = new LinearConstraint(frozenColumns, Integer.MAX_VALUE); // tables column models setup tlTable.setColumnModel(new ConstrainedColumnModel(columnModel, leftColsConstraint)); tlTable.getColumnModel().setSelectionModel(new ConstrainedListSelectionModel(columnsSelectionModel, leftColsConstraint)); trTable.setColumnModel(new ConstrainedColumnModel(columnModel, rightColsConstraint)); trTable.getColumnModel().setSelectionModel(new ConstrainedListSelectionModel(columnsSelectionModel, rightColsConstraint)); brTable.setColumnModel(new ConstrainedColumnModel(columnModel, rightColsConstraint)); brTable.getColumnModel().setSelectionModel(new ConstrainedListSelectionModel(columnsSelectionModel, rightColsConstraint)); blTable.setColumnModel(new ConstrainedColumnModel(columnModel, leftColsConstraint)); blTable.getColumnModel().setSelectionModel(new ConstrainedListSelectionModel(columnsSelectionModel, leftColsConstraint)); // headers setup lheader.setColumnModel(tlTable.getColumnModel()); rheader.setColumnModel(trTable.getColumnModel()); reindexColumns(); } @Override public void reindexColumns() { for (int i = 0; i < columnModel.getColumnCount(); i++) { TableColumn col = columnModel.getColumn(i); col.setModelIndex(i); } if (cachingModel != null) { cachingModel.clearCache(); } } protected void applyHeader() { // set header List<GridColumnsNode> lgroups = HeaderSplitter.split(header, 0, frozenColumns - 1); List<GridColumnsNode> rgroups = HeaderSplitter.split(header, frozenColumns, Integer.MAX_VALUE); lheader.setRoots(lgroups); rheader.setRoots(rgroups); lheader.regenerate(); rheader.regenerate(); } public void insertElementAtCursor() { try { if (insertable && rowsModel.getData() != null && rowsModel.getData().hasMember("splice")) { ListSelectionModel columnSelection = saveColumnsSelection(); JSObject ldata = rowsModel.getData(); JSObject jsSplice = (JSObject) ldata.getMember("splice"); JSObject jsIndexOf = (JSObject) ldata.getMember("indexOf"); Object oElementClass = ldata.getMember("elementClass"); JSObject jsElementClass = oElementClass instanceof JSObject && ((JSObject) oElementClass).isFunction() ? (JSObject) oElementClass : null; JSObject jsCreated = jsElementClass != null ? (JSObject) jsElementClass.newObject(new Object[]{}) : Scripts.getSpace().makeObj(); JSObject jsCursor = getCurrentRow(); rowsSelectionModel.removeListSelectionListener(generalSelectionChangesReflector); try { if (rowsModel instanceof ArrayTreedModel) { Object parentValue = jsCursor != null ? ModelWidget.getPathData(jsCursor, parentField) : null; int length = JSType.toInteger(ldata.getMember("length")); int cursorAt = JSType.toInteger(jsIndexOf.call(ldata, new Object[]{jsCursor})); int insertAt = cursorAt >= 0 && cursorAt < length ? cursorAt + 1 : length; jsSplice.call(ldata, new Object[]{insertAt, 0, jsCreated}); ModelWidget.setPathData(jsCreated, parentField, parentValue); } else { int length = deepModel.getRowCount(); int cursorAt = rowsSelectionModel.getLeadSelectionIndex(); int insertAt = cursorAt >= 0 && cursorAt < length ? cursorAt + 1 : length; jsSplice.call(ldata, new Object[]{insertAt, 0, jsCreated}); } } finally { rowsSelectionModel.addListSelectionListener(generalSelectionChangesReflector); } EventQueue.invokeLater(() -> { try { makeVisible(jsCreated); restoreColumnsSelection(columnSelection); } catch (Exception ex) { Logger.getLogger(ModelGrid.class.getName()).log(Level.SEVERE, null, ex); } }); } } catch (Exception ex) { Logger.getLogger(ModelGrid.class.getName()).log(Level.SEVERE, null, ex); } } protected ListSelectionModel saveRowsSelection() { ListSelectionModel savedSelection = new DefaultListSelectionModel(); for (int viewRowIndex = rowsSelectionModel.getMinSelectionIndex(); viewRowIndex <= rowsSelectionModel.getMaxSelectionIndex(); viewRowIndex++) { if (rowsSelectionModel.isSelectedIndex(viewRowIndex)) { // We have to act upon view coordinates here! savedSelection.addSelectionInterval(viewRowIndex, viewRowIndex); } } return savedSelection; } protected ListSelectionModel saveColumnsSelection() { ListSelectionModel savedSelection = new DefaultListSelectionModel(); for (int viewColumnIndex = columnsSelectionModel.getMinSelectionIndex(); viewColumnIndex <= columnsSelectionModel.getMaxSelectionIndex(); viewColumnIndex++) { if (columnsSelectionModel.isSelectedIndex(viewColumnIndex)) { // We have to act upon view coordinates here! savedSelection.addSelectionInterval(viewColumnIndex, viewColumnIndex); } } return savedSelection; } public void deleteSelectedElements() { if (deletable && rowsModel.getData() != null && rowsModel.getData().hasMember("splice")) { JSObject ldata = rowsModel.getData(); JSObject jsSplice = (JSObject) ldata.getMember("splice"); ListSelectionModel wasSeletedRows = saveRowsSelection(); ListSelectionModel wasSeletedColumns = saveColumnsSelection(); try { Set<Object> elements = new HashSet<>(); for (int viewRowIndex = rowsSelectionModel.getMinSelectionIndex(); viewRowIndex <= rowsSelectionModel.getMaxSelectionIndex(); viewRowIndex++) { if (rowsSelectionModel.isSelectedIndex(viewRowIndex)) { // We have to act upon model coordinates here! JSObject element = elementByViewIndex(viewRowIndex); if (element != null) { elements.add(element); } } } int length = JSType.toInteger(ldata.getMember("length")); for (int i = length - 1; i >= 0; i--) { Object oElement = ldata.getSlot(i); if (elements.contains(oElement)) { jsSplice.call(ldata, new Object[]{i, 1}); } } } finally { EventQueue.invokeLater(() -> { restoreRowsSelection(wasSeletedRows); restoreColumnsSelection(wasSeletedColumns); }); } } } protected void restoreRowsSelection(ListSelectionModel wasSeleted) { // We have to act upon view coordinates here! rowsSelectionModel.clearSelection(); for (int viewRowIndex = wasSeleted.getMinSelectionIndex(); viewRowIndex <= wasSeleted.getMaxSelectionIndex(); viewRowIndex++) { if (viewRowIndex >= 0 && viewRowIndex < rowSorter.getViewRowCount()) { rowsSelectionModel.addSelectionInterval(viewRowIndex, viewRowIndex); } else if (viewRowIndex == rowSorter.getViewRowCount()) { rowsSelectionModel.addSelectionInterval(viewRowIndex - 1, viewRowIndex - 1); } } } protected void restoreColumnsSelection(ListSelectionModel aSeleted) { // We have to act upon view coordinates here! columnsSelectionModel.clearSelection(); for (int viewColumnIndex = aSeleted.getMinSelectionIndex(); viewColumnIndex <= aSeleted.getMaxSelectionIndex(); viewColumnIndex++) { if (viewColumnIndex >= 0 && viewColumnIndex < columnModel.getColumnCount()) { columnsSelectionModel.addSelectionInterval(viewColumnIndex, viewColumnIndex); } } } protected class TablesFocusPropagator implements FocusListener { @Override public void focusGained(FocusEvent e) { FocusEvent fe = new FocusEvent(ModelGrid.this, e.getID(), e.isTemporary(), e.getOppositeComponent()); for (FocusListener l : getFocusListeners()) { l.focusGained(fe); } } @Override public void focusLost(FocusEvent e) { FocusEvent fe = new FocusEvent(ModelGrid.this, e.getID(), e.isTemporary(), e.getOppositeComponent()); for (FocusListener l : getFocusListeners()) { l.focusLost(fe); } } } protected class GeneralSelectionChangesReflector implements ListSelectionListener { @Override public void valueChanged(ListSelectionEvent e) { try { if (!try2StopAnyEditing()) { try2CancelAnyEditing(); } Object oModelData = field != null && !field.isEmpty() ? ModelWidget.getPathData(data, field) : data; JSObject modelData = oModelData instanceof JSObject ? (JSObject) oModelData : null; if (modelData != null) { JSObject jsNewCursor = rowsSelectionModel.getLeadSelectionIndex() != -1 ? elementByViewIndex(rowsSelectionModel.getLeadSelectionIndex()) : null; if (modelData.hasMember(cursorProperty)) { modelData.setMember(cursorProperty, jsNewCursor); } } repaint(); } catch (Exception ex) { Logger.getLogger(ModelGrid.class.getName()).log(Level.SEVERE, null, ex); } if (onItemSelected != null) { try { JSObject jsItem = elementByViewIndex(rowsSelectionModel.getLeadSelectionIndex()); onItemSelected.call(getPublished(), new Object[]{new com.eas.client.forms.events.ItemEvent(ModelGrid.this, jsItem).getPublished()}); } catch (Exception ex) { Logger.getLogger(ModelGrid.class.getName()).log(Level.SEVERE, null, ex); } } } } protected class ColumnHeaderScroller implements ChangeListener { protected JViewport columnHeader; protected JViewport content; protected boolean working; public ColumnHeaderScroller(JViewport aColumnHeader, JViewport aContent) { columnHeader = aColumnHeader; content = aContent; } @Override public void stateChanged(ChangeEvent e) { if (!working) { working = true; try { Point contentPos = content.getViewPosition(); contentPos.x = columnHeader.getViewPosition().x; content.setViewPosition(contentPos); } finally { working = false; } } } } protected class RowHeaderScroller implements ChangeListener { protected JViewport rowHeader; protected JViewport content; protected boolean working; public RowHeaderScroller(JViewport aRowHeader, JViewport aContent) { super(); rowHeader = aRowHeader; content = aContent; } @Override public void stateChanged(ChangeEvent e) { if (!working && frozenColumns > 0) { working = true; try { Point contentPos = content.getViewPosition(); contentPos.y = rowHeader.getViewPosition().y; content.setViewPosition(contentPos); } finally { working = false; } } } } public void refreshColumnModel() { List<GridColumnsNode> leaves = new ArrayList<>(); MultiLevelHeader.achieveLeaves(header, leaves); for (int i = columnModel.getColumnCount() - 1; i >= 0; i--) { columnModel.removeColumn(columnModel.getColumn(i)); } leaves.stream().sequential().forEach((GridColumnsNode aNode) -> { columnModel.addColumn(aNode.getTableColumn()); }); } protected boolean autoRefreshHeader = true; protected PropertyChangeListener columnNodesListener = (PropertyChangeEvent evt) -> { if (autoRefreshHeader) { refreshColumnModel(); applyColumns(); applyHeader(); } }; public boolean isAutoRefreshHeader() { return autoRefreshHeader; } public void setAutoRefreshHeader(boolean aValue) { autoRefreshHeader = aValue; } @ScriptFunction @Override public void addColumnNode(GridColumnsNode aColumn) { insertColumnNode(header.size(), aColumn); } @ScriptFunction public GridColumnsNode[] columnNodes() { return header.toArray(new GridColumnsNode[]{}); } @ScriptFunction @Override public void removeColumnNode(GridColumnsNode aNode) { if (aNode != null) { aNode.getChangeSupport().removePropertyChangeListener("children", columnNodesListener); header.remove(aNode); if (autoRefreshHeader) { refreshColumnModel(); applyColumns(); applyHeader(); } } } @ScriptFunction @Override public void insertColumnNode(int aIndex, GridColumnsNode aNode) { header.add(aIndex, aNode); aNode.getChangeSupport().removePropertyChangeListener("children", columnNodesListener); if (autoRefreshHeader) { refreshColumnModel(); applyColumns(); applyHeader(); } } public void removeColumn(ModelColumn aColumn) throws Exception { columnModel.removeColumn(aColumn); // edit header... // apply changes applyColumns(); applyHeader(); } @Override public Dimension getPreferredSize() { Dimension d = super.getPreferredSize(); int width = d.width; int height = d.height; if (width < ModelComponentDecorator.WIDGETS_DEFAULT_WIDTH) { width = ModelComponentDecorator.WIDGETS_DEFAULT_WIDTH; } if (height < ModelComponentDecorator.WIDGETS_DEFAULT_HEIGHT) { height = ModelComponentDecorator.WIDGETS_DEFAULT_HEIGHT; } return new Dimension(width, height); } @Override public void invalidate() { super.invalidate(); if (lheader != null) { lheader.invalidate(); } if (rheader != null) { rheader.invalidate(); } if (tlTable != null) { tlTable.invalidate(); } if (trTable != null) { trTable.invalidate(); } if (blTable != null) { blTable.invalidate(); } if (brTable != null) { brTable.invalidate(); } } @ScriptFunction @Override public boolean try2StopAnyEditing() { boolean res = true; if (tlTable.getCellEditor() != null && !tlTable.getCellEditor().stopCellEditing()) { res = false; } if (trTable.getCellEditor() != null && !trTable.getCellEditor().stopCellEditing()) { res = false; } if (blTable.getCellEditor() != null && !blTable.getCellEditor().stopCellEditing()) { res = false; } if (brTable.getCellEditor() != null && !brTable.getCellEditor().stopCellEditing()) { res = false; } return res; } @ScriptFunction @Override public boolean try2CancelAnyEditing() { if (tlTable.getCellEditor() != null) { tlTable.getCellEditor().cancelCellEditing(); } if (trTable.getCellEditor() != null) { trTable.getCellEditor().cancelCellEditing(); } if (blTable.getCellEditor() != null) { blTable.getCellEditor().cancelCellEditing(); } if (brTable.getCellEditor() != null) { brTable.getCellEditor().cancelCellEditing(); } return tlTable.getCellEditor() == null && trTable.getCellEditor() == null && blTable.getCellEditor() == null && brTable.getCellEditor() == null; } @Override public boolean isAutoRedraw() { Object oModelData = field != null && !field.isEmpty() ? ModelWidget.getPathData(data, field) : data; JSObject modelData = oModelData instanceof JSObject ? (JSObject) oModelData : null; return modelData != null && !likeEntity(modelData); } private boolean likeEntity(JSObject aCandidate) { return aCandidate.hasMember("onRequeried") && aCandidate.hasMember("append"); } protected Runnable redrawEnqueued; @Override public void enqueueRedraw() { redrawEnqueued = () -> { if (redrawEnqueued == this) { redrawEnqueued = null; redraw(); } }; EventQueue.invokeLater(redrawEnqueued); } public TableColumnModel getColumnModel() { return columnModel; } public TableModel getDeepModel() { return deepModel; } public ArrayModel getRowsetsModel() { return rowsModel; } public TabularRowsSorter<? extends TableModel> getRowSorter() { return rowSorter; } public static ModelGrid getFirstDbGrid(Component aComp) { Component lParent = aComp; while (lParent != null && !(lParent instanceof ModelGrid)) { lParent = lParent.getParent(); } if (lParent != null && lParent instanceof ModelGrid) { return (ModelGrid) lParent; } return null; } @Override public void addProcessedElement(JSObject aRow) { processedRows.add(aRow); progressIndicator.setVisible(true); gridScroll.repaint(); } @Override public void removeProcessedElement(JSObject aRow) { processedRows.remove(aRow); if (processedRows.isEmpty()) { progressIndicator.setVisible(false); } gridScroll.repaint(); } @Override public JSObject[] getProcessedElements() { return processedRows.toArray(new JSObject[]{}); } @Override public boolean isElementProcessed(JSObject anElement) { return processedRows.contains(anElement); } private static final String SELECT_JSDOC = "" + "/**\n" + " * Selects the specified element.\n" + " * @param instance Entity's instance to be selected.\n" + " */"; @ScriptFunction(jsDoc = SELECT_JSDOC, params = {"instance"}) public void select(JSObject anElement) throws Exception { if (anElement != null) { int idx = modelIndexByElement(anElement); if (idx != -1) { rowsSelectionModel.addSelectionInterval(idx, idx); } } } private static final String UNSELECT_JSDOC = "" + "/**\n" + " * Unselects the specified element.\n" + " * @param instance Entity's instance to be unselected\n" + " */"; @ScriptFunction(jsDoc = UNSELECT_JSDOC, params = {"instance"}) public void unselect(JSObject anElement) throws Exception { if (anElement != null) { int idx = modelIndexByElement(anElement); if (idx != -1) { rowsSelectionModel.removeSelectionInterval(idx, idx); } } } private static final String CLEAR_SELECTION_JSDOC = "" + "/**\n" + " * Clears current selection.\n" + " */"; @ScriptFunction(jsDoc = CLEAR_SELECTION_JSDOC) public void clearSelection() { columnsSelectionModel.clearSelection(); rowsSelectionModel.clearSelection(); } public void clearColumns() { Collections.list(columnModel.getColumns()).stream().forEach((TableColumn aColumn) -> { columnModel.removeColumn(aColumn); }); } public void addColumns(ModelColumn[] aColumns) { for (ModelColumn col : aColumns) { columnModel.addColumn(col); } applyColumns(); } public void setColumns(ModelColumn[] aColumns) { clearColumns(); addColumns(aColumns); } public ModelColumn[] getColumns() { ModelColumn[] res = new ModelColumn[columnModel.getColumnCount()]; for (int i = 0; i < res.length; i++) { res[i] = (ModelColumn) columnModel.getColumn(i); } return res; } private static final String FIND_SOMETHING_JSDOC = "" + "/**\n" + " * Shows find dialog.\n" + " * @deprecated Use find() instead. \n" + " */"; @ScriptFunction(jsDoc = FIND_SOMETHING_JSDOC) public void find() { findSomethingAction.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "startFinding", 0)); } public boolean makeVisible(JSObject anElement) throws Exception { return makeVisible(anElement, true); } private static final String MAKE_VISIBLE_JSDOC = "" + "/**\n" + " * Makes specified instance visible.\n" + " * @param instance Entity's instance to make visible.\n" + " * @param need2select true to select the instance (optional).\n" + " */"; @ScriptFunction(jsDoc = MAKE_VISIBLE_JSDOC, params = {"instance", "need2select"}) @Override public boolean makeVisible(JSObject anElement, boolean need2Select) throws Exception { if (anElement != null) { // Let's process possible tree paths if (rowsModel instanceof ArrayTreedModel) { assert deepModel instanceof TableFront2TreedModel<?>; TableFront2TreedModel<JSObject> front = (TableFront2TreedModel<JSObject>) deepModel; List<JSObject> path = front.buildPathTo(anElement); for (int i = 0; i < path.size() - 1; i++) { front.expand(path.get(i), true); } } int modelIndex = modelIndexByElement(anElement); if (modelIndex != -1) { int generalViewIndex = rowSorter.convertRowIndexToView(modelIndex); if (brTable.getColumnCount() > 0) { int brViewIndex = generalViewIndex - trTable.getRowCount(); if (brViewIndex >= 0) { Rectangle cellRect = brTable.getCellRect(brViewIndex, 0, false); cellRect.height *= 10; brTable.scrollRectToVisible(cellRect); } } else if (blTable.getColumnCount() > 0) { int blViewIndex = generalViewIndex - tlTable.getRowCount(); if (blViewIndex >= 0) { Rectangle cellRect = blTable.getCellRect(blViewIndex, 0, false); cellRect.height *= 10; blTable.scrollRectToVisible(cellRect); } } if (need2Select) { columnsSelectionModel.setSelectionInterval(0, columnModel.getColumnCount() - 1); rowsSelectionModel.setSelectionInterval(generalViewIndex, generalViewIndex); } return true; } } return false; } private static final String EXPANDED_JSDOC = "" + "/**\n" + " * Tests if node of specified .data array element is expanded.\n" + " * @param instance .data array element to test.\n" + " */"; @ScriptFunction(jsDoc = EXPANDED_JSDOC, params = {"instance"}) public boolean expanded(JSObject anElement) { if (deepModel instanceof TableFront2TreedModel<?>) { TableFront2TreedModel<JSObject> front = (TableFront2TreedModel<JSObject>) deepModel; return front.isExpanded(anElement); } else { return false; } } private static final String EXPAND_JSDOC = "" + "/**\n" + " * Makes node of specified .data array element expanded.\n" + " * @param instance .data array element to expand.\n" + " */"; @ScriptFunction(jsDoc = EXPAND_JSDOC, params = {"instance"}) public void expand(JSObject anElement) { if (deepModel instanceof TableFront2TreedModel<?>) { TableFront2TreedModel<JSObject> front = (TableFront2TreedModel<JSObject>) deepModel; front.expand(anElement, false); } } private static final String COLLAPSE_JSDOC = "" + "/**\n" + " * Makes node of specified .data array element collapsed.\n" + " * @param instance .data array element to collapsed.\n" + " */"; @ScriptFunction(jsDoc = COLLAPSE_JSDOC, params = {"instance"}) public void collapse(JSObject anElement) { if (deepModel instanceof TableFront2TreedModel<?>) { TableFront2TreedModel<JSObject> front = (TableFront2TreedModel<JSObject>) deepModel; front.collapse(anElement); } } private static final String TOGGLE_JSDOC = "" + "/**\n" + " * Makes node of specified .data array element expanded if it was already collapsed and collapsed otherwise.\n" + " * @param instance .data array element to expand or collpase.\n" + " */"; @ScriptFunction(jsDoc = TOGGLE_JSDOC, params = {"instance"}) public void toggle(JSObject anElement) { if (expanded(anElement)) { collapse(anElement); } else { expand(anElement); } } public boolean isCurrentRow(JSObject anElement) { JSObject jsCursor = getCurrentRow(); return jsCursor != null ? jsCursor.equals(anElement) : false; } protected JSObject getCurrentRow() { Object oCursor = rowsModel.getData().getMember(cursorProperty); return oCursor instanceof JSObject ? (JSObject) oCursor : null; } public JTable getFocusedTable() { JTable focusedTable = null; if (tlTable != null && tlTable.hasFocus()) { focusedTable = tlTable; } if (trTable != null && trTable.hasFocus()) { focusedTable = trTable; } if (blTable != null && blTable.hasFocus()) { focusedTable = blTable; } if (brTable != null && brTable.hasFocus()) { focusedTable = brTable; } return focusedTable; } /* private String transformCellValue(Object aValue, int aCol, boolean isData) { if (aValue != null) { Object value = null; if (isData) { if (aValue instanceof CellData) { CellData cd = (CellData) aValue; value = cd.getData(); } else { value = aValue; } } else { TableColumn tc = getColumnModel().getColumn(aCol); TableCellRenderer renderer = tc.getCellRenderer(); if (renderer instanceof DbCombo) { try { value = ((DbCombo) renderer).achiveDisplayValue(aValue instanceof CellData ? ((CellData) aValue).getData() : aValue); } catch (Exception ex) { Logger.getLogger(ModelGrid.class.getName()).log(Level.SEVERE, "Could not get cell value", ex); } } else { if (aValue instanceof CellData) { CellData cd = (CellData) aValue; value = cd.getDisplay() != null ? cd.getDisplay() : cd.getData(); } else { value = aValue; } } } if (value != null) { return value.toString(); } } return ""; } private String[] transformRow(int aRow, boolean selectedOnly, boolean isData) { TableModel view = getDeepModel(); int minCol = 0; int maxCol = view.getColumnCount(); String[] res = new String[maxCol]; int curentColumn = 0; for (int col = minCol; col < getColumnModel().getColumnCount(); col++) { TableColumn tc = getColumnModel().getColumn(col); if (tc.getWidth() > 0 && !(tc instanceof RowHeaderTableColumn)) { if (selectedOnly) { if (getColumnsSelectionModel().isSelectedIndex(col)) { res[curentColumn] = transformCellValue(view.getValueAt(aRow, tc.getModelIndex()), col, isData); curentColumn++; } } else { res[curentColumn] = transformCellValue(view.getValueAt(aRow, tc.getModelIndex()), col, isData); curentColumn++; } } } return res; } private Object[] convertView(String[][] aCells) { Object[] cells = new Object[aCells.length]; for (int i = 0; i < aCells.length; i++) { String[] row = aCells[i]; Object[] o = new Object[row.length]; System.arraycopy(row, 0, o, 0, row.length); cells[i] = o; } return cells; } public Object[] getCells() { Object[] cells = convertView(getGridView(false, false)); return cells; } public Object[] getSelectedCells() { Object[] selectedCells = convertView(getGridView(true, false)); return selectedCells; } private String[][] getGridView(boolean selectedOnly, boolean isData) { TableModel cellsModel = getDeepModel(); if (cellsModel != null) { int minRow = 0; int maxRow = cellsModel.getRowCount(); int columnCount = cellsModel.getColumnCount(); String[][] res = new String[maxRow][columnCount]; ListSelectionModel rowSelecter = getRowsSelectionModel(); for (int viewRow = minRow; viewRow < maxRow; viewRow++) { if (selectedOnly) { if (rowSelecter.isSelectedIndex(viewRow)) { res[viewRow] = transformRow(rowSorter.convertRowIndexToModel(viewRow), selectedOnly, isData); } } else { res[viewRow] = transformRow(rowSorter.convertRowIndexToModel(viewRow), selectedOnly, isData); } } return res; } else { return new String[0][0]; } } */ protected class TablesMousePropagator implements MouseListener, MouseMotionListener, MouseWheelListener { @Override public void mouseClicked(MouseEvent e) { if (e.getSource() instanceof Component) { e = SwingUtilities.convertMouseEvent((Component) e.getSource(), e, ModelGrid.this); for (MouseListener l : ModelGrid.this.getMouseListeners()) { l.mouseClicked(e); } } } @Override public void mousePressed(MouseEvent e) { if (e.getSource() instanceof Component) { e = SwingUtilities.convertMouseEvent((Component) e.getSource(), e, ModelGrid.this); for (MouseListener l : ModelGrid.this.getMouseListeners()) { l.mousePressed(e); } } } @Override public void mouseReleased(MouseEvent e) { if (e.getSource() instanceof Component) { e = SwingUtilities.convertMouseEvent((Component) e.getSource(), e, ModelGrid.this); for (MouseListener l : ModelGrid.this.getMouseListeners()) { l.mouseReleased(e); } } } @Override public void mouseEntered(MouseEvent e) { if (e.getSource() instanceof Component) { e = SwingUtilities.convertMouseEvent((Component) e.getSource(), e, ModelGrid.this); for (MouseListener l : ModelGrid.this.getMouseListeners()) { l.mouseEntered(e); } } } @Override public void mouseExited(MouseEvent e) { if (e.getSource() instanceof Component) { e = SwingUtilities.convertMouseEvent((Component) e.getSource(), e, ModelGrid.this); for (MouseListener l : ModelGrid.this.getMouseListeners()) { l.mouseExited(e); } } } @Override public void mouseDragged(MouseEvent e) { if (e.getSource() instanceof Component) { e = SwingUtilities.convertMouseEvent((Component) e.getSource(), e, ModelGrid.this); for (MouseMotionListener l : ModelGrid.this.getMouseMotionListeners()) { l.mouseDragged(e); } } } @Override public void mouseMoved(MouseEvent e) { if (e.getSource() instanceof Component) { e = SwingUtilities.convertMouseEvent((Component) e.getSource(), e, ModelGrid.this); for (MouseMotionListener l : ModelGrid.this.getMouseMotionListeners()) { l.mouseMoved(e); } } } protected MouseWheelEvent sendWheelTo(MouseWheelEvent e, Component aComp) { MouseEvent me = SwingUtilities.convertMouseEvent((Component) e.getSource(), e, aComp); e = new MouseWheelEvent(aComp, e.getID(), e.getWhen(), e.getModifiers(), me.getX(), me.getY(), me.getXOnScreen(), me.getYOnScreen(), e.getClickCount(), e.isPopupTrigger(), e.getScrollType(), e.getScrollAmount(), e.getWheelRotation()); for (MouseWheelListener l : aComp.getMouseWheelListeners()) { l.mouseWheelMoved(e); } return e; } @Override public void mouseWheelMoved(MouseWheelEvent e) { if (e.getSource() instanceof Component) { MouseWheelEvent sended = sendWheelTo(e, ModelGrid.this); if (!sended.isConsumed()) { sendWheelTo(e, ModelGrid.this.gridScroll); } } } } protected class TablesKeyboardManager implements KeyListener { @Override public void keyTyped(KeyEvent e) { } @Override public void keyPressed(KeyEvent e) { if (e.getModifiers() == 0) { if (e.getKeyCode() == KeyEvent.VK_DOWN) { if (e.getSource() == tlTable) { if (blTable != null && blTable.getRowCount() > 0 && tlTable.getSelectionModel().getLeadSelectionIndex() == tlTable.getRowCount() - 1) { int colIndex = tlTable.getColumnModel().getSelectionModel().getLeadSelectionIndex(); EventQueue.invokeLater(() -> { tlTable.clearSelection(); blTable.getSelectionModel().setSelectionInterval(0, 0); blTable.getColumnModel().getSelectionModel().setSelectionInterval(colIndex, colIndex); blTable.requestFocus(); }); } } else if (e.getSource() == trTable) { if (brTable != null && brTable.getRowCount() > 0 && trTable.getSelectionModel().getLeadSelectionIndex() == trTable.getRowCount() - 1) { EventQueue.invokeLater(() -> { int colIndex = trTable.getColumnModel().getSelectionModel().getLeadSelectionIndex(); trTable.clearSelection(); brTable.getSelectionModel().setSelectionInterval(0, 0); brTable.getColumnModel().getSelectionModel().setSelectionInterval(colIndex, colIndex); brTable.requestFocus(); }); } } } else if (e.getKeyCode() == KeyEvent.VK_UP) { if (e.getSource() == blTable) { if (tlTable != null && tlTable.getRowCount() > 0 && blTable.getSelectionModel().getLeadSelectionIndex() == 0) { int colIndex = blTable.getColumnModel().getSelectionModel().getLeadSelectionIndex(); EventQueue.invokeLater(() -> { blTable.clearSelection(); tlTable.getSelectionModel().setSelectionInterval(tlTable.getRowCount() - 1, tlTable.getRowCount() - 1); tlTable.getColumnModel().getSelectionModel().setSelectionInterval(colIndex, colIndex); tlTable.requestFocus(); }); } } else if (e.getSource() == brTable) { if (trTable != null && trTable.getRowCount() > 0 && brTable.getSelectionModel().getLeadSelectionIndex() == 0) { int colIndex = brTable.getColumnModel().getSelectionModel().getLeadSelectionIndex(); EventQueue.invokeLater(() -> { brTable.clearSelection(); trTable.getSelectionModel().setSelectionInterval(trTable.getRowCount() - 1, trTable.getRowCount() - 1); trTable.getColumnModel().getSelectionModel().setSelectionInterval(colIndex, colIndex); trTable.requestFocus(); }); } } } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) { if (e.getSource() == blTable) { int blLeadIndex = blTable.getColumnModel().getSelectionModel().getLeadSelectionIndex(); int blColCount = blTable.getColumnModel().getColumnCount(); while (blLeadIndex < blColCount - 1 && GridTable.skipableColumn(blTable.getColumnModel().getColumn(blLeadIndex + 1))) { blLeadIndex++; } if (brTable != null && brTable.getColumnCount() > 0 && blLeadIndex == blColCount - 1) { int lColIndex = 0; if (!(brTable.getColumnModel().getColumn(lColIndex) instanceof RadioServiceColumn)) { while (GridTable.skipableColumn(brTable.getColumnModel().getColumn(lColIndex))) { lColIndex++; } if (lColIndex >= 0 && lColIndex < brTable.getColumnModel().getColumnCount()) { final int colToSelect = lColIndex; EventQueue.invokeLater(() -> { brTable.getColumnModel().getSelectionModel().setSelectionInterval(colToSelect, colToSelect); brTable.requestFocus(); }); } } } } else if (e.getSource() == tlTable) { int tlLeadIndex = tlTable.getColumnModel().getSelectionModel().getLeadSelectionIndex(); int tlColCount = tlTable.getColumnModel().getColumnCount(); while (tlLeadIndex < tlColCount - 1 && GridTable.skipableColumn(tlTable.getColumnModel().getColumn(tlLeadIndex + 1))) { tlLeadIndex++; } if (trTable != null && trTable.getColumnCount() > 0 && (tlLeadIndex == tlColCount - 1)) { int lColIndex = 0; if (!(trTable.getColumnModel().getColumn(lColIndex) instanceof RadioServiceColumn)) { while (GridTable.skipableColumn(trTable.getColumnModel().getColumn(lColIndex))) { lColIndex++; } if (lColIndex >= 0 && lColIndex < trTable.getColumnModel().getColumnCount()) { final int col2Select = lColIndex; EventQueue.invokeLater(() -> { trTable.getColumnModel().getSelectionModel().setSelectionInterval(col2Select, col2Select); trTable.requestFocus(); }); } } } } } else if (e.getKeyCode() == KeyEvent.VK_LEFT) { if (e.getSource() == trTable) { int trLeadIndex = trTable.getColumnModel().getSelectionModel().getLeadSelectionIndex(); while (trLeadIndex > 0 && GridTable.skipableColumn(trTable.getColumnModel().getColumn(trLeadIndex - 1))) { trLeadIndex--; } if (tlTable != null && tlTable.getColumnCount() > 0 && trLeadIndex == 0) { if (tlTable.getColumnModel().getColumnCount() > 0) { int lColIndex = tlTable.getColumnModel().getColumnCount() - 1; if (!(tlTable.getColumnModel().getColumn(lColIndex) instanceof RadioServiceColumn)) { while (GridTable.skipableColumn(tlTable.getColumnModel().getColumn(lColIndex))) { lColIndex--; } if (lColIndex >= 0 && lColIndex < tlTable.getColumnModel().getColumnCount()) { final int col2Select = lColIndex; EventQueue.invokeLater(() -> { tlTable.getColumnModel().getSelectionModel().setSelectionInterval(col2Select, col2Select); tlTable.requestFocus(); }); } } } } } else if (e.getSource() == brTable) { int brLeadIndex = brTable.getColumnModel().getSelectionModel().getLeadSelectionIndex(); while (brLeadIndex > 0 && GridTable.skipableColumn(brTable.getColumnModel().getColumn(brLeadIndex - 1))) { brLeadIndex--; } if (blTable != null && blTable.getColumnCount() > 0 && brLeadIndex == 0) { if (blTable.getColumnModel().getColumnCount() > 0) { int lColIndex = blTable.getColumnModel().getColumnCount() - 1; if (!(blTable.getColumnModel().getColumn(lColIndex) instanceof RadioServiceColumn)) { while (GridTable.skipableColumn(blTable.getColumnModel().getColumn(lColIndex))) { lColIndex--; } if (lColIndex >= 0 && lColIndex < blTable.getColumnModel().getColumnCount()) { final int col2Select = lColIndex; EventQueue.invokeLater(() -> { blTable.getColumnModel().getSelectionModel().setSelectionInterval(col2Select, col2Select); blTable.requestFocus(); }); } } } } } } } } @Override public void keyReleased(KeyEvent e) { } } protected class DeleteAtCursorAction extends AbstractAction { DeleteAtCursorAction() { super(); putValue(Action.NAME, Forms.getLocalizedString(DeleteAtCursorAction.class.getSimpleName())); putValue(Action.SHORT_DESCRIPTION, Forms.getLocalizedString(DeleteAtCursorAction.class.getSimpleName())); putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0)); setEnabled(false); } @Override public boolean isEnabled() { return !rowsSelectionModel.isSelectionEmpty(); } @Override public void actionPerformed(ActionEvent e) { deleteSelectedElements(); } } protected class InsertAtCursorAction extends AbstractAction { InsertAtCursorAction() { super(); putValue(Action.NAME, Forms.getLocalizedString(InsertAtCursorAction.class.getSimpleName())); putValue(Action.SHORT_DESCRIPTION, Forms.getLocalizedString(InsertAtCursorAction.class.getSimpleName())); putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, 0)); setEnabled(true); } @Override public void actionPerformed(ActionEvent e) { insertElementAtCursor(); } } /* protected class DbGridInsertChildAction extends AbstractAction { DbGridInsertChildAction() { super(); putValue(Action.NAME, Form.getLocalizedString(DbGridInsertChildAction.class.getSimpleName())); putValue(Action.SHORT_DESCRIPTION, Form.getLocalizedString(DbGridInsertChildAction.class.getSimpleName())); putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, KeyEvent.ALT_DOWN_MASK)); setEnabled(false); } @Override public boolean isEnabled() { return true; } @Override public void actionPerformed(ActionEvent e) { try { if (insertable && !(rowsModel.getRowsRowset() instanceof ParametersRowset)) { rowsSelectionModel.removeListSelectionListener(generalSelectionChangesReflector); try { if (rowsModel instanceof ArrayTreedModel) { int parentColIndex = ((ArrayTreedModel) rowsModel).getParentFieldIndex(); Object parentColValue = null; if (!rowsModel.getRowsRowset().isEmpty() && !rowsModel.getRowsRowset().isBeforeFirst() && !rowsModel.getRowsRowset().isAfterLast()) { Object[] pkValues = rowsModel.getRowsRowset().getCurrentRow().getPKValues(); if (pkValues != null && pkValues.length == 1) { parentColValue = pkValues[0]; } } rowsModel.getRowsRowset().insert(parentColIndex, parentColValue); } else { rowsModel.getRowsRowset().insert(); } } finally { rowsSelectionModel.addListSelectionListener(generalSelectionChangesReflector); } Row insertedRow = rowsModel.getRowsRowset().getCurrentRow(); assert insertedRow.isInserted(); makeVisible(insertedRow); } } catch (Exception ex) { Logger.getLogger(ModelGrid.class.getName()).log(Level.SEVERE, null, ex); } } } protected class DbGridRowInfoAction extends AbstractAction { DbGridRowInfoAction() { super(); putValue(Action.NAME, Form.getLocalizedString(DbGridRowInfoAction.class.getSimpleName())); putValue(Action.SHORT_DESCRIPTION, Form.getLocalizedString(DbGridRowInfoAction.class.getSimpleName())); putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_I, KeyEvent.CTRL_DOWN_MASK)); setEnabled(false); } @Override public boolean isEnabled() { return !rowsSelectionModel.isSelectionEmpty(); } @Override public void actionPerformed(ActionEvent e) { if (!rowsModel.getRowsRowset().isEmpty() && !rowsModel.getRowsRowset().isBeforeFirst() && !rowsModel.getRowsRowset().isAfterLast()) { Row row = rowsModel.getRowsRowset().getCurrentRow(); if (row != null) { JOptionPane.showInputDialog(ModelGrid.this, Form.getLocalizedString("rowPkValues"), (String) getValue(Action.SHORT_DESCRIPTION), JOptionPane.INFORMATION_MESSAGE, null, null, StringUtils.join(", ", StringUtils.toStringArray(row.getPKValues()))); } } } } protected class DbGridGotoRowAction extends AbstractAction { DbGridGotoRowAction() { super(); putValue(Action.NAME, Form.getLocalizedString(DbGridGotoRowAction.class.getSimpleName())); putValue(Action.SHORT_DESCRIPTION, Form.getLocalizedString(DbGridGotoRowAction.class.getSimpleName())); putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_G, KeyEvent.CTRL_DOWN_MASK)); setEnabled(false); } @Override public boolean isEnabled() { return true; } @Override public void actionPerformed(ActionEvent e) { if (!rowsModel.getRowsRowset().isEmpty() && !rowsModel.getRowsRowset().isBeforeFirst() && !rowsModel.getRowsRowset().isAfterLast()) { Row row = rowsModel.getRowsRowset().getCurrentRow(); if (row != null) { Object oInput = JOptionPane.showInputDialog(ModelGrid.this, Form.getLocalizedString("rowPkValues"), (String) getValue(Action.SHORT_DESCRIPTION), JOptionPane.INFORMATION_MESSAGE, null, null, null); if (oInput != null) { try { makeVisible(oInput); } catch (Exception ex) { Logger.getLogger(ModelGrid.class.getName()).log(Level.SEVERE, null, ex); } } } } } } protected class DbGridCopyCellAction extends AbstractAction { DbGridCopyCellAction() { super(); putValue(Action.NAME, Form.getLocalizedString(DbGridCopyCellAction.class.getSimpleName())); putValue(Action.SHORT_DESCRIPTION, Form.getLocalizedString(DbGridCopyCellAction.class.getSimpleName())); putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.CTRL_DOWN_MASK)); setEnabled(false); } @Override public boolean isEnabled() { return true; } @Override public void actionPerformed(ActionEvent e) { StringBuilder sbCells = new StringBuilder(); String[][] cells = getGridView(true, false); for (int i = 0; i < cells.length; i++) { StringBuilder sbRow = new StringBuilder(); String[] row = cells[i]; for (int j = 0; j < row.length; j++) { String value = row[j]; if (value != null) { if (sbRow.length() > 0) { sbRow.append("\t"); } sbRow.append(value); } } if (sbRow.length() > 0) { if (sbCells.length() > 0) { sbCells.append("\n"); } sbCells.append(sbRow); } } StringSelection ss = new StringSelection(sbCells.toString()); Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss, ss); } } */ protected class ModelGridFindSomethingAction extends AbstractAction { protected JFrame findFrame; ModelGridFindSomethingAction() { super(); putValue(Action.NAME, Forms.getLocalizedString(ModelGridFindSomethingAction.class.getSimpleName())); putValue(Action.SHORT_DESCRIPTION, Forms.getLocalizedString(ModelGridFindSomethingAction.class.getSimpleName())); putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F, KeyEvent.CTRL_DOWN_MASK)); setEnabled(true); } @Override public boolean isEnabled() { return true; } @Override public void actionPerformed(ActionEvent e) { if (isEnabled()) { if (findFrame == null) { findFrame = new JFrame(Forms.getLocalizedString("Find")); findFrame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); findFrame.getContentPane().setLayout(new BorderLayout()); findFrame.getContentPane().add(new GridSearchView(ModelGrid.this), BorderLayout.CENTER); findFrame.setIconImage(IconCache.getIcon("16x16/binocular.png").getImage()); findFrame.setModalExclusionType(Dialog.ModalExclusionType.APPLICATION_EXCLUDE); findFrame.setAlwaysOnTop(true); findFrame.setLocationByPlatform(true); findFrame.pack(); } findFrame.setVisible(true); } } } protected void configureActions() { Action deleteAction = null; Action insertAction = null; Action insertChildAction = null; findSomethingAction = new ModelGridFindSomethingAction(); putAction(findSomethingAction); deleteAction = new DeleteAtCursorAction(); putAction(deleteAction); insertAction = new InsertAtCursorAction(); putAction(insertAction); /* insertChildAction = new DbGridInsertChildAction(); putAction(insertChildAction); Action rowInfoAction = new DbGridRowInfoAction(); putAction(rowInfoAction); Action goToRowAction = new DbGridGotoRowAction(); putAction(goToRowAction); Action copyCellAction = new DbGridCopyCellAction(); putAction(copyCellAction); */ fillInputMap(tlTable.getInputMap(), deleteAction, insertAction, insertChildAction, findSomethingAction/*, rowInfoAction, goToRowAction, copyCellAction*/); fillInputMap(trTable.getInputMap(), deleteAction, insertAction, insertChildAction, findSomethingAction/*, rowInfoAction, goToRowAction, copyCellAction*/); fillInputMap(blTable.getInputMap(), deleteAction, insertAction, insertChildAction, findSomethingAction/*, rowInfoAction, goToRowAction, copyCellAction*/); fillInputMap(brTable.getInputMap(), deleteAction, insertAction, insertChildAction, findSomethingAction/*, rowInfoAction, goToRowAction, copyCellAction*/); } protected void fillInputMap(InputMap aInputMap, Action... actions) { for (Action action : actions) { if (action != null) { /* if (action instanceof DbGridCopyCellAction) { aInputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, KeyEvent.CTRL_DOWN_MASK), action.getClass().getName()); } */ KeyStroke keyStroke = (KeyStroke) action.getValue(Action.ACCELERATOR_KEY); if (keyStroke != null) { aInputMap.put(keyStroke, action.getClass().getName()); } } } } protected void checkModelGridActions() { ActionMap actionMap = getActionMap(); if (actionMap != null) { for (Object lkey : actionMap.keys()) { if (lkey != null) { Action action = actionMap.get(lkey); action.setEnabled(action.isEnabled()); } } } } // Native API @ScriptFunction(jsDoc = NATIVE_COMPONENT_JSDOC) @Undesignable @Override public JComponent getComponent() { return this; } @ScriptFunction(jsDoc = NATIVE_ELEMENT_JSDOC) @Undesignable @Override public Object getElement() { return null; } @Override public JSObject getPublished() { if (published == null) { JSObject publisher = Scripts.getSpace().getPublisher(this.getClass().getName()); if (publisher == null || !publisher.isFunction()) { throw new NoPublisherException(); } published = (JSObject) publisher.call(null, new Object[]{this}); } return published; } @Override public void setPublished(JSObject aValue) { if (published != null) { throw new AlreadyPublishedException(); } published = aValue; } protected JSObject onItemSelected; @ScriptFunction(jsDoc = "" + "/**\n" + " * Event that is fired when selection lead changes in this ModelGrid.\n" + " */") @EventMethod(eventClass = com.eas.client.forms.events.ItemEvent.class) @Undesignable public JSObject getOnItemSelected() { return onItemSelected; } @ScriptFunction public void setOnItemSelected(JSObject aValue) { if (onItemSelected != aValue) { onItemSelected = aValue; } } @ScriptFunction(jsDoc = "" + "/**\n" + " * Event that is fired when user collapses ModelGrid's row.\n" + " */") @EventMethod(eventClass = com.eas.client.forms.events.ItemEvent.class) @Undesignable public JSObject getOnCollapse() { return onCollapse; } @ScriptFunction public void setOnCollapse(JSObject aValue) { if (onCollapse != aValue) { onCollapse = aValue; } } @ScriptFunction(jsDoc = "" + "/**\n" + " * Event that is fired when user expands ModelGrid's row.\n" + " */") @EventMethod(eventClass = com.eas.client.forms.events.ItemEvent.class) @Undesignable public JSObject getOnExpand() { return onExpand; } @ScriptFunction public void setOnExpand(JSObject aValue) { if (onExpand != aValue) { onExpand = aValue; } } protected ControlEventsIProxy eventsProxy = new ControlEventsIProxy(this); @ScriptFunction(jsDoc = ON_MOUSE_CLICKED_JSDOC) @EventMethod(eventClass = com.eas.client.forms.events.MouseEvent.class) @Undesignable @Override public JSObject getOnMouseClicked() { return eventsProxy.getHandlers().get(ControlEventsIProxy.mouseClicked); } @ScriptFunction @Override public void setOnMouseClicked(JSObject aValue) { eventsProxy.getHandlers().put(ControlEventsIProxy.mouseClicked, aValue); } @ScriptFunction(jsDoc = ON_MOUSE_DRAGGED_JSDOC) @EventMethod(eventClass = com.eas.client.forms.events.MouseEvent.class) @Undesignable @Override public JSObject getOnMouseDragged() { return eventsProxy.getHandlers().get(ControlEventsIProxy.mouseDragged); } @ScriptFunction @Override public void setOnMouseDragged(JSObject aValue) { eventsProxy.getHandlers().put(ControlEventsIProxy.mouseDragged, aValue); } @ScriptFunction(jsDoc = ON_MOUSE_ENTERED_JSDOC) @EventMethod(eventClass = com.eas.client.forms.events.MouseEvent.class) @Undesignable @Override public JSObject getOnMouseEntered() { return eventsProxy.getHandlers().get(ControlEventsIProxy.mouseEntered); } @ScriptFunction @Override public void setOnMouseEntered(JSObject aValue) { eventsProxy.getHandlers().put(ControlEventsIProxy.mouseEntered, aValue); } @ScriptFunction(jsDoc = ON_MOUSE_EXITED_JSDOC) @EventMethod(eventClass = com.eas.client.forms.events.MouseEvent.class) @Undesignable @Override public JSObject getOnMouseExited() { return eventsProxy.getHandlers().get(ControlEventsIProxy.mouseExited); } @ScriptFunction @Override public void setOnMouseExited(JSObject aValue) { eventsProxy.getHandlers().put(ControlEventsIProxy.mouseExited, aValue); } @ScriptFunction(jsDoc = ON_MOUSE_MOVED_JSDOC) @EventMethod(eventClass = com.eas.client.forms.events.MouseEvent.class) @Undesignable @Override public JSObject getOnMouseMoved() { return eventsProxy.getHandlers().get(ControlEventsIProxy.mouseMoved); } @ScriptFunction @Override public void setOnMouseMoved(JSObject aValue) { eventsProxy.getHandlers().put(ControlEventsIProxy.mouseMoved, aValue); } @ScriptFunction(jsDoc = ON_MOUSE_PRESSED_JSDOC) @EventMethod(eventClass = com.eas.client.forms.events.MouseEvent.class) @Undesignable @Override public JSObject getOnMousePressed() { return eventsProxy.getHandlers().get(ControlEventsIProxy.mousePressed); } @ScriptFunction @Override public void setOnMousePressed(JSObject aValue) { eventsProxy.getHandlers().put(ControlEventsIProxy.mousePressed, aValue); } @ScriptFunction(jsDoc = ON_MOUSE_RELEASED_JSDOC) @EventMethod(eventClass = com.eas.client.forms.events.MouseEvent.class) @Undesignable @Override public JSObject getOnMouseReleased() { return eventsProxy.getHandlers().get(ControlEventsIProxy.mouseReleased); } @ScriptFunction @Override public void setOnMouseReleased(JSObject aValue) { eventsProxy.getHandlers().put(ControlEventsIProxy.mouseReleased, aValue); } @ScriptFunction(jsDoc = ON_MOUSE_WHEEL_MOVED_JSDOC) @EventMethod(eventClass = com.eas.client.forms.events.MouseEvent.class) @Undesignable @Override public JSObject getOnMouseWheelMoved() { return eventsProxy.getOnMouseWheelMoved(); } @ScriptFunction @Override public void setOnMouseWheelMoved(JSObject aValue) { eventsProxy.setOnMouseWheelMoved(aValue); } @ScriptFunction(jsDoc = ON_ACTION_PERFORMED_JSDOC) @EventMethod(eventClass = com.eas.client.forms.events.ActionEvent.class) @Undesignable @Override public JSObject getOnActionPerformed() { return eventsProxy.getHandlers().get(ControlEventsIProxy.actionPerformed); } @ScriptFunction @Override public void setOnActionPerformed(JSObject aValue) { eventsProxy.getHandlers().put(ControlEventsIProxy.actionPerformed, aValue); } @ScriptFunction(jsDoc = ON_COMPONENT_HIDDEN_JSDOC) @EventMethod(eventClass = com.eas.client.forms.events.ComponentEvent.class) @Undesignable @Override public JSObject getOnComponentHidden() { return eventsProxy.getHandlers().get(ControlEventsIProxy.componentHidden); } @ScriptFunction @Override public void setOnComponentHidden(JSObject aValue) { eventsProxy.getHandlers().put(ControlEventsIProxy.componentHidden, aValue); } @ScriptFunction(jsDoc = ON_COMPONENT_MOVED_JSDOC) @EventMethod(eventClass = com.eas.client.forms.events.ComponentEvent.class) @Undesignable @Override public JSObject getOnComponentMoved() { return eventsProxy.getHandlers().get(ControlEventsIProxy.componentMoved); } @ScriptFunction @Override public void setOnComponentMoved(JSObject aValue) { eventsProxy.getHandlers().put(ControlEventsIProxy.componentMoved, aValue); } @ScriptFunction(jsDoc = ON_COMPONENT_RESIZED_JSDOC) @EventMethod(eventClass = com.eas.client.forms.events.ComponentEvent.class) @Undesignable @Override public JSObject getOnComponentResized() { return eventsProxy.getHandlers().get(ControlEventsIProxy.componentResized); } @ScriptFunction @Override public void setOnComponentResized(JSObject aValue) { eventsProxy.getHandlers().put(ControlEventsIProxy.componentResized, aValue); } @ScriptFunction(jsDoc = ON_COMPONENT_SHOWN_JSDOC) @EventMethod(eventClass = com.eas.client.forms.events.ComponentEvent.class) @Undesignable @Override public JSObject getOnComponentShown() { return eventsProxy.getHandlers().get(ControlEventsIProxy.componentShown); } @ScriptFunction @Override public void setOnComponentShown(JSObject aValue) { eventsProxy.getHandlers().put(ControlEventsIProxy.componentShown, aValue); } @ScriptFunction(jsDoc = ON_FOCUS_GAINED_JSDOC) @EventMethod(eventClass = FocusEvent.class) @Undesignable @Override public JSObject getOnFocusGained() { return eventsProxy.getHandlers().get(ControlEventsIProxy.focusGained); } @ScriptFunction @Override public void setOnFocusGained(JSObject aValue) { eventsProxy.getHandlers().put(ControlEventsIProxy.focusGained, aValue); } @ScriptFunction(jsDoc = ON_FOCUS_LOST_JSDOC) @EventMethod(eventClass = FocusEvent.class) @Undesignable @Override public JSObject getOnFocusLost() { return eventsProxy != null ? eventsProxy.getHandlers().get(ControlEventsIProxy.focusLost) : null; } @ScriptFunction @Override public void setOnFocusLost(JSObject aValue) { eventsProxy.getHandlers().put(ControlEventsIProxy.focusLost, aValue); } @ScriptFunction(jsDoc = ON_KEY_PRESSED_JSDOC) @EventMethod(eventClass = KeyEvent.class) @Undesignable @Override public JSObject getOnKeyPressed() { return eventsProxy.getHandlers().get(ControlEventsIProxy.keyPressed); } @ScriptFunction @Override public void setOnKeyPressed(JSObject aValue) { eventsProxy.getHandlers().put(ControlEventsIProxy.keyPressed, aValue); } @ScriptFunction(jsDoc = ON_KEY_RELEASED_JSDOC) @EventMethod(eventClass = KeyEvent.class) @Undesignable @Override public JSObject getOnKeyReleased() { return eventsProxy.getHandlers().get(ControlEventsIProxy.keyReleased); } @ScriptFunction @Override public void setOnKeyReleased(JSObject aValue) { eventsProxy.getHandlers().put(ControlEventsIProxy.keyReleased, aValue); } @ScriptFunction(jsDoc = ON_KEY_TYPED_JSDOC) @EventMethod(eventClass = KeyEvent.class) @Undesignable @Override public JSObject getOnKeyTyped() { return eventsProxy.getHandlers().get(ControlEventsIProxy.keyTyped); } @ScriptFunction @Override public void setOnKeyTyped(JSObject aValue) { eventsProxy.getHandlers().put(ControlEventsIProxy.keyTyped, aValue); } // published parent @ScriptFunction(name = "parent", jsDoc = PARENT_JSDOC) @Override public Widget getParentWidget() { return Forms.lookupPublishedParent(this); } private static final String UNSORT = "" + "/**\n" + " * Clears sort on all columns, works only in HTML5" + " */"; @ScriptFunction(jsDoc = UNSORT) public void unsort() { } }