/*
* 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.dialogs.data;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.*;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.core.CoreMessages;
import org.jkiss.dbeaver.model.DBIcon;
import org.jkiss.dbeaver.model.DBPDataKind;
import org.jkiss.dbeaver.model.DBValueFormatting;
import org.jkiss.dbeaver.model.data.DBDContent;
import org.jkiss.dbeaver.model.data.DBDContentCached;
import org.jkiss.dbeaver.model.data.DBDDisplayFormat;
import org.jkiss.dbeaver.model.exec.DBCExecutionPurpose;
import org.jkiss.dbeaver.model.exec.DBCSession;
import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor;
import org.jkiss.dbeaver.model.struct.DBSTypedObject;
import org.jkiss.dbeaver.ui.DBeaverIcons;
import org.jkiss.dbeaver.ui.UIUtils;
import org.jkiss.dbeaver.ui.data.IValueController;
import org.jkiss.dbeaver.ui.data.editors.ReferenceValueEditor;
import org.jkiss.dbeaver.ui.editors.binary.BinaryContent;
import org.jkiss.dbeaver.ui.editors.binary.HexEditControl;
import org.jkiss.dbeaver.utils.ContentUtils;
import org.jkiss.dbeaver.utils.GeneralUtils;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
/**
* TextViewDialog
*/
public class TextViewDialog extends ValueViewDialog {
private static final Log log = Log.getLog(TextViewDialog.class);
//private static final int DEFAULT_MAX_SIZE = 100000;
private static final String VALUE_TYPE_SELECTOR = "string.value.type";
private StyledText textEdit;
private Label lengthLabel;
private HexEditControl hexEditControl;
private CTabFolder editorContainer;
private boolean dirty;
public TextViewDialog(IValueController valueController) {
super(valueController);
}
@Override
protected Control createDialogArea(Composite parent)
{
Composite dialogGroup = (Composite)super.createDialogArea(parent);
ReferenceValueEditor referenceValueEditor = new ReferenceValueEditor(getValueController(), this);
boolean isForeignKey = referenceValueEditor.isReferenceValue();
Label label = new Label(dialogGroup, SWT.NONE);
label.setText(CoreMessages.dialog_data_label_value);
boolean readOnly = getValueController().isReadOnly();
boolean useHex = !isForeignKey;
final DBSTypedObject valueType = getValueController().getValueType();
long maxSize = valueType.getMaxLength();
if (useHex) {
editorContainer = new CTabFolder(dialogGroup, SWT.FLAT | SWT.TOP);
editorContainer.setLayoutData(new GridData(GridData.FILL_BOTH));
lengthLabel = new Label(editorContainer, SWT.RIGHT);
lengthLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
editorContainer.setTopRight(lengthLabel, SWT.FILL);
}
int selectedType = 0;
if (getDialogSettings().get(VALUE_TYPE_SELECTOR) != null) {
selectedType = getDialogSettings().getInt(VALUE_TYPE_SELECTOR);
}
{
int style = SWT.NONE;
if (readOnly) {
style |= SWT.READ_ONLY;
}
if (useHex) {
style |= SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.WRAP;
} else {
// Use border only for plain text editor, otherwise tab folder's border will be used
style |= SWT.BORDER;
}
textEdit = new StyledText(useHex ? editorContainer : dialogGroup, style);
textEdit.setMargins(3, 3, 3, 3);
if (maxSize > 0 && valueType.getDataKind() == DBPDataKind.STRING) {
textEdit.setTextLimit((int) maxSize);
}
if (readOnly) {
//textEdit.setBackground(getShell().getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
}
GridData gd = new GridData(isForeignKey ? GridData.FILL_HORIZONTAL : GridData.FILL_BOTH);
gd.widthHint = 300;
if (!isForeignKey) {
gd.heightHint = 200;
gd.grabExcessVerticalSpace = true;
}
textEdit.setLayoutData(gd);
textEdit.setFocus();
textEdit.setEditable(!readOnly);
textEdit.addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent e)
{
dirty = true;
updateValueLength();
}
});
UIUtils.fillDefaultStyledTextContextMenu(textEdit);
if (useHex) {
CTabItem item = new CTabItem(editorContainer, SWT.NO_FOCUS);
item.setText("Text");
item.setImage(DBeaverIcons.getImage(DBIcon.TYPE_TEXT));
item.setControl(textEdit);
}
}
Point minSize = null;
if (useHex) {
hexEditControl = new HexEditControl(editorContainer, readOnly ? SWT.READ_ONLY : SWT.NONE, 6, 8);
GridData gd = new GridData(GridData.FILL_BOTH);
gd.heightHint = 200;
gd.minimumWidth = hexEditControl.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
hexEditControl.setLayoutData(gd);
minSize = hexEditControl.computeSize(SWT.DEFAULT, SWT.DEFAULT);
minSize.x += 50;
minSize.y += 50;
CTabItem item = new CTabItem(editorContainer, SWT.NO_FOCUS);
item.setText("Hex");
item.setImage(DBeaverIcons.getImage(DBIcon.TYPE_BINARY));
item.setControl(hexEditControl);
if (selectedType >= editorContainer.getItemCount()) {
selectedType = 0;
}
editorContainer.setSelection(selectedType);
editorContainer.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event)
{
getDialogSettings().put(VALUE_TYPE_SELECTOR, editorContainer.getSelectionIndex());
}
});
hexEditControl.addListener(SWT.Modify, new Listener() {
@Override
public void handleEvent(Event event) {
dirty = true;
}
});
updateValueLength();
}
primeEditorValue(getValueController().getValue());
if (isForeignKey) {
referenceValueEditor.createEditorSelector(dialogGroup);
}
if (minSize != null) {
// Set default size as minimum
getShell().setMinimumSize(minSize);
}
return dialogGroup;
}
private byte[] getBinaryContent()
{
BinaryContent content = hexEditControl.getContent();
ByteBuffer buffer = ByteBuffer.allocate((int) content.length());
try {
content.get(buffer, 0);
} catch (IOException e) {
log.error(e);
}
return buffer.array();
}
private String getBinaryString()
{
byte[] bytes = getBinaryContent();
int length = bytes.length;
String stringValue;
try {
stringValue = new String(
bytes, 0, length,
getDefaultCharset());
} catch (UnsupportedEncodingException e) {
log.error(e);
stringValue = new String(bytes);
}
return stringValue;
}
private void setBinaryContent(String stringValue)
{
String charset = getDefaultCharset();
byte[] bytes;
try {
bytes = stringValue.getBytes(charset);
} catch (UnsupportedEncodingException e) {
log.error(e);
bytes = stringValue.getBytes(Charset.defaultCharset());
}
hexEditControl.setContent(bytes, charset);
}
private String getDefaultCharset() {
return DBValueFormatting.getDefaultBinaryFileEncoding(getValueController().getExecutionContext().getDataSource());
}
@Override
public Object extractEditorValue()
{
Object prevValue = getValueController().getValue();
Object rawValue;
if (prevValue instanceof DBDContent) {
if (ContentUtils.isTextContent((DBDContent) prevValue)) {
rawValue = isTextEditorActive() ? textEdit.getText() : getBinaryString();
} else {
rawValue = isTextEditorActive() ? GeneralUtils.convertToBytes(textEdit.getText()) : getBinaryContent();
}
} else {
if (isTextEditorActive()) {
rawValue = textEdit.getText();
} else {
rawValue = getBinaryString();
}
}
try (DBCSession session = getValueController().getExecutionContext().openSession(new VoidProgressMonitor(), DBCExecutionPurpose.UTIL, "Make text value from editor")) {
return getValueController().getValueHandler().getValueFromObject(
session,
getValueController().getValueType(),
rawValue,
false);
} catch (Exception e) {
UIUtils.showErrorDialog(getShell(), "Extract editor value", "Can't extract editor value", e);
return null;
}
}
@Override
public Control getControl()
{
if (isTextEditorActive()) {
return textEdit;
} else {
return hexEditControl;
}
}
private boolean isTextEditorActive()
{
return editorContainer == null || editorContainer.getSelectionIndex() == 0;
}
private void updateValueLength()
{
if (lengthLabel != null) {
long maxSize = getValueController().getValueType().getMaxLength();
long length = textEdit.getText().length();
lengthLabel.setText("Length: " + length + (maxSize > 0 ? " [" + maxSize + "]" : ""));
}
}
@Override
public void primeEditorValue(@Nullable Object value)
{
if (value instanceof DBDContentCached) {
value = ((DBDContentCached) value).getCachedValue();
}
if (value instanceof byte[]) {
// Binary
byte[] bytes = (byte[]) value;
textEdit.setText(GeneralUtils.convertToString(bytes, 0, bytes.length));
if (hexEditControl != null) {
hexEditControl.setContent(bytes, getDefaultCharset());
}
} else {
// Should be string
final IValueController valueController = getValueController();
final String strValue = valueController.getValueHandler().getValueDisplayString(valueController.getValueType(), value, DBDDisplayFormat.EDIT);
textEdit.setText(strValue);
if (hexEditControl != null) {
setBinaryContent(strValue);
}
}
}
@Override
public boolean isDirty() {
return dirty;
}
@Override
public void setDirty(boolean dirty) {
this.dirty = dirty;
}
}