/*
* 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;
import org.eclipse.jface.dialogs.ControlEnableState;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.viewers.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.*;
import org.eclipse.ui.ISharedImages;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.core.CoreMessages;
import org.jkiss.dbeaver.core.DBeaverUI;
import org.jkiss.dbeaver.model.DBIcon;
import org.jkiss.dbeaver.model.DBValueFormatting;
import org.jkiss.dbeaver.model.data.DBDAttributeBinding;
import org.jkiss.dbeaver.model.data.DBDAttributeConstraint;
import org.jkiss.dbeaver.model.data.DBDDataFilter;
import org.jkiss.dbeaver.model.exec.DBCExecutionContext;
import org.jkiss.dbeaver.model.sql.SQLUtils;
import org.jkiss.dbeaver.ui.DBeaverIcons;
import org.jkiss.dbeaver.ui.IHelpContextIds;
import org.jkiss.dbeaver.ui.UIIcon;
import org.jkiss.dbeaver.ui.UIUtils;
import org.jkiss.dbeaver.ui.controls.CustomTreeEditor;
import org.jkiss.dbeaver.ui.controls.TreeContentProvider;
import org.jkiss.dbeaver.ui.dialogs.HelpEnabledDialog;
import org.jkiss.utils.CommonUtils;
import java.util.*;
import java.util.List;
class FilterSettingsDialog extends HelpEnabledDialog {
private static final String DIALOG_ID = "DBeaver.FilterSettingsDialog";//$NON-NLS-1$
public final Comparator<DBDAttributeBinding> POSITION_SORTER = new Comparator<DBDAttributeBinding>() {
@Override
public int compare(DBDAttributeBinding o1, DBDAttributeBinding o2) {
final DBDAttributeConstraint c1 = getBindingConstraint(o1);
final DBDAttributeConstraint c2 = getBindingConstraint(o2);
return c1.getVisualPosition() - c2.getVisualPosition();
}
};
public final Comparator<DBDAttributeBinding> ALPHA_SORTER = new Comparator<DBDAttributeBinding>() {
@Override
public int compare(DBDAttributeBinding o1, DBDAttributeBinding o2) {
return o1.getName().compareTo(o2.getName());
}
};
private final ResultSetViewer resultSetViewer;
private final List<DBDAttributeBinding> attributes;
private CheckboxTreeViewer columnsViewer;
private DBDDataFilter dataFilter;
private Text whereText;
private Text orderText;
// Keep constraints in a copy because we use this list as table viewer model
private java.util.List<DBDAttributeConstraint> constraints;
private ToolItem moveTopButton;
private ToolItem moveUpButton;
private ToolItem moveDownButton;
private ToolItem moveBottomButton;
private Comparator<DBDAttributeBinding> activeSorter = POSITION_SORTER;
public FilterSettingsDialog(ResultSetViewer resultSetViewer)
{
super(resultSetViewer.getControl().getShell(), IHelpContextIds.CTX_DATA_FILTER);
this.resultSetViewer = resultSetViewer;
this.dataFilter = new DBDDataFilter(resultSetViewer.getModel().getDataFilter());
this.constraints = new ArrayList<>(dataFilter.getConstraints());
this.attributes = Arrays.asList(resultSetViewer.getModel().getAttributes());
}
@Override
protected IDialogSettings getDialogBoundsSettings()
{
return UIUtils.getDialogSettings(DIALOG_ID);
}
@Override
protected Control createDialogArea(Composite parent)
{
getShell().setText(CoreMessages.controls_resultset_filter_title);
getShell().setImage(DBeaverIcons.getImage(UIIcon.FILTER));
Composite composite = (Composite) super.createDialogArea(parent);
TabFolder tabFolder = new TabFolder(composite, SWT.NONE);
tabFolder.setLayoutData(new GridData(GridData.FILL_BOTH));
TreeColumn criteriaColumn;
{
Composite columnsGroup = UIUtils.createPlaceholder(tabFolder, 1);
columnsViewer = new CheckboxTreeViewer(columnsGroup, SWT.SINGLE | SWT.FULL_SELECTION | SWT.CHECK);
columnsViewer.setContentProvider(new TreeContentProvider() {
@Override
public Object[] getChildren(Object parentElement) {
final java.util.List<DBDAttributeBinding> nestedBindings = ((DBDAttributeBinding) parentElement).getNestedBindings();
if (nestedBindings == null || nestedBindings.isEmpty()) {
return null;
}
final DBDAttributeBinding[] res = nestedBindings.toArray(new DBDAttributeBinding[nestedBindings.size()]);
Arrays.sort(res, activeSorter);
return res;
}
@Override
public boolean hasChildren(Object element) {
final java.util.List<DBDAttributeBinding> nestedBindings = ((DBDAttributeBinding) element).getNestedBindings();
return nestedBindings != null && !nestedBindings.isEmpty();
}
});
columnsViewer.setLabelProvider(new ColumnLabelProvider());
columnsViewer.setCheckStateProvider(new CheckStateProvider());
final Tree columnsTree = columnsViewer.getTree();
GridData gd = new GridData(GridData.FILL_BOTH);
gd.heightHint = 300;
//gd.heightHint = 300;
columnsTree.setLayoutData(gd);
columnsTree.setHeaderVisible(true);
columnsTree.setLinesVisible(true);
UIUtils.createTreeColumn(columnsTree, SWT.LEFT, CoreMessages.controls_resultset_filter_column_name);
UIUtils.createTreeColumn(columnsTree, SWT.LEFT, "#");
UIUtils.createTreeColumn(columnsTree, SWT.LEFT, CoreMessages.controls_resultset_filter_column_order);
criteriaColumn = UIUtils.createTreeColumn(columnsTree, SWT.LEFT, CoreMessages.controls_resultset_filter_column_criteria);
new CustomTreeEditor(columnsTree) {
{
firstTraverseIndex = 3;
lastTraverseIndex = 3;
}
@Override
protected Control createEditor(Tree table, int index, TreeItem item) {
if (index == 2) {
toggleColumnOrder(item);
return null;
} else if (index == 3 && resultSetViewer.supportsDataFilter()) {
Text text = new Text(columnsTree, SWT.BORDER);
text.setText(item.getText(index));
text.selectAll();
return text;
}
return null;
}
@Override
protected void saveEditorValue(Control control, int index, TreeItem item) {
Text text = (Text) control;
String criteria = text.getText().trim();
DBDAttributeConstraint constraint = getBindingConstraint((DBDAttributeBinding) item.getData());
if (CommonUtils.isEmpty(criteria)) {
constraint.setCriteria(null);
} else {
constraint.setCriteria(criteria);
}
item.setText(3, criteria);
}
private void toggleColumnOrder(TreeItem item)
{
DBDAttributeConstraint constraint = getBindingConstraint((DBDAttributeBinding) item.getData());
if (constraint.getOrderPosition() == 0) {
// Add new ordered column
constraint.setOrderPosition(dataFilter.getMaxOrderingPosition() + 1);
constraint.setOrderDescending(false);
} else if (!constraint.isOrderDescending()) {
constraint.setOrderDescending(true);
} else {
// Remove ordered column
/*
for (DBDAttributeConstraint con2 : dataFilter.getConstraints()) {
if (con2.getOrderPosition() > constraint.getOrderPosition()) {
con2.setOrderPosition(con2.getOrderPosition() - 1);
}
}
*/
constraint.setOrderPosition(0);
constraint.setOrderDescending(false);
}
columnsViewer.refresh();
}
};
columnsViewer.addCheckStateListener(new ICheckStateListener() {
@Override
public void checkStateChanged(CheckStateChangedEvent event) {
DBDAttributeConstraint constraint = getBindingConstraint((DBDAttributeBinding) event.getElement());
constraint.setVisible(event.getChecked());
}
});
{
ToolBar toolbar = new ToolBar(columnsGroup, SWT.HORIZONTAL | SWT.RIGHT);
gd = new GridData(GridData.FILL_HORIZONTAL);
gd.verticalIndent = 3;
toolbar.setLayoutData(gd);
toolbar.setLayout(new FillLayout());
moveTopButton = createToolItem(toolbar, "Move to top", UIIcon.ARROW_TOP, new Runnable() {
@Override
public void run() {
int selectionIndex = getSelectionIndex(columnsViewer.getTree());
moveColumn(selectionIndex, 0);
}
});
moveTopButton.setEnabled(false);
moveUpButton = createToolItem(toolbar, "Move up", UIIcon.ARROW_UP, new Runnable() {
@Override
public void run() {
int selectionIndex = getSelectionIndex(columnsViewer.getTree());
moveColumn(selectionIndex, selectionIndex - 1);
}
});
moveUpButton.setEnabled(false);
moveDownButton = createToolItem(toolbar, "Move down", UIIcon.ARROW_DOWN, new Runnable() {
@Override
public void run() {
int selectionIndex = getSelectionIndex(columnsViewer.getTree());
moveColumn(selectionIndex, selectionIndex + 1);
}
});
moveDownButton.setEnabled(false);
moveBottomButton = createToolItem(toolbar, "Move to bottom", UIIcon.ARROW_BOTTOM, new Runnable() {
@Override
public void run() {
int selectionIndex = getSelectionIndex(columnsViewer.getTree());
moveColumn(selectionIndex, getItemsCount() - 1);
}
});
moveBottomButton.setEnabled(false);
UIUtils.createToolBarSeparator(toolbar, SWT.VERTICAL);
createToolItem(toolbar, "Sort", UIIcon.SORT, new Runnable() {
@Override
public void run() {
Collections.sort(attributes, ALPHA_SORTER);
for (int i = 0; i < attributes.size(); i++) {
final DBDAttributeConstraint constraint = getBindingConstraint(attributes.get(i));
constraint.setVisualPosition(i);
}
columnsViewer.refresh();
}
});
UIUtils.createToolBarSeparator(toolbar, SWT.VERTICAL);
ToolItem showAllButton = createToolItem(toolbar, "Show All", null, new Runnable() {
@Override
public void run() {
for (DBDAttributeConstraint constraint : constraints) {
constraint.setVisible(true);
}
columnsViewer.refresh();
}
});
showAllButton.setImage(UIUtils.getShardImage(ISharedImages.IMG_ETOOL_DEF_PERSPECTIVE));
ToolItem showNoneButton = createToolItem(toolbar, "Show None", null, new Runnable() {
@Override
public void run() {
for (DBDAttributeConstraint constraint : constraints) {
constraint.setVisible(false);
}
columnsViewer.refresh();
}
});
showNoneButton.setImage(UIUtils.getShardImage(ISharedImages.IMG_ELCL_REMOVEALL));
createToolItem(toolbar, "Reset", UIIcon.REFRESH, new Runnable() {
@Override
public void run() {
dataFilter.reset();
constraints = new ArrayList<>(dataFilter.getConstraints());
refreshData();
//columnsViewer.refresh();
orderText.setText(""); //$NON-NLS-1$
whereText.setText(""); //$NON-NLS-1$
}
});
columnsViewer.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
int selectionIndex = getSelectionIndex(columnsViewer.getTree());
moveTopButton.setEnabled(selectionIndex > 0);
moveUpButton.setEnabled(selectionIndex > 0);
moveDownButton.setEnabled(selectionIndex >= 0 && selectionIndex < getItemsCount() - 1);
moveBottomButton.setEnabled(selectionIndex >= 0 && selectionIndex < getItemsCount() - 1);
}
});
}
TabItem libsTab = new TabItem(tabFolder, SWT.NONE);
libsTab.setText(CoreMessages.controls_resultset_filter_group_columns);
libsTab.setToolTipText("Set criteria and order for individual column(s)");
libsTab.setControl(columnsGroup);
}
createCustomFilters(tabFolder);
// Fill columns
columnsViewer.setInput(attributes);
refreshData();
// Pack UI
DBeaverUI.asyncExec(new Runnable() {
@Override
public void run() {
UIUtils.packColumns(columnsViewer.getTree());
}
});
//UIUtils.packColumns(filterViewer.getTable());
if (criteriaColumn.getWidth() < 200) {
criteriaColumn.setWidth(200);
}
if (!resultSetViewer.supportsDataFilter()) {
Label warnLabel = new Label(composite, SWT.NONE);
warnLabel.setText(CoreMessages.controls_resultset_filter_warning_custom_order_disabled);
warnLabel.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_RED));
}
return parent;
}
private int getItemsCount() {
return columnsViewer.getTree().getItemCount();
}
private void refreshData() {
Collections.sort(attributes, activeSorter);
columnsViewer.refresh();
columnsViewer.expandAll();
}
private int getSelectionIndex(Tree tree) {
final TreeItem[] selection = tree.getSelection();
if (selection.length == 0) {
return 0;
}
return tree.indexOf(selection[0]);
}
private void moveColumn(int curIndex, int newIndex)
{
final DBDAttributeConstraint c1 = getBindingConstraint((DBDAttributeBinding) columnsViewer.getTree().getItem(curIndex).getData());
final DBDAttributeConstraint c2 = getBindingConstraint((DBDAttributeBinding) columnsViewer.getTree().getItem(newIndex).getData());
final int vp2 = c2.getVisualPosition();
c2.setVisualPosition(c1.getVisualPosition());
c1.setVisualPosition(vp2);
refreshData();
moveTopButton.setEnabled(newIndex > 0);
moveUpButton.setEnabled(newIndex > 0);
moveDownButton.setEnabled(newIndex < getItemsCount() - 1);
moveBottomButton.setEnabled(newIndex < getItemsCount() - 1);
}
private void createCustomFilters(TabFolder tabFolder)
{
Composite filterGroup = new Composite(tabFolder, SWT.NONE);
filterGroup.setLayoutData(new GridData(GridData.FILL_BOTH));
filterGroup.setLayout(new GridLayout(1, false));
UIUtils.createControlLabel(filterGroup, CoreMessages.controls_resultset_filter_label_where);
whereText = new Text(filterGroup, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL);
whereText.setLayoutData(new GridData(GridData.FILL_BOTH));
if (dataFilter.getWhere() != null) {
whereText.setText(dataFilter.getWhere());
}
UIUtils.createControlLabel(filterGroup, CoreMessages.controls_resultset_filter_label_orderby);
orderText = new Text(filterGroup, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL);
orderText.setLayoutData(new GridData(GridData.FILL_BOTH));
if (dataFilter.getOrder() != null) {
orderText.setText(dataFilter.getOrder());
}
if (!resultSetViewer.supportsDataFilter()) {
filterGroup.setEnabled(false);
ControlEnableState.disable(filterGroup);
}
TabItem libsTab = new TabItem(tabFolder, SWT.NONE);
libsTab.setText(CoreMessages.controls_resultset_filter_group_custom);
libsTab.setToolTipText("Set custom criteria and order for whole query");
libsTab.setControl(filterGroup);
}
@Override
public int open()
{
return super.open();
}
@Override
protected void createButtonsForButtonBar(Composite parent)
{
createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
//createButton(parent, IDialogConstants.ABORT_ID, CoreMessages.controls_resultset_filter_button_reset, false);
}
@Override
protected void buttonPressed(int buttonId)
{
super.buttonPressed(buttonId);
}
@Override
protected void okPressed()
{
boolean hasVisibleColumns = false;
for (DBDAttributeConstraint constraint : dataFilter.getConstraints()) {
// Set correct visible position
// constraint.setVisualPosition(this.constraints.indexOf(constraint));
if (constraint.isVisible()) {
hasVisibleColumns = true;
}
}
if (!hasVisibleColumns) {
UIUtils.showMessageBox(getShell(), "Bad filter", "You have to set at least one column visible", SWT.ICON_WARNING);
return;
}
if (!CommonUtils.isEmpty(orderText.getText())) {
dataFilter.setOrder(orderText.getText());
} else {
dataFilter.setOrder(null);
}
if (!CommonUtils.isEmpty(whereText.getText())) {
dataFilter.setWhere(whereText.getText());
} else {
dataFilter.setWhere(null);
}
boolean filtersChanged = true;
if (dataFilter.equalFilters(resultSetViewer.getModel().getDataFilter(), true)) {
// Only attribute visibility was changed
filtersChanged = false;
}
resultSetViewer.setDataFilter(
dataFilter,
filtersChanged);
super.okPressed();
}
class ColumnLabelProvider extends LabelProvider implements ITableLabelProvider
{
@Nullable
@Override
public Image getColumnImage(Object element, int columnIndex)
{
DBDAttributeBinding binding = (DBDAttributeBinding) element;
if (columnIndex == 0) {
return DBeaverIcons.getImage(
DBValueFormatting.getObjectImage(binding.getMetaAttribute()));
}
if (columnIndex == 2) {
DBDAttributeConstraint constraint = getBindingConstraint(binding);
if (constraint.getOrderPosition() > 0) {
return DBeaverIcons.getImage(constraint.isOrderDescending() ? UIIcon.SORT_DECREASE : UIIcon.SORT_INCREASE);
}
}
return null;
}
@Override
public String getColumnText(Object element, int columnIndex)
{
DBDAttributeBinding binding = (DBDAttributeBinding) element;
DBDAttributeConstraint constraint = getBindingConstraint(binding);
switch (columnIndex) {
case 0: return constraint.getAttribute().getName();
case 1: return String.valueOf(constraint.getOriginalVisualPosition() + 1);
case 2: {
int orderPosition = constraint.getOrderPosition();
if (orderPosition > 0) {
return String.valueOf(orderPosition);
}
return ""; //$NON-NLS-1$
}
case 3: {
DBCExecutionContext executionContext = resultSetViewer.getExecutionContext();
if (executionContext != null) {
String condition = SQLUtils.getConstraintCondition(executionContext.getDataSource(), constraint, true);
if (condition != null) {
return condition;
}
}
return ""; //$NON-NLS-1$
}
default: return ""; //$NON-NLS-1$
}
}
}
@NotNull
private DBDAttributeConstraint getBindingConstraint(DBDAttributeBinding binding) {
for (DBDAttributeConstraint constraint : constraints) {
if (constraint.matches(binding, true)) {
return constraint;
}
}
throw new IllegalStateException("Can't find constraint for binding " + binding);
}
class CheckStateProvider implements ICheckStateProvider {
@Override
public boolean isChecked(Object element)
{
return getBindingConstraint(((DBDAttributeBinding)element)).isVisible();
}
@Override
public boolean isGrayed(Object element)
{
return false;
}
}
public static ToolItem createToolItem(ToolBar toolBar, String text, DBIcon icon, final Runnable action)
{
ToolItem item = new ToolItem(toolBar, SWT.PUSH);
if (icon != null) {
item.setImage(DBeaverIcons.getImage(icon));
}
if (text != null) {
//item.setText(text);
item.setToolTipText(text);
}
item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e)
{
action.run();
}
});
return item;
}
}