/* * 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.fieldassist.ContentProposal; import org.eclipse.jface.fieldassist.IContentProposal; import org.eclipse.jface.fieldassist.IContentProposalProvider; import org.eclipse.jface.text.Document; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; 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.layout.GridLayout; import org.eclipse.swt.widgets.*; import org.eclipse.ui.IWorkbenchCommandConstants; import org.eclipse.ui.PartInitException; import org.eclipse.ui.texteditor.ITextEditorActionConstants; import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.core.CoreMessages; import org.jkiss.dbeaver.core.DBeaverUI; import org.jkiss.dbeaver.model.DBIcon; import org.jkiss.dbeaver.model.DBPImage; import org.jkiss.dbeaver.model.DBPImageProvider; 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.exec.DBCStatistics; import org.jkiss.dbeaver.model.navigator.DBNDatabaseNode; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.model.runtime.DBRRunnableWithProgress; import org.jkiss.dbeaver.model.runtime.SystemJob; import org.jkiss.dbeaver.model.sql.SQLSyntaxManager; import org.jkiss.dbeaver.model.sql.SQLUtils; import org.jkiss.dbeaver.model.struct.DBSDataContainer; import org.jkiss.dbeaver.model.struct.DBSEntity; import org.jkiss.dbeaver.ui.*; import org.jkiss.dbeaver.ui.controls.StyledTextContentAdapter; import org.jkiss.dbeaver.ui.editors.StringEditorInput; import org.jkiss.dbeaver.ui.editors.SubEditorSite; import org.jkiss.dbeaver.ui.editors.sql.SQLEditorBase; import org.jkiss.dbeaver.ui.editors.sql.handlers.OpenHandler; import org.jkiss.dbeaver.ui.editors.sql.syntax.SQLContextInformer; import org.jkiss.dbeaver.ui.editors.sql.syntax.SQLWordPartDetector; import org.jkiss.dbeaver.utils.GeneralUtils; import org.jkiss.utils.CommonUtils; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * ResultSetFilterPanel */ class ResultSetFilterPanel extends Composite implements IContentProposalProvider { private static final Log log = Log.getLog(ResultSetFilterPanel.class); public static final int MIN_FILTER_TEXT_WIDTH = 50; public static final int MIN_FILTER_TEXT_HEIGHT = 20; public static final int MAX_HISTORY_PANEL_HEIGHT = 200; private static final String DEFAULT_QUERY_TEXT = "SQL"; private final ResultSetViewer viewer; private final ActiveObjectPanel activeObjectPanel; private final RefreshPanel refreshPanel; private final HistoryPanel historyPanel; private final StyledText filtersText; private final ToolBar filterToolbar; private final ToolItem filtersApplyButton; private final ToolItem filtersClearButton; private final ToolItem historyBackButton; private final ToolItem historyForwardButton; private final Composite filterComposite; private final Color hoverBgColor; private final Color shadowColor; private final GC sizingGC; private final Font hintFont; private String activeDisplayName = DEFAULT_QUERY_TEXT; private String prevQuery = null; private final List<String> filtersHistory = new ArrayList<>(); private Menu historyMenu; public ResultSetFilterPanel(ResultSetViewer rsv) { super(rsv.getControl(), SWT.NONE); this.viewer = rsv; this.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); sizingGC = new GC(this); GridLayout gl = new GridLayout(4, false); gl.marginHeight = 3; gl.marginWidth = 3; this.setLayout(gl); hoverBgColor = getDisplay().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW); shadowColor = getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW); hintFont = UIUtils.modifyFont(getFont(), SWT.ITALIC); { this.filterComposite = new Composite(this, SWT.BORDER); gl = new GridLayout(4, false); gl.marginHeight = 0; gl.marginWidth = 0; gl.horizontalSpacing = 0; gl.verticalSpacing = 0; filterComposite.setLayout(gl); filterComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); activeObjectPanel = new ActiveObjectPanel(filterComposite); this.filtersText = new StyledText(filterComposite, SWT.SINGLE); this.filtersText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); UIUtils.fillDefaultStyledTextContextMenu(filtersText); this.historyPanel = new HistoryPanel(filterComposite); this.refreshPanel = new RefreshPanel(filterComposite); // Register filters text in focus service UIUtils.addFocusTracker(viewer.getSite(), UIUtils.INLINE_WIDGET_EDITOR_ID, this.filtersText); this.filtersText.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { // Unregister from focus service UIUtils.removeFocusTracker(viewer.getSite(), filtersText); } }); this.filtersText.addPaintListener(new PaintListener() { @Override public void paintControl(PaintEvent e) { /*if (viewer.getModel().hasData())*/ { final boolean supportsDataFilter = viewer.supportsDataFilter(); if (!supportsDataFilter || (filtersText.isEnabled() && filtersText.getCharCount() == 0)) { e.gc.setForeground(shadowColor); e.gc.setFont(hintFont); e.gc.drawText(supportsDataFilter ? "Enter a SQL expression to filter results (use Ctrl+Space)" : "Data filter is not supported", 2, 0, true); e.gc.setFont(null); } } } }); this.filtersText.addModifyListener(new ModifyListener() { @Override public void modifyText(ModifyEvent e) { String filterText = filtersText.getText(); filtersApplyButton.setEnabled(true); filtersClearButton.setEnabled(!CommonUtils.isEmpty(filterText)); } }); this.filtersText.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.keyCode == SWT.ARROW_DOWN) { historyPanel.showFilterHistoryPopup(); } } }); UIUtils.installContentProposal(filtersText, new StyledTextContentAdapter(filtersText), this); } // Handle all shortcuts by filters editor, not by host editor UIUtils.enableHostEditorKeyBindingsSupport(viewer.getSite(), this.filtersText); { filterToolbar = new ToolBar(this, SWT.HORIZONTAL | SWT.RIGHT); filtersApplyButton = new ToolItem(filterToolbar, SWT.PUSH | SWT.NO_FOCUS); filtersApplyButton.setImage(DBeaverIcons.getImage(UIIcon.FILTER_APPLY)); //filtersApplyButton.setText("Apply"); filtersApplyButton.setToolTipText("Apply filter criteria"); filtersApplyButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { setCustomDataFilter(); } }); filtersApplyButton.setEnabled(false); filtersClearButton = new ToolItem(filterToolbar, SWT.PUSH | SWT.NO_FOCUS); filtersClearButton.setImage(DBeaverIcons.getImage(UIIcon.FILTER_RESET)); filtersClearButton.setToolTipText("Remove all filters"); filtersClearButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { viewer.resetDataFilter(true); } }); filtersClearButton.setEnabled(false); ToolItem filtersCustomButton = new ToolItem(filterToolbar, SWT.PUSH | SWT.NO_FOCUS); filtersCustomButton.setImage(DBeaverIcons.getImage(UIIcon.FILTER)); filtersCustomButton.setToolTipText("Custom Filters"); filtersCustomButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { new FilterSettingsDialog(viewer).open(); } }); filtersCustomButton.setEnabled(true); UIUtils.createToolBarSeparator(filterToolbar, SWT.VERTICAL); historyBackButton = new ToolItem(filterToolbar, SWT.DROP_DOWN | SWT.NO_FOCUS); historyBackButton.setImage(DBeaverIcons.getImage(UIIcon.RS_BACK)); historyBackButton.setEnabled(false); historyBackButton.addSelectionListener(new HistoryMenuListener(historyBackButton, true)); historyForwardButton = new ToolItem(filterToolbar, SWT.DROP_DOWN | SWT.NO_FOCUS); historyForwardButton.setImage(DBeaverIcons.getImage(UIIcon.RS_FORWARD)); historyForwardButton.setEnabled(false); historyForwardButton.addSelectionListener(new HistoryMenuListener(historyForwardButton, false)); } this.addTraverseListener(new TraverseListener() { @Override public void keyTraversed(TraverseEvent e) { if (e.detail == SWT.TRAVERSE_RETURN) { setCustomDataFilter(); e.doit = false; e.detail = SWT.TRAVERSE_NONE; } } }); this.addControlListener(new ControlListener() { @Override public void controlMoved(ControlEvent e) { redrawPanels(); } @Override public void controlResized(ControlEvent e) { redrawPanels(); } }); enablePanelControls(false); this.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { if (historyMenu != null) { historyMenu.dispose(); historyMenu = null; } UIUtils.dispose(sizingGC); UIUtils.dispose(hintFont); } }); } private void enablePanelControls(boolean enable) { setRedraw(false); try { filterToolbar.setEnabled(enable); refreshPanel.setEnabled(enable); historyPanel.setEnabled(enable); filtersText.setEditable(enable && viewer.supportsDataFilter()); } finally { setRedraw(true); } } private boolean isFiltersAvailable() { DBSDataContainer dataContainer = viewer.getDataContainer(); return dataContainer != null && (dataContainer.getSupportedFeatures() & DBSDataContainer.DATA_FILTER) != 0; } private void redrawPanels() { if (activeObjectPanel != null && !activeObjectPanel.isDisposed()) { activeObjectPanel.redraw(); } if (historyPanel != null && !historyPanel.isDisposed()) { historyPanel.redraw(); } if (refreshPanel != null && !refreshPanel.isDisposed()) { refreshPanel.redraw(); } } @NotNull private String getActiveQueryText() { DBCStatistics statistics = viewer.getModel().getStatistics(); String queryText = statistics == null ? null : statistics.getQueryText(); if (queryText == null || queryText.isEmpty()) { DBSDataContainer dataContainer = viewer.getDataContainer(); if (dataContainer != null) { return dataContainer.getName(); } queryText = DEFAULT_QUERY_TEXT; } return queryText; } @Nullable private DBPImage getActiveObjectImage() { DBSDataContainer dataContainer = viewer.getDataContainer(); if (dataContainer instanceof DBSEntity) { DBNDatabaseNode dcNode = viewer.getDataContainer().getDataSource().getContainer().getPlatform().getNavigatorModel().findNode(dataContainer); if (dcNode != null) { return dcNode.getNodeIcon(); } } if (dataContainer instanceof DBPImageProvider) { return ((DBPImageProvider) dataContainer).getObjectImage(); } else if (dataContainer instanceof DBSEntity) { return DBIcon.TREE_TABLE; } else { return UIIcon.SQL_TEXT; } } void enableFilters(boolean enableFilters) { enablePanelControls(enableFilters); if (enableFilters) { final boolean supportsDataFilter = viewer.supportsDataFilter(); int historyPosition = viewer.getHistoryPosition(); List<ResultSetViewer.HistoryStateItem> stateHistory = viewer.getStateHistory(); String filterText = filtersText.getText(); filtersText.setEnabled(supportsDataFilter); filtersApplyButton.setEnabled(supportsDataFilter); filtersClearButton.setEnabled(supportsDataFilter && !CommonUtils.isEmpty(filterText)); // Update history buttons if (historyPosition > 0) { historyBackButton.setEnabled(true); historyBackButton.setToolTipText( stateHistory.get(historyPosition - 1).describeState() + " (" + ActionUtils.findCommandDescription(IWorkbenchCommandConstants.NAVIGATE_BACKWARD_HISTORY, viewer.getSite(), true) + ")"); } else { historyBackButton.setEnabled(false); } if (historyPosition < stateHistory.size() - 1) { historyForwardButton.setEnabled(true); historyForwardButton.setToolTipText( stateHistory.get(historyPosition + 1).describeState() + " (" + ActionUtils.findCommandDescription(IWorkbenchCommandConstants.NAVIGATE_FORWARD_HISTORY, viewer.getSite(), true) + ")"); } else { historyForwardButton.setEnabled(false); } } filterComposite.setBackground(filtersText.getBackground()); { String displayName = getActiveSourceQuery(); if (prevQuery == null || !prevQuery.equals(displayName)) { loadFiltersHistory(displayName); prevQuery = displayName; } Pattern mlCommentsPattern = Pattern.compile("/\\*.*\\*/", Pattern.DOTALL); Matcher m = mlCommentsPattern.matcher(displayName); if (m.find()) { displayName = m.replaceAll(""); } displayName = displayName.replaceAll("--.+", ""); displayName = TextUtils.compactWhiteSpaces(displayName); activeDisplayName = CommonUtils.notEmpty(CommonUtils.truncateString(displayName, 200)); if (CommonUtils.isEmpty(activeDisplayName)) { activeDisplayName = DEFAULT_QUERY_TEXT; } } filterComposite.layout(); redrawPanels(); } @NotNull private String getActiveSourceQuery() { String displayName; DBSDataContainer dataContainer = viewer.getDataContainer(); if (dataContainer != null) { displayName = dataContainer.getName(); } else { displayName = getActiveQueryText(); } return displayName; } private void loadFiltersHistory(String query) { filtersHistory.clear(); try { final Collection<String> history = ResultSetViewer.getFilterManager().getQueryFilterHistory(query); filtersHistory.addAll(history); } catch (Throwable e) { log.debug("Error reading history", e); } } private void setCustomDataFilter() { DBCExecutionContext context = viewer.getExecutionContext(); if (context == null) { return; } String condition = filtersText.getText(); StringBuilder currentCondition = new StringBuilder(); SQLUtils.appendConditionString(viewer.getModel().getDataFilter(), context.getDataSource(), null, currentCondition, true); if (currentCondition.toString().trim().equals(condition.trim())) { // The same return; } DBDDataFilter newFilter = new DBDDataFilter(viewer.getModel().getDataFilter()); for (DBDAttributeConstraint ac : newFilter.getConstraints()) { ac.setCriteria(null); } newFilter.setWhere(condition); viewer.setDataFilter(newFilter, true); //viewer.getControl().setFocus(); } void addFiltersHistory(String whereCondition) { final boolean oldFilter = filtersHistory.remove(whereCondition); filtersHistory.add(whereCondition); if (!oldFilter) { try { ResultSetViewer.getFilterManager().saveQueryFilterValue(getActiveSourceQuery(), whereCondition); } catch (Throwable e) { log.debug("Error saving filter", e); } } setFilterValue(whereCondition); } Control getEditControl() { return filtersText; } void setFilterValue(String whereCondition) { if (whereCondition != null && !filtersText.getText().trim().equals(whereCondition.trim())) { filtersText.setText(whereCondition); } } @NotNull private Control createObjectPanel(Shell popup) throws PartInitException { Composite panel = new Composite(popup, SWT.NONE); GridLayout gl = new GridLayout(2, false); // gl.marginWidth = 0; gl.marginHeight = 0; // gl.horizontalSpacing = 0; panel.setLayout(gl); Label iconLabel = new Label(panel, SWT.NONE); DBPImage activeObjectImage = getActiveObjectImage(); if (activeObjectImage != null) { iconLabel.setImage(DBeaverIcons.getImage(activeObjectImage)); } iconLabel.setToolTipText("Click to open query in editor"); iconLabel.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING)); iconLabel.setCursor(getDisplay().getSystemCursor(SWT.CURSOR_HAND)); iconLabel.addMouseListener(new MouseAdapter() { @Override public void mouseUp(MouseEvent e) { openEditorForActiveQuery(); } }); Composite editorPH = new Composite(panel, SWT.NONE); editorPH.setLayoutData(new GridData(GridData.FILL_BOTH)); editorPH.setLayout(new FillLayout()); final SQLEditorBase editor = new SQLEditorBase() { @Nullable @Override public DBCExecutionContext getExecutionContext() { return viewer.getExecutionContext(); } @Override public void createPartControl(Composite parent) { super.createPartControl(parent); getAction(ITextEditorActionConstants.CONTEXT_PREFERENCES).setEnabled(false); } }; editor.setHasVerticalRuler(false); editor.init(new SubEditorSite(viewer.getSite()), new StringEditorInput(DEFAULT_QUERY_TEXT, getActiveQueryText(), true, GeneralUtils.getDefaultFileEncoding())); editor.createPartControl(editorPH); editor.reloadSyntaxRules(); StyledText textWidget = editor.getTextViewer().getTextWidget(); //textWidget.setAlwaysShowScrollBars(false); panel.setBackground(textWidget.getBackground()); panel.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { editor.dispose(); } }); return textWidget; } private void openEditorForActiveQuery() { DBSDataContainer dataContainer = viewer.getDataContainer(); String editorName; if (dataContainer instanceof DBSEntity) { editorName = dataContainer.getName(); } else { editorName = "Query"; } OpenHandler.openSQLConsole( DBeaverUI.getActiveWorkbenchWindow(), dataContainer == null || dataContainer.getDataSource() == null ? null : dataContainer.getDataSource().getContainer(), editorName, getActiveQueryText() ); } @Override public IContentProposal[] getProposals(String contents, int position) { SQLSyntaxManager syntaxManager = new SQLSyntaxManager(); if (viewer.getDataContainer() != null) { syntaxManager.init(viewer.getDataContainer().getDataSource()); } SQLWordPartDetector wordDetector = new SQLWordPartDetector(new Document(contents), syntaxManager, position); final String word = wordDetector.getFullWord().toLowerCase(Locale.ENGLISH); final List<IContentProposal> proposals = new ArrayList<>(); final DBRRunnableWithProgress reader = new DBRRunnableWithProgress() { @Override public void run(DBRProgressMonitor monitor) throws InvocationTargetException, InterruptedException { for (DBDAttributeBinding attribute : viewer.getModel().getAttributes()) { final String name = attribute.getName(); if (CommonUtils.isEmpty(word) || name.toLowerCase(Locale.ENGLISH).startsWith(word)) { final String content = name.substring(word.length()) + " "; proposals.add( new ContentProposal( content, attribute.getName(), SQLContextInformer.makeObjectDescription(monitor, attribute.getAttribute(), false), content.length())); } } } }; SystemJob searchJob = new SystemJob("Extract attribute proposals", reader); searchJob.schedule(); UIUtils.waitJobCompletion(searchJob); return proposals.toArray(new IContentProposal[proposals.size()]); } private class FilterPanel extends Canvas { protected boolean hover = false; public FilterPanel(Composite parent, int style) { super(parent, style); addPaintListener(new PaintListener() { @Override public void paintControl(PaintEvent e) { paintPanel(e); } }); addMouseTrackListener(new MouseTrackAdapter() { @Override public void mouseEnter(MouseEvent e) { hover = true; redraw(); } @Override public void mouseExit(MouseEvent e) { hover = false; redraw(); } }); } protected void paintPanel(PaintEvent e) { } } private class ActiveObjectPanel extends FilterPanel { public static final int MIN_INFO_PANEL_WIDTH = 300; public static final int MIN_INFO_PANEL_HEIGHT = 100; public static final int MAX_INFO_PANEL_HEIGHT = 400; private Shell popup; public ActiveObjectPanel(Composite addressBar) { super(addressBar, SWT.NONE); setLayoutData(new GridData(GridData.FILL_VERTICAL)); setToolTipText("Ctrl+click to open SQL console"); this.addMouseListener(new MouseAdapter() { @Override public void mouseDoubleClick(MouseEvent e) { openEditorForActiveQuery(); } @Override public void mouseDown(final MouseEvent e) { DBeaverUI.asyncExec(new Runnable() { @Override public void run() { showObjectInfoPopup(e); } }); } }); } private void showObjectInfoPopup(MouseEvent e) { if (popup != null) { popup.dispose(); } if ((e.stateMask & SWT.CTRL) != 0) { openEditorForActiveQuery(); return; } popup = new Shell(getShell(), SWT.ON_TOP | SWT.RESIZE); popup.setLayout(new FillLayout()); Control editControl; try { editControl = createObjectPanel(popup); } catch (PartInitException e1) { UIUtils.showErrorDialog(getShell(), "Object info", "Error opening object info", e1); popup.dispose(); return; } Point controlRect = editControl.computeSize(-1, -1); Rectangle parentRect = getDisplay().map(activeObjectPanel, null, getBounds()); Rectangle displayRect = getMonitor().getClientArea(); int width = Math.min(filterComposite.getSize().x, Math.max(MIN_INFO_PANEL_WIDTH, controlRect.x + 30)); int height = Math.min(MAX_INFO_PANEL_HEIGHT, Math.max(MIN_INFO_PANEL_HEIGHT, controlRect.y + 30)); int x = parentRect.x + e.x + 1; int y = parentRect.y + e.y + 1; if (y + height > displayRect.y + displayRect.height) { y = parentRect.y - height; } popup.setBounds(x, y, width, height); popup.setVisible(true); editControl.setFocus(); editControl.addFocusListener(new FocusAdapter() { @Override public void focusLost(FocusEvent e) { popup.dispose(); } }); } @Override public Point computeSize(int wHint, int hHint, boolean changed) { int maxWidth = 0; for (Control control = viewer.getControl().getParent(); maxWidth == 0 && control != null; control = control.getParent()) { maxWidth = control.getSize().x / 4; } Point textSize = sizingGC.textExtent(activeDisplayName); DBPImage activeObjectImage = getActiveObjectImage(); if (activeObjectImage != null) { Image image = DBeaverIcons.getImage(activeObjectImage); textSize.x += image.getBounds().width + 4; } return new Point( Math.max(MIN_FILTER_TEXT_WIDTH, Math.min(textSize.x + 10, maxWidth)), Math.min(textSize.y + 6, MIN_FILTER_TEXT_HEIGHT)); } @Override protected void paintPanel(PaintEvent e) { e.gc.setForeground(shadowColor); if (hover) { e.gc.setBackground(hoverBgColor); e.gc.fillRectangle(e.x, e.y, e.width - 3, e.height); e.gc.drawLine( e.x + e.width - 4, e.y, e.x + e.width - 4, e.y + e.height); } else { e.gc.drawLine( e.x + e.width - 4, e.y + 2, e.x + e.width - 4, e.y + e.height - 4); } e.gc.setForeground(e.gc.getDevice().getSystemColor(SWT.COLOR_DARK_GREEN)); e.gc.setClipping(e.x, e.y, e.width - 8, e.height); int textOffset = 2; int panelHeight = getSize().y; DBPImage activeObjectImage = getActiveObjectImage(); if (activeObjectImage != null) { Image icon = DBeaverIcons.getImage(activeObjectImage); Rectangle iconBounds = icon.getBounds(); e.gc.drawImage(icon, 2, (panelHeight - iconBounds.height) / 2); textOffset += iconBounds.width + 2; } int textHeight = e.gc.getFontMetrics().getHeight(); e.gc.drawText(activeDisplayName, textOffset, (panelHeight - textHeight) / 2); e.gc.setClipping((Rectangle) null); } } private class HistoryPanel extends FilterPanel { private final Image dropImageE, dropImageD; private TableItem hoverItem; private Shell popup; public HistoryPanel(Composite addressBar) { super(addressBar, SWT.NONE); dropImageE = DBeaverIcons.getImage(UIIcon.DROP_DOWN); dropImageD = new Image(dropImageE.getDevice(), dropImageE, SWT.IMAGE_GRAY); GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); gd.heightHint = MIN_FILTER_TEXT_HEIGHT; gd.widthHint = dropImageE.getBounds().width; setLayoutData(gd); addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { UIUtils.dispose(dropImageD); } }); addMouseListener(new MouseAdapter() { @Override public void mouseUp(MouseEvent e) { showFilterHistoryPopup(); } }); } @Override protected void paintPanel(PaintEvent e) { e.gc.setForeground(shadowColor); if (hover) { e.gc.drawImage(dropImageE, e.x, e.y + 2); } else { e.gc.drawImage(dropImageD, e.x, e.y + 2); } } private void showFilterHistoryPopup() { if (popup != null) { popup.dispose(); } popup = new Shell(getShell(), SWT.NO_TRIM | SWT.ON_TOP | SWT.RESIZE); popup.setLayout(new FillLayout()); Table editControl = createFilterHistoryPanel(popup); Point parentRect = getDisplay().map(filtersText, null, new Point(0, 0)); Rectangle displayRect = getMonitor().getClientArea(); final Point filterTextSize = filtersText.getSize(); int width = filterTextSize.x + historyPanel.getSize().x + refreshPanel.getSize().x; int height = Math.min(MAX_HISTORY_PANEL_HEIGHT, editControl.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); int x = parentRect.x; int y = parentRect.y + getSize().y; if (y + height > displayRect.y + displayRect.height) { y = parentRect.y - height; } popup.setBounds(x, y, width, height); int tableWidth = editControl.getSize().x - editControl.getBorderWidth() * 2; final ScrollBar vsb = editControl.getVerticalBar(); if (vsb != null) { tableWidth -= vsb.getSize().x; } editControl.getColumn(0).setWidth(tableWidth); popup.setVisible(true); editControl.setFocus(); editControl.addFocusListener(new FocusAdapter() { @Override public void focusLost(FocusEvent e) { popup.dispose(); } }); } @NotNull private Table createFilterHistoryPanel(final Shell popup) { final Table historyTable = new Table(popup, SWT.BORDER | SWT.FULL_SELECTION | SWT.SINGLE); new TableColumn(historyTable, SWT.NONE); if (filtersHistory.isEmpty()) { // nothing } else { String curFilterValue = filtersText.getText(); for (int i = filtersHistory.size(); i > 0; i--) { String hi = filtersHistory.get(i - 1); if (!CommonUtils.equalObjects(hi, curFilterValue)) { new TableItem(historyTable, SWT.NONE).setText(hi); } } historyTable.deselectAll(); } historyTable.addMouseTrackListener(new MouseTrackAdapter() { @Override public void mouseHover(MouseEvent e) { //hoverItem = historyTable.getItem(new Point(e.x, e.y)); } @Override public void mouseExit(MouseEvent e) { hoverItem = null; } }); historyTable.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { TableItem item = hoverItem; if (item == null) { final int selectionIndex = historyTable.getSelectionIndex(); if (selectionIndex != -1) { item = historyTable.getItem(selectionIndex); } } if (item != null) { if (e.keyCode == SWT.DEL) { final String filterValue = item.getText(); try { ResultSetViewer.getFilterManager().deleteQueryFilterValue(getActiveSourceQuery(), filterValue); } catch (DBException e1) { log.warn("Error deleting filter value [" + filterValue + "]", e1); } filtersHistory.remove(filterValue); item.dispose(); hoverItem = null; } else if (e.keyCode == SWT.CR || e.keyCode == SWT.SPACE) { final String newFilter = item.getText(); popup.dispose(); setFilterValue(newFilter); setCustomDataFilter(); } } } }); historyTable.addMouseMoveListener(new MouseMoveListener() { @Override public void mouseMove(MouseEvent e) { hoverItem = historyTable.getItem(new Point(e.x, e.y)); } }); historyTable.addMouseListener(new MouseAdapter() { @Override public void mouseDown(MouseEvent e) { if (hoverItem != null) { final String newFilter = hoverItem.getText(); popup.dispose(); setFilterValue(newFilter); setCustomDataFilter(); } } }); return historyTable; } } private class RefreshPanel extends FilterPanel { private final Image enabledImage, disabledImage; public RefreshPanel(Composite addressBar) { super(addressBar, SWT.NONE); setToolTipText(CoreMessages.controls_resultset_viewer_action_refresh); enabledImage = DBeaverIcons.getImage(UIIcon.RS_REFRESH); disabledImage = new Image(enabledImage.getDevice(), enabledImage, SWT.IMAGE_GRAY); addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { UIUtils.dispose(disabledImage); } }); addMouseListener(new MouseAdapter() { @Override public void mouseUp(MouseEvent e) { if (!viewer.isRefreshInProgress() && e.x > 8) { viewer.refreshData(null); redraw(); } } }); GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); gd.heightHint = MIN_FILTER_TEXT_HEIGHT; gd.widthHint = 10 + enabledImage.getBounds().width + 6; setLayoutData(gd); } @Override protected void paintPanel(PaintEvent e) { e.gc.setForeground(shadowColor); e.gc.drawLine( e.x + 4, e.y + 2, e.x + 4, e.y + e.height - 4); if (viewer.isRefreshInProgress()) { e.gc.drawImage(DBeaverIcons.getImage(UIIcon.CLOSE), e.x + 10, e.y + 2); } else if (hover) { e.gc.drawImage(enabledImage, e.x + 10, e.y + 2); } else { e.gc.drawImage(disabledImage, e.x + 10, e.y + 2); } } } private class HistoryMenuListener extends SelectionAdapter { private final ToolItem dropdown; private final boolean back; public HistoryMenuListener(ToolItem item, boolean back) { this.dropdown = item; this.back = back; } @Override public void widgetSelected(SelectionEvent e) { int historyPosition = viewer.getHistoryPosition(); List<ResultSetViewer.HistoryStateItem> stateHistory = viewer.getStateHistory(); if (e.detail == SWT.ARROW) { ToolItem item = (ToolItem) e.widget; Rectangle rect = item.getBounds(); Point pt = item.getParent().toDisplay(new Point(rect.x, rect.y)); if (historyMenu != null) { historyMenu.dispose(); } historyMenu = new Menu(dropdown.getParent().getShell()); for (int i = historyPosition + (back ? -1 : 1); i >= 0 && i < stateHistory.size(); i += back ? -1 : 1) { MenuItem mi = new MenuItem(historyMenu, SWT.NONE); ResultSetViewer.HistoryStateItem state = stateHistory.get(i); mi.setText(state.describeState()); final int statePosition = i; mi.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { viewer.navigateHistory(statePosition); } }); } historyMenu.setLocation(pt.x, pt.y + rect.height); historyMenu.setVisible(true); } else { int newPosition = back ? historyPosition - 1 : historyPosition + 1; viewer.navigateHistory(newPosition); } } } }