<commit_before>/*
 * DBeaver - Universal Database Manager
 * Copyright (C) 2010-2020 DBeaver Corp and others
 *
 * 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.core.runtime.IAdaptable;
import org.eclipse.jface.fieldassist.ContentProposal;
import org.eclipse.jface.fieldassist.ContentProposalAdapter;
import org.eclipse.jface.fieldassist.IContentProposal;
import org.eclipse.jface.fieldassist.IContentProposalProvider;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IUndoManager;
import org.eclipse.jface.text.TextViewer;
import org.eclipse.jface.text.TextViewerUndoManager;
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.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.*;
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.navigator.DBNDatabaseNode;
import org.jkiss.dbeaver.model.runtime.DBRRunnableWithProgress;
import org.jkiss.dbeaver.model.runtime.SystemJob;
import org.jkiss.dbeaver.model.sql.SQLConstants;
import org.jkiss.dbeaver.model.sql.SQLSyntaxManager;
import org.jkiss.dbeaver.model.sql.SQLUtils;
import org.jkiss.dbeaver.model.sql.parser.SQLWordPartDetector;
import org.jkiss.dbeaver.model.struct.DBSDataContainer;
import org.jkiss.dbeaver.model.struct.DBSEntity;
import org.jkiss.dbeaver.runtime.DBWorkbench;
import org.jkiss.dbeaver.runtime.ui.UIServiceSQL;
import org.jkiss.dbeaver.ui.*;
import org.jkiss.dbeaver.ui.contentassist.ContentAssistUtils;
import org.jkiss.dbeaver.ui.contentassist.ContentProposalExt;
import org.jkiss.dbeaver.ui.controls.StyledTextUtils;
import org.jkiss.dbeaver.ui.controls.resultset.handler.ResultSetHandlerMain;
import org.jkiss.dbeaver.ui.controls.resultset.internal.ResultSetMessages;
import org.jkiss.dbeaver.ui.css.CSSUtils;
import org.jkiss.dbeaver.ui.css.DBStyles;
import org.jkiss.dbeaver.ui.editors.TextEditorUtils;
import org.jkiss.utils.CommonUtils;

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, IAdaptable
{
    private static final Log log = Log.getLog(ResultSetFilterPanel.class);

    private static final int MIN_FILTER_TEXT_WIDTH = 50;
    private static final int MIN_FILTER_TEXT_HEIGHT = 20;
    private static final int MAX_HISTORY_PANEL_HEIGHT = 200;

    private final ResultSetViewer viewer;

    private final ActiveObjectPanel activeObjectPanel;
    private final FilterExpandPanel filterExpandPanel;
    //private final RefreshPanel refreshPanel;
    private final HistoryPanel historyPanel;
    private final ExecutePanel executePanel;

    private final TextViewer filtersTextViewer;
    private final StyledText filtersText;
    private final ContentProposalAdapter filtersProposalAdapter;

    private final ToolBar filterToolbar;
    private final ToolItem filtersClearButton;
    private final ToolItem filtersSaveButton;
    private final ToolItem refreshButton;
    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 String activeDisplayName = ResultSetViewer.DEFAULT_QUERY_TEXT;

    private String prevQuery = null;
    private final List<String> filtersHistory = new ArrayList<>();
    private Menu historyMenu;
    private boolean filterExpanded = false;

    ResultSetFilterPanel(ResultSetViewer rsv, Composite parent) {
        super(parent, SWT.NONE);
        this.viewer = rsv;
        CSSUtils.setCSSClass(this, DBStyles.COLORED_BY_CONNECTION_TYPE);

        this.sizingGC = new GC(this);

        GridLayout gl = new GridLayout(4, false);
        gl.marginHeight = 3;
        gl.marginWidth = 3;
        this.setLayout(gl);

        this.hoverBgColor = getDisplay().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW);
        this.shadowColor = getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);

        {
            this.filterComposite = new Composite(this, SWT.BORDER);
            gl = new GridLayout(5, false);
            gl.marginHeight = 0;
            gl.marginWidth = 0;
            gl.horizontalSpacing = 0;
            gl.verticalSpacing = 0;
            this.filterComposite.setLayout(gl);
            this.filterComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
            CSSUtils.setCSSClass(this.filterComposite, DBStyles.COLORED_BY_CONNECTION_TYPE);

            this.activeObjectPanel = new ActiveObjectPanel(filterComposite);
            this.filterExpandPanel = new FilterExpandPanel(filterComposite);

            this.filtersTextViewer = new TextViewer(filterComposite, SWT.MULTI);
            this.filtersTextViewer.setDocument(new Document());
            this.filtersTextViewer.getTextWidget().setForeground(UIStyles.getDefaultTextForeground());
            TextViewerUndoManager undoManager = new TextViewerUndoManager(200);
            undoManager.connect(filtersTextViewer);
            this.filtersTextViewer.setUndoManager(undoManager);
            this.filtersText = this.filtersTextViewer.getTextWidget();

            GridData gd = new GridData(GridData.FILL_BOTH);
            gd.verticalIndent = 1;
            this.filtersText.setLayoutData(gd);
            StyledTextUtils.fillDefaultStyledTextContextMenu(filtersText);
            StyledTextUtils.enableDND(this.filtersText);

            this.executePanel = new ExecutePanel(filterComposite);
            //this.refreshPanel = new RefreshPanel(filterComposite);
            this.historyPanel = new HistoryPanel(filterComposite);

            // Register filters text in focus service
            UIUtils.addDefaultEditActionsSupport(viewer.getSite(), this.filtersText);

            UIUtils.addEmptyTextHint(this.filtersText, styledText ->
                viewer.supportsDataFilter() ?
                    ResultSetMessages.sql_editor_resultset_filter_panel_text_enter_sql_to_filter:
                    ResultSetMessages.sql_editor_resultset_filter_panel_text_enter_filter_not_support
            );
            this.filtersText.addFocusListener(new FocusListener() {
                @Override
                public void focusGained(FocusEvent e) {
                    filtersText.redraw();
                }

                @Override
                public void focusLost(FocusEvent e) {
                    filtersText.redraw();
                }
            });
            this.filtersText.addModifyListener(new ModifyListener() {
                @Override
                public void modifyText(ModifyEvent e) {
                    String filterText = filtersText.getText();
                    executePanel.setEnabled(true);
                    executePanel.redraw();
                    filtersClearButton.setEnabled(!CommonUtils.isEmpty(filterText));
                }
            });
            this.filtersText.addTraverseListener(e -> {
                if (e.detail == SWT.TRAVERSE_RETURN) {
                    if (filterExpanded) {
                        e.doit = true;
                        return;
                    }
                    e.doit = false;
                    e.detail = SWT.TRAVERSE_NONE;
                }
            });

            this.filtersText.addVerifyKeyListener(e -> {
                if (e.keyCode == SWT.CR || e.keyCode == SWT.LF || e.character == SWT.CR) {
                    if (filterExpanded && (e.stateMask & SWT.CTRL) == 0) {
                        return;
                    }
                    // Suppress Enter handling if filter is not expanded
                    e.doit = false;
                }
            });
            this.filtersText.addKeyListener(new KeyAdapter() {<commit_msg>#8475 issue "Autocomplete doesn't go away when pasting value in table viewer filter box" fixed<commit_after>/*
 * DBeaver - Universal Database Manager
 * Copyright (C) 2010-2020 DBeaver Corp and others
 *
 * 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.core.runtime.IAdaptable;
import org.eclipse.jface.fieldassist.ContentProposal;
import org.eclipse.jface.fieldassist.ContentProposalAdapter;
import org.eclipse.jface.fieldassist.IContentProposal;
import org.eclipse.jface.fieldassist.IContentProposalProvider;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IUndoManager;
import org.eclipse.jface.text.TextViewer;
import org.eclipse.jface.text.TextViewerUndoManager;
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.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.*;
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.navigator.DBNDatabaseNode;
import org.jkiss.dbeaver.model.runtime.DBRRunnableWithProgress;
import org.jkiss.dbeaver.model.runtime.SystemJob;
import org.jkiss.dbeaver.model.sql.SQLConstants;
import org.jkiss.dbeaver.model.sql.SQLSyntaxManager;
import org.jkiss.dbeaver.model.sql.SQLUtils;
import org.jkiss.dbeaver.model.sql.parser.SQLWordPartDetector;
import org.jkiss.dbeaver.model.struct.DBSDataContainer;
import org.jkiss.dbeaver.model.struct.DBSEntity;
import org.jkiss.dbeaver.runtime.DBWorkbench;
import org.jkiss.dbeaver.runtime.ui.UIServiceSQL;
import org.jkiss.dbeaver.ui.*;
import org.jkiss.dbeaver.ui.contentassist.ContentAssistUtils;
import org.jkiss.dbeaver.ui.contentassist.ContentProposalExt;
import org.jkiss.dbeaver.ui.controls.StyledTextUtils;
import org.jkiss.dbeaver.ui.controls.resultset.handler.ResultSetHandlerMain;
import org.jkiss.dbeaver.ui.controls.resultset.internal.ResultSetMessages;
import org.jkiss.dbeaver.ui.css.CSSUtils;
import org.jkiss.dbeaver.ui.css.DBStyles;
import org.jkiss.dbeaver.ui.editors.TextEditorUtils;
import org.jkiss.utils.CommonUtils;

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, IAdaptable
{
    private static final Log log = Log.getLog(ResultSetFilterPanel.class);

    private static final int MIN_FILTER_TEXT_WIDTH = 50;
    private static final int MIN_FILTER_TEXT_HEIGHT = 20;
    private static final int MAX_HISTORY_PANEL_HEIGHT = 200;

    private final ResultSetViewer viewer;

    private final ActiveObjectPanel activeObjectPanel;
    private final FilterExpandPanel filterExpandPanel;
    //private final RefreshPanel refreshPanel;
    private final HistoryPanel historyPanel;
    private final ExecutePanel executePanel;

    private final TextViewer filtersTextViewer;
    private final StyledText filtersText;
    private final ContentProposalAdapter filtersProposalAdapter;

    private final ToolBar filterToolbar;
    private final ToolItem filtersClearButton;
    private final ToolItem filtersSaveButton;
    private final ToolItem refreshButton;
    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 String activeDisplayName = ResultSetViewer.DEFAULT_QUERY_TEXT;

    private String prevQuery = null;
    private final List<String> filtersHistory = new ArrayList<>();
    private Menu historyMenu;
    private boolean filterExpanded = false;

    ResultSetFilterPanel(ResultSetViewer rsv, Composite parent) {
        super(parent, SWT.NONE);
        this.viewer = rsv;
        CSSUtils.setCSSClass(this, DBStyles.COLORED_BY_CONNECTION_TYPE);

        this.sizingGC = new GC(this);

        GridLayout gl = new GridLayout(4, false);
        gl.marginHeight = 3;
        gl.marginWidth = 3;
        this.setLayout(gl);

        this.hoverBgColor = getDisplay().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW);
        this.shadowColor = getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);

        {
            this.filterComposite = new Composite(this, SWT.BORDER);
            gl = new GridLayout(5, false);
            gl.marginHeight = 0;
            gl.marginWidth = 0;
            gl.horizontalSpacing = 0;
            gl.verticalSpacing = 0;
            this.filterComposite.setLayout(gl);
            this.filterComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
            CSSUtils.setCSSClass(this.filterComposite, DBStyles.COLORED_BY_CONNECTION_TYPE);

            this.activeObjectPanel = new ActiveObjectPanel(filterComposite);
            this.filterExpandPanel = new FilterExpandPanel(filterComposite);

            this.filtersTextViewer = new TextViewer(filterComposite, SWT.MULTI);
            this.filtersTextViewer.setDocument(new Document());
            this.filtersTextViewer.getTextWidget().setForeground(UIStyles.getDefaultTextForeground());
            TextViewerUndoManager undoManager = new TextViewerUndoManager(200);
            undoManager.connect(filtersTextViewer);
            this.filtersTextViewer.setUndoManager(undoManager);
            this.filtersText = this.filtersTextViewer.getTextWidget();

            GridData gd = new GridData(GridData.FILL_BOTH);
            gd.verticalIndent = 1;
            this.filtersText.setLayoutData(gd);
            StyledTextUtils.fillDefaultStyledTextContextMenu(filtersText);
            StyledTextUtils.enableDND(this.filtersText);

            this.executePanel = new ExecutePanel(filterComposite);
            //this.refreshPanel = new RefreshPanel(filterComposite);
            this.historyPanel = new HistoryPanel(filterComposite);

            // Register filters text in focus service
            UIUtils.addDefaultEditActionsSupport(viewer.getSite(), this.filtersText);

            UIUtils.addEmptyTextHint(this.filtersText, styledText ->
                viewer.supportsDataFilter() ?
                    ResultSetMessages.sql_editor_resultset_filter_panel_text_enter_sql_to_filter:
                    ResultSetMessages.sql_editor_resultset_filter_panel_text_enter_filter_not_support
            );
            this.filtersText.addFocusListener(new FocusListener() {
                @Override
                public void focusGained(FocusEvent e) {
                    filtersText.redraw();
                }

                @Override
                public void focusLost(FocusEvent e) {
                    filtersText.redraw();
                }
            });
            this.filtersText.addModifyListener(new ModifyListener() {
                @Override
                public void modifyText(ModifyEvent e) {
                    String filterText = filtersText.getText();
                    executePanel.setEnabled(true);
                    executePanel.redraw();
                    filtersClearButton.setEnabled(!CommonUtils.isEmpty(filterText));
                    filtersProposalAdapter.refresh();
                }
            });
            this.filtersText.addTraverseListener(e -> {
                if (e.detail == SWT.TRAVERSE_RETURN) {
                    if (filterExpanded) {
                        e.doit = true;
                        return;
                    }
                    e.doit = false;
                    e.detail = SWT.TRAVERSE_NONE;
                }
            });

            this.filtersText.addVerifyKeyListener(e -> {
                if (e.keyCode == SWT.CR || e.keyCode == SWT.LF || e.character == SWT.CR) {
                    if (filterExpanded && (e.stateMask & SWT.CTRL) == 0) {
                        return;
                    }
                    // Suppress Enter handling if filter is not expanded
                    e.doit = false;
                }
            });
            this.filtersText.addKeyListener(new KeyAdapter() {
{'fraction_non_alphanumeric': 0.08470588235294117, 'fraction_numerical': 0.0037209302325581397, 'mean_word_length': 2.961846954259701, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7666296', 'n_tokens_mistral': 5269, 'n_tokens_neox': 5092, 'n_words': 1009}