/* This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV This program is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program; if not, see http://www.gnu.org/licenses or write to the Free Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 */ package com.servoy.j2db.property; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.Insets; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.DefaultCellEditor; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellRenderer; import com.servoy.j2db.FormController; import com.servoy.j2db.FormManager; import com.servoy.j2db.IApplication; import com.servoy.j2db.dataprocessing.FoundSetManager; import com.servoy.j2db.dataprocessing.SortColumn; import com.servoy.j2db.gui.FixedJTable; import com.servoy.j2db.persistence.Column; import com.servoy.j2db.persistence.ColumnWrapper; import com.servoy.j2db.persistence.Form; import com.servoy.j2db.persistence.IColumn; import com.servoy.j2db.persistence.IDataProvider; import com.servoy.j2db.persistence.ITable; import com.servoy.j2db.persistence.RepositoryException; import com.servoy.j2db.smart.J2DBClient; import com.servoy.j2db.util.Debug; /** * Editor to fill the initial sort property * * @author jblok */ public class SortEditor implements IOptimizedPropertyEditor { private SEditor editor; private String selected_value; public void addActionListener(ActionListener l) { // dpe.addActionListener(l); } public void removeActionListener(ActionListener l) { // dpe.removeActionListener(l); } // private void fireActionEvent() // { // ActionEvent ae = null; // Iterator iter = elist.iterator(); // while (iter.hasNext()) // { // ActionListener listener = (ActionListener) iter.next(); // // Lazily create the event: // if (ae == null) ae = new ActionEvent(this,ActionEvent.ACTION_PERFORMED,"OK"); // listener.actionPerformed(ae); // } // } public void addPropertyChangeListener(PropertyChangeListener listener) { } public void removePropertyChangeListener(PropertyChangeListener listener) { } public String getJavaInitializationString() { return null; } public String[] getTags() { return null; } public boolean isPaintable() { return false; } public void paintValue(Graphics gfx, Rectangle box) { } public void setAsText(String text) { } public String getAsText() { return selected_value; } public Object getValue() { return selected_value; } public void setValue(Object value) { if (value != null) { selected_value = value.toString(); } else { selected_value = null; } } public void prepareForVisible(IApplication app, boolean b) { if (b) { ((SEditor)getCustomEditor()).setValue(app, selected_value); } else { selected_value = ((SEditor)getCustomEditor()).getSelectedValue(); } } public Component getCustomEditor() { if (editor == null) { editor = new SEditor(); } return editor; } public boolean supportsCustomEditor() { return true; } public void init(IApplication app) { //ignore } public void init(IApplication app, ITable t, List<SortColumn> sortColumns) { ((SEditor)getCustomEditor()).init(app, t, sortColumns); } public List<SortColumn> getData() { return ((SEditor)getCustomEditor()).getData(); } } class SEditor extends JPanel implements ActionListener, ListSelectionListener { private IApplication application; private final JTable table; private final DataProviderEditor dpe; private SortModel model; public SEditor() { JPanel movePane = new JPanel(); movePane.setLayout(new BoxLayout(movePane, BoxLayout.Y_AXIS)); movePane.setMaximumSize(new Dimension(100, 200)); JButton downButton = new JButton("move down"); //$NON-NLS-1$ Dimension minimumSize = downButton.getPreferredSize();//new Dimension(100,20); final JButton rightButton = new JButton(" >> "); //$NON-NLS-1$ rightButton.addActionListener(this); rightButton.setActionCommand("right"); //$NON-NLS-1$ rightButton.setPreferredSize(minimumSize); rightButton.setMinimumSize(minimumSize); rightButton.setMaximumSize(minimumSize); // rightButton.setAlignmentX(0); // rightButton.setAlignmentY(0); movePane.add(rightButton); movePane.add(Box.createRigidArea(new Dimension(0, J2DBClient.BUTTON_SPACING))); JButton leftButton = new JButton(" << "); //$NON-NLS-1$ leftButton.addActionListener(this); leftButton.setActionCommand("left"); //$NON-NLS-1$ leftButton.setPreferredSize(minimumSize); leftButton.setMinimumSize(minimumSize); leftButton.setMaximumSize(minimumSize); // leftButton.setAlignmentX(0); // leftButton.setAlignmentY(0); movePane.add(leftButton); movePane.add(Box.createRigidArea(new Dimension(0, J2DBClient.BUTTON_SPACING))); JButton upButton = new JButton("move up"); //$NON-NLS-1$ upButton.addActionListener(this); upButton.setActionCommand("up"); //$NON-NLS-1$ upButton.setPreferredSize(minimumSize); upButton.setMinimumSize(minimumSize); upButton.setMaximumSize(minimumSize); movePane.add(upButton); movePane.add(Box.createRigidArea(new Dimension(0, J2DBClient.BUTTON_SPACING))); downButton.addActionListener(this); downButton.setActionCommand("down"); //$NON-NLS-1$ downButton.setPreferredSize(minimumSize); downButton.setMinimumSize(minimumSize); downButton.setMaximumSize(minimumSize); movePane.add(downButton); movePane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10)); dpe = new DataProviderEditor(false); dpe.addActionListener(this); table = new FixedJTable(application); table.setRowHeight(20); JScrollPane tableScroll = new JScrollPane(table); tableScroll.setPreferredSize(new Dimension(320, 200)); // setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); add(dpe);//listScroll);//, BorderLayout.WEST); add(movePane);//,BorderLayout.CENTER); add(tableScroll);//, BorderLayout.EAST); } public String getSelectedValue() { return FoundSetManager.getSortColumnsAsString(getData()); } public List<SortColumn> getData() { if (model != null) { if (table.isEditing()) { if (table.getCellEditor() != null) table.getCellEditor().stopCellEditing(); } return model.getData(); } else { return new ArrayList<SortColumn>(); } } void setValue(IApplication app, String notused) { application = app; try { FormManager fm = (FormManager)application.getFormManager(); FormController fc = fm.getCurrentMainShowingFormController(); if (fc != null) { Form form = fc.getForm(); ITable t = application.getFoundSetManager().getTable(form.getDataSource()); if (t != null) { List<SortColumn> list = application.getFoundSetManager().getSortColumns(t, form.getInitialSort()); init(app, t, list); } } } catch (RepositoryException e) { Debug.error(e); } } void init(IApplication app, ITable t, List<SortColumn> sortColumns) { application = app; try { dpe.init(app); dpe.setDefinedTable(t); dpe.setAllowMultipleSelections(true); dpe.setShowRelatedOnly(false); dpe.setShowColumnsOnly(true); dpe.setHideMediaColumns(true); dpe.setShowSortableOnly(true); dpe.setRelatedEnabled(true); dpe.dontShowNoneOption(); dpe.setReturnValueAsString(false); dpe.showDataEx(null); if (sortColumns == null) { model = new SortModel(new ArrayList<SortColumn>()); } else { model = new SortModel(sortColumns); } table.setModel(model); table.getColumnModel().getColumn(0).setMinWidth(110); table.getColumnModel().getColumn(0).setWidth(120); // table.getColumnModel().getColumn(1).setWidth(60); // table.getColumnModel().getColumn(2).setWidth(60); table.getColumnModel().getColumn(1).setCellRenderer(new RadioRenderer()); table.getColumnModel().getColumn(1).setCellEditor(new RadioRenderer()); table.setAutoCreateColumnsFromModel(false); table.getSelectionModel().addListSelectionListener(this); } catch (Exception ex) { Debug.error(ex); } } public void actionPerformed(ActionEvent event) { if (table.isEditing()) { TableCellEditor tce = table.getCellEditor(); tce.stopCellEditing(); } String command = event.getActionCommand(); if (command.equals("left")) left(); //$NON-NLS-1$ else if (command.equals("right") || command.equals("OK")) right(); //$NON-NLS-1$ //$NON-NLS-2$ else if (command.equals("up")) up(); //$NON-NLS-1$ else if (command.equals("down")) down(); //$NON-NLS-1$ } void flagChanged() { // stringVal = null; } private void up() { flagChanged(); int[] rows = table.getSelectedRows(); if (rows.length > 0) { Arrays.sort(rows); if (rows[0] > 0) { for (int r : rows) { model.up(r); } table.clearSelection(); for (int r : rows) { table.addRowSelectionInterval(r - 1, r - 1); } } } } private void down() { flagChanged(); int[] rows = table.getSelectedRows(); if (rows.length > 0) { Arrays.sort(rows); if (rows[rows.length - 1] < model.getRowCount() - 1) { for (int i = rows.length - 1; i >= 0; i--) { int r = rows[i]; model.down(r); } table.clearSelection(); for (int r : rows) { table.addRowSelectionInterval(r + 1, r + 1); } } } } private void left() { flagChanged(); model.deleteRows(table.getSelectedRows()); table.clearSelection(); } private void right() { flagChanged(); Object o = dpe.getValue(); if (o != null) { int currentSize = model.getRowCount(); if (o instanceof Column) { if (model.addRow(new SortColumn((Column)o))) table.setRowSelectionInterval(currentSize, currentSize); } else if (o instanceof ColumnWrapper) { if (model.addRow(new SortColumn((ColumnWrapper)o))) table.setRowSelectionInterval(currentSize, currentSize); } else { boolean clear = false; IDataProvider[] array = (IDataProvider[])o; for (IDataProvider element : array) { boolean added = false; if (element instanceof ColumnWrapper) { added = model.addRow(new SortColumn((ColumnWrapper)element)); } else if (element instanceof Column) { added = model.addRow(new SortColumn((Column)element)); } if (added) { if (!clear) { table.clearSelection(); clear = true; } table.addRowSelectionInterval(currentSize, currentSize); currentSize++; } } } } } /** * @see javax.swing.event.ListSelectionListener#valueChanged(javax.swing.event.ListSelectionEvent) */ public void valueChanged(ListSelectionEvent e) { flagChanged();//if clicked in table assume editted } } class RadioRenderer extends DefaultCellEditor implements TableCellRenderer, ActionListener { private final JRadioButton r1; private final JRadioButton r2; // private JPanel comp; public RadioRenderer() { super(new JCheckBox()); editorComponent = new JPanel(new FlowLayout(FlowLayout.CENTER, 4, 0)); r1 = new JRadioButton("asc"); //$NON-NLS-1$ r1.setMargin(new Insets(0, 0, 0, 0)); r1.setOpaque(false); r1.setFocusable(false); r1.addActionListener(this); r2 = new JRadioButton("desc"); //$NON-NLS-1$ r2.setMargin(new Insets(0, 0, 0, 0)); r2.setOpaque(false); r2.setFocusable(false); r2.addActionListener(this); ButtonGroup group = new ButtonGroup(); group.add(r1); group.add(r2); editorComponent.add(r1); editorComponent.add(r2); } @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { return getTableCellRendererComponent(table, value, isSelected, true, row, column); } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Integer i = (Integer)value; if (i.intValue() == SortColumn.DESCENDING) { r2.setSelected(true); } else { r1.setSelected(true); } // use color that won't get overridden again by laf Color foreground = table.getForeground(); Color background = table.getBackground(); if (!hasFocus && isSelected) { foreground = table.getSelectionForeground(); background = table.getSelectionBackground(); } editorComponent.setBackground(background); r1.setForeground(foreground); r2.setForeground(foreground); return editorComponent; } @Override public Object getCellEditorValue() { int val = SortColumn.ASCENDING; if (r2.isSelected()) { val = SortColumn.DESCENDING; } return new Integer(val); } public void actionPerformed(ActionEvent a) { fireEditingStopped(); } } class SortModel extends AbstractTableModel { private final List<SortColumn> rows; SortModel(List<SortColumn> rows) throws Exception { this.rows = rows; } public List<SortColumn> getData() { return rows; } public boolean addRow(SortColumn c) { if (!rows.contains(c)) { rows.add(c); fireTableRowsInserted(rows.size() - 1, rows.size() - 1); return true; } return false; } public void deleteRows(int[] indexes) { Arrays.sort(indexes); for (int i = indexes.length - 1; i >= 0; i--) { rows.remove(indexes[i]); fireTableRowsDeleted(rows.size() - 1, rows.size() - 1); } } public void up(int index) { if (index > 0) { SortColumn obj = rows.get(index - 1); rows.remove(index - 1); rows.add(index, obj); } fireTableDataChanged(); } public void down(int index) { if (index >= 0 && index < rows.size() - 1) { SortColumn obj = rows.get(index); rows.remove(index); rows.add(index + 1, obj); } fireTableDataChanged(); } public Object getRow(int row) { return rows.get(row); } final String[] columnNames = { "Name", "Sorting" }; //$NON-NLS-1$ //$NON-NLS-2$ public int getColumnCount() { return columnNames.length; } public int getRowCount() { int rowCount = rows.size(); return rowCount; } @Override public String getColumnName(int col) { return columnNames[col]; } public Object getValueAt(int row, int col) { SortColumn v = rows.get(row); switch (col) { case -1 : return v; // case 0: // return new Boolean(v.getInUse()); case 0 : String title = null; IColumn c = v.getColumn(); if (c instanceof Column) { title = ((Column)c).getTitle(); } return (title == null ? v.getName() : title); case 1 : return new Integer(v.getSortOrder()); default : return null; } } /* * JTable uses this method to determine the default renderer/ editor for each cell. If we didn't implement this method, then the last aggregateVariable * would contain text ("true"/"false"), rather than a check box. */ @Override public Class< ? > getColumnClass(int c) { switch (c) { // case 0: // return Boolean.class; case 0 : return String.class; case 1 : return Integer.class; default : return String.class; } } /* * Don't need to implement this method unless your table's editable. */ @Override public boolean isCellEditable(int row, int col) { if (col == 0) { return false; } else { return true; } } /* * Don't need to implement this method unless your table's data can change. */ @Override public void setValueAt(Object value, int row, int col) { SortColumn sc = rows.get(row); switch (col) { // case 0: // sc.setInUse(((Boolean)value).booleanValue()); // break; case 0 : //ignore break; case 1 : sc.setSortOrder(((Integer)value).intValue()); break; default : } } }