| /******************************************************************************* |
| * Copyright (c) 2010 BSI Business Systems Integration AG. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * BSI Business Systems Integration AG - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.scout.rt.ui.swing.basic.table; |
| |
| import java.awt.Color; |
| import java.awt.Component; |
| import java.awt.Cursor; |
| import java.awt.Font; |
| import java.awt.FontMetrics; |
| import java.awt.Graphics; |
| import java.awt.Insets; |
| import java.awt.Point; |
| import java.awt.Rectangle; |
| import java.awt.Toolkit; |
| import java.awt.datatransfer.Clipboard; |
| import java.awt.datatransfer.DataFlavor; |
| import java.awt.datatransfer.Transferable; |
| import java.awt.dnd.DnDConstants; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ComponentAdapter; |
| import java.awt.event.ComponentEvent; |
| import java.awt.event.MouseAdapter; |
| import java.awt.event.MouseEvent; |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.EventObject; |
| import java.util.List; |
| |
| import javax.swing.AbstractAction; |
| import javax.swing.ActionMap; |
| import javax.swing.DefaultListSelectionModel; |
| import javax.swing.Icon; |
| import javax.swing.ImageIcon; |
| import javax.swing.InputMap; |
| import javax.swing.JComponent; |
| import javax.swing.JLabel; |
| import javax.swing.JScrollPane; |
| import javax.swing.JTable; |
| import javax.swing.KeyStroke; |
| import javax.swing.ListSelectionModel; |
| import javax.swing.SwingConstants; |
| import javax.swing.SwingUtilities; |
| import javax.swing.UIManager; |
| import javax.swing.event.ListSelectionEvent; |
| import javax.swing.event.ListSelectionListener; |
| import javax.swing.event.MouseInputAdapter; |
| import javax.swing.plaf.basic.BasicHTML; |
| import javax.swing.table.JTableHeader; |
| import javax.swing.table.TableCellEditor; |
| import javax.swing.table.TableCellRenderer; |
| import javax.swing.table.TableColumn; |
| import javax.swing.table.TableColumnModel; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.scout.commons.CollectionUtility; |
| import org.eclipse.scout.commons.CompareUtility; |
| import org.eclipse.scout.commons.StringUtility; |
| import org.eclipse.scout.commons.dnd.TransferObject; |
| import org.eclipse.scout.commons.holders.Holder; |
| import org.eclipse.scout.commons.logger.IScoutLogger; |
| import org.eclipse.scout.commons.logger.ScoutLogManager; |
| import org.eclipse.scout.rt.client.ClientSyncJob; |
| import org.eclipse.scout.rt.client.ui.IEventHistory; |
| import org.eclipse.scout.rt.client.ui.action.IAction; |
| import org.eclipse.scout.rt.client.ui.action.keystroke.IKeyStroke; |
| import org.eclipse.scout.rt.client.ui.action.menu.TableMenuType; |
| import org.eclipse.scout.rt.client.ui.action.menu.root.IContextMenu; |
| import org.eclipse.scout.rt.client.ui.basic.cell.ICell; |
| import org.eclipse.scout.rt.client.ui.basic.table.ISortOrderColumn; |
| import org.eclipse.scout.rt.client.ui.basic.table.ITable; |
| import org.eclipse.scout.rt.client.ui.basic.table.ITableRow; |
| import org.eclipse.scout.rt.client.ui.basic.table.TableEvent; |
| import org.eclipse.scout.rt.client.ui.basic.table.TableListener; |
| import org.eclipse.scout.rt.client.ui.basic.table.columns.IBooleanColumn; |
| import org.eclipse.scout.rt.client.ui.basic.table.columns.IColumn; |
| import org.eclipse.scout.rt.client.ui.basic.table.columns.ISmartColumn; |
| import org.eclipse.scout.rt.client.ui.basic.table.columns.IStringColumn; |
| import org.eclipse.scout.rt.shared.AbstractIcons; |
| import org.eclipse.scout.rt.shared.security.CopyToClipboardPermission; |
| import org.eclipse.scout.rt.shared.services.common.security.ACCESS; |
| import org.eclipse.scout.rt.ui.swing.SwingPopupWorker; |
| import org.eclipse.scout.rt.ui.swing.SwingUtility; |
| import org.eclipse.scout.rt.ui.swing.action.SwingScoutAction; |
| import org.eclipse.scout.rt.ui.swing.basic.ColorUtility; |
| import org.eclipse.scout.rt.ui.swing.basic.SwingLinkDetectorMouseMotionListener; |
| import org.eclipse.scout.rt.ui.swing.basic.SwingScoutComposite; |
| import org.eclipse.scout.rt.ui.swing.basic.table.celleditor.SwingScoutTableCellEditor; |
| import org.eclipse.scout.rt.ui.swing.dnd.TransferHandlerEx; |
| import org.eclipse.scout.rt.ui.swing.ext.HtmlViewCache; |
| import org.eclipse.scout.rt.ui.swing.ext.JScrollPaneEx; |
| import org.eclipse.scout.rt.ui.swing.ext.JTableEx; |
| import org.eclipse.scout.rt.ui.swing.ext.JTableHeaderEx; |
| import org.eclipse.scout.rt.ui.swing.ext.MouseClickedBugFix; |
| import org.eclipse.scout.rt.ui.swing.icons.CheckboxIcon; |
| import org.eclipse.scout.rt.ui.swing.icons.CompositeIcon; |
| |
| /** |
| * The prefix SwingScout... denotes a model COMPOSITION between a swing and a |
| * scout component The prefix Swing... denotes a SUBCLASS of a swing component |
| * Base table class without selection handling. |
| */ |
| public class SwingScoutTable extends SwingScoutComposite<ITable> implements ISwingScoutTable { |
| private static final IScoutLogger LOG = ScoutLogManager.getLogger(SwingScoutTable.class); |
| |
| /** |
| * The distance from the top of a label to the approximate top position of an uppercase character. As this distance |
| * cannot be obtained by {@link FontMetrics}, this approximation is used. |
| */ |
| public static final int FONT_PADDING_TOP = 4; |
| |
| private P_ScoutTableListener m_scoutTableListener; |
| private JTableHeader m_swingTableHeader; |
| private JScrollPane m_swingScrollPane; |
| private HtmlViewCache m_htmlViewCache; |
| private ClientSyncJob m_storeColumnWidthsJob; |
| private Job m_swingAutoOptimizeColumnWidthsJob; |
| |
| // cache |
| private List<IKeyStroke> m_installedScoutKs; |
| |
| // keyboard navigation |
| private TableKeyboardNavigationSupport m_keyboardNavigationSupport; |
| |
| private SwingScoutTableCellEditor m_editor; |
| |
| public SwingScoutTable() { |
| super(); |
| } |
| |
| @Override |
| protected void initializeSwing() { |
| m_htmlViewCache = new HtmlViewCache(); |
| JTableEx table = new P_SwingTable(); |
| table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE); |
| m_swingScrollPane = new JScrollPaneEx(table); |
| m_swingScrollPane.setBackground(table.getBackground()); |
| setSwingField(table); |
| m_swingTableHeader = table.getTableHeader(); |
| if (m_swingTableHeader == null) { |
| m_swingTableHeader = new JTableHeaderEx(); |
| table.setTableHeader(m_swingTableHeader); |
| } |
| // swing properties |
| table.setAutoCreateColumnsFromModel(false); |
| m_swingTableHeader.setReorderingAllowed(true); |
| // models |
| table.setAutoCreateColumnsFromModel(false); |
| m_editor = new SwingScoutTableCellEditor(this); |
| m_editor.initialize(); |
| //disable auto-start editing |
| table.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE); |
| table.setSelectionModel(new DefaultListSelectionModel()); |
| // listeners |
| table.getSelectionModel().addListSelectionListener(new P_SwingSelectionListener()); |
| // re-attach observer when selection model changes |
| table.addPropertyChangeListener("selectionModel", new PropertyChangeListener() { |
| @Override |
| public void propertyChange(PropertyChangeEvent evt) { |
| getSwingTable().getSelectionModel().addListSelectionListener(new P_SwingSelectionListener()); |
| } |
| }); |
| m_swingTableHeader.addMouseListener(new P_SwingHeadMouseListener()); |
| table.addMouseListener(new P_SwingRowMouseListener()); |
| //add hyperlink click listener |
| table.addMouseListener(new MouseAdapter() { |
| @Override |
| public void mouseReleased(MouseEvent e) { |
| if (!e.isPopupTrigger()) { |
| TableHtmlLinkDetector detector = new TableHtmlLinkDetector(); |
| if (detector.detect((JTable) e.getComponent(), e.getPoint())) { |
| handleSwingHyperlinkAction(detector.getRowIndex(), detector.getColumnIndex(), detector.getHyperlink()); |
| } |
| } |
| } |
| }); |
| table.addMouseMotionListener(new SwingLinkDetectorMouseMotionListener<JTable>(new TableHtmlLinkDetector())); |
| m_swingScrollPane.getViewport().addMouseListener(new P_SwingEmptySpaceMouseListener()); |
| //ticket 87030, bug 365161 |
| m_swingScrollPane.addComponentListener(new ComponentAdapter() { |
| private int m_oldHeight = -1; |
| |
| @Override |
| public void componentResized(ComponentEvent e) { |
| int newHeight = e.getComponent().getHeight(); |
| if (m_oldHeight >= 0 && m_oldHeight == newHeight) { |
| return; |
| } |
| m_oldHeight = newHeight; |
| ITable t = getScoutObject(); |
| if (t != null && t.isScrollToSelection()) { |
| if (e.getComponent().isShowing()) { |
| SwingUtilities.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| scrollToSelection(); |
| } |
| }); |
| } |
| } |
| } |
| }); |
| //add context menu key stroke |
| table.getInputMap(JComponent.WHEN_FOCUSED).put(SwingUtility.createKeystroke("CONTEXT_MENU"), "contextMenu"); |
| table.getActionMap().put("contextMenu", new AbstractAction() { |
| private static final long serialVersionUID = 1L; |
| |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| if (getUpdateSwingFromScoutLock().isAcquired()) { |
| return; |
| } |
| // |
| if (getScoutObject() != null) { |
| int[] rowIndexes = getSwingTable().getSelectedRows(); |
| Point p = new Point(0, 0); |
| if (rowIndexes != null && rowIndexes.length > 0) { |
| p = getSwingTable().getCellRect(rowIndexes[0], 0, false).getLocation(); |
| } |
| p.translate(2, 2); |
| // notify Scout |
| final Component compF = getSwingTable(); |
| final Point pFinal = p; |
| Runnable t = new Runnable() { |
| @Override |
| public void run() { |
| // call swing menu |
| IContextMenu contextMenu = getScoutObject().getContextMenu(); |
| SwingPopupWorker swingPopupWorker = new SwingPopupWorker(getSwingEnvironment(), compF, pFinal, contextMenu, contextMenu.getCurrentMenuTypes()); |
| swingPopupWorker.setLightWeightPopup(true); |
| swingPopupWorker.enqueue(); |
| } |
| }; |
| getSwingEnvironment().invokeScoutLater(t, 5678); |
| // end notify |
| } |
| } |
| }); |
| } |
| |
| @Override |
| public JTableEx getSwingTable() { |
| return (JTableEx) getSwingField(); |
| } |
| |
| protected SwingTableModel getSwingTableModel() { |
| return (SwingTableModel) getSwingTable().getModel(); |
| } |
| |
| protected SwingTableColumnModel getSwingTableColumnModel() { |
| return (SwingTableColumnModel) getSwingTable().getColumnModel(); |
| } |
| |
| protected ListSelectionModel getSwingTableSelectionModel() { |
| return getSwingTable().getSelectionModel(); |
| } |
| |
| @Override |
| public JScrollPane getSwingScrollPane() { |
| return m_swingScrollPane; |
| } |
| |
| protected void setContextColumnFromSwing(int viewIndex) { |
| if (getUpdateSwingFromScoutLock().isAcquired()) { |
| return; |
| } |
| // |
| registerColumnHeaderPopupOwner(viewIndex); |
| // |
| if (getScoutObject() != null) { |
| final IColumn scoutCol = (getSwingTableColumnModel().swingToScoutColumn(viewIndex)); |
| // notify Scout |
| Runnable t = new Runnable() { |
| @Override |
| public void run() { |
| getScoutObject().getUIFacade().setContextColumnFromUI(scoutCol); |
| } |
| }; |
| getSwingEnvironment().invokeScoutLater(t, 0); |
| // end notify |
| } |
| } |
| |
| @Override |
| protected void attachScout() { |
| super.attachScout(); |
| if (getScoutObject() == null) { |
| return; |
| } |
| if (m_scoutTableListener == null) { |
| m_scoutTableListener = new P_ScoutTableListener(); |
| getScoutObject().addUITableListener(m_scoutTableListener); |
| } |
| |
| // header renderer must be set before models |
| m_swingTableHeader.setDefaultRenderer(new SwingTableHeaderCellRenderer(m_swingTableHeader.getDefaultRenderer(), this)); |
| |
| getSwingTable().setColumnModel(new SwingTableColumnModel(getSwingEnvironment(), this)); |
| getSwingTable().setModel(new SwingTableModel(getSwingEnvironment(), this)); |
| getSwingTable().getSelectionModel().setAnchorSelectionIndex(0); |
| |
| setMultiSelectFromScout(getScoutObject().isMultiSelect()); |
| setMultilineTextFromScout(getScoutObject().isMultilineText()); |
| setKeyboardNavigationFromScout(); |
| setHeaderVisibleFromScout(getScoutObject().isHeaderVisible()); |
| setColumnsAutoResizeFromScout(getScoutObject().isAutoResizeColumns()); |
| setKeyStrokesFromScout(); |
| |
| // Install DND and CopyPaste transfer handler |
| |
| // BSI ticket 104'549 |
| // Even if no DND is configured, the handler must be installed to ensure equivalent copy-paste behavior with DND support installed or not. |
| // Otherwise, Swing installs @{link TableTransferHandler} whose implementation cannot be accessed which would be necessary to ensure the same copy-paste behavior in case of DND installed. |
| // Furthermore, the copy-paste output should not depend on any drag handler installed to always have the same result. |
| m_swingScrollPane.getViewport().setTransferHandler(new P_SwingEmptySpaceTransferHandler()); |
| getSwingTable().setTransferHandler(new P_SwingRowTransferHandler()); |
| getSwingTable().setDragEnabled(getScoutObject().getDragType() != 0 || getScoutObject().getDropType() != 0); |
| |
| setSelectionFromScout(); |
| // add checkable key mappings |
| if (getScoutObject().isCheckable()) { |
| getSwingTable().getInputMap(JComponent.WHEN_FOCUSED).put(SwingUtility.createKeystroke("SPACE"), "toggleRow"); |
| getSwingTable().getActionMap().put("toggleRow", new AbstractAction() { |
| private static final long serialVersionUID = 1L; |
| |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| handleSwingRowClick(getSwingTable().getSelectedRow(), 99); |
| } |
| }); |
| } |
| enqueueAutoOptimizeColumnWidths(); |
| //handle events from recent history |
| final IEventHistory<TableEvent> h = getScoutObject().getEventHistory(); |
| if (h != null) { |
| SwingUtilities.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| for (TableEvent e : h.getRecentEvents()) { |
| handleScoutTableEventInSwing(e); |
| } |
| } |
| }); |
| } |
| } |
| |
| @Override |
| protected void detachScout() { |
| super.detachScout(); |
| if (getScoutObject() == null) { |
| return; |
| } |
| |
| if (m_editor != null) { |
| m_editor.dispose(); |
| } |
| |
| if (m_scoutTableListener != null) { |
| getScoutObject().removeTableListener(m_scoutTableListener); |
| m_scoutTableListener = null; |
| } |
| } |
| |
| protected void setMultiSelectFromScout(boolean on) { |
| if (on) { |
| getSwingTable().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); |
| } |
| else { |
| getSwingTable().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); |
| } |
| } |
| |
| protected void setScrollToSelectionFromScout() { |
| if (getScoutObject().isScrollToSelection()) { |
| scrollToSelection(); |
| } |
| } |
| |
| protected void setMultilineTextFromScout(boolean on) { |
| getSwingTable().setDynamicRowHeight(on); |
| } |
| |
| protected void setKeyboardNavigationFromScout() { |
| boolean hasKeyboardNavigation = getScoutObject().hasKeyboardNavigation(); |
| if (hasKeyboardNavigation) { |
| if (m_keyboardNavigationSupport == null) { |
| m_keyboardNavigationSupport = new P_KeyboardNavigationSupport(getSwingTable()); |
| } |
| } |
| else { |
| if (m_keyboardNavigationSupport != null) { |
| m_keyboardNavigationSupport.dispose(); |
| m_keyboardNavigationSupport = null; |
| } |
| } |
| } |
| |
| protected void setHeaderVisibleFromScout(boolean on) { |
| if (on) { |
| getSwingTable().setTableHeader(m_swingTableHeader); |
| } |
| else { |
| getSwingTable().setTableHeader(null); |
| } |
| } |
| |
| protected void setColumnsAutoResizeFromScout(boolean on) { |
| getSwingTable().setAutoResizeMode(on ? JTable.AUTO_RESIZE_ALL_COLUMNS : JTable.AUTO_RESIZE_OFF); |
| } |
| |
| protected void setKeyStrokesFromScout() { |
| JComponent component = getSwingContainer(); |
| if (component == null) { |
| component = getSwingField(); |
| } |
| if (component != null) { |
| // remove old key strokes |
| |
| if (m_installedScoutKs != null) { |
| for (IKeyStroke ks : m_installedScoutKs) { |
| KeyStroke swingKs = SwingUtility.createKeystroke(ks); |
| // |
| InputMap imap = component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); |
| imap.remove(swingKs); |
| ActionMap amap = component.getActionMap(); |
| amap.remove(ks.getActionId()); |
| } |
| } |
| m_installedScoutKs = null; |
| // add new key strokes |
| for (IKeyStroke scoutKs : getScoutObject().getKeyStrokes()) { |
| int swingWhen = JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT; |
| KeyStroke swingKs = SwingUtility.createKeystroke(scoutKs); |
| SwingScoutAction<IAction> action = new SwingScoutAction<IAction>(); |
| action.createField(scoutKs, getSwingEnvironment()); |
| // |
| InputMap imap = component.getInputMap(swingWhen); |
| imap.put(swingKs, scoutKs.getActionId()); |
| ActionMap amap = component.getActionMap(); |
| amap.put(scoutKs.getActionId(), action.getSwingAction()); |
| } |
| m_installedScoutKs = getScoutObject().getKeyStrokes(); |
| } |
| } |
| |
| protected void setSelectionFromScout() { |
| if (getScoutObject() == null) { |
| return; |
| } |
| List<ITableRow> scoutRows = getScoutObject().getSelectedRows(); |
| ListSelectionModel lsm = getSwingTableSelectionModel(); |
| // |
| int[] oldSwingRows = getSwingTable().getSelectedRows(); |
| int[] newSwingRows = scoutToSwingRows(scoutRows); |
| Arrays.sort(oldSwingRows); |
| Arrays.sort(newSwingRows); |
| // restore selection only, if list selection model has received its final value. |
| if (!CompareUtility.equals(oldSwingRows, newSwingRows) && !lsm.getValueIsAdjusting()) { |
| try { |
| lsm.setValueIsAdjusting(true); |
| // new |
| if (newSwingRows.length > 0) { |
| lsm.setLeadSelectionIndex(newSwingRows[0]); |
| lsm.setAnchorSelectionIndex(newSwingRows[0]); |
| lsm.clearSelection(); |
| for (int rowIndex : newSwingRows) { |
| lsm.addSelectionInterval(rowIndex, rowIndex); |
| } |
| } |
| else if (getSwingTable().getRowCount() > 0) { |
| // set anchor lead |
| lsm.setLeadSelectionIndex(0); |
| lsm.setAnchorSelectionIndex(0); |
| lsm.clearSelection(); |
| } |
| } |
| finally { |
| lsm.setValueIsAdjusting(false); |
| } |
| } |
| //ticket 96051 |
| if (getScoutObject().isScrollToSelection()) { |
| scrollToSelection(); |
| } |
| } |
| |
| protected void setSelectionFromSwing(final int[] swingRows) { |
| if (getUpdateSwingFromScoutLock().isAcquired()) { |
| return; |
| } |
| // |
| if (getScoutObject() != null) { |
| if (getSwingTable().getSelectionModel().getValueIsAdjusting()) { |
| return; |
| } |
| final List<ITableRow> scoutRows = swingToScoutRows(swingRows); |
| // notify Scout |
| Runnable t = new Runnable() { |
| @Override |
| public void run() { |
| try { |
| addIgnoredScoutEvent(TableEvent.class, "" + TableEvent.TYPE_ROWS_SELECTED); |
| // |
| getScoutObject().getUIFacade().setSelectedRowsFromUI(scoutRows); |
| } |
| finally { |
| removeIgnoredScoutEvent(TableEvent.class, "" + TableEvent.TYPE_ROWS_SELECTED); |
| } |
| } |
| }; |
| |
| getSwingEnvironment().invokeScoutLater(t, 0); |
| // end notify |
| } |
| } |
| |
| /** |
| * ticket 88564, do NOT use {@link #scrollToSelection()} when setting scout rows to swing, since this can lead to |
| * confusing effects when doing multiple selection on a table |
| */ |
| protected void scrollToSelection() { |
| int[] swingRows = getSwingTable().getSelectedRows(); |
| if (swingRows.length > 0) { |
| Rectangle viewRect = getSwingTable().getVisibleRect(); |
| if (viewRect.isEmpty()) { |
| //if component is not yet fully layouted |
| return; |
| } |
| Rectangle selBeginRect = getSwingTable().getCellRect(swingRows[0], 0, true); |
| Rectangle selEndRect = getSwingTable().getCellRect(swingRows[swingRows.length - 1], 0, true); |
| Rectangle selRect = selBeginRect.union(selEndRect); |
| Rectangle rootRect = new Rectangle(selRect.x, 0, selRect.width, selRect.y + selRect.height); |
| if (rootRect.height <= viewRect.height) { |
| getSwingTable().scrollRectToVisible(rootRect); |
| } |
| else if (selRect.height <= viewRect.height) { |
| getSwingTable().scrollRectToVisible(selRect); |
| } |
| else if (selBeginRect.height <= viewRect.height) { |
| getSwingTable().scrollRectToVisible(selBeginRect); |
| } |
| } |
| } |
| |
| protected void storeColumnWidthsFromSwing(List<TableColumn> swingColumns) { |
| if (getUpdateSwingFromScoutLock().isAcquired()) { |
| return; |
| } |
| // |
| // store gui index and width of column |
| SwingTableColumnModel cm = getSwingTableColumnModel(); |
| int n = cm.getColumnCount(); |
| final IColumn[] scoutCols = new IColumn[swingColumns.size()]; |
| final int[] scoutColWidths = new int[swingColumns.size()]; |
| for (int index = 0; index < swingColumns.size(); index++) { |
| TableColumn swingCol = swingColumns.get(index); |
| for (int i = 0; i < n; i++) { |
| if (cm.getColumn(i) == swingCol) { |
| scoutCols[index] = ((SwingTableColumn) swingCol).getScoutColumn(); |
| scoutColWidths[index] = swingCol.getPreferredWidth(); |
| break; |
| } |
| } |
| } |
| if (m_storeColumnWidthsJob != null) { |
| m_storeColumnWidthsJob.cancel(); |
| } |
| m_storeColumnWidthsJob = new ClientSyncJob("Store column widths", getSwingEnvironment().getScoutSession()) { |
| @Override |
| protected IStatus runStatus(IProgressMonitor monitor) { |
| if (getScoutObject() != null) { |
| for (int i = 0; i < scoutCols.length; i++) { |
| if (scoutCols[i] != null) { |
| getScoutObject().getUIFacade().setColumnWidthFromUI(scoutCols[i], scoutColWidths[i]); |
| } |
| } |
| } |
| return Status.OK_STATUS; |
| } |
| }; |
| m_storeColumnWidthsJob.schedule(400); |
| } |
| |
| /** |
| * scout property observer |
| */ |
| @Override |
| protected void handleScoutPropertyChange(String propName, Object newValue) { |
| if (propName.equals(ITable.PROP_MULTI_SELECT)) { |
| setMultiSelectFromScout(((Boolean) newValue).booleanValue()); |
| } |
| else if (propName.equals(ITable.PROP_MULTILINE_TEXT)) { |
| setMultilineTextFromScout(((Boolean) newValue).booleanValue()); |
| } |
| else if (propName.equals(ITable.PROP_HEADER_VISIBLE)) { |
| setHeaderVisibleFromScout(((Boolean) newValue).booleanValue()); |
| } |
| else if (propName.equals(ITable.PROP_AUTO_RESIZE_COLUMNS)) { |
| setColumnsAutoResizeFromScout(((Boolean) newValue).booleanValue()); |
| } |
| else if (propName.equals(ITable.PROP_KEY_STROKES)) { |
| setKeyStrokesFromScout(); |
| } |
| else if (propName.equals(ITable.PROP_DRAG_TYPE)) { |
| getSwingTable().setDragEnabled(getScoutObject().getDragType() != 0 || getScoutObject().getDropType() != 0); |
| } |
| else if (propName.equals(ITable.PROP_DROP_TYPE)) { |
| getSwingTable().setDragEnabled(getScoutObject().getDragType() != 0 || getScoutObject().getDropType() != 0); |
| } |
| else if (propName.equals(ITable.PROP_KEYBOARD_NAVIGATION)) { |
| setKeyboardNavigationFromScout(); |
| } |
| else if (propName.equals(ITable.PROP_SCROLL_TO_SELECTION)) { |
| setScrollToSelectionFromScout(); |
| } |
| } |
| |
| /** |
| * scout table observer |
| */ |
| protected boolean isHandleScoutTableEvent(List<? extends TableEvent> events) { |
| for (TableEvent event : events) { |
| switch (event.getType()) { |
| case TableEvent.TYPE_REQUEST_FOCUS: |
| case TableEvent.TYPE_REQUEST_FOCUS_IN_CELL: |
| case TableEvent.TYPE_ROWS_INSERTED: |
| case TableEvent.TYPE_ROWS_UPDATED: |
| case TableEvent.TYPE_ROWS_DELETED: |
| case TableEvent.TYPE_ALL_ROWS_DELETED: |
| case TableEvent.TYPE_ROW_ORDER_CHANGED: |
| case TableEvent.TYPE_ROW_FILTER_CHANGED: |
| case TableEvent.TYPE_COLUMN_ORDER_CHANGED: |
| case TableEvent.TYPE_COLUMN_HEADERS_UPDATED: |
| case TableEvent.TYPE_COLUMN_STRUCTURE_CHANGED: |
| case TableEvent.TYPE_ROWS_SELECTED: |
| case TableEvent.TYPE_SCROLL_TO_SELECTION: { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| protected void handleScoutTableEventInSwing(TableEvent e) { |
| SwingTableModel swingTableModel = (SwingTableModel) getSwingTable().getModel(); |
| int newRowCount = getScoutObject().getFilteredRowCount(); |
| /* |
| * check the scout observer to filter all events that are used here |
| * @see isHandleScoutTableEvent() |
| */ |
| switch (e.getType()) { |
| case TableEvent.TYPE_REQUEST_FOCUS: { |
| getSwingTable().requestFocus(); |
| break; |
| } |
| case TableEvent.TYPE_REQUEST_FOCUS_IN_CELL: { |
| //start editing |
| TableColumnModel tcm = getSwingTable().getColumnModel(); |
| int swingCol = -1; |
| for (int c = 0; c < tcm.getColumnCount(); c++) { |
| if (tcm.getColumn(c) instanceof SwingTableColumn) { |
| if (((SwingTableColumn) tcm.getColumn(c)).getScoutColumn() == e.getFirstColumn()) { |
| swingCol = c; |
| break; |
| } |
| } |
| } |
| int swingRow = scoutToSwingRow(e.getFirstRow()); |
| if (swingRow >= 0 && swingCol >= 0) { |
| JTable table = getSwingTable(); |
| table.scrollRectToVisible(table.getCellRect(swingRow, swingCol, true)); |
| table.editCellAt(swingRow, swingCol); |
| } |
| break; |
| } |
| case TableEvent.TYPE_ROWS_DELETED: |
| case TableEvent.TYPE_ALL_ROWS_DELETED: { |
| swingTableModel.updateModelState(newRowCount); |
| break; |
| } |
| case TableEvent.TYPE_ROWS_UPDATED: |
| case TableEvent.TYPE_ROW_ORDER_CHANGED: { |
| swingTableModel.updateModelState(newRowCount); |
| break; |
| } |
| case TableEvent.TYPE_ROWS_INSERTED: |
| case TableEvent.TYPE_ROW_FILTER_CHANGED: { |
| swingTableModel.updateModelState(newRowCount); |
| break; |
| } |
| case TableEvent.TYPE_COLUMN_ORDER_CHANGED: |
| case TableEvent.TYPE_COLUMN_HEADERS_UPDATED: |
| case TableEvent.TYPE_COLUMN_STRUCTURE_CHANGED: { |
| // re-install columns |
| SwingTableColumnModel swingColModel = (SwingTableColumnModel) getSwingTable().getColumnModel(); |
| swingColModel.initializeColumns(); |
| swingTableModel.updateModelState(newRowCount); |
| break; |
| } |
| case TableEvent.TYPE_ROWS_SELECTED: { |
| break; |
| } |
| case TableEvent.TYPE_SCROLL_TO_SELECTION: { |
| scrollToSelection(); |
| break; |
| } |
| } |
| //recover selection |
| switch (e.getType()) { |
| case TableEvent.TYPE_ALL_ROWS_DELETED: |
| case TableEvent.TYPE_ROWS_INSERTED: |
| case TableEvent.TYPE_ROWS_UPDATED: |
| case TableEvent.TYPE_ROWS_DELETED: |
| case TableEvent.TYPE_ROW_FILTER_CHANGED: |
| case TableEvent.TYPE_ROW_ORDER_CHANGED: |
| case TableEvent.TYPE_COLUMN_ORDER_CHANGED: |
| case TableEvent.TYPE_COLUMN_HEADERS_UPDATED: |
| case TableEvent.TYPE_ROWS_SELECTED: |
| case TableEvent.TYPE_COLUMN_STRUCTURE_CHANGED: { |
| setSelectionFromScout(); |
| getSwingTable().repaint(); |
| break; |
| } |
| } |
| //refresh html view cache |
| switch (e.getType()) { |
| case TableEvent.TYPE_ALL_ROWS_DELETED: |
| case TableEvent.TYPE_ROWS_INSERTED: |
| case TableEvent.TYPE_ROWS_UPDATED: |
| case TableEvent.TYPE_ROWS_DELETED: { |
| m_htmlViewCache = new HtmlViewCache(); |
| break; |
| } |
| } |
| //auto optimize column sizes |
| switch (e.getType()) { |
| case TableEvent.TYPE_ALL_ROWS_DELETED: |
| case TableEvent.TYPE_ROWS_INSERTED: |
| case TableEvent.TYPE_ROWS_UPDATED: |
| case TableEvent.TYPE_ROWS_DELETED: |
| case TableEvent.TYPE_ROW_FILTER_CHANGED: |
| case TableEvent.TYPE_COLUMN_STRUCTURE_CHANGED: { |
| for (IColumn<?> c : getScoutObject().getColumns()) { |
| if (c.isAutoOptimizeWidth()) { |
| enqueueAutoOptimizeColumnWidths(); |
| break; |
| } |
| } |
| break; |
| } |
| } |
| } |
| |
| private void enqueueAutoOptimizeColumnWidths() { |
| if (m_swingAutoOptimizeColumnWidthsJob != null) { |
| m_swingAutoOptimizeColumnWidthsJob.cancel(); |
| } |
| m_swingAutoOptimizeColumnWidthsJob = new P_SwingAutoOptimizeColumnWidthsJob(); |
| m_swingAutoOptimizeColumnWidthsJob.schedule(200); |
| } |
| |
| protected void handleSwingEmptySpaceSelection(final MouseEvent e) { |
| if (getUpdateSwingFromScoutLock().isAcquired()) { |
| return; |
| } |
| |
| // |
| if (getScoutObject() != null) { |
| // notify Scout |
| Runnable t = new Runnable() { |
| @Override |
| public void run() { |
| // reset selection |
| |
| if (e.getID() == MouseEvent.MOUSE_PRESSED) { |
| getScoutObject().getUIFacade().setSelectedRowsFromUI(new ArrayList<ITableRow>(0)); |
| } |
| if (e.isPopupTrigger()) { |
| // call swing menu |
| IContextMenu contextMenu = getScoutObject().getContextMenu(); |
| SwingPopupWorker swingPopupWorker = new SwingPopupWorker(getSwingEnvironment(), e.getComponent(), e.getPoint(), contextMenu, contextMenu.getCurrentMenuTypes()); |
| swingPopupWorker.setLightWeightPopup(true); |
| swingPopupWorker.enqueue(); |
| } |
| } |
| }; |
| getSwingEnvironment().invokeScoutLater(t, 5678); |
| // end notify |
| } |
| } |
| |
| protected void handleSwingDropAction(int rowIndex, Transferable swingTransferable) { |
| if (getUpdateSwingFromScoutLock().isAcquired()) { |
| return; |
| } |
| // |
| if (getScoutObject() != null) { |
| if (swingTransferable != null) { |
| final TransferObject scoutTransferable = SwingUtility.createScoutTransferable(swingTransferable); |
| final ITableRow scoutRow = swingToScoutRow(rowIndex); |
| if (scoutTransferable != null) { |
| // notify Scout (asynchronous !) |
| Runnable t = new Runnable() { |
| @Override |
| public void run() { |
| getScoutObject().getUIFacade().fireRowDropActionFromUI(scoutRow, scoutTransferable); |
| } |
| }; |
| getSwingEnvironment().invokeScoutLater(t, 0); |
| // end notify |
| } |
| } |
| } |
| } |
| |
| protected void handleSwingRowPopup(final MouseEvent e) { |
| if (getUpdateSwingFromScoutLock().isAcquired()) { |
| return; |
| } |
| // |
| if (getScoutObject() != null) { |
| // notify Scout |
| Runnable t = new Runnable() { |
| @Override |
| public void run() { |
| // about to show |
| IContextMenu contextMenu = getScoutObject().getContextMenu(); |
| // call swing menu |
| SwingPopupWorker swingPopupWorker = new SwingPopupWorker(getSwingEnvironment(), e.getComponent(), e.getPoint(), contextMenu, contextMenu.getCurrentMenuTypes()); |
| swingPopupWorker.setLightWeightPopup(false); |
| swingPopupWorker.enqueue(); |
| } |
| }; |
| getSwingEnvironment().invokeScoutLater(t, 5678); |
| // end notify |
| } |
| } |
| |
| protected void handleSwingRowClick(int rowIndex, final int swingButton) { |
| if (getUpdateSwingFromScoutLock().isAcquired()) { |
| return; |
| } |
| // |
| if (getScoutObject() != null && rowIndex >= 0) { |
| final ITableRow scoutRow = swingToScoutRow(rowIndex); |
| if (scoutRow != null) { |
| // notify Scout |
| Runnable t = new Runnable() { |
| @Override |
| public void run() { |
| getScoutObject().getUIFacade().fireRowClickFromUI(scoutRow, SwingUtility.swingToScoutMouseButton(swingButton)); |
| } |
| }; |
| |
| getSwingEnvironment().invokeScoutLater(t, 0); |
| // end notify |
| } |
| } |
| } |
| |
| protected void handleSwingHyperlinkAction(int rowIndex, int colIndex, final URL url) { |
| if (getUpdateSwingFromScoutLock().isAcquired()) { |
| return; |
| } |
| // |
| if (getScoutObject() != null && rowIndex >= 0 && colIndex >= 0) { |
| final ITableRow scoutRow = swingToScoutRow(rowIndex); |
| final IColumn scoutCol = getSwingTableColumnModel().swingToScoutColumn(colIndex); |
| if (scoutRow != null && scoutCol != null) { |
| // notify Scout |
| Runnable t = new Runnable() { |
| @Override |
| public void run() { |
| getScoutObject().getUIFacade().fireHyperlinkActionFromUI(scoutRow, scoutCol, url); |
| } |
| }; |
| |
| getSwingEnvironment().invokeScoutLater(t, 0); |
| // end notify |
| } |
| } |
| } |
| |
| protected void handleSwingRowAction(int rowIndex) { |
| if (getUpdateSwingFromScoutLock().isAcquired()) { |
| return; |
| } |
| // |
| if (getScoutObject() != null && rowIndex >= 0) { |
| final ITableRow scoutRow = swingToScoutRow(rowIndex); |
| if (scoutRow != null) { |
| // notify Scout |
| Runnable t = new Runnable() { |
| @Override |
| public void run() { |
| getScoutObject().getUIFacade().fireRowActionFromUI(scoutRow); |
| } |
| }; |
| getSwingEnvironment().invokeScoutLater(t, 0); |
| // end notify |
| } |
| } |
| } |
| |
| protected void handleSwingHeaderSort(final int sortIndex, final boolean shiftDown, final boolean controlDown) { |
| if (getUpdateSwingFromScoutLock().isAcquired()) { |
| return; |
| } |
| // |
| setContextColumnFromSwing(sortIndex); |
| if (sortIndex >= 0) { |
| // notify Scout |
| if (getScoutObject() != null) { |
| Runnable t = new Runnable() { |
| @Override |
| public void run() { |
| IColumn<?> scoutCol = getScoutObject().getColumnSet().getVisibleColumn(sortIndex); |
| getScoutObject().getUIFacade().setContextColumnFromUI(scoutCol); |
| getScoutObject().getUIFacade().fireHeaderSortFromUI(scoutCol, controlDown); |
| } |
| }; |
| getSwingEnvironment().invokeScoutLater(t, 200); |
| } |
| // end notify |
| } |
| } |
| |
| protected void handleSwingHeaderPopup(final MouseEvent e) { |
| if (getUpdateSwingFromScoutLock().isAcquired()) { |
| return; |
| } |
| // |
| final int sortIndex = getSwingTable().getTableHeader().columnAtPoint(e.getPoint()); |
| setContextColumnFromSwing(sortIndex); |
| if (getScoutObject() != null) { |
| // notify Scout |
| Runnable t = new Runnable() { |
| @Override |
| public void run() { |
| // call swing menu |
| SwingPopupWorker swingPopupWorker = new SwingPopupWorker(getSwingEnvironment(), e.getComponent(), e.getPoint(), getScoutObject().getContextMenu(), CollectionUtility.hashSet(TableMenuType.EmptySpace, TableMenuType.Header)); |
| swingPopupWorker.setLightWeightPopup(false); |
| swingPopupWorker.enqueue(); |
| } |
| }; |
| getSwingEnvironment().invokeScoutLater(t, 5678); |
| // end notify |
| } |
| } |
| |
| private void registerColumnHeaderPopupOwner(int columnViewIndex) { |
| getSwingEnvironment().setPopupOwner(null, null); |
| if (columnViewIndex < 0) { |
| return; |
| } |
| //register the popup location in case ui is interested in it |
| JTableHeader header = getSwingTable().getTableHeader(); |
| if (header == null) { |
| return; |
| } |
| Point p = header.getLocationOnScreen(); |
| Point dp = header.getHeaderRect(columnViewIndex).getLocation(); |
| final Rectangle headerCellRect = new Rectangle(p.x + dp.x, p.y + dp.y + header.getHeight(), 0, 0); |
| getSwingEnvironment().setPopupOwner(getSwingTable(), headerCellRect); |
| } |
| |
| protected Transferable handleSwingDragRequest() { |
| if (getUpdateSwingFromScoutLock().isAcquired()) { |
| return null; |
| } |
| |
| if (getScoutObject().getDragType() == 0) { |
| return null; |
| } |
| final Holder<TransferObject> result = new Holder<TransferObject>(TransferObject.class, null); |
| if (getScoutObject() != null) { |
| // notify Scout |
| Runnable t = new Runnable() { |
| @Override |
| public void run() { |
| TransferObject scoutTransferable = getScoutObject().getUIFacade().fireRowsDragRequestFromUI(); |
| result.setValue(scoutTransferable); |
| } |
| }; |
| try { |
| getSwingEnvironment().invokeScoutLater(t, 20000).join(20000); |
| } |
| catch (InterruptedException e) { |
| //nop |
| } |
| // end notify |
| } |
| TransferObject scoutTransferable = result.getValue(); |
| return SwingUtility.createSwingTransferable(scoutTransferable); |
| } |
| |
| protected Transferable handleSwingCopyRequest() { |
| if (getUpdateSwingFromScoutLock().isAcquired()) { |
| return null; |
| } |
| |
| final Holder<TransferObject> result = new Holder<TransferObject>(TransferObject.class, null); |
| if (getScoutObject() != null) { |
| // notify Scout |
| Runnable t = new Runnable() { |
| @Override |
| public void run() { |
| if (!ACCESS.check(new CopyToClipboardPermission())) { |
| return; |
| } |
| TransferObject scoutTransferable = getScoutObject().getUIFacade().fireRowsCopyRequestFromUI(); |
| result.setValue(scoutTransferable); |
| } |
| }; |
| try { |
| getSwingEnvironment().invokeScoutLater(t, 20000).join(20000); |
| } |
| catch (InterruptedException e) { |
| //nop |
| } |
| // end notify |
| } |
| TransferObject scoutTransferable = result.getValue(); |
| if (scoutTransferable != null) { |
| return SwingUtility.createSwingTransferable(scoutTransferable); |
| } |
| return null; |
| } |
| |
| protected void handleKeyboardNavigationFromSwing(int rowIndex) { |
| ListSelectionModel selectionModel = getSwingTable().getSelectionModel(); |
| selectionModel.setSelectionInterval(rowIndex, rowIndex); |
| scrollToSelection(); |
| } |
| |
| protected ITableRow swingToScoutRow(int rowIndex) { |
| ITable table = getScoutObject(); |
| if (table != null && rowIndex >= 0) { |
| return table.getFilteredRow(rowIndex); |
| } |
| return null; |
| } |
| |
| protected List<ITableRow> swingToScoutRows(int[] swingRows) { |
| if (swingRows == null || swingRows.length == 0) { |
| return CollectionUtility.emptyArrayList(); |
| } |
| |
| ITable table = getScoutObject(); |
| if (table != null) { |
| List<ITableRow> result = new ArrayList<ITableRow>(swingRows.length); |
| for (int swingRowIndex : swingRows) { |
| ITableRow row = table.getFilteredRow(swingRowIndex); |
| if (row != null) { |
| result.add(row); |
| } |
| } |
| return result; |
| } |
| return CollectionUtility.emptyArrayList(); |
| } |
| |
| protected int scoutToSwingRow(ITableRow row) { |
| ITable table = getScoutObject(); |
| if (table != null && row != null) { |
| return table.getFilteredRowIndex(row);//row.getRowIndex(); |
| } |
| return -1; |
| } |
| |
| protected int[] scoutToSwingRows(List<? extends ITableRow> rows) { |
| if (!CollectionUtility.hasElements(rows)) { |
| return new int[0]; |
| } |
| // |
| ITable table = getScoutObject(); |
| if (table != null) { |
| int mismatchCount = 0; |
| int[] swingRows = new int[rows.size()]; |
| int i = 0; |
| |
| for (ITableRow row : rows) { |
| int scoutIndex = table.getFilteredRowIndex(row); |
| if (scoutIndex >= 0) { |
| swingRows[i] = scoutIndex; |
| } |
| else { |
| swingRows[i] = -1; |
| mismatchCount++; |
| } |
| i++; |
| } |
| |
| if (mismatchCount > 0) { |
| int[] newSwingRows = new int[swingRows.length - mismatchCount]; |
| int index = 0; |
| for (int row : swingRows) { |
| if (row >= 0) { |
| newSwingRows[index] = row; |
| index++; |
| } |
| } |
| swingRows = newSwingRows; |
| } |
| return swingRows; |
| } |
| return new int[0]; |
| } |
| |
| private void closeEditor() { |
| TableCellEditor cellEditor = getSwingTable().getCellEditor(); |
| if (cellEditor != null) { |
| cellEditor.stopCellEditing(); |
| } |
| } |
| |
| private class P_SwingTable extends SwingTable { |
| private static final long serialVersionUID = 1L; |
| |
| /** |
| * override to decorate cells |
| */ |
| @Override |
| public Component prepareRenderer(TableCellRenderer renderer, int row, int column) { |
| // try to reset before super call |
| if (renderer instanceof JComponent) { |
| JComponent jc = ((JComponent) renderer); |
| //use html view cache. must set html view to null, otherwise the cached view is reset every time (by setting all view.parent=null in BasicHtml.updateRenderer) |
| jc.putClientProperty(BasicHTML.propertyKey, null); |
| jc.putClientProperty("html.disable", Boolean.TRUE); |
| jc.setBackground(getBackground()); |
| jc.setForeground(getForeground()); |
| jc.setFont(getFont()); |
| } |
| JComponent c = (JComponent) super.prepareRenderer(renderer, row, column); |
| boolean isSelected = isCellSelected(row, column); |
| // scout cell |
| JTable swingTable = P_SwingTable.this; |
| ITable scoutTable = getScoutObject(); |
| IColumn scoutCol = ((SwingTableColumn) swingTable.getColumnModel().getColumn(column)).getScoutColumn(); |
| ITableRow scoutRow = swingToScoutRow(row); |
| if (scoutTable != null) { |
| ICell cell = scoutTable.getCell(scoutRow, scoutCol); |
| if (cell != null) { |
| // enabled |
| c.setEnabled(scoutTable.isEnabled() && scoutRow.isEnabled() && cell.isEnabled()); |
| |
| if (c instanceof JLabel) { |
| JLabel label = (JLabel) c; |
| |
| // horizontal alignment |
| int align = cell.getHorizontalAlignment(); |
| // first column always left-aligned |
| if (column == 0 && (!StringUtility.isNullOrEmpty(scoutRow.getIconId()) || !StringUtility.isNullOrEmpty(cell.getIconId()))) { |
| align = -1; |
| } |
| if (align > 0) { |
| label.setHorizontalAlignment(SwingConstants.RIGHT); |
| } |
| else if (align == 0) { |
| label.setHorizontalAlignment(SwingConstants.CENTER); |
| } |
| else { |
| label.setHorizontalAlignment(SwingConstants.LEFT); |
| } |
| |
| // vertical alignment (only supported by boolean columns) |
| if (scoutCol instanceof IBooleanColumn) { |
| switch (((IBooleanColumn) scoutCol).getVerticalAlignment()) { |
| case 0: |
| label.setVerticalAlignment(SwingConstants.CENTER); |
| break; |
| case 1: |
| label.setVerticalAlignment(SwingConstants.BOTTOM); |
| break; |
| default: |
| label.setVerticalAlignment(SwingConstants.TOP); |
| break; |
| } |
| } |
| |
| // icon |
| boolean firstCellOfCheckableTable = scoutTable.isCheckable() && column == 0; |
| Icon checkboxIcon = getCheckboxIcon(firstCellOfCheckableTable, scoutCol, scoutRow, label); |
| Icon decoIcon = getDecoIcon(column, scoutRow, cell); |
| Icon icon = getCompositeIcon(checkboxIcon, decoIcon); |
| if (cell.isEditable()) { |
| icon = new P_IconWithMarker(icon); |
| } |
| label.setIcon(icon); |
| label.setDisabledIcon(icon); |
| |
| // Sort Order Column |
| if (scoutCol instanceof ISortOrderColumn) { |
| IColumn sortCol = (IColumn) cell.getValue(); |
| if (sortCol != null && sortCol.isSortActive() && sortCol.isSortExplicit()) { |
| label.setIcon(SortIconUtility.createSortIcon(sortCol, sortCol.getTable().getColumnSet().getSortColumns(), sortCol.isSortAscending())); |
| } |
| } |
| } |
| |
| // foreground |
| //TODO use row fg if cells value is null |
| if (cell.getForegroundColor() != null) { |
| Color color = ColorUtility.createColor(cell.getForegroundColor()); |
| if (isSelected) { |
| Color selectionColor = ColorUtility.createColor(cell.getBackgroundColor()); |
| color = ColorUtility.multiplyColors(selectionColor, color); |
| } |
| c.setForeground(color); |
| } |
| // font (must be set before text, otherwise html view is null again) |
| //TODO use row font if cells value is null |
| if (cell.getFont() != null) { |
| Font oldf = getFont(); |
| Font newf = SwingUtility.createFont(cell.getFont(), oldf); |
| if (oldf != null) {// only override font style, not size and face |
| c.setFont(new Font(oldf.getName(), newf != null ? newf.getStyle() : oldf.getStyle(), oldf.getSize())); |
| } |
| } |
| // text |
| String text = cell.getText(); |
| boolean wrapText = (scoutCol instanceof IStringColumn && ((IStringColumn) scoutCol).isTextWrap()); |
| if (scoutCol.getDataType() == Boolean.class && (!(scoutCol instanceof ISmartColumn) || ((ISmartColumn) scoutCol).getLookupCall() == null)) { |
| text = null; |
| } |
| |
| final boolean wrapOrMultilineText = wrapText || (scoutTable.isMultilineText() && SwingUtility.isMultilineLabelText(text)); |
| if (wrapOrMultilineText) { |
| text = SwingUtility.createHtmlLabelText(text, wrapText); |
| } |
| else { |
| // make single line |
| if (text != null) { |
| text = text.replaceAll("[\\n\\r]+", " "); |
| } |
| } |
| if (c instanceof JLabel) { |
| JLabel label = (JLabel) c; |
| |
| boolean removedHtmlRenderer = false; |
| if (!wrapOrMultilineText) { // wrapped and multi-line texts need to be processed a HTML renderer, so it will not be removed |
| removedHtmlRenderer = getSwingEnvironment().getHtmlValidator().removeHtmlRenderer(cell, text, label); |
| } |
| |
| label.setText(text); |
| if (!removedHtmlRenderer && m_htmlViewCache != null) { |
| m_htmlViewCache.updateHtmlView(label, cell.getForegroundColor() != null); |
| } |
| } |
| // tooltip |
| //TODO use row tt if cells value is null |
| String s = cell.getTooltipText(); |
| if (s != null && s.length() == 0) { |
| s = null; |
| } |
| s = SwingUtility.createHtmlLabelText(s, true); |
| c.setToolTipText(s); |
| // background |
| //TODO use row bg if cells value is null |
| if (cell.getBackgroundColor() != null) { |
| Color color = ColorUtility.createColor(cell.getBackgroundColor()); |
| if (isSelected) { |
| if (c.getBackground() != null) { |
| // bsh 2010-10-08: if possible, merge colors instead of just using a darker version |
| if (c.getBackground().getRGB() == Color.WHITE.getRGB()) { |
| color = ColorUtility.mergeColors(c.getBackground(), 0.5f, color, 0.5f); |
| } |
| color = ColorUtility.multiplyColors(c.getBackground(), color); |
| } |
| else { |
| color = color.darker(); |
| } |
| } |
| c.setBackground(color); |
| } |
| else { |
| // bsh 2010-10-08: consider table background color as well (e.g. in ListBoxes) |
| if (swingTable.getBackground() != null && isSelected) { |
| Color color = swingTable.getBackground(); |
| if (c.getBackground() != null) { |
| if (c.getBackground().getRGB() == Color.WHITE.getRGB()) { |
| color = ColorUtility.mergeColors(c.getBackground(), 0.5f, color, 0.5f); |
| } |
| color = ColorUtility.multiplyColors(c.getBackground(), color); |
| } |
| else { |
| color = color.darker(); |
| } |
| c.setBackground(color); |
| } |
| } |
| } |
| } |
| return c; |
| } |
| |
| /** |
| * @param icon1 |
| * @param icon2 |
| * @return |
| */ |
| private Icon getCompositeIcon(Icon icon1, Icon icon2) { |
| Icon icon = null; |
| if (icon1 != null && icon2 != null) { |
| icon = new CompositeIcon(0, icon1, icon2); |
| } |
| else if (icon1 != null) { |
| icon = icon1; |
| } |
| else if (icon2 != null) { |
| icon = icon2; |
| } |
| return icon; |
| } |
| |
| /** |
| * @param firstCellOfCheckableTable |
| * @param scoutCol |
| * @param scoutRow |
| * @param label |
| * @return |
| */ |
| private Icon getCheckboxIcon(boolean firstCellOfCheckableTable, IColumn scoutCol, ITableRow scoutRow, JLabel label) { |
| boolean booleanColumn = scoutCol.getDataType() == Boolean.class && (!(scoutCol instanceof ISmartColumn) || ((ISmartColumn) scoutCol).getLookupCall() == null); |
| CheckboxIcon checkboxIcon = null; |
| if (firstCellOfCheckableTable) { |
| // top inset is used to ensure the checkbox to be on the same position as the label text displayed |
| checkboxIcon = getSwingEnvironment().createCheckboxWithMarginIcon(new Insets(FONT_PADDING_TOP, 0, 0, 5)); |
| checkboxIcon.setSelected(scoutRow.isChecked()); |
| checkboxIcon.setEnabled(label.isEnabled()); |
| } |
| else if (booleanColumn) { |
| // font padding is only applied if checkbox is vertically aligned on top |
| int fontPaddingTop = 0; |
| if (label.getVerticalAlignment() == SwingConstants.TOP) { |
| fontPaddingTop = FONT_PADDING_TOP; |
| } |
| checkboxIcon = getSwingEnvironment().createCheckboxWithMarginIcon(new Insets(fontPaddingTop, 0, 0, 0)); |
| ICell cell = scoutRow.getCell(scoutCol); |
| Boolean b = (Boolean) cell.getValue(); |
| checkboxIcon.setSelected(b != null && b.booleanValue()); |
| checkboxIcon.setEnabled(label.isEnabled()); |
| } |
| return checkboxIcon; |
| } |
| |
| /** |
| * @param column |
| * @param scoutRow |
| * @param cell |
| * @return in case of an error the error icon otherwise the cell or row icon, if set |
| */ |
| private Icon getDecoIcon(int column, ITableRow scoutRow, ICell cell) { |
| Icon decoIcon = null; |
| if (cell.getErrorStatus() != null && cell.getErrorStatus().getSeverity() == IStatus.ERROR) { |
| decoIcon = getSwingEnvironment().getIcon(AbstractIcons.StatusError); |
| } |
| else if (cell.getIconId() != null) { |
| decoIcon = getSwingEnvironment().getIcon(cell.getIconId()); |
| } |
| else if (column == 0) { |
| decoIcon = getSwingEnvironment().getIcon(scoutRow.getIconId()); |
| } |
| return decoIcon; |
| } |
| |
| @Override |
| public boolean editCellAt(int row, int column, EventObject e) { |
| //no editing for tables with multi-select, if control or shift is pressed |
| if (e instanceof MouseEvent && ListSelectionModel.MULTIPLE_INTERVAL_SELECTION == getSwingTable().getSelectionModel().getSelectionMode()) { |
| MouseEvent me = (MouseEvent) e; |
| if (me.isControlDown() || me.isShiftDown()) { |
| return false; |
| } |
| } |
| return super.editCellAt(row, column, e); |
| } |
| } |
| |
| private class P_IconWithMarker implements Icon { |
| |
| private static final int MARKER_SIZE = 5; |
| private Icon m_icon; |
| |
| private P_IconWithMarker(Icon icon) { |
| m_icon = icon; |
| if (m_icon == null) { |
| // in case of no custom icon is set |
| m_icon = new ImageIcon(); |
| } |
| } |
| |
| @Override |
| public int getIconHeight() { |
| return m_icon.getIconHeight(); |
| } |
| |
| @Override |
| public int getIconWidth() { |
| return m_icon.getIconWidth(); |
| } |
| |
| @Override |
| public void paintIcon(Component c, Graphics g, int x, int y) { |
| // draw marker into icon |
| Color editableCellMarkerColor = UIManager.getColor("Table.cell.markerColorEditableCell"); |
| if (editableCellMarkerColor == null) { |
| editableCellMarkerColor = Color.GRAY; |
| } |
| g.setColor(editableCellMarkerColor); |
| g.fillPolygon(new int[]{0, MARKER_SIZE, 0}, new int[]{0, 0, MARKER_SIZE}, 3); |
| m_icon.paintIcon(c, g, x, y); |
| } |
| } |
| |
| /** |
| * Implementation of Sort and Popup Functionality for table header |
| */ |
| private class P_SwingHeadMouseListener extends MouseAdapter { |
| private Point m_pressedPoint;// to avoid sorting on column move |
| MouseClickedBugFix fix; |
| |
| @Override |
| public void mousePressed(MouseEvent e) { |
| closeEditor(); |
| fix = new MouseClickedBugFix(e); |
| m_pressedPoint = e.getPoint(); |
| JTableEx table = getSwingTable(); |
| int colIndex = table.getTableHeader().getColumnModel().getColumnIndexAtX(e.getX()); |
| if (colIndex >= 0) { |
| table.setColumnSelectionInterval(colIndex, colIndex); |
| } |
| // Mac popup |
| if (e.isPopupTrigger()) { |
| handleSwingHeaderPopup(e); |
| } |
| } |
| |
| @Override |
| public void mouseReleased(MouseEvent e) { |
| JTableEx table = getSwingTable(); |
| if (e.isPopupTrigger()) { |
| handleSwingHeaderPopup(e); |
| } |
| else { |
| // sorting only if there was no dragging |
| if (m_pressedPoint == null) { |
| m_pressedPoint = e.getPoint(); |
| } |
| m_pressedPoint.translate(-e.getX(), -e.getY()); |
| int r = Math.abs(m_pressedPoint.x) + Math.abs(m_pressedPoint.y); |
| if (r < 4 && e.getClickCount() == 1) { |
| if (table.getTableHeader().getCursor().getType() == Cursor.DEFAULT_CURSOR) { |
| int index = table.getTableHeader().columnAtPoint(e.getPoint()); |
| if (index >= 0) { |
| int scoutViewIndex = index; |
| handleSwingHeaderSort(scoutViewIndex, e.isShiftDown(), e.isControlDown()); |
| } |
| } |
| } |
| } |
| if (fix != null) { |
| fix.mouseReleased(this, e); |
| } |
| } |
| |
| @Override |
| public void mouseClicked(MouseEvent e) { |
| if (fix != null && fix.mouseClicked()) { |
| return; |
| } |
| if (e.getClickCount() == 2) { |
| if (getSwingTable().getTableHeader().getCursor().getType() != Cursor.DEFAULT_CURSOR) { |
| final int[] oldColumnWidths = new int[getSwingTableColumnModel().getColumnCount()]; |
| for (int i = 0; i < oldColumnWidths.length; i++) { |
| oldColumnWidths[i] = getSwingTableColumnModel().getColumn(i).getWidth(); |
| } |
| // optimize column widths |
| ((P_SwingTable) getSwingTable()).setOptimalColumnWidths(); |
| // build delta |
| ArrayList<TableColumn> list = new ArrayList<TableColumn>(); |
| for (int i = 0; i < oldColumnWidths.length; i++) { |
| TableColumn tc = getSwingTableColumnModel().getColumn(i); |
| int newWidth = tc.getPreferredWidth(); |
| if (newWidth != oldColumnWidths[i]) { |
| list.add(tc); |
| } |
| } |
| if (list.size() > 0) { |
| storeColumnWidthsFromSwing(list); |
| } |
| } |
| } |
| } |
| }// end class |
| |
| /** |
| * Click behaviour for checkable tables |
| */ |
| private class P_SwingRowMouseListener extends MouseInputAdapter { |
| MouseClickedBugFix fix; |
| |
| @Override |
| public void mousePressed(MouseEvent e) { |
| JTableEx table = getSwingTable(); |
| Point editingCell = new Point(table.getEditingColumn(), table.getEditingRow()); |
| Point currentCell = new Point(table.columnAtPoint(e.getPoint()), table.rowAtPoint(e.getPoint())); |
| if (CompareUtility.notEquals(editingCell, currentCell)) { |
| //Make sure editor is closed when clicking on another cell. Mainly necessary when using the second mouse button to open the context menu |
| closeEditor(); |
| } |
| fix = new MouseClickedBugFix(e); |
| // |
| int pressedRow = table.rowAtPoint(e.getPoint()); |
| // if selection is empty or click outside selection then make section of |
| // this row |
| // IMO 3.6.03 multi-selection with Ctrl can deselect rows |
| // right click down |
| // ONLY when enabled (see: SwingScoutListbox selections=checked row) |
| if (e.isMetaDown()) { |
| if (table.isEnabled()) { |
| boolean autoSelectRow = true; |
| if (table.getSelectionModel().getSelectionMode() != ListSelectionModel.SINGLE_SELECTION) { |
| if (e.isControlDown() || e.isShiftDown()) { |
| if (table.getSelectedRowCount() > 0) { |
| autoSelectRow = false; |
| } |
| } |
| } |
| if (autoSelectRow) { |
| if (pressedRow >= 0 && !table.isRowSelected(pressedRow)) { |
| table.setRowSelectionInterval(pressedRow, pressedRow); |
| } |
| } |
| } |
| } |
| // doubleclick |
| if (e.getClickCount() == 2 && pressedRow >= 0) { |
| if (table.isEnabled()) { |
| if (pressedRow >= 0 && table.getSelectedRowCount() <= 1 && (!table.isRowSelected(pressedRow))) { |
| table.setRowSelectionInterval(pressedRow, pressedRow); |
| } |
| } |
| } |
| int swingViewIndex = getSwingTable().columnAtPoint(e.getPoint()); |
| setContextColumnFromSwing(swingViewIndex); |
| // Mac popup |
| if (e.isPopupTrigger()) { |
| handleSwingRowPopup(e); |
| } |
| } |
| |
| @Override |
| public void mouseReleased(MouseEvent e) { |
| if (e.isPopupTrigger()) { |
| handleSwingRowPopup(e); |
| } |
| if (fix != null) { |
| fix.mouseReleased(this, e); |
| } |
| } |
| |
| @Override |
| public void mouseClicked(MouseEvent e) { |
| if (fix != null && fix.mouseClicked()) { |
| return; |
| } |
| if (e.getClickCount() == 1 && e.getButton() == MouseEvent.BUTTON1) { |
| int pressedRow = getSwingTable().rowAtPoint(e.getPoint()); |
| if (pressedRow >= 0) { |
| handleSwingRowClick(pressedRow, e.getButton()); |
| } |
| } |
| else if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) { |
| int pressedRow = getSwingTable().rowAtPoint(e.getPoint()); |
| if (pressedRow >= 0) { |
| handleSwingRowAction(pressedRow); |
| } |
| } |
| } |
| }// end private class |
| |
| /** |
| * Implementation of Popup Functionality for table rows |
| */ |
| private class P_SwingEmptySpaceMouseListener extends MouseAdapter { |
| @Override |
| public void mousePressed(MouseEvent e) { |
| closeEditor(); |
| // Mac popup |
| handleSwingEmptySpaceSelection(e); |
| |
| } |
| |
| @Override |
| public void mouseReleased(MouseEvent e) { |
| handleSwingEmptySpaceSelection(e); |
| } |
| }// end private class |
| |
| private class P_ScoutTableListener implements TableListener { |
| @Override |
| public void tableChanged(final TableEvent e) { |
| if (isHandleScoutTableEvent(CollectionUtility.arrayList(e))) { |
| if (isIgnoredScoutEvent(TableEvent.class, "" + e.getType())) { |
| return; |
| } |
| // |
| Runnable t = new Runnable() { |
| @Override |
| public void run() { |
| try { |
| getUpdateSwingFromScoutLock().acquire(); |
| // |
| handleScoutTableEventInSwing(e); |
| } |
| finally { |
| getUpdateSwingFromScoutLock().release(); |
| } |
| } |
| }; |
| getSwingEnvironment().invokeSwingLater(t); |
| } |
| } |
| |
| @Override |
| public void tableChangedBatch(final List<? extends TableEvent> events) { |
| if (isHandleScoutTableEvent(events)) { |
| final List<TableEvent> filteredList = new ArrayList<TableEvent>(); |
| for (TableEvent event : events) { |
| if (!isIgnoredScoutEvent(TableEvent.class, "" + event.getType())) { |
| filteredList.add(event); |
| } |
| } |
| if (CollectionUtility.hasElements(filteredList)) { |
| Runnable t = new Runnable() { |
| @Override |
| public void run() { |
| try { |
| getUpdateSwingFromScoutLock().acquire(); |
| // |
| for (TableEvent e : filteredList) { |
| handleScoutTableEventInSwing(e); |
| } |
| } |
| finally { |
| getUpdateSwingFromScoutLock().release(); |
| } |
| } |
| }; |
| getSwingEnvironment().invokeSwingLater(t); |
| } |
| } |
| } |
| }// end private class |
| |
| /** |
| * Implementation of DropSource's DragGestureListener support for drag/drop |
| * |
| * @since Build 202 |
| */ |
| private class P_SwingRowTransferHandler extends TransferHandlerEx { |
| private static final long serialVersionUID = 1L; |
| |
| @Override |
| public int getSourceActions(JComponent c) { |
| return DnDConstants.ACTION_COPY; |
| } |
| |
| @Override |
| public boolean canDrag() { |
| return getScoutObject().getDragType() != 0; |
| } |
| |
| @Override |
| public void exportToClipboard(JComponent comp, Clipboard clip, int action) throws IllegalStateException { |
| Transferable transferable = handleSwingCopyRequest(); |
| if (transferable != null) { |
| Toolkit.getDefaultToolkit().getSystemClipboard().setContents(transferable, null); |
| } |
| } |
| |
| @Override |
| public Transferable createTransferable(JComponent c) { |
| return handleSwingDragRequest(); |
| } |
| |
| @Override |
| public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) { |
| return SwingUtility.isSupportedTransfer(getScoutObject().getDropType(), transferFlavors); |
| } |
| |
| @Override |
| public boolean importDataEx(JComponent comp, Transferable t, Point location) { |
| if (location != null) { |
| int droppingRow = getSwingTable().rowAtPoint(location); |
| handleSwingDropAction(droppingRow, t); |
| return true; |
| } |
| return false; |
| } |
| }// end private class |
| |
| private class P_SwingEmptySpaceTransferHandler extends TransferHandlerEx { |
| private static final long serialVersionUID = 1L; |
| |
| @Override |
| public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) { |
| return SwingUtility.isSupportedTransfer(getScoutObject().getDropType(), transferFlavors); |
| } |
| |
| @Override |
| public boolean importDataEx(JComponent comp, Transferable t, Point location) { |
| int selectedRow = -1; |
| handleSwingDropAction(selectedRow, t); |
| return true; |
| } |
| |
| @Override |
| public boolean canDrag() { |
| // always false |
| return false; |
| } |
| |
| @Override |
| public Transferable createTransferable(JComponent c) { |
| return null; |
| } |
| |
| }// end private class |
| |
| private class P_KeyboardNavigationSupport extends TableKeyboardNavigationSupport { |
| public P_KeyboardNavigationSupport(JTableEx table) { |
| super(table); |
| } |
| |
| @Override |
| void handleKeyboardNavigation(int rowIndex) { |
| handleKeyboardNavigationFromSwing(rowIndex); |
| } |
| |
| } // end class P_KeyboardNavigationSupport |
| |
| private class P_SwingSelectionListener implements ListSelectionListener { |
| @Override |
| public void valueChanged(ListSelectionEvent e) { |
| if (!getSwingTable().getSelectionModel().getValueIsAdjusting()) { |
| int[] swingRows = getSwingTable().getSelectedRows(); |
| setSelectionFromSwing(swingRows); |
| } |
| } |
| }// end private class |
| |
| private class P_SwingAutoOptimizeColumnWidthsJob extends Job { |
| /** |
| * @param name |
| */ |
| public P_SwingAutoOptimizeColumnWidthsJob() { |
| super("Swing:AutoOptimizeColumnWidths"); |
| } |
| |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| SwingUtilities.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| if (getSwingTable() == null) { |
| return; |
| } |
| TableColumnModel tcm = getSwingTableColumnModel(); |
| if (tcm == null) { |
| return; |
| } |
| int tcCount = tcm.getColumnCount(); |
| for (int i = 0; i < tcCount; i++) { |
| TableColumn tc = tcm.getColumn(i); |
| if (tc instanceof SwingTableColumn && ((SwingTableColumn) tc).getScoutColumn().isAutoOptimizeWidth()) { |
| ((P_SwingTable) getSwingTable()).setOptimalColumnWidth(tc); |
| } |
| } |
| } |
| }); |
| return Status.OK_STATUS; |
| } |
| } |
| } |