/** * Copyright 2004-2016 Riccardo Solmi. All rights reserved. * This file is part of the Whole Platform. * * The Whole Platform is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The Whole Platform is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the Whole Platform. If not, see <http://www.gnu.org/licenses/>. */ package org.whole.lang.e4.ui.dialogs; import javax.inject.Inject; import javax.inject.Named; import org.eclipse.e4.core.contexts.Active; import org.eclipse.e4.core.contexts.ContextInjectionFactory; import org.eclipse.e4.core.contexts.EclipseContextFactory; import org.eclipse.e4.core.contexts.IEclipseContext; import org.eclipse.e4.core.di.annotations.Optional; import org.eclipse.e4.ui.model.application.ui.basic.MPart; import org.eclipse.e4.ui.services.IServiceConstants; import org.eclipse.gef.ContextMenuProvider; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.FocusListener; import org.eclipse.swt.events.ShellAdapter; import org.eclipse.swt.events.ShellEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.whole.lang.bindings.BindingManagerFactory; import org.whole.lang.bindings.IBindingManager; import org.whole.lang.commons.factories.CommonsEntityFactory; import org.whole.lang.e4.ui.actions.ActionRegistry; import org.whole.lang.e4.ui.actions.E4KeyHandler; import org.whole.lang.e4.ui.actions.E4NavigationKeyHandler; import org.whole.lang.e4.ui.actions.IE4UIConstants; import org.whole.lang.e4.ui.util.E4Utils; import org.whole.lang.e4.ui.viewers.E4GraphicalViewer; import org.whole.lang.iterators.IteratorFactory; import org.whole.lang.iterators.MatcherIterator; import org.whole.lang.matchers.Matcher; import org.whole.lang.model.IEntity; import org.whole.lang.ui.commands.ModelTransactionCommand; import org.whole.lang.ui.editparts.IEntityPart; import org.whole.lang.ui.editparts.IPartFocusListener; import org.whole.lang.ui.viewers.IEntityPartViewer; import org.whole.lang.util.EntityUtils; /** * @author Enrico Persiani */ public class E4FindReplaceDialog extends E4Dialog { private static final int FIND_ID = IDialogConstants.CLIENT_ID + 1; private static final int REPLACE_ID = IDialogConstants.CLIENT_ID + 2; private static final int REPLACE_FIND_ID = IDialogConstants.CLIENT_ID + 3; private static final int REPLACE_ALL_ID = IDialogConstants.CLIENT_ID + 4; private static final int VIEWER_MINIMUM_HEIGHT = 200; private static final int VIEWER_MINIMUM_WIDTH = 400; protected MatcherIterator<IEntity> iterator; protected IBindingManager bindings; protected IBindingManager selection; protected boolean selectionTracking; protected Control replaceArea; protected E4GraphicalViewer replaceViewer; protected ActionRegistry replaceActionRegistry; protected Control buttonPanel; protected Control statusPanel; protected Label statusLabel; protected IEntity foundEntity; protected boolean freshTemplate; @Inject public E4FindReplaceDialog(@Named(IServiceConstants.ACTIVE_SHELL) Shell shell) { super(shell); setShellStyle(getShellStyle() ^ SWT.APPLICATION_MODAL); setBlockOnOpen(false); this.iterator = IteratorFactory.descendantOrSelfMatcherIterator(); this.bindings = BindingManagerFactory.instance.createArguments(); enableSelectionTracking(true); clearFoundEntity(); clearFreshTemplate(); } @Override protected boolean isResizable() { return true; } @Override protected void configureShell(Shell shell) { super.configureShell(shell); shell.setText(IE4UIConstants.FIND_REPLACE_DIALOG_TEXT); setBlockOnOpen(false); } @Override protected Control createDialogArea(Composite parent) { SashForm sashForm = new SashForm(parent, SWT.HORIZONTAL | SWT.SMOOTH); sashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); super.createDialogArea(sashForm); this.replaceArea = createReplaceArea(sashForm); return sashForm; } protected Control createReplaceArea(Composite parent) { IEclipseContext params = EclipseContextFactory.create(); params.set("parent", parent); replaceViewer = ContextInjectionFactory.make(E4GraphicalViewer.class, context, params); replaceViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); replaceViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { updateSelection(E4Utils.createSelectionBindings(event, context)); } }); replaceViewer.getControl().addFocusListener(new FocusListener() { @Override public void focusLost(FocusEvent e) { } @SuppressWarnings("unchecked") @Override public void focusGained(FocusEvent e) { context.set(IEntityPartViewer.class, replaceViewer); context.set(ActionRegistry.class, replaceActionRegistry); updateSelection(E4Utils.createSelectionBindings(replaceViewer.getSelectedEditParts(), replaceViewer, context)); } }); replaceViewer.addPartFocusListener(new IPartFocusListener() { @SuppressWarnings("unchecked") public void focusChanged(IEntityPart oldPart, IEntityPart newPart) { updateSelection(E4Utils.createSelectionBindings(replaceViewer.getSelectedEditParts(), replaceViewer, context)); } }); E4KeyHandler keyHandler = new E4KeyHandler(context); keyHandler.setParent(new E4NavigationKeyHandler(context)); replaceViewer.setKeyHandler(keyHandler); replaceViewer.setEntityContents(createDefaultContents()); context.set(IEntityPartViewer.class, replaceViewer); replaceActionRegistry = ContextInjectionFactory.make(ActionRegistry.class, context); replaceActionRegistry.registerWorkbenchActions(); context.set(ActionRegistry.class, replaceActionRegistry); replaceViewer.setContextMenu(new ContextMenuProvider(replaceViewer) { @Override public void buildContextMenu(IMenuManager menuManager) { contextMenuProvider.populate(menuManager); } }); return parent; } @Override protected Control createButtonBar(Composite parent) { buttonPanel = createButtonPanel(parent); statusPanel = createStatusPanel(parent); return buttonPanel; } protected Control createButtonPanel(Composite parent) { Composite composite = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(-2, false); composite.setLayout(layout); //FIXME workaround to prevent button reordering on Shell.setDefaultButton() final Button button = createButton(composite, FIND_ID, IE4UIConstants.FIND_BUTTON_TEXT, false); getShell().addShellListener(new ShellAdapter() { @Override public void shellActivated(ShellEvent e) { ((Shell) e.widget).setDefaultButton(button); } }); createButton(composite, REPLACE_ID, IE4UIConstants.REPLACE_BUTTON_TEXT, false); createButton(composite, REPLACE_FIND_ID, IE4UIConstants.REPLACE_FIND_BUTTON_TEXT, false); createButton(composite, REPLACE_ALL_ID, IE4UIConstants.REPLACE_ALL_BUTTON_TEXT, false); composite.setLayoutData(new GridData(SWT.RIGHT, SWT.BOTTOM, true, false)); return composite; } protected Control createStatusPanel(Composite parent) { Composite composite = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(1, false); composite.setLayout(layout); statusLabel = new Label(composite, SWT.LEFT); statusLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); Button button = createButton(composite, IDialogConstants.CLOSE_ID, "Close", false); GridData gridData = (GridData) button.getLayoutData(); gridData.horizontalAlignment = SWT.RIGHT; gridData.verticalAlignment = SWT.BOTTOM; gridData.grabExcessHorizontalSpace = gridData.grabExcessVerticalSpace = false; composite.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true, false)); return composite; } protected void updateButtonsEnablement(boolean enabled) { Button button = getButton(REPLACE_FIND_ID); if (button != null) button.setEnabled(enabled); button = getButton(REPLACE_ID); if (button != null) button.setEnabled(enabled); } protected Control getButtonPanel() { return buttonPanel; } protected Control getStatusPanel() { return statusPanel; } protected void setStatusMessage(String message) { statusLabel.setText(message); } @Override protected void buttonPressed(int buttonId) { super.buttonPressed(buttonId); clearFreshTemplate(); boolean state = enableSelectionTracking(false); try { switch (buttonId) { case FIND_ID: doFind(); break; case REPLACE_ID: doReplace(true); break; case REPLACE_FIND_ID: doReplace(false); doFind(); break; case REPLACE_ALL_ID: doReplaceAll(); break; case IDialogConstants.CLOSE_ID: default: okPressed(); break; } } finally { enableSelectionTracking(state); } } protected void doFind() { iterator.withPattern(viewer.getEntityContents()); if (findNext(true)) selectAndReveal(getFoundEntity()); } protected void doReplace(boolean updateSelection) { if (!hasFoundEntity()) return; final IEntity replacement = EntityUtils.clone(replaceViewer.getEntityContents()); Matcher.substitute(replacement, bindings, false); ModelTransactionCommand command = new ModelTransactionCommand(); try { command.setModel(getFoundEntity()); command.begin(); iterator.set(replacement); command.commit(); } catch (Exception e) { command.rollbackIfNeeded(); } finally { clearFoundEntity(); } IEntityPartViewer viewer = (IEntityPartViewer) selection.wGetValue("viewer"); viewer.getCommandStack().execute(command); if (updateSelection) { Control control = viewer.getControl(); control.getDisplay().asyncExec(new Runnable() { @Override public void run() { boolean state = enableSelectionTracking(false); selectAndReveal(replacement); enableSelectionTracking(state); } }); } } protected void doReplaceAll() { IEntity self = selection.wGet("self"); iterator.reset(self); if (!findNext(true)) return; boolean state = enableSelectionTracking(false); IEntity replacement = replaceViewer.getEntityContents(); IEntity lastReplaced = null; ModelTransactionCommand command = new ModelTransactionCommand(); command.setModel(getFoundEntity()); try { command.begin(); do { lastReplaced = EntityUtils.clone(replacement); Matcher.substitute(lastReplaced, bindings, false); iterator.set(lastReplaced); } while (findNext(true)); command.commit(); } catch (Exception e) { command.rollbackIfNeeded(); } finally { clearFoundEntity(); } IEntityPartViewer viewer = (IEntityPartViewer) selection.wGetValue("viewer"); viewer.getCommandStack().execute(command); Control control = viewer.getControl(); if (lastReplaced != null) { final IEntity revealEntity = lastReplaced; control.getDisplay().asyncExec(new Runnable() { @Override public void run() { boolean state = enableSelectionTracking(false); selectAndReveal(revealEntity); enableSelectionTracking(state); } }); } enableSelectionTracking(state); } protected void selectAndReveal(IEntity entity) { IEntityPartViewer viewer = (IEntityPartViewer) selection.wGetValue("viewer"); viewer.selectAndReveal(entity); } @Inject protected void setSelection(@Optional @Named(IServiceConstants.ACTIVE_SELECTION) IBindingManager selection,@Named(IServiceConstants.ACTIVE_PART) Object aPart, @Active MPart activePart, MPart part) { if (getShell() == null || getShell().isDisposed()) return; if (selection == null || !isSelectionTracking() || selection.wGetValue("viewer") == viewer || selection.wGetValue("viewer") == replaceViewer) return; this.selection = selection.clone(); IEntity self = this.selection.wGet("self"); iterator.reset(self); if (this.selection.wIsSet("primarySelectedEntity")) { IEntity primarySelectedEntity = this.selection.wGet("primarySelectedEntity"); if (primarySelectedEntity != self) { iterator.skipToSame(primarySelectedEntity); if (isFreshTemplate()) findNext(false); } } } protected boolean isSelectionTracking() { return selectionTracking; } protected boolean enableSelectionTracking(boolean enable) { boolean state = selectionTracking; selectionTracking = enable; return state; } protected boolean findNext(boolean updateStatus) { iterator.setBindings(bindings); boolean hasNext = iterator.hasNext(); foundEntity = hasNext ? iterator.next() : null; updateButtonsEnablement(hasNext); if (updateStatus) setStatusMessage(hasNext ? "" : IE4UIConstants.PATTERN_NOT_FOUND_TEXT); return hasNext; } protected void clearFoundEntity() { foundEntity = null; updateButtonsEnablement(false); } protected IEntity getFoundEntity() { return foundEntity; } protected boolean hasFoundEntity() { return foundEntity != null; } protected void setFreshTemplate() { this.freshTemplate = true; } protected void clearFreshTemplate() { this.freshTemplate = false; } protected boolean isFreshTemplate() { return freshTemplate; } public void setTemplate(IEntity template) { viewer.setEntityContents(template); replaceViewer.setEntityContents(CommonsEntityFactory.instance.createResolver()); // set dialog minimum size Point buttonBarSize = getStatusPanel().getSize(); Point buttonPanelSize = getButtonPanel().getSize(); int minWidth = Math.max(VIEWER_MINIMUM_WIDTH, Math.max(buttonBarSize.x, buttonPanelSize.x)); int minHeight = VIEWER_MINIMUM_HEIGHT + buttonBarSize.y + buttonPanelSize.y; getShell().setMinimumSize(new Point(minWidth, minHeight)); setFreshTemplate(); setStatusMessage(""); } }