| /*=============================================================================# |
| # Copyright (c) 2010, 2021 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.internal.r.ui.dataeditor; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import org.eclipse.jface.dialogs.IDialogConstants; |
| import org.eclipse.jface.dialogs.IDialogSettings; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.widgets.Button; |
| import org.eclipse.swt.widgets.Combo; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.Group; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.Listener; |
| import org.eclipse.ui.IPartListener; |
| import org.eclipse.ui.IWindowListener; |
| import org.eclipse.ui.IWorkbenchPage; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.IWorkbenchWindow; |
| |
| import org.eclipse.statet.ecommons.ui.dialogs.DialogUtils; |
| import org.eclipse.statet.ecommons.ui.dialogs.ExtStatusDialog; |
| import org.eclipse.statet.ecommons.ui.util.LayoutUtils; |
| |
| import org.eclipse.statet.internal.r.ui.RUIPlugin; |
| import org.eclipse.statet.r.ui.dataeditor.RDataTableComposite; |
| |
| |
| public class FindDataDialog extends ExtStatusDialog { |
| |
| |
| private static final Map<IWorkbenchWindow, FindDataDialog> gDialogs= new HashMap<>(); |
| |
| |
| public static FindDataDialog get(final IWorkbenchWindow window, final boolean create) { |
| FindDataDialog dialog= gDialogs.get(window); |
| if (dialog == null && create) { |
| dialog= new FindDataDialog(window); |
| dialog.create(); |
| gDialogs.put(window, dialog); |
| } |
| return dialog; |
| } |
| |
| |
| private static final int FIND_NEXT_ID= 101; |
| private static final int FIND_PREVIOUS_ID= 102; |
| |
| |
| private class PartListener implements IWindowListener, IPartListener { |
| |
| @Override |
| public void windowOpened(final IWorkbenchWindow window) { |
| } |
| @Override |
| public void windowClosed(final IWorkbenchWindow window) { |
| if (FindDataDialog.this.fWindow == window) { |
| close(); |
| } |
| } |
| @Override |
| public void windowActivated(final IWorkbenchWindow window) { |
| } |
| @Override |
| public void windowDeactivated(final IWorkbenchWindow window) { |
| } |
| |
| @Override |
| public void partOpened(final IWorkbenchPart part) { |
| } |
| @Override |
| public void partClosed(final IWorkbenchPart part) { |
| if (FindDataDialog.this.fPart == part) { |
| FindDataDialog.this.fPart= null; |
| } |
| } |
| @Override |
| public void partActivated(final IWorkbenchPart part) { |
| FindDataDialog.this.fPart= part; |
| update(part); |
| } |
| @Override |
| public void partDeactivated(final IWorkbenchPart part) { |
| } |
| @Override |
| public void partBroughtToTop(final IWorkbenchPart part) { |
| } |
| |
| } |
| |
| |
| private final IWorkbenchWindow fWindow; |
| private IWorkbenchPart fPart; |
| |
| private final PartListener fPartListener= new PartListener(); |
| |
| private RDataTableComposite fTable; |
| |
| private Combo fTextControl; |
| private Button fRExpressionModeControl; |
| private Button fIsNaModeControl; |
| private Button fDirectionFirstInColumnControl; |
| private Button fDirectionFirstInRowControl; |
| private Button fSelectedOnlyControl; |
| |
| private final Map<String, String> fHistoryModeMap= new HashMap<>(); |
| |
| |
| private final IFindListener fFindListener= new IFindListener() { |
| @Override |
| public void handleFindEvent(final IFindListener.FindEvent event) { |
| updateStatus(event.status); |
| } |
| }; |
| |
| |
| protected FindDataDialog(final IWorkbenchWindow window) { |
| super(window.getShell()); |
| |
| this.fWindow= window; |
| this.fWindow.getWorkbench().addWindowListener(this.fPartListener); |
| this.fWindow.getActivePage().addPartListener(this.fPartListener); |
| this.fPart= this.fWindow.getActivePage().getActivePart(); |
| |
| setTitle("Find"); |
| setShellStyle((getShellStyle() & ~SWT.APPLICATION_MODAL) | SWT.MODELESS); |
| setBlockOnOpen(false); |
| setStatusLineAboveButtons(false); |
| setHelpAvailable(false); |
| } |
| |
| |
| protected IDialogSettings getDialogSettings() { |
| return DialogUtils.getDialogSettings(RUIPlugin.getInstance(), "RDataTable.FindDialog"); |
| } |
| |
| @Override |
| protected IDialogSettings getDialogBoundsSettings() { |
| return getDialogSettings(); |
| } |
| |
| @Override |
| public void create() { |
| super.create(); |
| |
| loadSettings(); |
| } |
| |
| @Override |
| public boolean close() { |
| this.fWindow.getWorkbench().removeWindowListener(this.fPartListener); |
| final IWorkbenchPage page= this.fWindow.getActivePage(); |
| if (page != null) { |
| page.removePartListener(this.fPartListener); |
| } |
| |
| gDialogs.remove(this.fWindow); |
| saveSettings(); |
| |
| return super.close(); |
| } |
| |
| @Override |
| protected Control createDialogArea(final Composite parent) { |
| final Composite composite= new Composite(parent, SWT.NONE); |
| composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); |
| composite.setLayout(LayoutUtils.newDialogGrid(2)); |
| |
| final Composite textInput= createTextInput(composite); |
| textInput.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1)); |
| |
| final Composite additionalOptions= createAdditionalOptions(composite); |
| additionalOptions.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1)); |
| |
| final Composite directionOptions= createDirectionOptions(composite); |
| directionOptions.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1)); |
| |
| // final Composite scopeOptions= createScopeOptions(composite); |
| // scopeOptions.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); |
| // |
| LayoutUtils.addSmallFiller(composite, false); |
| final Composite navigateButtons= createNavigateButtons(composite); |
| navigateButtons.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 2, 1)); |
| |
| applyDialogFont(composite); |
| return composite; |
| } |
| |
| protected Composite createTextInput(final Composite parent) { |
| final Composite composite= new Composite(parent, SWT.NONE); |
| composite.setLayout(LayoutUtils.newCompositeGrid(2)); |
| |
| final Label label= new Label(composite, SWT.NONE); |
| label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); |
| label.setText("Find:"); |
| |
| this.fTextControl= new Combo(composite, SWT.DROP_DOWN | SWT.BORDER); |
| this.fTextControl.setLayoutData(LayoutUtils.hintWidth( |
| new GridData(SWT.FILL, SWT.CENTER, true, false), this.fTextControl, 25 )); |
| |
| this.fTextControl.addListener(SWT.Modify, new Listener() { |
| @Override |
| public void handleEvent(final Event event) { |
| updateStatus(null); |
| updateState(); |
| } |
| }); |
| this.fTextControl.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(final SelectionEvent e) { |
| final int selectionIdx; |
| if (!FindDataDialog.this.fTextControl.getListVisible() |
| && (selectionIdx= FindDataDialog.this.fTextControl.getSelectionIndex()) >= 0) { |
| loadQuery(FindDataDialog.this.fTextControl.getItem(selectionIdx)); |
| } |
| } |
| }); |
| |
| return composite; |
| } |
| |
| protected Composite createDirectionOptions(final Composite parent) { |
| final Group composite= new Group(parent, SWT.NONE); |
| composite.setText("Direction"); |
| composite.setLayout(LayoutUtils.newGroupGrid(2, true)); |
| |
| { this.fDirectionFirstInColumnControl= new Button(composite, SWT.RADIO); |
| this.fDirectionFirstInColumnControl.setText("&Column, Row"); |
| this.fDirectionFirstInColumnControl.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); |
| this.fDirectionFirstInColumnControl.setSelection(true); |
| } |
| { this.fDirectionFirstInRowControl= new Button(composite, SWT.RADIO); |
| this.fDirectionFirstInRowControl.setText("&Row, Column"); |
| this.fDirectionFirstInRowControl.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); |
| } |
| { this.fSelectedOnlyControl= new Button(composite, SWT.CHECK); |
| this.fSelectedOnlyControl.setText("Only Selec&ted Cells"); |
| this.fSelectedOnlyControl.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); |
| } |
| |
| return composite; |
| } |
| |
| protected Composite createScopeOptions(final Composite parent) { |
| final Group composite= new Group(parent, SWT.NONE); |
| composite.setText("Scope"); |
| composite.setLayout(LayoutUtils.newGroupGrid(1)); |
| |
| { final Button button= new Button(composite, SWT.RADIO); |
| button.setText("A&ll"); |
| button.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); |
| button.setSelection(true); |
| } |
| { this.fSelectedOnlyControl= new Button(composite, SWT.CHECK); |
| this.fSelectedOnlyControl.setText("Selec&ted Cells"); |
| this.fSelectedOnlyControl.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); |
| } |
| return composite; |
| } |
| |
| protected Composite createAdditionalOptions(final Composite parent) { |
| final Group composite= new Group(parent, SWT.NONE); |
| composite.setText("Options"); |
| composite.setLayout(LayoutUtils.newGroupGrid(2, true)); |
| |
| { this.fRExpressionModeControl= new Button(composite, SWT.CHECK); |
| this.fRExpressionModeControl.setText("R expression"); |
| this.fRExpressionModeControl.setToolTipText("Use x to reference the object itself, e.g. 'x >= 1'"); |
| this.fRExpressionModeControl.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); |
| this.fRExpressionModeControl.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(final SelectionEvent e) { |
| if (FindDataDialog.this.fRExpressionModeControl.getSelection()) { |
| FindDataDialog.this.fIsNaModeControl.setSelection(false); |
| } |
| } |
| }); |
| } |
| |
| { this.fIsNaModeControl= new Button(composite, SWT.CHECK); |
| this.fIsNaModeControl.setText("Is NA"); |
| this.fIsNaModeControl.setToolTipText("The search expression is ignored"); |
| this.fIsNaModeControl.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); |
| this.fIsNaModeControl.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(final SelectionEvent e) { |
| if (FindDataDialog.this.fIsNaModeControl.getSelection()) { |
| FindDataDialog.this.fRExpressionModeControl.setSelection(false); |
| } |
| } |
| }); |
| } |
| |
| return composite; |
| } |
| |
| protected Composite createNavigateButtons(final Composite parent) { |
| final Composite composite= new Composite(parent, SWT.NONE); |
| composite.setLayout(LayoutUtils.newCompositeGrid(0)); |
| |
| createButton(composite, FIND_PREVIOUS_ID, "&Previous", false); |
| createButton(composite, FIND_NEXT_ID, "&Next", true); |
| |
| return composite; |
| } |
| |
| @Override |
| protected void createButtonsForButtonBar(final Composite parent) { |
| createButton(parent, IDialogConstants.CLOSE_ID, IDialogConstants.CLOSE_LABEL, false); |
| } |
| |
| @Override |
| protected void buttonPressed(final int buttonId) { |
| switch (buttonId) { |
| case IDialogConstants.CLOSE_ID: |
| close(); |
| return; |
| case FIND_NEXT_ID: |
| doFind(true); |
| return; |
| case FIND_PREVIOUS_ID: |
| doFind(false); |
| return; |
| } |
| } |
| |
| private void loadQuery(final String text) { |
| if (text == null || text.isEmpty()) { |
| return; |
| } |
| |
| final String mode= this.fHistoryModeMap.get(text); |
| if (mode != null) { |
| if ("r_expression".equals(mode)) { |
| this.fRExpressionModeControl.setSelection(true); |
| this.fIsNaModeControl.setSelection(false); |
| } |
| else { |
| this.fRExpressionModeControl.setSelection(false); |
| } |
| } |
| } |
| |
| private void doFind(final boolean forward) { |
| final String expression= getExpression(); |
| if (!expression.equals("is.na(x)")) { |
| final String text= this.fTextControl.getText(); |
| final int idx= this.fTextControl.indexOf(text); |
| if (idx != 0) { |
| if (idx > 0) { |
| this.fTextControl.remove(idx); |
| } |
| this.fTextControl.add(text, 0); |
| this.fTextControl.setText(text); |
| } |
| this.fHistoryModeMap.put(text, |
| this.fRExpressionModeControl.getSelection() ? "r_expression" : "default"); |
| } |
| this.fTable.find(expression, |
| this.fSelectedOnlyControl.getSelection(), |
| this.fDirectionFirstInRowControl.getSelection(), |
| forward); |
| } |
| |
| protected String getExpression() { |
| if (this.fRExpressionModeControl.getSelection()) { |
| return this.fTextControl.getText(); |
| } |
| else if (this.fIsNaModeControl.getSelection()) { |
| return "is.na(x)"; |
| } |
| else { |
| return "x == " + this.fTextControl.getText(); |
| } |
| } |
| |
| protected void loadSettings() { |
| final IDialogSettings settings= getDialogSettings(); |
| final String[] history= DialogUtils.noNull(settings.getArray("SearchText.history")); |
| this.fTextControl.setItems(history); |
| final String[] historyModes= DialogUtils.noNull(settings.getArray("SearchMode.history")); |
| if (history.length == historyModes.length) { |
| for (int i= 0; i < history.length; i++) { |
| this.fHistoryModeMap.put(history[i], historyModes[i]); |
| } |
| } |
| if (history.length > 0) { |
| this.fTextControl.setText(history[0]); |
| loadQuery(history[0]); |
| } |
| if (settings.getBoolean("Direction.firstInRow")) { |
| this.fDirectionFirstInColumnControl.setSelection(false); |
| this.fDirectionFirstInRowControl.setSelection(true); |
| } |
| else { |
| this.fDirectionFirstInColumnControl.setSelection(true); |
| this.fDirectionFirstInRowControl.setSelection(false); |
| } |
| } |
| |
| protected void saveSettings() { |
| final IDialogSettings settings= getDialogSettings(); |
| final String[] history= DialogUtils.combineHistoryItems(this.fTextControl.getItems(), null); |
| settings.put("SearchText.history", history); |
| final String[] historyModes= new String[history.length]; |
| for (int i= 0; i < history.length; i++) { |
| String mode= this.fHistoryModeMap.get(history[i]); |
| if (mode == null) { |
| mode= "default"; |
| } |
| historyModes[i]= mode; |
| } |
| settings.put("SearchMode.history", historyModes); |
| settings.put("Direction.firstInRow", this.fDirectionFirstInRowControl.getSelection()); |
| } |
| |
| |
| public void update(final IWorkbenchPart part) { |
| if (part != null && this.fPart == part) { |
| final RDataTableComposite table= part.getAdapter(RDataTableComposite.class); |
| if (this.fTable != null && this.fTable != table) { |
| this.fTable.removeFindListener(this.fFindListener); |
| updateStatus(null); |
| } |
| |
| this.fTable= table; |
| if (this.fTable != null) { |
| table.addFindListener(this.fFindListener); |
| } |
| updateState(); |
| } |
| } |
| |
| private void updateState() { |
| final boolean enabled= (this.fTable != null && this.fTextControl.getText().length() > 0); |
| getButton(FIND_NEXT_ID).setEnabled(enabled); |
| getButton(FIND_PREVIOUS_ID).setEnabled(enabled); |
| } |
| |
| } |