/*
* 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;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.viewers.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
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.*;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.progress.UIJob;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.core.DBeaverUI;
import org.jkiss.dbeaver.model.DBConstants;
import org.jkiss.dbeaver.model.DBIcon;
import org.jkiss.dbeaver.model.DBPDataSourceContainer;
import org.jkiss.dbeaver.ui.DBeaverIcons;
import org.jkiss.dbeaver.ui.TextUtils;
import org.jkiss.dbeaver.ui.UIIcon;
import org.jkiss.dbeaver.ui.UIUtils;
import org.jkiss.dbeaver.ui.actions.navigator.NavigatorHandlerObjectOpen;
import org.jkiss.dbeaver.ui.resources.ResourceUtils;
import org.jkiss.dbeaver.ui.resources.ResourceUtils.ResourceInfo;
import org.jkiss.utils.CommonUtils;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
/**
* Script selector panel (shell)
*/
public class ScriptSelectorPanel {
private static final Log log = Log.getLog(ScriptSelectorPanel.class);
public static final String CONFIG_BOUNDS_PARAM = "bounds";
private final IWorkbenchWindow workbenchWindow;
private final Shell popup;
private final Text patternText;
private final TreeViewer scriptViewer;
private final Button newButton;
private volatile FilterJob filterJob;
public ScriptSelectorPanel(@NotNull final IWorkbenchWindow workbenchWindow, @NotNull final DBPDataSourceContainer[] containers, @NotNull final IFolder rootFolder) {
this.workbenchWindow = workbenchWindow;
Shell parent = this.workbenchWindow.getShell();
popup = new Shell(parent, SWT.RESIZE | SWT.TITLE | SWT.CLOSE);
if (containers.length == 1) {
popup.setText("Choose SQL script for '" + containers[0].getName() + "'");
popup.setImage(DBeaverIcons.getImage(containers[0].getDriver().getIcon()));
} else {
popup.setText("Choose SQL script");
}
popup.setLayout(new FillLayout());
Rectangle bounds = new Rectangle(100, 100, 500, 200);
final String boundsStr = getBoundsSettings().get(CONFIG_BOUNDS_PARAM);
if (boundsStr != null && !boundsStr.isEmpty()) {
final String[] bc = boundsStr.split(",");
try {
bounds = new Rectangle(
Integer.parseInt(bc[0]),
Integer.parseInt(bc[1]),
Integer.parseInt(bc[2]),
Integer.parseInt(bc[3]));
} catch (NumberFormatException e) {
log.warn(e);
}
}
popup.setBounds(bounds);
Composite composite = new Composite(popup, SWT.NONE);
final GridLayout gl = new GridLayout(2, false);
//gl.marginHeight = 0;
//gl.marginWidth = 0;
composite.setLayout(gl);
patternText = new Text(composite, SWT.NONE);
patternText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
//patternText.setForeground(fg);
//patternText.setBackground(bg);
patternText.addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
if (filterJob != null) {
return;
}
filterJob = new FilterJob();
filterJob.schedule(250);
}
});
final Color fg = patternText.getForeground();//parent.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND);
final Color bg = patternText.getBackground();//parent.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND);
composite.setForeground(fg);
composite.setBackground(bg);
newButton = new Button(composite, SWT.PUSH | SWT.FLAT);
newButton.setText("&New Script");
newButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
popup.dispose();
IFile scriptFile;
try {
scriptFile = ResourceUtils.createNewScript(rootFolder.getProject(), rootFolder, containers.length == 0 ? null : containers[0]);
NavigatorHandlerObjectOpen.openResource(scriptFile, workbenchWindow);
} catch (CoreException ex) {
log.error(ex);
}
}
});
((GridData) UIUtils.createHorizontalLine(composite).getLayoutData()).horizontalSpan = 2;
Tree scriptTree = new Tree(composite, SWT.SINGLE | SWT.FULL_SELECTION);
final GridData gd = new GridData(GridData.FILL_BOTH);
gd.horizontalSpan = 2;
scriptTree.setLayoutData(gd);
scriptTree.setForeground(fg);
scriptTree.setBackground(bg);
scriptTree.setLinesVisible(true);
//scriptViewer.setHeaderVisible(true);
this.scriptViewer = new TreeViewer(scriptTree);
ColumnViewerToolTipSupport.enableFor(this.scriptViewer);
//scriptTree.setS
this.scriptViewer.setContentProvider(new TreeContentProvider() {
@Override
public Object[] getChildren(Object parentElement) {
if (parentElement instanceof ResourceInfo) {
final List<ResourceInfo> children = ((ResourceInfo) parentElement).getChildren();
return CommonUtils.isEmpty(children) ? null : children.toArray();
}
return null;
}
@Override
public boolean hasChildren(Object element) {
if (element instanceof ResourceInfo) {
final List<ResourceInfo> children = ((ResourceInfo) element).getChildren();
return !CommonUtils.isEmpty(children);
}
return false;
}
});
ViewerColumnController columnController = new ViewerColumnController("scriptSelectorViewer", scriptViewer);
columnController.addColumn("Script", "Resource name", SWT.LEFT, true, true, new ColumnLabelProvider() {
@Override
public Image getImage(Object element) {
final ResourceInfo ri = (ResourceInfo) element;
if (!ri.isDirectory()) {
if (ri.getDataSource() == null) {
return DBeaverIcons.getImage(UIIcon.SQL_SCRIPT);
} else {
return DBeaverIcons.getImage(ri.getDataSource().getDriver().getIcon());
}
} else {
return DBeaverIcons.getImage(DBIcon.TREE_FOLDER);
}
}
@Override
public String getText(Object element) {
return ((ResourceInfo) element).getName();
}
@Override
public String getToolTipText(Object element) {
final DBPDataSourceContainer dataSource = ((ResourceInfo) element).getDataSource();
return dataSource == null ? null : dataSource.getName();
}
@Override
public Image getToolTipImage(Object element) {
final DBPDataSourceContainer dataSource = ((ResourceInfo) element).getDataSource();
return dataSource == null ? null : DBeaverIcons.getImage(dataSource.getDriver().getIcon());
}
});
columnController.addColumn("Time", "Modification time", SWT.LEFT, true, true, new ColumnLabelProvider() {
private SimpleDateFormat sdf = new SimpleDateFormat(DBConstants.DEFAULT_TIMESTAMP_FORMAT);
@Override
public String getText(Object element) {
final File localFile = ((ResourceInfo) element).getLocalFile();
if (localFile.isDirectory()) {
return null;
} else {
return sdf.format(new Date(localFile.lastModified()));
}
}
});
columnController.addColumn("Info", "Script preview", SWT.LEFT, true, true, new ColumnLabelProvider() {
@Override
public String getText(Object element) {
return "";//((ResourceInfo)element).getDescription();
}
@Override
public Color getForeground(Object element) {
return popup.getDisplay().getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW);
}
});
columnController.createColumns();
columnController.sortByColumn(1, SWT.UP);
scriptTree.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetDefaultSelected(SelectionEvent e) {
List<ResourceInfo> files = new ArrayList<>();
for (Object item : ((IStructuredSelection)scriptViewer.getSelection()).toArray()) {
if (!((ResourceInfo)item).isDirectory()) {
files.add((ResourceInfo) item);
}
}
if (files.isEmpty()) {
return;
}
popup.dispose();
for (ResourceInfo ri : files) {
NavigatorHandlerObjectOpen.openResourceEditor(ScriptSelectorPanel.this.workbenchWindow, ri);
}
}
});
scriptTree.addListener(SWT.PaintItem, new Listener() {
public void handleEvent(Event event) {
final TreeItem item = (TreeItem) event.item;
final ResourceInfo ri = (ResourceInfo) item.getData();
if (ri != null && !ri.isDirectory() && CommonUtils.isEmpty(item.getText(2))) {
DBeaverUI.asyncExec(new Runnable() {
@Override
public void run() {
if (!item.isDisposed()) {
item.setText(2, TextUtils.getSingleLineString(CommonUtils.notEmpty(ri.getDescription())));
}
}
});
}
}
});
this.patternText.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
final Tree tree = scriptViewer.getTree();
if (e.keyCode == SWT.ARROW_DOWN) {
//scriptViewer.get
scriptViewer.setSelection(new StructuredSelection(tree.getItem(0).getData()));
tree.setFocus();
} else if (e.keyCode == SWT.ARROW_UP) {
scriptViewer.setSelection(new StructuredSelection(tree.getItem(tree.getItemCount() - 1).getData()));
tree.setFocus();
}
}
});
final Listener focusFilter = new Listener() {
public void handleEvent(Event event) {
if (event.widget != scriptViewer.getTree() && event.widget != patternText && event.widget != newButton) {
popup.dispose();
}
}
};
popup.getDisplay().addFilter(SWT.FocusIn, focusFilter);
popup.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
final Rectangle bounds = popup.getBounds();
getBoundsSettings().put(CONFIG_BOUNDS_PARAM, bounds.x + "," + bounds.y + "," + bounds.width + "," + bounds.height);
popup.getDisplay().removeFilter(SWT.FocusIn, focusFilter);
}
});
}
IDialogSettings getBoundsSettings() {
return UIUtils.getDialogSettings("DBeaver.ScriptSelectorPanel");
}
public void showTree(List<ResourceInfo> scriptFiles) {
// Fill script list
popup.layout();
popup.setVisible(true);
loadScriptTree(scriptFiles);
final Tree tree = scriptViewer.getTree();
final TreeColumn[] columns = tree.getColumns();
columns[0].pack();
columns[0].setWidth(columns[0].getWidth() + 10);
columns[1].pack();
columns[2].setWidth(200 * 8);
patternText.setFocus();
}
private void loadScriptTree(List<ResourceInfo> scriptFiles) {
scriptViewer.setInput(scriptFiles);
scriptViewer.expandToLevel(2);
}
private class ScriptFilter extends ViewerFilter {
private final String pattern;
public ScriptFilter() {
pattern = patternText.getText().toLowerCase(Locale.ENGLISH);
}
@Override
public boolean select(Viewer viewer, Object parentElement, Object element) {
final IResource resource = ((ResourceInfo) element).getResource();
if (resource instanceof IFolder) {
return isAnyChildMatches((ResourceInfo) element);
} else {
return ((ResourceInfo) element).getName().toLowerCase(Locale.ENGLISH).contains(pattern);
}
}
private boolean isAnyChildMatches(ResourceInfo ri) {
for (ResourceInfo child : ri.getChildren()) {
if (child.getResource() instanceof IFolder) {
if (isAnyChildMatches(child)) {
return true;
}
} else {
if (child.getName().toLowerCase(Locale.ENGLISH).contains(pattern)) {
return true;
}
}
}
return false;
}
}
private class FilterJob extends UIJob {
public FilterJob() {
super("Filter scripts");
}
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
filterJob = null;
scriptViewer.setFilters(new ViewerFilter[] { new ScriptFilter() });
scriptViewer.expandAll();
final Tree tree = scriptViewer.getTree();
if (tree.getItemCount() > 0) {
scriptViewer.reveal(tree.getItem(0).getData());
}
return Status.OK_STATUS;
}
}
}