/* ****************************************************************************** * Copyright (c) 2006-2012 XMind Ltd. and others. * * This file is a part of XMind 3. XMind releases 3 and * above are dual-licensed under the Eclipse Public License (EPL), * which is available at http://www.eclipse.org/legal/epl-v10.html * and the GNU Lesser General Public License (LGPL), * which is available at http://www.gnu.org/licenses/lgpl.html * See http://www.xmind.net/license.html for details. * * Contributors: * XMind Ltd. - initial API and implementation *******************************************************************************/ package org.xmind.ui.internal.findreplace; import static org.eclipse.jface.dialogs.IDialogConstants.CLIENT_ID; import static org.eclipse.jface.dialogs.IDialogConstants.CLOSE_ID; import static org.eclipse.jface.dialogs.IDialogConstants.CLOSE_LABEL; import static org.eclipse.jface.dialogs.IDialogConstants.HORIZONTAL_SPACING; import static org.eclipse.jface.dialogs.IDialogConstants.VERTICAL_SPACING; import static org.xmind.ui.internal.findreplace.IFindReplaceOperationProvider.PARAM_CURRENT_MAP; import static org.xmind.ui.internal.findreplace.IFindReplaceOperationProvider.PARAM_FORWARD; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; 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.Display; 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.swt.widgets.Monitor; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IPartListener; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchWindow; import org.xmind.ui.internal.MindMapUIPlugin; import org.xmind.ui.internal.dialogs.DialogMessages; import org.xmind.ui.resources.FontUtils; /** * @author Frank Shaka */ public class FindReplaceDialog extends Dialog implements IPartListener { private static final String SETTINGS_SECTION_NAME = "org.xmind.ui.findreplace"; //$NON-NLS-1$ private static final String P_PARAMETER = "parameter"; //$NON-NLS-1$ private static final String EMPTY = ""; //$NON-NLS-1$ private static final String STRING_NOT_FOUND = DialogMessages.FindReplaceDialog_StringNotFound; private static Map<IWorkbenchWindow, FindReplaceDialog> SINGLETONS = new HashMap<IWorkbenchWindow, FindReplaceDialog>(); private static final int TEXT_WIDTH = 120; private static final int FIND_ID = CLIENT_ID + 1; private static final int FIND_ALL_ID = CLIENT_ID + 2; private static final int REPLACE_ID = CLIENT_ID + 3; private static final int REPLACE_ALL_ID = CLIENT_ID + 4; private static List<String> FindHistory = new ArrayList<String>(); private static List<String> ReplaceHistory = new ArrayList<String>(); private class EventHandler implements Listener { public void handleEvent(Event event) { if (event.type == SWT.Modify) { updateOperationButtons(); infoLabel.setText(EMPTY); } else if (event.type == SWT.FocusIn) { if (event.widget instanceof Combo) { Combo input = (Combo) event.widget; input.setSelection(new Point(0, input.getText().length())); } } else if (event.type == SWT.Selection) { Button b = (Button) event.widget; if (opButtons != null && opButtons.containsValue(b)) { buttonPressed((Integer) b.getData()); } else if (paramWidgets != null) { List<Button> list = paramWidgets.get(b.getData()); if (list.contains(b)) { int param = ((Integer) b.getData()).intValue(); if (b.getSelection()) { parameter |= param; } else { parameter &= ~param; } if (operationProvider != null) { operationProvider.setParameter(param, b.getSelection()); } updateOperationButtons(); } } } } } private IWorkbenchWindow window; private IWorkbenchPart currentPart; private Label contextLabel; private Combo findInput; private Combo replaceInput; private Label infoLabel; private Map<Integer, Button> opButtons = new HashMap<Integer, Button>(); private Map<Integer, List<Button>> paramWidgets = new HashMap<Integer, List<Button>>(); private Listener eventHandler = new EventHandler(); private IFindReplaceOperationProvider operationProvider; private int parameter = PARAM_FORWARD | PARAM_CURRENT_MAP; private String initialFindText = null; protected FindReplaceDialog(final IWorkbenchWindow window) { super(window.getShell()); this.window = window; setShellStyle(SWT.TITLE | SWT.MODELESS | SWT.CLOSE); SINGLETONS.put(window, this); window.getShell().addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { close(); SINGLETONS.remove(window); } }); } /** * */ private void startListeningToPartChanges() { window.getPartService().addPartListener(this); } private void stopListeningToPartChanges() { window.getPartService().removePartListener(this); } public void setInitialFindText(String initialFindText) { this.initialFindText = initialFindText; } private void loadParameter() { IDialogSettings ds = MindMapUIPlugin.getDefault().getDialogSettings( SETTINGS_SECTION_NAME); if (ds != null) { try { parameter = ds.getInt(P_PARAMETER); } catch (Exception e) { } } } private void saveParameter() { IDialogSettings ds = MindMapUIPlugin.getDefault().getDialogSettings( SETTINGS_SECTION_NAME); if (ds != null) { ds.put(P_PARAMETER, parameter); } } /** * @see org.eclipse.jface.dialogs.Dialog#getInitialLocation(org.eclipse.swt.graphics.Point) */ @Override protected Point getInitialLocation(Point initialSize) { Composite parent = getShell().getParent(); Monitor monitor = getShell().getDisplay().getPrimaryMonitor(); if (parent != null) { monitor = parent.getMonitor(); } Rectangle monitorBounds = monitor.getClientArea(); Point centerPoint; if (parent != null) { centerPoint = getDialogCenter(parent.getBounds()); } else { centerPoint = getDialogCenter(monitorBounds); } return new Point(centerPoint.x - (initialSize.x / 2), Math.max( monitorBounds.y, Math.min(centerPoint.y - (initialSize.y / 2), monitorBounds.y + monitorBounds.height - initialSize.y))); } private static Point getDialogCenter(Rectangle rect) { return new Point(rect.x + rect.width * 2 / 3, rect.y + rect.height / 2); } public void create() { super.create(); IWorkbenchPart activePart = window.getActivePage().getActivePart(); partActivated(activePart); updateContextName(); } /** * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite) */ @Override protected Control createDialogArea(Composite parent) { Composite composite = (Composite) super.createDialogArea(parent); GridLayout layout = (GridLayout) composite.getLayout(); layout.verticalSpacing = 10; layout.marginTop = layout.marginHeight; layout.marginHeight = 0; layout.marginBottom = 0; createContextLabel(composite); createInputGroup(composite); createParameterGroups(composite); createOperationButtonGroup(composite); return composite; } protected void createContextLabel(Composite parent) { contextLabel = new Label(parent, SWT.NONE); contextLabel.setFont(FontUtils.getNewHeight( JFaceResources.DEFAULT_FONT, 11)); GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, false); contextLabel.setLayoutData(layoutData); } /** * @param inputArea * @param factory */ protected void createInputGroup(Composite parent) { Composite composite = new Composite(parent, SWT.NO_FOCUS); composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); GridLayout inputLayout = new GridLayout(2, false); inputLayout.marginWidth = 0; inputLayout.marginHeight = 0; composite.setLayout(inputLayout); findInput = createInputWidget(composite, DialogMessages.FindReplaceDialog_Find_label, FindHistory, initialFindText); replaceInput = createInputWidget(composite, DialogMessages.FindReplaceDialog_ReplaceWith_label, ReplaceHistory, null); } /** * @param parent * @param factory * @param label * @param history * @param initText */ private Combo createInputWidget(Composite parent, String label, List<String> history, String initText) { Label labelWidget = new Label(parent, SWT.NONE); labelWidget.setText(label); labelWidget .setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true)); final Combo input = new Combo(parent, SWT.SINGLE | SWT.BORDER | SWT.DROP_DOWN); GridData layoutData = new GridData(SWT.FILL, SWT.CENTER, true, true); layoutData.widthHint = TEXT_WIDTH; input.setLayoutData(layoutData); for (String t : history) { input.add(t); } if (initText != null) input.setText(initText); else if (!history.isEmpty()) input.setText(history.get(0)); input.addListener(SWT.Modify, eventHandler); input.addListener(SWT.FocusIn, eventHandler); return input; } /** * @param optionsGroup * @param factory */ protected void createParameterGroups(Composite parent) { Composite composite = new Composite(parent, SWT.NO_FOCUS); composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); GridLayout layout = new GridLayout(2, true); layout.marginWidth = 0; layout.marginHeight = 0; layout.verticalSpacing = convertVerticalDLUsToPixels(VERTICAL_SPACING); layout.horizontalSpacing = convertHorizontalDLUsToPixels(HORIZONTAL_SPACING); composite.setLayout(layout); Composite optionGroup = createParameterGroup(composite, DialogMessages.FindReplaceDialog_OptionGroup); ((GridData) optionGroup.getLayoutData()).horizontalSpan = 2; createParameterWidget(optionGroup, SWT.CHECK, DialogMessages.FindReplaceDialog_CaseSensitive, IFindReplaceOperationProvider.PARAM_CASE_SENSITIVE); createParameterWidget(optionGroup, SWT.CHECK, DialogMessages.FindReplaceDialog_WholeWord, IFindReplaceOperationProvider.PARAM_WHOLE_WORD); Composite directionGroup = createParameterGroup(composite, DialogMessages.FindReplaceDialog_DirectionGroup); createParameterWidget(directionGroup, SWT.RADIO, DialogMessages.FindReplaceDialog_Forward, IFindReplaceOperationProvider.PARAM_FORWARD); createParameterWidget(directionGroup, SWT.RADIO, DialogMessages.FindReplaceDialog_Backward, IFindReplaceOperationProvider.PARAM_BACKWARD); Composite scopeGroup = createParameterGroup(composite, DialogMessages.FindReplaceDialog_ScopeGroup); createParameterWidget(scopeGroup, SWT.RADIO, DialogMessages.FindReplaceDialog_CurrentMap, IFindReplaceOperationProvider.PARAM_CURRENT_MAP); createParameterWidget(scopeGroup, SWT.RADIO, DialogMessages.FindReplaceDialog_Workbook, IFindReplaceOperationProvider.PARAM_WORKBOOK); } private Composite createParameterGroup(Composite composite, String text) { Group group = new Group(composite, SWT.NO_FOCUS); group.setText(text); GridData groupLayoutData = new GridData(SWT.FILL, SWT.FILL, true, true); group.setLayoutData(groupLayoutData); GridLayout groupLayout = new GridLayout(); groupLayout.horizontalSpacing = 7; groupLayout.verticalSpacing = 7; group.setLayout(groupLayout); return group; } protected Button createParameterWidget(Composite parent, int style, String text, int paramId) { Button button = new Button(parent, style); button.setText(text); button.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); addParameterWidget(paramId, button); button.setData(Integer.valueOf(paramId)); button.addListener(SWT.Selection, eventHandler); return button; } private void addParameterWidget(final int paramId, final Button widget) { Integer key = Integer.valueOf(paramId); List<Button> widgets = paramWidgets.get(key); if (widgets == null) { widgets = new ArrayList<Button>(); paramWidgets.put(key, widgets); } widgets.add(widget); paramWidgets.put(key, widgets); widget.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { removeParameterWidget(paramId, widget); } }); } private void removeParameterWidget(int paramId, Button widget) { Integer key = Integer.valueOf(paramId); List<Button> widgets = paramWidgets.get(key); if (widgets != null) { widgets.remove(widget); if (widgets.isEmpty()) { paramWidgets.remove(key); } } } private void updateParameterWidgets() { for (Integer paramId : paramWidgets.keySet()) { updateParameterWidget(paramId); } } private void updateParameterWidget(Integer paramId) { List<Button> widgets = paramWidgets.get(paramId); if (widgets != null) { boolean enabled = operationProvider != null && operationProvider.understandsPatameter(paramId .intValue()); for (Button widget : widgets) { widget.setEnabled(enabled); widget.setSelection(hasParameter(paramId.intValue())); } } } private boolean hasParameter(int paramId) { if (operationProvider == null) return false; int bit = parameter & paramId; return bit != 0; } /** * @param buttons * @param factory */ protected void createOperationButtonGroup(Composite parent) { Composite buttons = new Composite(parent, SWT.NO_FOCUS); buttons.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); GridLayout buttonsLayout = new GridLayout(2, true); buttonsLayout.marginHeight = 0; buttonsLayout.marginWidth = 0; buttons.setLayout(buttonsLayout); createOperationButton(buttons, FIND_ID, DialogMessages.FindReplaceDialog_Find_text, true); createOperationButton(buttons, FIND_ALL_ID, DialogMessages.FindReplaceDialog_FindAll_text, false); createOperationButton(buttons, REPLACE_ID, DialogMessages.FindReplaceDialog_Replace_text, false); createOperationButton(buttons, REPLACE_ALL_ID, DialogMessages.FindReplaceDialog_ReplaceAll_text, false); } protected Button createOperationButton(Composite parent, int id, String text, boolean defaultButton) { Button button = new Button(parent, SWT.PUSH); button.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); button.setText(text); button.setData(id); button.addListener(SWT.Selection, eventHandler); if (defaultButton) { Shell shell = parent.getShell(); if (shell != null) { shell.setDefaultButton(button); } } registerOperationButton(id, button); return button; } protected void registerOperationButton(final int id, final Button widget) { opButtons.put(Integer.valueOf(id), widget); widget.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { unregisterOperationButton(id, widget); } }); } private void unregisterOperationButton(int id, Button widget) { Integer key = Integer.valueOf(id); if (opButtons.get(key) == widget) { opButtons.remove(key); } } /** * @see org.eclipse.jface.dialogs.Dialog#getButton(int) */ @Override protected Button getButton(int id) { Button button = super.getButton(id); if (button == null) button = opButtons.get(Integer.valueOf(id)); return button; } /** * @see org.eclipse.jface.dialogs.Dialog#createButtonBar(org.eclipse.swt.widgets.Composite) */ @Override protected Control createButtonBar(Composite parent) { Composite container = new Composite(parent, SWT.NO_FOCUS); container.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); GridLayout containerLayout = new GridLayout(2, false); containerLayout.horizontalSpacing = 0; containerLayout.verticalSpacing = 0; containerLayout.marginHeight = 7; containerLayout.marginWidth = 7; container.setLayout(containerLayout); infoLabel = new Label(container, SWT.NONE); infoLabel.setBackground(container.getBackground()); infoLabel.setFont(container.getFont()); infoLabel.setForeground(container.getDisplay().getSystemColor( SWT.COLOR_RED)); infoLabel.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, true, true)); Composite composite = (Composite) super.createButtonBar(container); GridLayout layout = (GridLayout) composite.getLayout(); layout.marginHeight = 0; layout.marginWidth = 0; return composite; } /** * @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite) */ @Override protected void createButtonsForButtonBar(Composite parent) { createButton(parent, CLOSE_ID, CLOSE_LABEL, false); } /** * @see org.eclipse.jface.dialogs.Dialog#buttonPressed(int) */ @Override protected void buttonPressed(int buttonId) { if (CLOSE_ID == buttonId) { closePressed(); } else if (FIND_ID == buttonId) { findPressed(); } else if (FIND_ALL_ID == buttonId) { findAllPressed(); } else if (REPLACE_ID == buttonId) { replacePressed(); } else if (REPLACE_ALL_ID == buttonId) { replaceAllPressed(); } else super.buttonPressed(buttonId); } /** * */ protected void findPressed() { saveHistories(); if (operationProvider == null) return; if (operationProvider.find(getFindText())) { setInfoText(EMPTY); } else { setInfoText(STRING_NOT_FOUND); } final Shell shell = getShell(); final Display display = shell.getDisplay(); display.asyncExec(new Runnable() { public void run() { if (!shell.isDisposed()) { shell.setActive(); findInput.setFocus(); } } }); } /** * */ protected void findAllPressed() { saveHistories(); if (operationProvider == null) return; boolean all = (parameter & IFindReplaceOperationProvider.PARAM_ALL) != 0; operationProvider.setParameter(IFindReplaceOperationProvider.PARAM_ALL, true); if (operationProvider.find(getFindText())) { setInfoText(EMPTY); } else { setInfoText(STRING_NOT_FOUND); } operationProvider.setParameter(IFindReplaceOperationProvider.PARAM_ALL, all); findInput.setFocus(); } /** * */ protected void replacePressed() { saveHistories(); if (operationProvider == null) return; if (operationProvider.replace(getFindText(), getReplaceText())) { setInfoText(EMPTY); } else { setInfoText(STRING_NOT_FOUND); } findInput.setFocus(); } /** * */ protected void replaceAllPressed() { saveHistories(); if (operationProvider == null) return; boolean all = (parameter & IFindReplaceOperationProvider.PARAM_ALL) != 0; operationProvider.setParameter(IFindReplaceOperationProvider.PARAM_ALL, true); if (operationProvider.replace(getFindText(), getReplaceText())) { setInfoText(EMPTY); } else { setInfoText(STRING_NOT_FOUND); } operationProvider.setParameter(IFindReplaceOperationProvider.PARAM_ALL, all); findInput.setFocus(); } protected void closePressed() { close(); } /** * @see org.eclipse.jface.dialogs.Dialog#close() */ @Override public boolean close() { saveHistories(); return super.close(); } /** * @return */ public String getFindText() { return findInput == null || findInput.isDisposed() ? EMPTY : findInput .getText(); } /** * @return */ public String getReplaceText() { return replaceInput == null || replaceInput.isDisposed() ? EMPTY : replaceInput.getText(); } protected void setInfoText(String text) { if (infoLabel != null && !infoLabel.isDisposed()) { infoLabel.setText(text); infoLabel.getParent().layout(); } } protected void saveHistories() { if (getShell() == null || getShell().isDisposed()) return; saveHistory(FindHistory, findInput); saveHistory(ReplaceHistory, replaceInput); } protected void saveHistory(List<String> history, Combo input) { String text = input.getText(); if (text == null || EMPTY.equals(text)) return; if (history.remove(text) && !input.isDisposed()) input.remove(text); history.add(0, text); if (!input.isDisposed()) { input.add(text, 0); input.setText(text); } } protected void updateOperationButtons() { if (getShell() == null || getShell().isDisposed()) return; getButton(FIND_ID).setEnabled(canFind()); getButton(FIND_ALL_ID).setEnabled(canFindAll()); getButton(REPLACE_ID).setEnabled(canReplace()); getButton(REPLACE_ALL_ID).setEnabled(canReplaceAll()); } protected boolean canFind() { return getFindText() != null && !EMPTY.equals(getFindText()) && operationProvider != null && operationProvider.canFind(getFindText()); } protected boolean canReplace() { return canFind() && getReplaceText() != null && operationProvider != null && operationProvider .canReplace(getFindText(), getReplaceText()); } protected boolean canFindAll() { return getFindText() != null && !EMPTY.equals(getFindText()) && operationProvider != null && operationProvider.canFindAll(getFindText()); } protected boolean canReplaceAll() { return getFindText() != null && !EMPTY.equals(getFindText()) && getReplaceText() != null && operationProvider != null && operationProvider.canReplaceAll(getFindText(), getReplaceText()); } /** * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell) */ @Override protected void configureShell(Shell newShell) { super.configureShell(newShell); newShell.setText(DialogMessages.FindReplaceDialog_windowTitle); loadParameter(); startListeningToPartChanges(); newShell.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { saveParameter(); stopListeningToPartChanges(); } }); } private void updateContextName() { if (getShell() == null || getShell().isDisposed()) return; String contextName = operationProvider == null ? null : operationProvider.getContextName(contextLabel.getSize().x, contextLabel.getFont()); if (contextName == null || "".equals(contextName)) { //$NON-NLS-1$ contextLabel.setText(""); //$NON-NLS-1$ } else { contextLabel.setText(contextName); } contextLabel.getParent().getParent().layout(); } /** * @param operationProvider * the operationProvider to set */ public void setOperationProvider( IFindReplaceOperationProvider operationProvider) { this.operationProvider = operationProvider; if (operationProvider != null) { operationProvider.setParameter(parameter); } } /** * @return the operationProvider */ public IFindReplaceOperationProvider getOperationProvider() { return operationProvider; } public void partActivated(IWorkbenchPart part) { this.currentPart = part; setOperationProvider(getOperationProvider(part)); updateContextName(); updateOperationButtons(); updateParameterWidgets(); } public void partBroughtToTop(IWorkbenchPart part) { } public void partClosed(IWorkbenchPart part) { if (part != this.currentPart) return; setOperationProvider(null); updateContextName(); updateOperationButtons(); updateParameterWidgets(); } public void partDeactivated(IWorkbenchPart part) { } public void partOpened(IWorkbenchPart part) { } private IFindReplaceOperationProvider getOperationProvider( IWorkbenchPart part) { if (part == null) return null; return (IFindReplaceOperationProvider) part .getAdapter(IFindReplaceOperationProvider.class); } public static FindReplaceDialog getInstance(IWorkbenchWindow window) { FindReplaceDialog instance = SINGLETONS.get(window); if (instance != null) return instance; return new FindReplaceDialog(window); } }