/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jkiss.dbeaver.ui.controls.resultset.spreadsheet;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.resource.ColorRegistry;
import org.eclipse.jface.text.IFindReplaceTarget;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.menus.CommandContributionItem;
import org.eclipse.ui.progress.UIJob;
import org.eclipse.ui.themes.ITheme;
import org.eclipse.ui.themes.IThemeManager;
import org.eclipse.ui.views.properties.IPropertySheetPage;
import org.eclipse.ui.views.properties.IPropertySource;
import org.eclipse.ui.views.properties.IPropertySourceProvider;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.DBeaverPreferences;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.core.CoreMessages;
import org.jkiss.dbeaver.model.*;
import org.jkiss.dbeaver.model.data.*;
import org.jkiss.dbeaver.model.exec.DBCSession;
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
import org.jkiss.dbeaver.model.runtime.AbstractJob;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor;
import org.jkiss.dbeaver.model.struct.DBSDataContainer;
import org.jkiss.dbeaver.runtime.properties.PropertyCollector;
import org.jkiss.dbeaver.ui.ActionUtils;
import org.jkiss.dbeaver.ui.DBeaverIcons;
import org.jkiss.dbeaver.ui.UIUtils;
import org.jkiss.dbeaver.ui.controls.PropertyPageStandard;
import org.jkiss.dbeaver.ui.controls.lightgrid.GridCell;
import org.jkiss.dbeaver.ui.controls.lightgrid.GridPos;
import org.jkiss.dbeaver.ui.controls.lightgrid.IGridContentProvider;
import org.jkiss.dbeaver.ui.controls.lightgrid.IGridLabelProvider;
import org.jkiss.dbeaver.ui.controls.resultset.*;
import org.jkiss.dbeaver.ui.controls.resultset.panel.ViewValuePanel;
import org.jkiss.dbeaver.ui.data.IMultiController;
import org.jkiss.dbeaver.ui.data.IValueController;
import org.jkiss.dbeaver.ui.data.IValueEditor;
import org.jkiss.dbeaver.ui.data.IValueEditorStandalone;
import org.jkiss.dbeaver.ui.data.managers.BaseValueManager;
import org.jkiss.dbeaver.ui.dialogs.ConfirmationDialog;
import org.jkiss.dbeaver.ui.properties.PropertySourceDelegate;
import org.jkiss.dbeaver.utils.GeneralUtils;
import org.jkiss.utils.ArrayUtils;
import org.jkiss.utils.CommonUtils;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
/**
* Spreadsheet presentation.
* Visualizes results as grid.
*/
public class SpreadsheetPresentation extends AbstractPresentation implements IResultSetEditor, ISelectionProvider, IStatefulControl, IAdaptable {
private static final Log log = Log.getLog(SpreadsheetPresentation.class);
private Spreadsheet spreadsheet;
@Nullable
private DBDAttributeBinding curAttribute;
private int columnOrder = SWT.NONE;
private final Map<SpreadsheetValueController, IValueEditorStandalone> openEditors = new HashMap<>();
private SpreadsheetFindReplaceTarget findReplaceTarget;
// UI modifiers
private IThemeManager themeManager;
private IPropertyChangeListener themeChangeListener;
private Color backgroundAdded;
private Color backgroundDeleted;
private Color backgroundModified;
private Color backgroundNormal;
private Color backgroundOdd;
private Color backgroundReadOnly;
private Color foregroundDefault;
private Color foregroundNull;
private Font boldFont, italicFont, bolItalicFont;
private boolean showOddRows = true;
private boolean showCelIcons = true;
public SpreadsheetPresentation() {
findReplaceTarget = new SpreadsheetFindReplaceTarget(this);
}
public Spreadsheet getSpreadsheet() {
return spreadsheet;
}
@Nullable
DBPDataSource getDataSource() {
DBSDataContainer dataContainer = controller.getDataContainer();
return dataContainer == null ? null : dataContainer.getDataSource();
}
@Override
public void createPresentation(@NotNull IResultSetController controller, @NotNull Composite parent) {
super.createPresentation(controller, parent);
this.boldFont = UIUtils.makeBoldFont(parent.getFont());
this.italicFont = UIUtils.modifyFont(parent.getFont(), SWT.ITALIC);
this.foregroundNull = parent.getShell().getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
this.spreadsheet = new Spreadsheet(
parent,
SWT.MULTI | SWT.VIRTUAL | SWT.H_SCROLL | SWT.V_SCROLL,
controller.getSite(),
this,
new ContentProvider(),
new GridLabelProvider());
this.spreadsheet.setLayoutData(new GridData(GridData.FILL_BOTH));
this.spreadsheet.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e)
{
if (e.detail != SWT.DRAG && e.detail != SWT.DROP_DOWN) {
updateGridCursor((GridCell) e.data);
}
fireSelectionChanged(new SpreadsheetSelectionImpl());
}
});
spreadsheet.addFocusListener(new FocusListener() {
@Override
public void focusGained(FocusEvent e) {
SpreadsheetPresentation.this.controller.updateEditControls();
}
@Override
public void focusLost(FocusEvent e) {
SpreadsheetPresentation.this.controller.updateEditControls();
}
});
this.themeManager = controller.getSite().getWorkbenchWindow().getWorkbench().getThemeManager();
this.themeChangeListener = new IPropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
if (event.getProperty().startsWith(ThemeConstants.RESULTS_PROP_PREFIX)) {
applyThemeSettings();
}
}
};
this.themeManager.addPropertyChangeListener(themeChangeListener);
applyThemeSettings();
this.spreadsheet.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
dispose();
}
});
trackPresentationControl();
UIUtils.enableHostEditorKeyBindingsSupport(controller.getSite(), spreadsheet);
}
private void dispose()
{
closeEditors();
clearMetaData();
themeManager.removePropertyChangeListener(themeChangeListener);
UIUtils.dispose(this.italicFont);
UIUtils.dispose(this.boldFont);
}
public void scrollToRow(@NotNull RowPosition position)
{
boolean recordMode = controller.isRecordMode();
ResultSetRow curRow = controller.getCurrentRow();
ResultSetModel model = controller.getModel();
switch (position) {
case FIRST:
if (recordMode) {
if (model.getRowCount() > 0) {
controller.setCurrentRow(model.getRow(0));
} else {
controller.setCurrentRow(null);
}
} else {
spreadsheet.shiftCursor(0, -spreadsheet.getItemCount(), false);
}
break;
case PREVIOUS:
if (recordMode && curRow != null && curRow.getVisualNumber() > 0) {
controller.setCurrentRow(model.getRow(curRow.getVisualNumber() - 1));
} else {
spreadsheet.shiftCursor(0, -1, false);
}
break;
case NEXT:
if (recordMode && curRow != null && curRow.getVisualNumber() < model.getRowCount() - 1) {
controller.setCurrentRow(model.getRow(curRow.getVisualNumber() + 1));
} else {
spreadsheet.shiftCursor(0, 1, false);
}
break;
case LAST:
if (recordMode && model.getRowCount() > 0) {
controller.setCurrentRow(model.getRow(model.getRowCount() - 1));
} else {
spreadsheet.shiftCursor(0, spreadsheet.getItemCount(), false);
}
break;
case CURRENT:
if (curRow != null) {
GridPos curPos = spreadsheet.getCursorPosition();
GridCell newCell = spreadsheet.posToCell(new GridPos(curPos.col, curRow.getVisualNumber()));
if (newCell != null) {
spreadsheet.setCursor(newCell, false);
}
}
break;
}
if (controller.isRecordMode()) {
// Update focus cell
restoreState(curAttribute);
}
// Update controls
controller.updateEditControls();
controller.updateStatusMessage();
if (recordMode) {
// Refresh meta if we are in record mode
refreshData(true, false, true);
}
}
@Nullable
@Override
public DBDAttributeBinding getCurrentAttribute() {
return curAttribute;
}
@Override
public void setCurrentAttribute(@NotNull DBDAttributeBinding attribute) {
this.curAttribute = attribute;
ResultSetRow curRow = controller.getCurrentRow();
if (curRow == null) {
return;
}
GridCell cell = controller.isRecordMode() ?
new GridCell(curRow, this.curAttribute) :
new GridCell(this.curAttribute, curRow);
this.spreadsheet.setCursor(cell, false);
//this.spreadsheet.showColumn(this.curAttribute);
}
@Override
public Point getCursorLocation() {
GridPos focusPos = spreadsheet.getFocusPos();
Rectangle columnBounds = spreadsheet.getColumnBounds(focusPos.col);
if (columnBounds != null) {
columnBounds.y += spreadsheet.getHeaderHeight();
return new Point(columnBounds.x, columnBounds.y);
}
return super.getCursorLocation();
}
@Override
public Object saveState() {
return curAttribute;
}
@Override
public void restoreState(Object state) {
this.curAttribute = controller.getModel().getAttributeBinding((DBDAttributeBinding) state);
ResultSetRow curRow = controller.getCurrentRow();
if (curRow != null && this.curAttribute != null) {
GridCell cell = controller.isRecordMode() ?
new GridCell(curRow, this.curAttribute) :
new GridCell(this.curAttribute, curRow);
//spreadsheet.selectCell(cell);
spreadsheet.setCursor(cell, false);
}
}
private void updateGridCursor(GridCell cell)
{
boolean changed;
Object newCol = cell == null ? null : cell.col;
Object newRow = cell == null ? null : cell.row;
ResultSetRow curRow = controller.getCurrentRow();
if (!controller.isRecordMode()) {
changed = curRow != newRow || curAttribute != newCol;
if (newRow instanceof ResultSetRow) {
curRow = (ResultSetRow) newRow;
controller.setCurrentRow(curRow);
}
if (newCol instanceof DBDAttributeBinding) {
curAttribute = (DBDAttributeBinding) newCol;
}
} else {
changed = curAttribute != newRow;
if (newRow instanceof DBDAttributeBinding) {
curAttribute = (DBDAttributeBinding) newRow;
}
}
if (changed) {
spreadsheet.cancelInlineEditor();
ResultSetPropertyTester.firePropertyChange(ResultSetPropertyTester.PROP_CAN_MOVE);
ResultSetPropertyTester.firePropertyChange(ResultSetPropertyTester.PROP_EDITABLE);
spreadsheet.redrawGrid();
}
}
@Nullable
public String copySelectionToString(ResultSetCopySettings settings)
{
String columnDelimiter = settings.getColumnDelimiter();
if (columnDelimiter == null) {
columnDelimiter = "\t";
}
String rowDelimiter = settings.getRowDelimiter();
if (rowDelimiter == null) {
rowDelimiter = GeneralUtils.getDefaultLineSeparator();
}
List<Object> selectedColumns = spreadsheet.getColumnSelection();
IGridLabelProvider labelProvider = spreadsheet.getLabelProvider();
StringBuilder tdt = new StringBuilder();
if (settings.isCopyHeader()) {
if (settings.isCopyRowNumbers()) {
tdt.append("#");
}
for (Object column : selectedColumns) {
if (tdt.length() > 0) {
tdt.append(columnDelimiter);
}
tdt.append(labelProvider.getText(column));
}
tdt.append(rowDelimiter);
}
List<GridCell> selectedCells = spreadsheet.getCellSelection();
boolean quoteCells = settings.isQuoteCells() && selectedCells.size() > 1;
GridCell prevCell = null;
for (GridCell cell : selectedCells) {
if (prevCell == null || cell.row != prevCell.row) {
// Next row
if (prevCell != null && prevCell.col != cell.col) {
// Fill empty row tail
int prevColIndex = selectedColumns.indexOf(prevCell.col);
for (int i = prevColIndex; i < selectedColumns.size() - 1; i++) {
tdt.append(columnDelimiter);
}
}
if (prevCell != null) {
tdt.append(rowDelimiter);
}
if (settings.isCopyRowNumbers()) {
tdt.append(labelProvider.getText(cell.row)).append(columnDelimiter);
}
}
if (prevCell != null && prevCell.col != cell.col) {
int prevColIndex = selectedColumns.indexOf(prevCell.col);
int curColIndex = selectedColumns.indexOf(cell.col);
for (int i = prevColIndex; i < curColIndex; i++) {
tdt.append(columnDelimiter);
}
}
boolean recordMode = controller.isRecordMode();
DBDAttributeBinding column = (DBDAttributeBinding)(!recordMode ? cell.col : cell.row);
ResultSetRow row = (ResultSetRow) (!recordMode ? cell.row : cell.col);
Object value = controller.getModel().getCellValue(column, row);
String cellText = column.getValueRenderer().getValueDisplayString(
column.getAttribute(),
value,
settings.getFormat());
if (quoteCells && cellText != null) {
if (cellText.contains(columnDelimiter) || cellText.contains(rowDelimiter)) {
cellText = '"' + cellText + '"';
}
}
tdt.append(cellText);
if (settings.isCut()) {
IValueController valueController = new SpreadsheetValueController(
controller, column, row, IValueController.EditType.NONE, null);
if (!valueController.isReadOnly()) {
valueController.updateValue(BaseValueManager.makeNullValue(valueController), false);
}
}
prevCell = cell;
}
if (settings.isCut()) {
controller.redrawData(false, false);
controller.updatePanelsContent(false);
}
return tdt.toString();
}
@Override
public void pasteFromClipboard(boolean extended)
{
try {
if (extended) {
DBPDataSource dataSource = getDataSource();
String strValue;
Clipboard clipboard = new Clipboard(Display.getCurrent());
try {
strValue = (String) clipboard.getContents(TextTransfer.getInstance());
} finally {
clipboard.dispose();
}
if (CommonUtils.isEmpty(strValue)) {
return;
}
GridPos focusPos = spreadsheet.getFocusPos();
int rowNum = focusPos.row;
if (rowNum < 0) {
return;
}
try (DBCSession session = DBUtils.openUtilSession(new VoidProgressMonitor(), dataSource, "Advanced paste")) {
String[][] newLines = parseGridLines(strValue);
// Create new rows on demand
while (rowNum + newLines.length > spreadsheet.getItemCount()) {
controller.addNewRow(false, true);
}
if (rowNum < 0 || rowNum >= spreadsheet.getItemCount()) {
return;
}
for (String[] line : newLines) {
int colNum = focusPos.col;
Object rowElement = spreadsheet.getRowElement(rowNum);
for (String value : line) {
if (colNum >= spreadsheet.getColumnCount()) {
break;
}
Object colElement = spreadsheet.getColumnElement(colNum);
final DBDAttributeBinding attr = (DBDAttributeBinding)(controller.isRecordMode() ? rowElement : colElement);
final ResultSetRow row = (ResultSetRow)(controller.isRecordMode() ? colElement : rowElement);
if (controller.isAttributeReadOnly(attr)) {
continue;
}
Object newValue = attr.getValueHandler().getValueFromObject(
session, attr.getAttribute(), value, true);
new SpreadsheetValueController(
controller,
attr,
row,
IValueController.EditType.NONE,
null).updateValue(newValue, true);
colNum++;
}
rowNum++;
if (rowNum >= spreadsheet.getItemCount()) {
// Shouldn't be here
break;
}
}
}
} else {
DBDAttributeBinding attr = getFocusAttribute();
ResultSetRow row = controller.getCurrentRow();
if (attr == null || row == null) {
return;
}
if (controller.isAttributeReadOnly(attr)) {
// No inline editors for readonly columns
return;
}
Object newValue = ResultSetUtils.getAttributeValueFromClipboard(attr);
if (newValue == null) {
return;
}
new SpreadsheetValueController(
controller,
attr,
row,
IValueController.EditType.NONE,
null).updateValue(newValue, true);
}
}
catch (Exception e) {
UIUtils.showErrorDialog(spreadsheet.getShell(), "Cannot replace cell value", null, e);
}
}
private String[][] parseGridLines(String strValue) {
final char columnDelimiter = '\t';
final char rowDelimiter = '\n';
final char trashDelimiter = '\r';
final char quote = '"';
final List<String[]> lines = new ArrayList<>();
final StringBuilder cellValue = new StringBuilder();
final List<String> curLine = new ArrayList<>();
boolean inQuote = false;
int length = strValue.length();
for (int i = 0; i < length; i++) {
char c = strValue.charAt(i);
if (inQuote && c != quote) {
cellValue.append(c);
} else {
switch (c) {
case columnDelimiter:
curLine.add(cellValue.toString());
cellValue.setLength(0);
break;
case rowDelimiter:
curLine.add(cellValue.toString());
lines.add(curLine.toArray(new String[curLine.size()]));
curLine.clear();
cellValue.setLength(0);
break;
case trashDelimiter:
// Ignore
continue;
case quote:
if (inQuote) {
if (i == length - 1 ||
strValue.charAt(i + 1) == columnDelimiter ||
strValue.charAt(i + 1) == trashDelimiter ||
strValue.charAt(i + 1) == rowDelimiter)
{
inQuote = false;
continue;
}
} else if (cellValue.length() == 0) {
// Search for end quote
for (int k = i + 1; k < length; k++) {
if (strValue.charAt(k) == quote &&
(k == length - 1 ||
strValue.charAt(k + 1) == columnDelimiter ||
strValue.charAt(k + 1) == trashDelimiter ||
strValue.charAt(k + 1) == rowDelimiter))
{
inQuote = true;
break;
}
}
if (inQuote) {
continue;
}
}
default:
cellValue.append(c);
break;
}
}
}
if (cellValue.length() > 0) {
curLine.add(cellValue.toString());
}
if (!curLine.isEmpty()) {
lines.add(curLine.toArray(new String[curLine.size()]));
}
return lines.toArray(new String[lines.size()][]);
}
@Override
public Control getControl() {
return spreadsheet;
}
@Override
public void refreshData(boolean refreshMetadata, boolean append, boolean keepState) {
// Cache preferences
DBPPreferenceStore preferenceStore = getPreferenceStore();
showOddRows = preferenceStore.getBoolean(DBeaverPreferences.RESULT_SET_SHOW_ODD_ROWS);
showCelIcons = preferenceStore.getBoolean(DBeaverPreferences.RESULT_SET_SHOW_CELL_ICONS);
spreadsheet.setRedraw(false);
try {
spreadsheet.refreshData(refreshMetadata, keepState);
} finally {
spreadsheet.setRedraw(true);
}
}
@Override
public void formatData(boolean refreshData) {
reorderLocally();
spreadsheet.refreshData(false, true);
}
@Override
public void clearMetaData() {
this.curAttribute = null;
this.columnOrder = SWT.NONE;
}
@Override
public void updateValueView() {
spreadsheet.redrawGrid();
spreadsheet.updateScrollbars();
if (curAttribute == null) {
curAttribute = getFocusAttribute();
}
if (curAttribute != null) {
spreadsheet.showColumn(curAttribute);
}
}
@Override
public void fillMenu(@NotNull IMenuManager menu) {
menu.add(ActionUtils.makeCommandContribution(
controller.getSite(),
ResultSetCommandHandler.CMD_TOGGLE_PANELS,
CommandContributionItem.STYLE_CHECK));
}
@Override
public void changeMode(boolean recordMode) {
ResultSetRow oldRow = controller.getCurrentRow();
DBDAttributeBinding oldAttribute = this.curAttribute;
int rowCount = controller.getModel().getRowCount();
if (rowCount > 0) {
// Fix row number if needed
if (oldRow == null) {
oldRow = controller.getModel().getRow(0);
} else if (oldRow.getVisualNumber() >= rowCount) {
oldRow = controller.getModel().getRow(rowCount - 1);
}
}
if (oldAttribute == null && controller.getModel().getVisibleAttributeCount() > 0) {
oldAttribute = controller.getModel().getVisibleAttribute(0);
}
this.columnOrder = recordMode ? SWT.DEFAULT : SWT.NONE;
if (oldRow != null && oldAttribute != null) {
if (!recordMode) {
spreadsheet.setCursor(new GridCell(oldAttribute, oldRow), false);
} else {
spreadsheet.setCursor(new GridCell(oldRow, oldAttribute), false);
}
}
spreadsheet.layout(true, true);
}
public void fillContextMenu(@NotNull IMenuManager manager, @Nullable Object colObject, @Nullable Object rowObject) {
final DBDAttributeBinding attr = (DBDAttributeBinding)(controller.isRecordMode() ? rowObject : colObject);
final ResultSetRow row = (ResultSetRow)(controller.isRecordMode() ? colObject : rowObject);
controller.fillContextMenu(manager, attr, row);
if (attr != null && row != null) {
final List<Object> selectedColumns = spreadsheet.getColumnSelection();
if (!controller.isRecordMode() && !selectedColumns.isEmpty()) {
String hideTitle;
if (selectedColumns.size() == 1) {
DBDAttributeBinding columnToHide = (DBDAttributeBinding) selectedColumns.get(0);
hideTitle = "Hide column '" + columnToHide.getName() + "'";
} else {
hideTitle = "Hide selected columns (" + selectedColumns.size() + ")";
}
manager.insertAfter(IResultSetController.MENU_GROUP_EDIT, new Action(hideTitle) {
@Override
public void run()
{
ResultSetModel model = controller.getModel();
if (selectedColumns.size() >= model.getVisibleAttributeCount()) {
UIUtils.showMessageBox(getControl().getShell(), "Hide columns", "Can't hide all result columns, at least one column must be visible", SWT.ERROR);
} else {
for (int i = 0, selectedColumnsSize = selectedColumns.size(); i < selectedColumnsSize; i++) {
model.setAttributeVisibility((DBDAttributeBinding) selectedColumns.get(i), false);
}
refreshData(true, false, true);
}
}
});
}
}
}
/////////////////////////////////////////////////
// Edit
private void closeEditors() {
List<IValueEditorStandalone> editors = new ArrayList<>(openEditors.values());
for (IValueEditorStandalone editor : editors) {
if (editor.getControl() != null && !editor.getControl().isDisposed()) {
editor.closeValueEditor();
}
}
openEditors.clear();
}
@Override
@Nullable
public Control openValueEditor(final boolean inline)
{
// The control that will be the editor must be a child of the Table
DBDAttributeBinding attr = getFocusAttribute();
ResultSetRow row = getFocusRow();
if (attr == null || row == null) {
return null;
}
if (!inline) {
for (Iterator<SpreadsheetValueController> iterator = openEditors.keySet().iterator(); iterator.hasNext(); ) {
SpreadsheetValueController valueController = iterator.next();
if (attr == valueController.getBinding() && row == valueController.getCurRow()) {
IValueEditorStandalone editor = openEditors.get(valueController);
if (editor.getControl() != null && !editor.getControl().isDisposed()) {
editor.showValueEditor();
return null;
} else {
// Remove disposed editor from the list
iterator.remove();
}
}
}
}
// if (controller.isAttributeReadOnly(attr) && inline) {
// // No inline editors for readonly columns
// return null;
// }
Composite placeholder = null;
if (inline) {
if (controller.isReadOnly()) {
return null;
}
spreadsheet.cancelInlineEditor();
placeholder = new Composite(spreadsheet, SWT.NONE);
placeholder.setFont(spreadsheet.getFont());
placeholder.setLayout(new FillLayout());
GridData gd = new GridData(GridData.FILL_BOTH);
gd.horizontalIndent = 0;
gd.verticalIndent = 0;
gd.grabExcessHorizontalSpace = true;
gd.grabExcessVerticalSpace = true;
placeholder.setLayoutData(gd);
controller.lockActionsByControl(placeholder);
}
SpreadsheetValueController valueController = new SpreadsheetValueController(
controller,
attr,
row,
inline ? IValueController.EditType.INLINE : IValueController.EditType.EDITOR,
placeholder);
IValueController.EditType[] supportedEditTypes = valueController.getValueManager().getSupportedEditTypes();
if (supportedEditTypes.length == 0) {
if (placeholder != null) {
placeholder.dispose();
}
return null;
}
/*
if (inline &&
(!ArrayUtils.contains(supportedEditTypes, IValueController.EditType.INLINE) || controller.isAttributeReadOnly(attr)) &&
ArrayUtils.contains(supportedEditTypes, IValueController.EditType.PANEL))
{
// Inline editor isn't supported but panel viewer is
// Enable panel
if (!isPreviewVisible()) {
togglePreview();
}
placeholder.dispose();
return null;
}
*/
final IValueEditor editor;
try {
editor = valueController.getValueManager().createEditor(valueController);
}
catch (Exception e) {
UIUtils.showErrorDialog(spreadsheet.getShell(), "Cannot edit value", null, e);
return null;
}
if (editor != null) {
editor.createControl();
}
if (editor instanceof IValueEditorStandalone) {
valueController.registerEditor((IValueEditorStandalone)editor);
// show dialog in separate job to avoid block
new UIJob("Open separate editor") {
@Override
public IStatus runInUIThread(IProgressMonitor monitor)
{
((IValueEditorStandalone)editor).showValueEditor();
return Status.OK_STATUS;
}
}.schedule();
//((IValueEditorStandalone)editor).showValueEditor();
} else {
// Set editable value
if (editor != null) {
try {
editor.primeEditorValue(valueController.getValue());
} catch (DBException e) {
log.error(e);
}
editor.setDirty(false);
}
}
if (inline) {
if (editor != null) {
spreadsheet.showCellEditor(placeholder);
return editor.getControl();
} else {
// No editor was created so just drop placeholder
placeholder.dispose();
// Probably we can just show preview panel
if (ArrayUtils.contains(supportedEditTypes, IValueController.EditType.PANEL)) {
// Inline editor isn't supported but panel viewer is
// Enable panel
controller.activatePanel(ViewValuePanel.PANEL_ID, true, true);
return null;
}
}
}
return null;
}
public void resetCellValue(@NotNull Object colElement, @NotNull Object rowElement, boolean delete)
{
boolean recordMode = controller.isRecordMode();
final DBDAttributeBinding attr = (DBDAttributeBinding)(recordMode ? rowElement : colElement);
final ResultSetRow row = (ResultSetRow)(recordMode ? colElement : rowElement);
controller.getModel().resetCellValue(attr, row);
updateValueView();
controller.updatePanelsContent(false);
}
///////////////////////////////////////////////
// Links
public void navigateLink(@NotNull GridCell cell, final int state) {
boolean recordMode = controller.isRecordMode();
final DBDAttributeBinding attr = (DBDAttributeBinding)(recordMode ? cell.row : cell.col);
final ResultSetRow row = (ResultSetRow)(recordMode ? cell.col : cell.row);
Object value = controller.getModel().getCellValue(attr, row);
if (DBUtils.isNullValue(value)) {
log.warn("Can't navigate to NULL value");
return;
}
if (!CommonUtils.isEmpty(attr.getReferrers())) {
// Navigate association
new AbstractJob("Navigate association") {
@Override
protected IStatus run(DBRProgressMonitor monitor) {
try {
boolean ctrlPressed = (state & SWT.CTRL) == SWT.CTRL;
controller.navigateAssociation(monitor, attr, row, ctrlPressed);
} catch (DBException e) {
return GeneralUtils.makeExceptionStatus(e);
}
return Status.OK_STATUS;
}
}.schedule();
} else {
// Navigate hyperlink
String strValue = attr.getValueHandler().getValueDisplayString(attr, value, DBDDisplayFormat.UI);
UIUtils.launchProgram(strValue);
}
}
///////////////////////////////////////////////
// Themes
private void applyThemeSettings()
{
ITheme currentTheme = themeManager.getCurrentTheme();
Font rsFont = currentTheme.getFontRegistry().get(ThemeConstants.FONT_SQL_RESULT_SET);
if (rsFont != null) {
this.spreadsheet.setFont(rsFont);
}
final ColorRegistry colorRegistry = currentTheme.getColorRegistry();
Color previewBack = colorRegistry.get(ThemeConstants.COLOR_SQL_RESULT_SET_PREVIEW_BACK);
if (previewBack != null) {
// this.previewPane.getViewPlaceholder().setBackground(previewBack);
// for (Control control : this.previewPane.getViewPlaceholder().getChildren()) {
// control.setBackground(previewBack);
// }
}
//this.foregroundDefault = currentTheme.getColorRegistry().get(ThemeConstants.COLOR_SQL_RESULT_CELL_FORE);
this.backgroundAdded = colorRegistry.get(ThemeConstants.COLOR_SQL_RESULT_CELL_NEW_BACK);
this.backgroundDeleted = colorRegistry.get(ThemeConstants.COLOR_SQL_RESULT_CELL_DELETED_BACK);
this.backgroundModified = colorRegistry.get(ThemeConstants.COLOR_SQL_RESULT_CELL_MODIFIED_BACK);
this.backgroundOdd = colorRegistry.get(ThemeConstants.COLOR_SQL_RESULT_CELL_ODD_BACK);
this.backgroundReadOnly = colorRegistry.get(ThemeConstants.COLOR_SQL_RESULT_CELL_READ_ONLY);
this.spreadsheet.setLineColor(colorRegistry.get(ThemeConstants.COLOR_SQL_RESULT_LINES_NORMAL));
this.spreadsheet.setLineSelectedColor(colorRegistry.get(ThemeConstants.COLOR_SQL_RESULT_LINES_SELECTED));
this.spreadsheet.recalculateSizes();
}
///////////////////////////////////////////////
// Ordering
private boolean supportsDataFilter()
{
DBSDataContainer dataContainer = controller.getDataContainer();
return dataContainer != null &&
(dataContainer.getSupportedFeatures() & DBSDataContainer.DATA_FILTER) == DBSDataContainer.DATA_FILTER;
}
private void reorderLocally()
{
controller.rejectChanges();
controller.getModel().resetOrdering();
refreshData(false, false, true);
}
public void changeSorting(Object columnElement, final int state)
{
if (columnElement == null) {
columnOrder = columnOrder == SWT.DEFAULT ? SWT.UP : (columnOrder == SWT.UP ? SWT.DOWN : SWT.DEFAULT);
spreadsheet.refreshData(false, true);
spreadsheet.redrawGrid();
return;
}
DBDDataFilter dataFilter = controller.getModel().getDataFilter();
boolean ctrlPressed = (state & SWT.CTRL) == SWT.CTRL;
boolean altPressed = (state & SWT.ALT) == SWT.ALT;
if (ctrlPressed) {
dataFilter.resetOrderBy();
}
DBDAttributeBinding metaColumn = (DBDAttributeBinding)columnElement;
DBDAttributeConstraint constraint = dataFilter.getConstraint(metaColumn);
assert constraint != null;
//int newSort;
if (constraint.getOrderPosition() == 0) {
if (ResultSetUtils.isServerSideFiltering(controller) && supportsDataFilter()) {
if (ConfirmationDialog.showConfirmDialogEx(
spreadsheet.getShell(),
DBeaverPreferences.CONFIRM_ORDER_RESULTSET,
ConfirmationDialog.QUESTION,
ConfirmationDialog.WARNING,
metaColumn.getName()) != IDialogConstants.YES_ID)
{
return;
}
}
constraint.setOrderPosition(dataFilter.getMaxOrderingPosition() + 1);
constraint.setOrderDescending(altPressed);
} else if (!constraint.isOrderDescending()) {
constraint.setOrderDescending(true);
} else {
for (DBDAttributeConstraint con2 : dataFilter.getConstraints()) {
if (con2.getOrderPosition() > constraint.getOrderPosition()) {
con2.setOrderPosition(con2.getOrderPosition() - 1);
}
}
constraint.setOrderPosition(0);
constraint.setOrderDescending(false);
}
if (!ResultSetUtils.isServerSideFiltering(controller) || !controller.isHasMoreData()) {
reorderLocally();
} else {
controller.refreshData(null);
}
}
///////////////////////////////////////////////
// Misc
public DBPPreferenceStore getPreferenceStore() {
return controller.getPreferenceStore();
}
@Override
public <T> T getAdapter(Class<T> adapter) {
if (adapter == IPropertySheetPage.class) {
// Show cell properties
PropertyPageStandard page = new PropertyPageStandard();
page.setPropertySourceProvider(new IPropertySourceProvider() {
@Nullable
@Override
public IPropertySource getPropertySource(Object object) {
if (object instanceof GridCell) {
GridCell cell = (GridCell) object;
boolean recordMode = controller.isRecordMode();
final DBDAttributeBinding attr = (DBDAttributeBinding) (recordMode ? cell.row : cell.col);
final ResultSetRow row = (ResultSetRow) (recordMode ? cell.col : cell.row);
final SpreadsheetValueController valueController = new SpreadsheetValueController(
controller,
attr,
row,
IValueController.EditType.NONE,
null);
PropertyCollector props = new PropertyCollector(valueController.getBinding().getAttribute(), false);
props.collectProperties();
valueController.getValueManager().contributeProperties(props, valueController);
return new PropertySourceDelegate(props);
}
return null;
}
});
return adapter.cast(page);
} else if (adapter == IFindReplaceTarget.class) {
return adapter.cast(findReplaceTarget);
}
return null;
}
@Nullable
public DBDAttributeBinding getFocusAttribute()
{
return controller.isRecordMode() ?
(DBDAttributeBinding) spreadsheet.getFocusRowElement() :
(DBDAttributeBinding) spreadsheet.getFocusColumnElement();
}
@Nullable
public ResultSetRow getFocusRow()
{
return controller.isRecordMode() ?
(ResultSetRow) spreadsheet.getFocusColumnElement() :
(ResultSetRow) spreadsheet.getFocusRowElement();
}
///////////////////////////////////////////////
// Selection provider
@Override
public IResultSetSelection getSelection() {
return new SpreadsheetSelectionImpl();
}
@Override
public void setSelection(ISelection selection) {
if (selection instanceof IResultSetSelection && ((IResultSetSelection) selection).getController() == getController()) {
// It may occur on simple focus change so we won't do anything
return;
}
spreadsheet.deselectAll();
if (!selection.isEmpty() && selection instanceof IStructuredSelection) {
List<GridPos> cellSelection = new ArrayList<>();
for (Iterator iter = ((IStructuredSelection) selection).iterator(); iter.hasNext(); ) {
Object cell = iter.next();
if (cell instanceof GridPos) {
cellSelection.add((GridPos) cell);
} else {
log.warn("Bad selection object: " + cell);
}
}
spreadsheet.selectCells(cellSelection);
spreadsheet.showSelection();
}
fireSelectionChanged(selection);
}
private class SpreadsheetSelectionImpl implements IResultSetSelection {
@Nullable
@Override
public GridPos getFirstElement()
{
Collection<GridPos> ssSelection = spreadsheet.getSelection();
if (ssSelection.isEmpty()) {
return null;
}
return ssSelection.iterator().next();
}
@Override
public Iterator<GridPos> iterator()
{
return spreadsheet.getSelection().iterator();
}
@Override
public int size()
{
return spreadsheet.getSelection().size();
}
@Override
public Object[] toArray()
{
return spreadsheet.getSelection().toArray();
}
@Override
public List toList()
{
return new ArrayList<>(spreadsheet.getSelection());
}
@Override
public boolean isEmpty()
{
return spreadsheet.getSelection().isEmpty();
}
@NotNull
@Override
public IResultSetController getController()
{
return SpreadsheetPresentation.this.getController();
}
@NotNull
@Override
public Collection<DBDAttributeBinding> getSelectedAttributes() {
if (controller.isRecordMode()) {
List<DBDAttributeBinding> attrs = new ArrayList<>();
for (Integer row : spreadsheet.getRowSelection()) {
attrs.add(controller.getModel().getVisibleAttribute(row));
}
return attrs;
} else {
List<DBDAttributeBinding> attrs = new ArrayList<>();
for (Object row : spreadsheet.getColumnSelection()) {
attrs.add((DBDAttributeBinding) row);
}
return attrs;
}
}
@NotNull
@Override
public Collection<ResultSetRow> getSelectedRows()
{
if (controller.isRecordMode()) {
ResultSetRow currentRow = controller.getCurrentRow();
if (currentRow == null) {
return Collections.emptyList();
}
return Collections.singletonList(currentRow);
} else {
List<ResultSetRow> rows = new ArrayList<>();
for (Integer row : spreadsheet.getRowSelection()) {
rows.add(controller.getModel().getRow(row));
}
Collections.sort(rows, new Comparator<ResultSetRow>() {
@Override
public int compare(ResultSetRow o1, ResultSetRow o2) {
return o1.getVisualNumber() - o2.getVisualNumber();
}
});
return rows;
}
}
@Override
public DBDAttributeBinding getElementAttribute(Object element) {
GridPos pos = (GridPos)element;
return (DBDAttributeBinding) (controller.isRecordMode() ?
spreadsheet.getRowElement(pos.row) :
spreadsheet.getColumnElement(pos.col));
}
@Override
public ResultSetRow getElementRow(Object element) {
return (ResultSetRow) (controller.isRecordMode() ?
controller.getCurrentRow() :
spreadsheet.getRowElement(((GridPos) element).row));
}
}
private class ContentProvider implements IGridContentProvider {
@NotNull
@Override
public Object[] getElements(boolean horizontal) {
boolean recordMode = controller.isRecordMode();
ResultSetModel model = controller.getModel();
if (horizontal) {
// columns
if (!recordMode) {
return model.getVisibleAttributes().toArray();
} else {
Object curRow = controller.getCurrentRow();
return curRow == null ? new Object[0] : new Object[] {curRow};
}
} else {
// rows
if (!recordMode) {
return model.getAllRows().toArray();
} else {
DBDAttributeBinding[] columns = model.getVisibleAttributes().toArray(new DBDAttributeBinding[model.getVisibleAttributeCount()]);
if (columnOrder != SWT.NONE && columnOrder != SWT.DEFAULT) {
Arrays.sort(columns, new Comparator<DBDAttributeBinding>() {
@Override
public int compare(DBDAttributeBinding o1, DBDAttributeBinding o2) {
return o1.getName().compareTo(o2.getName()) * (columnOrder == SWT.UP ? 1 : -1);
}
});
}
return columns;
}
}
}
@Nullable
@Override
public Object[] getChildren(Object element) {
if (element instanceof DBDAttributeBinding) {
DBDAttributeBinding binding = (DBDAttributeBinding) element;
switch (binding.getDataKind()) {
case ARRAY:
if (controller.isRecordMode()) {
ResultSetRow curRow = controller.getCurrentRow();
if (curRow != null) {
Object value = controller.getModel().getCellValue(binding, curRow);
if (value instanceof DBDCollection) {
return curRow.getCollectionData(
binding,
((DBDCollection)value)).getElements();
}
}
return null;
}
case STRUCT:
case DOCUMENT:
case ANY:
final List<DBDAttributeBinding> children = controller.getModel().getVisibleAttributes(binding);
if (children != null) {
return children.toArray();
}
break;
}
}
return null;
}
@Override
public int getSortOrder(@Nullable Object column)
{
if (column instanceof DBDAttributeBinding) {
DBDAttributeBinding binding = (DBDAttributeBinding) column;
if (!binding.hasNestedBindings()) {
DBDAttributeConstraint co = controller.getModel().getDataFilter().getConstraint(binding);
if (co != null && co.getOrderPosition() > 0) {
return co.isOrderDescending() ? SWT.DOWN : SWT.UP;
}
return SWT.DEFAULT;
}
} else if (column == null && controller.isRecordMode()) {
// Columns order in record mode
return columnOrder;
}
return SWT.NONE;
}
@Override
public ElementState getDefaultState(@NotNull Object element) {
if (element instanceof DBDAttributeBinding) {
DBDAttributeBinding binding = (DBDAttributeBinding) element;
switch (binding.getAttribute().getDataKind()) {
case STRUCT:
case DOCUMENT:
return ElementState.EXPANDED;
case ARRAY:
ResultSetRow curRow = controller.getCurrentRow();
if (curRow != null) {
Object cellValue = controller.getModel().getCellValue(binding, curRow);
if (cellValue instanceof DBDCollection && ((DBDCollection) cellValue).getItemCount() < 3) {
return ElementState.EXPANDED;
}
}
return ElementState.COLLAPSED;
default:
break;
}
}
return ElementState.NONE;
}
@Override
public int getCellState(Object colElement, Object rowElement, String cellText) {
int state = STATE_NONE;
boolean recordMode = controller.isRecordMode();
DBDAttributeBinding attr = (DBDAttributeBinding)(recordMode ? rowElement : colElement);
ResultSetRow row = (ResultSetRow)(recordMode ? colElement : rowElement);
Object value = controller.getModel().getCellValue(attr, row);
if (!CommonUtils.isEmpty(attr.getReferrers()) && !DBUtils.isNullValue(value)) {
state |= STATE_LINK;
} else {
String strValue = cellText != null ? cellText : attr.getValueHandler().getValueDisplayString(attr, value, DBDDisplayFormat.UI);
try {
new URL(strValue);
state |= STATE_HYPER_LINK;
} catch (MalformedURLException e) {
// Not a hyperlink
}
}
if (attr.isTransformed()) {
state |= STATE_TRANSFORMED;
}
return state;
}
@Override
public void dispose()
{
}
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput)
{
}
@Nullable
@Override
public Object getCellValue(Object colElement, Object rowElement, boolean formatString)
{
DBDAttributeBinding attr = (DBDAttributeBinding)(rowElement instanceof DBDAttributeBinding ? rowElement : colElement);
ResultSetRow row = (ResultSetRow)(colElement instanceof ResultSetRow ? colElement : rowElement);
int rowNum = row.getVisualNumber();
Object value = controller.getModel().getCellValue(attr, row);
boolean recordMode = controller.isRecordMode();
if (rowNum > 0 &&
rowNum == controller.getModel().getRowCount() - 1 &&
controller.getPreferenceStore().getBoolean(DBeaverPreferences.RESULT_SET_AUTO_FETCH_NEXT_SEGMENT) &&
(recordMode || spreadsheet.isRowVisible(rowNum)) && controller.isHasMoreData())
{
controller.readNextSegment();
}
if (formatString) {
if (recordMode) {
if (attr.getDataKind() == DBPDataKind.ARRAY && value instanceof DBDCollection) {
return "[" + ((DBDCollection) value).getItemCount() + "]";
} else if (attr.getDataKind() == DBPDataKind.STRUCT && value instanceof DBDComposite) {
return "[" + ((DBDComposite) value).getDataType().getName() + "]";
}
}
return attr.getValueRenderer().getValueDisplayString(
attr.getAttribute(),
value,
DBDDisplayFormat.UI);
} else {
return value;
}
}
@Nullable
@Override
public DBPImage getCellImage(Object colElement, Object rowElement)
{
// TODO: tired from cell icons. But maybe they make some sense - let's keep them commented
/*
if (!showCelIcons) {
return null;
}
Object cellValue = getCellValue(colElement, rowElement, false);
if (cellValue instanceof DBDContent || cellValue instanceof DBDReference) {
DBDAttributeBinding attr = (DBDAttributeBinding)(controller.isRecordMode() ? rowElement : colElement);
return DBUtils.getObjectImage(attr);
} else {
return null;
}
*/
return null;
}
@NotNull
@Override
public String getCellText(Object colElement, Object rowElement)
{
return String.valueOf(getCellValue(colElement, rowElement, true));
}
@Nullable
@Override
public Color getCellForeground(Object colElement, Object rowElement)
{
ResultSetRow row = (ResultSetRow) (!controller.isRecordMode() ? rowElement : colElement);
if (row.foreground != null) {
return row.foreground;
}
Object value = getCellValue(colElement, rowElement, false);
if (DBUtils.isNullValue(value)) {
return foregroundNull;
} else {
if (foregroundDefault == null) {
foregroundDefault = controller.getDefaultForeground();
}
return foregroundDefault;
}
}
@Nullable
@Override
public Color getCellBackground(Object colElement, Object rowElement)
{
boolean recordMode = controller.isRecordMode();
ResultSetRow row = (ResultSetRow) (!recordMode ? rowElement : colElement);
DBDAttributeBinding attribute = (DBDAttributeBinding)(!recordMode ? colElement : rowElement);
boolean odd = row.getVisualNumber() % 2 == 0;
if (row.getState() == ResultSetRow.STATE_ADDED) {
return backgroundAdded;
}
if (row.getState() == ResultSetRow.STATE_REMOVED) {
return backgroundDeleted;
}
if (row.changes != null && row.changes.containsKey(attribute)) {
return backgroundModified;
}
if (row.background != null) {
return row.background;
}
if (attribute.getValueHandler() instanceof DBDValueHandlerComposite) {
return backgroundReadOnly;
}
if (!recordMode && odd && showOddRows) {
return backgroundOdd;
}
if (backgroundNormal == null) {
backgroundNormal = controller.getDefaultBackground();
}
return backgroundNormal;
}
@Override
public void resetColors() {
backgroundNormal = null;
foregroundDefault = null;
}
}
private class GridLabelProvider implements IGridLabelProvider {
@Nullable
@Override
public Image getImage(Object element)
{
if (element instanceof DBDAttributeBinding/* && (!isRecordMode() || !model.isDynamicMetadata())*/) {
return DBeaverIcons.getImage(DBValueFormatting.getObjectImage(((DBDAttributeBinding) element).getAttribute()));
}
return null;
}
@Nullable
@Override
public Color getForeground(Object element) {
if (element == null) {
if (foregroundDefault == null) {
foregroundDefault = controller.getDefaultForeground();
}
return foregroundDefault;
}
return null;
}
@Nullable
@Override
public Color getBackground(Object element) {
if (backgroundNormal == null) {
backgroundNormal = controller.getDefaultBackground();
}
if (element == null) {
return backgroundNormal;
}
/*
ResultSetRow row = (ResultSetRow) (!recordMode ? element : curRow);
boolean odd = row != null && row.getVisualNumber() % 2 == 0;
if (!recordMode && odd && showOddRows) {
return backgroundOdd;
}
return backgroundNormal;
*/
return null;
}
@NotNull
@Override
public String getText(Object element)
{
if (element instanceof DBDAttributeBinding) {
DBDAttributeBinding attributeBinding = (DBDAttributeBinding) element;
if (CommonUtils.isEmpty(attributeBinding.getLabel())) {
return attributeBinding.getName();
} else {
return attributeBinding.getLabel();
}
} else {
if (!controller.isRecordMode()) {
return String.valueOf(((ResultSetRow)element).getVisualNumber() + 1);
} else {
return CoreMessages.controls_resultset_viewer_value;
}
}
}
@Nullable
@Override
public String getDescription(Object element) {
if (!getPreferenceStore().getBoolean(DBeaverPreferences.RESULT_SET_SHOW_DESCRIPTION)) {
return null;
}
if (element instanceof DBDAttributeBinding) {
return ((DBDAttributeBinding) element).getDescription();
} else {
return null;
}
}
@Nullable
@Override
public Font getFont(Object element)
{
if (element instanceof DBDAttributeBinding) {
DBDAttributeBinding attributeBinding = (DBDAttributeBinding) element;
DBDAttributeConstraint constraint = controller.getModel().getDataFilter().getConstraint(attributeBinding);
if (constraint != null && constraint.hasCondition()) {
return boldFont;
}
if (attributeBinding.isTransformed()) {
return italicFont;
}
}
return null;
}
@Nullable
@Override
public String getToolTipText(Object element)
{
if (element instanceof DBDAttributeBinding) {
DBDAttributeBinding attributeBinding = (DBDAttributeBinding) element;
final String name = attributeBinding.getName();
final String typeName = attributeBinding.getFullTypeName();
final String description = attributeBinding.getDescription();
return CommonUtils.isEmpty(description) ?
name + ": " + typeName :
name + ": " + typeName + "\n" + description;
}
return null;
}
}
/////////////////////////////
// Value controller
public class SpreadsheetValueController extends ResultSetValueController implements IMultiController {
public SpreadsheetValueController(@NotNull IResultSetController controller, @NotNull DBDAttributeBinding binding, @NotNull ResultSetRow row, @NotNull EditType editType, @Nullable Composite inlinePlaceholder) {
super(controller, binding, row, editType, inlinePlaceholder);
}
@Override
public Object getValue()
{
return spreadsheet.getContentProvider().getCellValue(curRow, binding, false);
}
@Override
public void closeInlineEditor()
{
spreadsheet.cancelInlineEditor();
}
@Override
public void nextInlineEditor(boolean next) {
spreadsheet.cancelInlineEditor();
int colOffset = next ? 1 : -1;
int rowOffset = 0;
//final int rowCount = spreadsheet.getItemCount();
final int colCount = spreadsheet.getColumnCount();
final GridPos curPosition = spreadsheet.getCursorPosition();
if (colOffset > 0 && curPosition.col + colOffset >= colCount) {
colOffset = -colCount;
rowOffset = 1;
} else if (colOffset < 0 && curPosition.col + colOffset < 0) {
colOffset = colCount;
rowOffset = -1;
}
spreadsheet.shiftCursor(colOffset, rowOffset, false);
openValueEditor(true);
}
@Override
public void updateValue(@Nullable Object value, boolean updatePresentation) {
super.updateValue(value, updatePresentation);
if (updatePresentation) {
spreadsheet.redrawGrid();
}
}
public void registerEditor(IValueEditorStandalone editor) {
openEditors.put(this, editor);
}
public void unregisterEditor(IValueEditorStandalone editor) {
openEditors.remove(this);
}
}
}