/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.ui.table; import java.util.ArrayList; import java.util.List; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableLayout; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CLabel; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.custom.TableEditor; import org.eclipse.swt.custom.ViewForm; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.FocusListener; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.Text; import org.teiid.core.designer.util.CoreArgCheck; import org.teiid.core.designer.util.CoreStringUtil; import org.teiid.core.designer.util.I18nUtil; import org.teiid.designer.ui.PluginConstants; import org.teiid.designer.ui.UiConstants; import org.teiid.designer.ui.UiPlugin; import org.teiid.designer.ui.common.util.UiUtil; import org.teiid.designer.ui.common.util.WidgetFactory; import org.teiid.designer.ui.common.widget.ExtendedTitleAreaDialog; /** * ClipboardPasteProblemDialog * * @since 8.0 */ public class ClipboardPasteProblemDialog extends ExtendedTitleAreaDialog implements UiConstants, PluginConstants.Images { private static final String PREFIX = I18nUtil.getPropertyPrefix(ClipboardPasteProblemDialog.class); private static final String[] MESSAGE_TBL_HDRS = new String[] {CoreStringUtil.Constants.EMPTY_STRING, Util.getString(PREFIX + "pasteColumnHdr"), //$NON-NLS-1$ Util.getString(PREFIX + "pasteDataHdr"), //$NON-NLS-1$ Util.getString(PREFIX + "descriptionHdr")}; //$NON-NLS-1$ private static final int ICON_INDEX = 0; private static final int COLUMN_INDEX = 1; private static final int DATA_INDEX = 2; private static final int DESCRIPTION_INDEX = 3; private static final Image BLANK_IMAGE; private static final Image ERROR_IMAGE; private static final Image INFO_IMAGE; private static final Image WARNING_IMAGE; public static final String TRUNCATED_COLUMN_TITLE = Util.getString(PREFIX + "truncatedColumnHdr"); //$NON-NLS-1$ static { BLANK_IMAGE = UiPlugin.getDefault().getImage(BLANK_ICON); ERROR_IMAGE = org.teiid.designer.ui.common.UiPlugin.getDefault().getImage(TASK_ERROR); WARNING_IMAGE = org.teiid.designer.ui.common.UiPlugin.getDefault().getImage(TASK_WARNING); INFO_IMAGE = org.teiid.designer.ui.common.UiPlugin.getDefault().getImage(TASK_INFO); } private CLabel lblMessagesTitle; private SashForm sash; private TableEditor dataTableEditor; TableViewer dataViewer; private DataViewerContentProvider dataViewerContentProvider; // private String dialogTitle; TableViewer messageViewer; private MessageViewerContentProvider messageViewerContentProvider; int selectedColumn; // selected column index in data table List statusRecords; ITablePasteValidator validator; private int errorCount; private int infoCount; private int validCount; private int warningCount; private int truncatedColumnCount; private int truncatedRowCount; public ClipboardPasteProblemDialog( Shell theParent, List theRecords ) { super(theParent, UiPlugin.getDefault()); statusRecords = theRecords; } public ClipboardPasteProblemDialog( Shell theParent, List theRecords, ITablePasteValidator theValidator ) { super(theParent, UiPlugin.getDefault()); statusRecords = theRecords; validator = theValidator; } /** * @see org.eclipse.jface.window.Window#create() */ @Override public void create() { super.create(); // // Data Table setup // // create columns using first row Table table = dataViewer.getTable(); List firstRow = (List)statusRecords.get(0); for (int size = firstRow.size(), i = 0; i < size; i++) { TableColumn column = new TableColumn(table, SWT.LEFT); ClipboardPasteStatusRecord record = (ClipboardPasteStatusRecord)firstRow.get(i); column.setText(record.getColumnName()); } // populate data table dataViewer.setInput(statusRecords); // pack data table columns TableColumn[] columns = dataViewer.getTable().getColumns(); for (int i = 0; i < columns.length; ++i) { columns[i].pack(); } // // Message Table setup // // populate message table messageViewer.setInput(statusRecords); // pack message table columns columns = messageViewer.getTable().getColumns(); for (int i = 0; i < columns.length; ++i) { columns[i].pack(); } // now that table is populated set the sash weights sash.setWeights(new int[] {60, 40}); // set window message and message table view form title setDynamicMessages(); // get the OK button to enable correctly when first displayed. setOkEnabledState(true); } /** * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite) */ @Override protected Control createDialogArea( Composite theParent ) { Composite pnlContents = (Composite)super.createDialogArea(theParent); pnlContents.setLayout(new GridLayout()); sash = new SashForm(pnlContents, SWT.VERTICAL); sash.setLayoutData(new GridData(GridData.FILL_BOTH)); createPasteDataArea(sash); createMessageArea(sash); setDialogTitle(Util.getString(PREFIX + "windowTitle")); //$NON-NLS-1$ setTitle(Util.getString(PREFIX + "title")); //$NON-NLS-1$; // setTitleImage(UiPlugin.getDefault().getImage(TABLE_PASTE_WINDOW_ICON)); return pnlContents; } private Composite createMessageArea( Composite theParent ) { ViewForm viewForm = new ViewForm(theParent, SWT.BORDER); lblMessagesTitle = WidgetFactory.createLabel(viewForm); viewForm.setTopLeft(lblMessagesTitle); // this composite is needed in order to size the table correctly. // the viewform doesn't look at it's content's layout data. Composite pnlContents = new Composite(viewForm, SWT.NONE) { // need to override and set height based on a minimum of 5 visible rows in the table. @Override public Point computeSize( int wHint, int hHint, boolean changed ) { Table table = (Table)getChildren()[0]; return super.computeSize(wHint, table.getItemHeight() * 5 + table.getHeaderHeight(), changed); } }; final GridLayout layout = new GridLayout(); layout.marginWidth = layout.marginHeight = 0; pnlContents.setLayout(layout); pnlContents.setLayoutData(new GridData(GridData.FILL_BOTH)); viewForm.setContent(pnlContents); int style = SWT.V_SCROLL | SWT.H_SCROLL | SWT.FULL_SELECTION | SWT.READ_ONLY; messageViewer = new TableViewer(pnlContents, style); messageViewerContentProvider = new MessageViewerContentProvider(); messageViewer.setContentProvider(messageViewerContentProvider); messageViewer.setLabelProvider(new MessageViewerLabelProvider()); Table table = messageViewer.getTable(); table.setHeaderVisible(true); table.setLinesVisible(true); table.setLayout(new TableLayout()); table.setLayoutData(new GridData(GridData.FILL_BOTH)); // create columns for (int i = 0; i < MESSAGE_TBL_HDRS.length; i++) { TableColumn column = new TableColumn(table, SWT.LEFT); column.setText(MESSAGE_TBL_HDRS[i]); } return viewForm; } private Composite createPasteDataArea( Composite theParent ) { ViewForm viewForm = new ViewForm(theParent, SWT.BORDER); viewForm.setTopLeft(WidgetFactory.createLabel(viewForm, Util.getString(PREFIX + "pasteDataTitle"))); //$NON-NLS-1$ // this composite is needed in order to size the table correctly. // the viewform doesn't look at it's content's layout data. Composite pnlContents = new Composite(viewForm, SWT.NONE) { // need to override and set height based on a minimum visible rows in the table. @Override public Point computeSize( int wHint, int hHint, boolean changed ) { Table table = (Table)getChildren()[0]; return super.computeSize(wHint, table.getItemHeight() * 10 + table.getHeaderHeight(), changed); } }; final GridLayout layout = new GridLayout(); layout.marginWidth = layout.marginHeight = 0; pnlContents.setLayout(layout); pnlContents.setLayoutData(new GridData(GridData.FILL_BOTH)); viewForm.setContent(pnlContents); int style = SWT.V_SCROLL | SWT.H_SCROLL | SWT.FULL_SELECTION | SWT.READ_ONLY; dataViewer = new TableViewer(pnlContents, style); dataViewerContentProvider = new DataViewerContentProvider(); dataViewer.setContentProvider(dataViewerContentProvider); dataViewer.setLabelProvider(new DataViewerLabelProvider()); Table table = dataViewer.getTable(); table.setHeaderVisible(true); table.setLinesVisible(true); table.setLayout(new TableLayout()); table.setLayoutData(new GridData(GridData.FILL_BOTH)); table.addMouseListener(new MouseAdapter() { @Override public void mouseDown( MouseEvent theEvent ) { handleDataViewerMouseEvent(theEvent); } }); table.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected( SelectionEvent theEvent ) { handleDataViewerSelectionEvent(); } }); // The table text editor must have the same size as the cell and must not be smaller than 50 pixels. dataTableEditor = new TableEditor(table); dataTableEditor.horizontalAlignment = SWT.LEFT; dataTableEditor.grabHorizontal = true; dataTableEditor.minimumWidth = 50; return viewForm; } String getPasteData( int theRow, int theColumn ) { List row = (List)statusRecords.get(theRow); ClipboardPasteStatusRecord record = (ClipboardPasteStatusRecord)row.get(theColumn); return record.getPasteData(); } private String getDialogMessage() { String msg = null; if (errorCount > 0) { msg = Util.getString(PREFIX + "errorsExist.msg", new Object[] {new Integer(errorCount)}); //$NON-NLS-1$ } else if (validCount == 0) { msg = Util.getString(PREFIX + "noPasteData.msg"); //$NON-NLS-1$ } else if (warningCount > 0) { if (truncatedRowCount > 0) { if (truncatedColumnCount > 0) { msg = Util.getString(PREFIX + "rowsAndColumnsTruncated.msg", //$NON-NLS-1$ new Object[] {new Integer(truncatedRowCount), new Integer(truncatedColumnCount)}); } else { msg = Util.getString(PREFIX + "rowsTruncated.msg", new Object[] {new Integer(truncatedRowCount)}); //$NON-NLS-1$ } } else if (truncatedColumnCount > 0) { msg = Util.getString(PREFIX + "columnsTruncated.msg", new Object[] {new Integer(truncatedColumnCount)}); //$NON-NLS-1$ } else { msg = Util.getString(PREFIX + "warningsExist.msg"); //$NON-NLS-1$ } } else if (infoCount > 0) { msg = Util.getString(PREFIX + "infosExist.msg"); //$NON-NLS-1$ } else { msg = Util.getString(PREFIX + "validData.msg"); //$NON-NLS-1$ } return msg; } private int getDialogMessageType() { int errorType = IMessageProvider.NONE; if (errorCount > 0) { errorType = IMessageProvider.ERROR; } else if (validCount == 0) { errorType = IMessageProvider.INFORMATION; } else if (warningCount > 0) { errorType = IMessageProvider.WARNING; } else if (infoCount > 0) { errorType = IMessageProvider.INFORMATION; } return errorType; } Image getStatusImage( ClipboardPasteStatusRecord theRecord ) { Image result = null; if (theRecord.isValid()) { result = BLANK_IMAGE; } else if (theRecord.isError()) { result = ERROR_IMAGE; } else if (theRecord.isRowTruncated() || theRecord.isColumnTruncated() || theRecord.isProtectedColumn() || theRecord.isWarning()) { result = WARNING_IMAGE; } else if (theRecord.isInfo()) { result = INFO_IMAGE; } return result; } public List getStatusRecords() { return statusRecords; } void handleDataViewerMouseEvent( MouseEvent theEvent ) { /* ----- DESIGN NOTE ----- * This mouse event happens before the selection event. * The selection event depends on the selectedColumn being set. */ Table table = dataViewer.getTable(); TableItem[] selectedItems = table.getSelection(); selectedColumn = -1; if (selectedItems.length > 0) { for (int numCols = table.getColumnCount(), i = 0; i < numCols; i++) { Rectangle bounds = selectedItems[0].getBounds(i); if (bounds.contains(theEvent.x, theEvent.y)) { selectedColumn = i; break; } } if (selectedColumn != -1) { int index = table.getSelectionIndex(); List row = (List)statusRecords.get(index); ClipboardPasteStatusRecord record = (ClipboardPasteStatusRecord)row.get(selectedColumn); StructuredSelection selection = new StructuredSelection(record); messageViewer.setSelection(selection, true); } } } void handleDataViewerSelectionEvent() { /* ----- DESIGN NOTE ----- * The mouse event happens before this selection event. * This selection event depends on the selectedColumn being set by the mouse event. */ Table table = dataViewer.getTable(); final int selectedRow = table.getSelectionIndex(); // only edit if row and column has selection if ((selectedRow != -1) && (selectedColumn != -1)) { final List row = (List)statusRecords.get(selectedRow); ClipboardPasteStatusRecord record = (ClipboardPasteStatusRecord)row.get(selectedColumn); // allow editing if row is not being truncated if (!record.isRowTruncated() /*&& !record.isColumnTruncated()*/) { // The control that will be the editor must be a child of the table if (!record.isColumnTruncated() && !record.isProtectedColumn()) { final Text textWidget = new Text(table, SWT.NONE); textWidget.setText(record.getPasteData()); textWidget.setSelection(0, textWidget.getText().length()); textWidget.addFocusListener(new FocusListener() { @Override public void focusGained( FocusEvent theEvent ) { setOkEnabledState(false); } @Override public void focusLost( FocusEvent theEvent ) { String oldText = getPasteData(selectedRow, selectedColumn); String newText = textWidget.getText(); boolean changed = false; if (newText == null) { changed = (oldText != null); } else { changed = (oldText == null) || !oldText.equals(newText); } if (changed) { // the validator needs to know the selected table row and column int[] tableSelectedRowColumn = validator.getSelectedRowAndColumn(); ClipboardPasteStatusRecord newRecord = validator.constructPasteStatusRecord(newText, selectedRow + tableSelectedRowColumn[0], selectedColumn + tableSelectedRowColumn[1]); setRecord(newRecord, selectedRow, selectedColumn); dataViewer.refresh(row, true); messageViewer.setInput(statusRecords); } textWidget.dispose(); setOkEnabledState(true); } }); // Open the text editor in the selected column of the selected row. dataTableEditor.setEditor(textWidget, table.getItem(selectedRow), selectedColumn); textWidget.setFocus(); } } } } void resetCounts() { errorCount = infoCount = truncatedColumnCount = truncatedRowCount = validCount = warningCount = 0; } public void setDialogTitle( String theTitle ) { CoreArgCheck.isNotNull(theTitle); Shell shell = getShell(); if (shell == null) { // dialogTitle = theTitle; } else { shell.setText(theTitle); } } private void setDynamicMessages() { lblMessagesTitle.setText(Util.getString(PREFIX + "messagesTitle", //$NON-NLS-1$ new Object[] {new Integer(errorCount), new Integer(warningCount), new Integer(infoCount)})); setMessage(getDialogMessage(), getDialogMessageType()); } void setOkEnabledState( boolean theEnableState ) { boolean enable = theEnableState; if (theEnableState) { // no errors and something to paste enable = ((errorCount == 0) && (validCount > 0)); } getButton(IDialogConstants.OK_ID).setEnabled(enable); } void setRecord( ClipboardPasteStatusRecord theRecord, int theRow, int theColumn ) { List row = (List)statusRecords.get(theRow); ClipboardPasteStatusRecord oldRecord = (ClipboardPasteStatusRecord)row.get(theColumn); // update type counts updateCounts(oldRecord, false); updateCounts(theRecord, true); // set data, description, and type. data and column should be the same. oldRecord.setPasteData(theRecord.getPasteData()); oldRecord.setDescription(theRecord.getDescription()); oldRecord.setType(theRecord.getType()); // update dialog message setDynamicMessages(); // enable/disable OK button getButton(IDialogConstants.OK_ID).setEnabled(errorCount == 0); } void updateCounts( ClipboardPasteStatusRecord theRecord, boolean theIncreaseFlag ) { // an easy way to know what count to update is to see what image would be used. // this way only the logic to determine the type is only in the getStatusImage() method. // this method is called by the content provider. int addend = (theIncreaseFlag) ? 1 : -1; Image image = getStatusImage(theRecord); if (image == BLANK_IMAGE) validCount += addend; else if (image == ERROR_IMAGE) errorCount += addend; else if (image == WARNING_IMAGE) warningCount += addend; else if (image == INFO_IMAGE) infoCount += addend; } /** * Update table cell background colors if cell is being truncated. */ void updateTableCellBackground( List theRow, int theColumnIndex ) { ClipboardPasteStatusRecord record = (ClipboardPasteStatusRecord)theRow.get(theColumnIndex); // currently SWT has no way of setting background color of a column cell. only can set row background. if (record.isRowTruncated() /*|| record.isColumnTruncated()*/) { int index = statusRecords.indexOf(theRow); if (index != -1) { TableItem item = dataViewer.getTable().getItem(index); item.setBackground(UiUtil.getSystemColor(SWT.COLOR_INFO_BACKGROUND)); } } } class DataViewerContentProvider implements IStructuredContentProvider { Object[] rows = null; /** * @see org.eclipse.jface.viewers.IContentProvider#dispose() */ @Override public void dispose() { } /** * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object) */ @Override public Object[] getElements( Object theInputElement ) { return (rows == null) ? new Object[0] : rows; } /** * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, * java.lang.Object) */ @Override public void inputChanged( Viewer theViewer, Object theOldInput, Object theNewInput ) { boolean validInput = false; List result = null; resetCounts(); if ((theNewInput != null) && (theNewInput instanceof List) && !(((List)theNewInput).isEmpty())) { result = (List)theNewInput; // set initial counts for (int numRows = result.size(), i = 0; i < numRows; i++) { List row = (List)result.get(i); for (int numCols = row.size(), j = 0; j < numCols; j++) { updateCounts((ClipboardPasteStatusRecord)row.get(j), true); } } validInput = true; } if (validInput) { rows = result.toArray(); } else { rows = new Object[0]; } } } class DataViewerLabelProvider extends LabelProvider implements ITableLabelProvider { /** * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object, int) */ @Override public Image getColumnImage( Object theElement, int theColumnIndex ) { Image result = null; if (theElement instanceof List) { Object obj = ((List)theElement).get(theColumnIndex); if (obj instanceof ClipboardPasteStatusRecord) { result = getStatusImage((ClipboardPasteStatusRecord)obj); } } return result; } /** * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object, int) */ @Override public String getColumnText( Object theElement, int theColumnIndex ) { String result = null; if (theElement instanceof List) { Object obj = ((List)theElement).get(theColumnIndex); if (obj instanceof ClipboardPasteStatusRecord) { ClipboardPasteStatusRecord record = (ClipboardPasteStatusRecord)obj; result = record.getPasteData(); updateTableCellBackground((List)theElement, theColumnIndex); } else { result = super.getText(theElement); } } return (result == null) ? "" : result; //$NON-NLS-1$ } } class MessageViewerContentProvider implements IStructuredContentProvider { Object[] rows = null; /** * @see org.eclipse.jface.viewers.IContentProvider#dispose() */ @Override public void dispose() { } /** * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object) */ @Override public Object[] getElements( Object theInputElement ) { return (rows == null) ? new Object[0] : rows; } /** * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, * java.lang.Object) */ @Override public void inputChanged( Viewer theViewer, Object theOldInput, Object theNewInput ) { boolean validInput = true; List result = null; if ((theNewInput != null) && (theNewInput instanceof List) && !(((List)theNewInput).isEmpty())) { List input = (List)theNewInput; result = new ArrayList(input.size()); ROW_LOOP: for (int numRows = input.size(), i = 0; i < numRows; i++) { Object objRow = input.get(i); if (objRow instanceof List) { List row = (List)objRow; // COLUMN_LOOP: for (int numColumns = row.size(), j = 0; j < numColumns; j++) { Object objColumn = row.get(j); if (objColumn instanceof ClipboardPasteStatusRecord) { if (!((ClipboardPasteStatusRecord)objColumn).isValid()) { result.add(objColumn); } } else { validInput = false; break ROW_LOOP; } } } else { validInput = false; break ROW_LOOP; } } } else { validInput = false; } if (validInput) { rows = result.toArray(); } else { rows = new Object[0]; } } } class MessageViewerLabelProvider extends LabelProvider implements ITableLabelProvider { /** * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object, int) */ @Override public Image getColumnImage( Object theElement, int theColumnIndex ) { Image result = null; if ((theColumnIndex == ICON_INDEX) && (theElement instanceof ClipboardPasteStatusRecord)) { result = getStatusImage((ClipboardPasteStatusRecord)theElement); } return result; } /** * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object, int) */ @Override public String getColumnText( Object theElement, int theColumnIndex ) { String result = null; if (theElement instanceof ClipboardPasteStatusRecord) { ClipboardPasteStatusRecord row = (ClipboardPasteStatusRecord)theElement; switch (theColumnIndex) { case COLUMN_INDEX: result = row.getColumnName(); break; case DATA_INDEX: result = row.getPasteData(); break; case DESCRIPTION_INDEX: result = row.getDescription(); break; default: result = ""; //$NON-NLS-1$ break; } } else { result = super.getText(theElement); } return result; } } }