/*
* 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.panel;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IContributionManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
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.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.DBeaverPreferences;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.core.DBeaverCore;
import org.jkiss.dbeaver.model.DBPImage;
import org.jkiss.dbeaver.model.data.DBDAttributeBinding;
import org.jkiss.dbeaver.model.data.DBDValue;
import org.jkiss.dbeaver.ui.ActionUtils;
import org.jkiss.dbeaver.ui.DBeaverIcons;
import org.jkiss.dbeaver.ui.UIIcon;
import org.jkiss.dbeaver.ui.UIUtils;
import org.jkiss.dbeaver.ui.controls.resultset.*;
import org.jkiss.dbeaver.ui.data.IValueController;
import org.jkiss.dbeaver.ui.data.IValueEditor;
import org.jkiss.dbeaver.ui.data.IValueManager;
import org.jkiss.dbeaver.ui.data.editors.BaseValueEditor;
import org.jkiss.dbeaver.ui.data.editors.ReferenceValueEditor;
import org.jkiss.utils.CommonUtils;
/**
* RSV value view panel
*/
public class ViewValuePanel implements IResultSetPanel, IAdaptable {
private static final Log log = Log.getLog(ViewValuePanel.class);
public static final String PANEL_ID = "value-view";
public static final String SETTINGS_SECTION = "panel-" + PANEL_ID;
private static final String VALUE_VIEW_CONTROL_ID = "org.jkiss.dbeaver.ui.resultset.panel.valueView";
private IResultSetPresentation presentation;
private Composite viewPlaceholder;
private ResultSetValueController previewController;
private IValueEditor valueEditor;
private ReferenceValueEditor referenceValueEditor;
private volatile boolean valueSaving;
private IValueManager valueManager;
public static IDialogSettings getPanelSettings() {
return ResultSetUtils.getViewerSettings(SETTINGS_SECTION);
}
public ViewValuePanel() {
}
@Override
public String getPanelTitle() {
return "Value";
}
@Override
public DBPImage getPanelImage() {
return UIIcon.PANEL_VALUE;
}
@Override
public String getPanelDescription() {
return "Value view/edit";
}
@Override
public Control createContents(IResultSetPresentation presentation, Composite parent) {
this.presentation = presentation;
viewPlaceholder = new Composite(parent, SWT.NONE);
viewPlaceholder.setLayout(new FillLayout());
viewPlaceholder.addPaintListener(new PaintListener() {
@Override
public void paintControl(PaintEvent e) {
if (viewPlaceholder.getChildren().length == 0) {
String hidePanelCmd = ActionUtils.findCommandDescription(
ResultSetCommandHandler.CMD_TOGGLE_PANELS,
ViewValuePanel.this.presentation.getController().getSite(),
true);
UIUtils.drawMessageOverControl(viewPlaceholder, e, "Select a cell to view/edit value", 0);
UIUtils.drawMessageOverControl(viewPlaceholder, e, "Press " + hidePanelCmd + " to hide this panel", 20);
}
}
});
/*
addTraverseListener(new TraverseListener() {
@Override
public void keyTraversed(TraverseEvent e) {
if (e.detail == SWT.TRAVERSE_ESCAPE) {
hidePanel();
e.doit = false;
}
}
});
*/
if (this.presentation instanceof ISelectionProvider) {
final ISelectionProvider selectionProvider = (ISelectionProvider) this.presentation;
final ISelectionChangedListener selectionListener = new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
if (ViewValuePanel.this.presentation.getController().getVisiblePanel() == ViewValuePanel.this) {
refreshValue(false);
}
}
};
selectionProvider.addSelectionChangedListener(selectionListener);
viewPlaceholder.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
selectionProvider.removeSelectionChangedListener(selectionListener);
}
});
}
return viewPlaceholder;
}
@Override
public void activatePanel() {
refreshValue(false);
}
@Override
public void deactivatePanel() {
// Dispose panel control
if (viewPlaceholder != null && !viewPlaceholder.isDisposed()) {
viewPlaceholder.dispose();
viewPlaceholder = null;
}
}
@Override
public void refresh(boolean force) {
refreshValue(force);
}
@Override
public void contributeActions(ToolBarManager manager) {
fillToolBar(manager);
}
private void refreshValue(boolean force) {
DBDAttributeBinding attr = presentation.getCurrentAttribute();
ResultSetRow row = presentation.getController().getCurrentRow();
if (attr == null || row == null) {
clearValue();
return;
}
boolean updateActions;
if (previewController == null) {
previewController = new ResultSetValueController(
presentation.getController(),
attr,
row,
IValueController.EditType.PANEL,
viewPlaceholder)
{
@Override
public void updateValue(@Nullable Object value, boolean updatePresentation) {
valueSaving = true;
try {
super.updateValue(value, updatePresentation);
} finally {
valueSaving = false;
}
presentation.updateValueView();
}
};
updateActions = true;
force = true;
} else {
updateActions = force = (force || previewController.getBinding() != attr);
previewController.setCurRow(row);
previewController.setBinding(attr);
}
viewValue(force);
if (updateActions) {
presentation.getController().updatePanelActions();
}
}
private void viewValue(boolean forceRefresh)
{
if (valueSaving) {
return;
}
if (valueManager == null || valueEditor == null) {
forceRefresh = true;
}
if (forceRefresh) {
cleanupPanel();
// Create a new one
valueManager = previewController.getValueManager();
try {
valueEditor = valueManager.createEditor(previewController);
} catch (Throwable e) {
UIUtils.showErrorDialog(viewPlaceholder.getShell(), "Value preview", "Can't create value viewer", e);
return;
}
if (valueEditor != null) {
try {
valueEditor.createControl();
} catch (Exception e) {
log.error(e);
}
Control control = valueEditor.getControl();
if (control != null) {
UIUtils.addFocusTracker(presentation.getController().getSite(), VALUE_VIEW_CONTROL_ID, control);
presentation.getController().lockActionsByFocus(control);
}
referenceValueEditor = new ReferenceValueEditor(previewController, valueEditor);
if (referenceValueEditor.isReferenceValue()) {
GridLayout gl = new GridLayout(1, false);
viewPlaceholder.setLayout(gl);
valueEditor.getControl().setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
referenceValueEditor.createEditorSelector(viewPlaceholder);
} else {
viewPlaceholder.setLayout(new FillLayout());
}
} else {
final Composite placeholder = UIUtils.createPlaceholder(viewPlaceholder, 1);
placeholder.setBackground(placeholder.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
placeholder.addPaintListener(new PaintListener() {
@Override
public void paintControl(PaintEvent e)
{
Rectangle bounds = placeholder.getBounds();
String message = "No editor for [" + previewController.getValueType().getTypeName() + "]";
Point ext = e.gc.textExtent(message);
e.gc.drawText(message, (bounds.width - ext.x) / 2, bounds.height / 3 + 20);
}
});
referenceValueEditor = null;
}
viewPlaceholder.layout();
}
if (valueEditor instanceof BaseValueEditor) {
((BaseValueEditor) valueEditor).setAutoSaveEnabled(false);
}
if (valueEditor != null) {
try {
Object newValue = previewController.getValue();
if (newValue instanceof DBDValue) {
// Do not check for difference
valueEditor.primeEditorValue(newValue);
} else {
Object oldValue = null;
try {
if (previewController.getExecutionContext() != null) {
oldValue = valueEditor.extractEditorValue();
}
} catch (Throwable e) {
// Some error extracting current value
// This may happen if we were disconnected
}
if (!CommonUtils.equalObjects(oldValue, newValue)) {
valueEditor.primeEditorValue(newValue);
}
}
} catch (DBException e) {
log.error(e);
}
valueEditor.setDirty(false);
}
if (valueEditor instanceof BaseValueEditor) {
((BaseValueEditor) valueEditor).setAutoSaveEnabled(true);
}
}
public void saveValue()
{
if (valueEditor == null) {
return;
}
try {
valueSaving = true;
Object newValue = valueEditor.extractEditorValue();
previewController.updateValue(newValue, true);
presentation.updateValueView();
} catch (Exception e) {
UIUtils.showErrorDialog(null, "Value save", "Can't save edited value", e);
} finally {
valueSaving = false;
}
}
public void clearValue()
{
cleanupPanel();
valueManager = null;
valueEditor = null;
presentation.getController().updateEditControls();
viewPlaceholder.layout();
}
private void cleanupPanel()
{
// Cleanup previous viewer
for (Control child : viewPlaceholder.getChildren()) {
child.dispose();
}
}
private void fillToolBar(final IContributionManager contributionManager)
{
contributionManager.add(new Separator());
//contributionManager.add(new Separator());
if (valueManager != null) {
try {
valueManager.contributeActions(contributionManager, previewController, valueEditor);
} catch (Exception e) {
log.error("Can't contribute value manager actions", e);
}
}
contributionManager.add(
ActionUtils.makeCommandContribution(presentation.getController().getSite(), ValueViewCommandHandler.CMD_SAVE_VALUE));
contributionManager.add(
new Action("Auto-save value", Action.AS_CHECK_BOX) {
{
setImageDescriptor(DBeaverIcons.getImageDescriptor(UIIcon.LINK_TO_EDITOR));
}
@Override
public boolean isChecked() {
return DBeaverCore.getGlobalPreferenceStore().getBoolean(DBeaverPreferences.RS_EDIT_AUTO_UPDATE_VALUE);
}
@Override
public void run() {
boolean newValue = !isChecked();
DBeaverCore.getGlobalPreferenceStore().setValue(DBeaverPreferences.RS_EDIT_AUTO_UPDATE_VALUE, newValue);
presentation.getController().updatePanelActions();
}
});
}
@Override
public <T> T getAdapter(Class<T> adapter) {
if (valueEditor != null) {
if (adapter.isAssignableFrom(valueEditor.getClass())) {
return adapter.cast(valueEditor);
}
if (valueEditor instanceof IAdaptable) {
return ((IAdaptable) valueEditor).getAdapter(adapter);
}
}
return null;
}
}