| /******************************************************************************* |
| * Copyright (c) 2000, 2017 IBM Corporation and others. |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| *******************************************************************************/ |
| package org.eclipse.dltk.internal.ui.wizards.buildpath.newsourcepage; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IFolder; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.dltk.core.IModelElement; |
| import org.eclipse.dltk.core.IScriptProject; |
| import org.eclipse.dltk.core.ModelException; |
| import org.eclipse.dltk.core.environment.IEnvironment; |
| import org.eclipse.dltk.internal.corext.buildpath.IBuildpathInformationProvider; |
| import org.eclipse.dltk.internal.corext.buildpath.IPackageExplorerActionListener; |
| import org.eclipse.dltk.internal.corext.buildpath.PackageExplorerActionEvent; |
| import org.eclipse.dltk.internal.corext.util.Messages; |
| import org.eclipse.dltk.internal.ui.preferences.ScrolledPageContent; |
| import org.eclipse.dltk.internal.ui.wizards.NewWizardMessages; |
| import org.eclipse.dltk.internal.ui.wizards.buildpath.newsourcepage.BuildpathModifierQueries.IAddArchivesQuery; |
| import org.eclipse.dltk.internal.ui.wizards.buildpath.newsourcepage.BuildpathModifierQueries.IAddLibrariesQuery; |
| import org.eclipse.dltk.internal.ui.wizards.buildpath.newsourcepage.BuildpathModifierQueries.ICreateFolderQuery; |
| import org.eclipse.dltk.internal.ui.wizards.buildpath.newsourcepage.BuildpathModifierQueries.IInclusionExclusionQuery; |
| import org.eclipse.dltk.internal.ui.wizards.buildpath.newsourcepage.BuildpathModifierQueries.ILinkToQuery; |
| import org.eclipse.dltk.internal.ui.wizards.buildpath.newsourcepage.BuildpathModifierQueries.IRemoveLinkedFolderQuery; |
| import org.eclipse.dltk.internal.ui.wizards.buildpath.newsourcepage.DialogPackageExplorerActionGroup.DialogExplorerActionContext; |
| import org.eclipse.dltk.internal.ui.wizards.dialogfields.StringDialogField; |
| import org.eclipse.dltk.ui.DLTKUIPlugin; |
| import org.eclipse.dltk.ui.util.ExceptionHandler; |
| import org.eclipse.dltk.ui.util.PixelConverter; |
| import org.eclipse.jface.operation.IRunnableContext; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.ui.forms.events.HyperlinkAdapter; |
| import org.eclipse.ui.forms.events.HyperlinkEvent; |
| import org.eclipse.ui.forms.widgets.FormText; |
| import org.eclipse.ui.forms.widgets.FormToolkit; |
| import org.eclipse.ui.forms.widgets.TableWrapData; |
| import org.eclipse.ui.forms.widgets.TableWrapLayout; |
| |
| |
| /** |
| * Displays a set of available links to modify or adjust the project. |
| * The links contain a short description about the consequences of |
| * this action. |
| * |
| * The content depends on the selection made on the project. |
| * If selection changes, then the <code>HintTextGroup</code> will be |
| * notified through the <code>IPackageExplorerActionListener</code> interface. |
| */ |
| public final class HintTextGroup implements IBuildpathInformationProvider, IPackageExplorerActionListener { |
| |
| /** |
| * Order of actions in the group. First is first. |
| * Only actions in ACTION_ORDER are part of the group. |
| */ |
| private final static int[] ACTION_ORDER= { |
| IBuildpathInformationProvider.CREATE_FOLDER, |
| IBuildpathInformationProvider.CREATE_LINK, |
| IBuildpathInformationProvider.EDIT_FILTERS, |
| IBuildpathInformationProvider.EXCLUDE, |
| IBuildpathInformationProvider.INCLUDE, |
| IBuildpathInformationProvider.UNEXCLUDE, |
| IBuildpathInformationProvider.UNINCLUDE, |
| IBuildpathInformationProvider.ADD_SEL_SF_TO_BP, |
| IBuildpathInformationProvider.REMOVE_FROM_BP, |
| IBuildpathInformationProvider.ADD_SEL_LIB_TO_BP, |
| IBuildpathInformationProvider.ADD_LIB_TO_BP, |
| IBuildpathInformationProvider.ADD_ZIP_TO_BP |
| }; |
| |
| private StringDialogField fOutputLocationField; |
| private Composite fTopComposite; |
| private DialogPackageExplorerActionGroup fActionGroup; |
| private DialogPackageExplorer fPackageExplorer; |
| private IRunnableContext fRunnableContext; |
| private IScriptProject fCurrJProject; |
| private List fNewFolders; |
| private HashMap fImageMap; |
| |
| public HintTextGroup(DialogPackageExplorer packageExplorer, IRunnableContext runnableContext) { |
| fPackageExplorer= packageExplorer; |
| fRunnableContext= runnableContext; |
| fCurrJProject= null; |
| fNewFolders= new ArrayList(); |
| fImageMap= new HashMap(); |
| } |
| |
| public Composite createControl(Composite parent) { |
| fTopComposite= new Composite(parent, SWT.NONE); |
| fTopComposite.setFont(parent.getFont()); |
| |
| GridData gridData= new GridData(GridData.FILL_BOTH); |
| PixelConverter converter= new PixelConverter(parent); |
| gridData.heightHint= converter.convertHeightInCharsToPixels(12); |
| GridLayout gridLayout= new GridLayout(); |
| gridLayout.marginWidth= 0;//-converter.convertWidthInCharsToPixels(2); |
| gridLayout.marginHeight= 0;//= -4; |
| fTopComposite.setLayout(gridLayout); |
| fTopComposite.setLayoutData(gridData); |
| fTopComposite.setData(null); |
| fTopComposite.addDisposeListener(e -> { |
| Collection collection= fImageMap.values(); |
| Iterator iterator= collection.iterator(); |
| while(iterator.hasNext()) { |
| Image image= (Image)iterator.next(); |
| image.dispose(); |
| } |
| }); |
| return fTopComposite; |
| } |
| |
| private Shell getShell() { |
| return DLTKUIPlugin.getActiveWorkbenchShell(); |
| } |
| |
| /** |
| * Set thescriptproject |
| * |
| * @param jProject thescriptproject to work on |
| */ |
| public void setScriptProject(IScriptProject jProject) { |
| fCurrJProject= jProject; |
| } |
| |
| /** |
| * An action group managing the actions needed by |
| * the <code>HintTextGroup</code>. |
| * |
| * Note: This method has to be called on initialization. |
| * Calling this method in the constructor is not |
| * possible because the actions might need a reference to |
| * this class. |
| * |
| * @param actionGroup the action group containing the necessary |
| * actions |
| * |
| * @see DialogPackageExplorerActionGroup |
| */ |
| public void setActionGroup(DialogPackageExplorerActionGroup actionGroup) { |
| fActionGroup= actionGroup; |
| } |
| |
| /** |
| * Creates a form text. |
| * |
| * @param parent the parent to put the form text on |
| * @param text the form text to be displayed |
| * @return the created form text |
| * |
| * @see FormToolkit#createFormText(org.eclipse.swt.widgets.Composite, boolean) |
| */ |
| private FormText createFormText(Composite parent, String text) { |
| FormToolkit toolkit= new FormToolkit(getShell().getDisplay()); |
| try { |
| FormText formText= toolkit.createFormText(parent, true); |
| formText.setFont(parent.getFont()); |
| try { |
| formText.setText(text, true, false); |
| } catch (IllegalArgumentException e) { |
| formText.setText(e.getMessage(), false, false); |
| DLTKUIPlugin.log(e); |
| } |
| formText.marginHeight= 2; |
| formText.marginWidth= 0; |
| formText.setBackground(null); |
| formText.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB)); |
| return formText; |
| } finally { |
| toolkit.dispose(); |
| } |
| } |
| |
| /** |
| * Create a label with a hyperlink and a picture. |
| * |
| * @param parent the parent widget of the label |
| * @param text the text of the label |
| * @param action the action to be executed if the hyperlink is activated |
| * @param context the runnable context under which the action is executed |
| */ |
| private void createLabel(Composite parent, String text, final BuildpathModifierAction action, final IRunnableContext context) { |
| FormText formText= createFormText(parent, text); |
| Image image= (Image)fImageMap.get(action.getId()); |
| if (image == null) { |
| image= action.getImageDescriptor().createImage(); |
| fImageMap.put(action.getId(), image); |
| } |
| formText.setImage("defaultImage", image); //$NON-NLS-1$ |
| formText.addHyperlinkListener(new HyperlinkAdapter() { |
| |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| try { |
| context.run(false, false, action.getOperation()); |
| } catch (InvocationTargetException err) { |
| ExceptionHandler.handle(err, getShell(), Messages.format(NewWizardMessages.HintTextGroup_Exception_Title, action.getName()), err.getMessage()); |
| } catch (InterruptedException err) { |
| // Cancel pressed |
| } |
| } |
| |
| }); |
| } |
| |
| /** |
| * Returns the current selection which is a list of |
| * elements in a tree. |
| * |
| * @return the current selection |
| * @see IBuildpathInformationProvider#getSelection() |
| */ |
| @Override |
| public IStructuredSelection getSelection() { |
| return fPackageExplorer.getSelection(); |
| } |
| |
| /** |
| * Set the selection for the <code>DialogPackageExplorer</code> |
| * |
| * @param elements a list of elements to be selected |
| * |
| * @see DialogPackageExplorer#setSelection(List) |
| */ |
| public void setSelection(List elements) { |
| fPackageExplorer.setSelection(elements); |
| } |
| |
| /** |
| * Returns the script project. |
| * |
| * @see IBuildpathInformationProvider#getScriptProject() |
| */ |
| @Override |
| public IScriptProject getScriptProject() { |
| return fCurrJProject; |
| } |
| |
| /** |
| * Handle the result of an action. Note that first, the exception has to be |
| * checked and computation can only continue if the exception is <code>null</code>. |
| * |
| * @see IBuildpathInformationProvider#handleResult(List, CoreException, int) |
| */ |
| @Override |
| public void handleResult(List resultElements, CoreException exception, int actionType) { |
| if (exception != null) { |
| ExceptionHandler.handle(exception, getShell(), Messages.format(NewWizardMessages.HintTextGroup_Exception_Title_refresh, fActionGroup.getAction(actionType).getName()), exception.getLocalizedMessage()); |
| return; |
| } |
| |
| switch(actionType) { |
| case CREATE_FOLDER: handleFolderCreation(resultElements); break; |
| case CREATE_LINK: handleFolderCreation(resultElements); break; |
| case EDIT_FILTERS: defaultHandle(resultElements, false); break; |
| case ADD_SEL_SF_TO_BP: case ADD_SEL_LIB_TO_BP: case ADD_ZIP_TO_BP: case ADD_LIB_TO_BP: |
| handleAddToCP(resultElements); break; |
| case REMOVE_FROM_BP: handleRemoveFromBP(resultElements, false); break; |
| case INCLUDE: defaultHandle(resultElements, true); break; |
| case EXCLUDE: defaultHandle(resultElements, false); break; |
| case UNINCLUDE: defaultHandle(resultElements, false); break; |
| case UNEXCLUDE: defaultHandle(resultElements, true); break; |
| case RESET: defaultHandle(resultElements, false); break; |
| case RESET_ALL: handleResetAll(); break; |
| default: break; |
| } |
| } |
| |
| /** |
| * Default result handle. This includes: |
| * <li>Set the selection of the <code>fPackageExplorer</code> |
| * to the result object</li> |
| * <li>Force the hint text to be rebuilt if necessary</li> |
| * |
| * @param result the result list of object to be selected by the |
| * <code>fPackageExplorer</code> |
| * @param forceRebuild <code>true</code> if the area containing |
| * the links and their descriptions should be rebuilt, <code> |
| * false</code> otherwise |
| * |
| * @see DialogPackageExplorer#setSelection(List) |
| * @see DialogPackageExplorerActionGroup#refresh(DialogExplorerActionContext) |
| */ |
| private void defaultHandle(List result, boolean forceRebuild) { |
| try { |
| fPackageExplorer.setSelection(result); |
| if (forceRebuild) { |
| fActionGroup.refresh(new DialogExplorerActionContext(result, fCurrJProject)); |
| } |
| } catch (ModelException e) { |
| ExceptionHandler.handle(e, getShell(), NewWizardMessages.HintTextGroup_Exception_Title_refresh, e.getLocalizedMessage()); |
| } |
| } |
| |
| /** |
| * Handle folder creation. This includes: |
| * <li>Set the selection of the <code>fPackageExplorer</code> |
| * to the result object, unless the result object is <code> |
| * null</code></li> |
| * <li>Add the created folder to the list of new folders</li> |
| * <li>Adjust the text of <code>fOutputLocationField</code> |
| * and add the project's new output location to the list of |
| * new folders, if necessary |
| * |
| * In this case, the list consists only of one element on which the |
| * new folder has been created |
| * |
| * @param result a list with only one element to be selected by the |
| * <code>fPackageExplorer</code>, or an empty list if creation was |
| * aborted |
| */ |
| private void handleFolderCreation(List result) { |
| if (result.size() == 1) { |
| fNewFolders.add(result.get(0)); |
| fPackageExplorer.setSelection(result); |
| } |
| } |
| |
| /** |
| * Handle adding to Buildpath. This includes: |
| * <li>Set the selection of the <code>fPackageExplorer</code> |
| * to the list of result elements</li> |
| * <li>Set adjust the text of <code>fOutputLocationField</code> |
| * and add the project's new output location to the list of |
| * new folders, if necessary |
| * |
| * @param result the result list of object to be selected by the |
| * <code>fPackageExplorer</code> |
| */ |
| private void handleAddToCP(List result) { |
| try { |
| if (containsScriptProject(result)) { |
| fPackageExplorer.setSelection(result); |
| fActionGroup.refresh(new DialogExplorerActionContext(result, fCurrJProject)); |
| } |
| else |
| fPackageExplorer.setSelection(result); |
| } catch (ModelException e) { |
| ExceptionHandler.handle(e, getShell(), NewWizardMessages.HintTextGroup_Exception_Title_refresh, e.getLocalizedMessage()); |
| } |
| } |
| |
| /** |
| * Handle removing from Buildpath. The goup area is rebuilt if |
| * either <code>forceRebuild</code> is <code>true</code> or |
| * the result contains one element of type <code>IScriptProject</code>. |
| * |
| * @param result the result to be selected in the explorer |
| * @param forceRebuild <code>true</code> if the hint text group must |
| * be rebuilt, <code>false</code> otherwise. |
| */ |
| private void handleRemoveFromBP(List result, boolean forceRebuild) { |
| fPackageExplorer.setSelection(result); |
| try { |
| if (forceRebuild || containsScriptProject(result)) { |
| fActionGroup.refresh(new DialogExplorerActionContext(result, fCurrJProject)); |
| } |
| } catch (ModelException e) { |
| ExceptionHandler.handle(e, getShell(), NewWizardMessages.HintTextGroup_Exception_Title_refresh, e.getLocalizedMessage()); |
| } |
| } |
| |
| /** |
| * Handle resetting of the script project to the original |
| * state. This only means that the package explorer |
| * needs to be updated and the selection should |
| * be set to the script project root itself. |
| */ |
| private void handleResetAll() { |
| List list= new ArrayList(); |
| list.add(fCurrJProject); |
| setSelection(list); |
| } |
| |
| /** |
| * Find out whether the list contains one element of |
| * type <code>IScriptProject</code> |
| * |
| * @param elements the list to be examined |
| * @return <code>true</code> if the list contains one element of |
| * type <code>IScriptProject</code>, <code>false</code> otherwise |
| */ |
| private boolean containsScriptProject(List elements) { |
| for(int i= 0; i < elements.size(); i++) { |
| if (elements.get(i) instanceof IScriptProject) |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Return an <code>IInclusionExclusionQuery</code>. |
| * |
| * @see BuildpathModifierQueries#getDefaultInclusionExclusionQuery(Shell) |
| * @see IBuildpathInformationProvider#getInclusionExclusionQuery() |
| */ |
| @Override |
| public IInclusionExclusionQuery getInclusionExclusionQuery() { |
| return BuildpathModifierQueries.getDefaultInclusionExclusionQuery(getShell()); |
| } |
| |
| /** |
| * Return an <code>ILinkToQuery</code>. |
| * |
| * @see BuildpathModifierQueries#getDefaultLinkQuery(Shell, IScriptProject, IPath) |
| * @see IBuildpathInformationProvider#getLinkFolderQuery() |
| */ |
| @Override |
| public ILinkToQuery getLinkFolderQuery() throws ModelException { |
| return BuildpathModifierQueries.getDefaultLinkQuery(getShell(), fCurrJProject, new Path(fOutputLocationField.getText())); |
| } |
| |
| /** |
| * Return an <code>IFolderCreationQuery</code>. |
| * |
| * @see BuildpathModifierQueries#getDefaultCreateFolderQuery(Shell, IScriptProject) |
| * @see IBuildpathInformationProvider#getCreateFolderQuery() |
| */ |
| @Override |
| public ICreateFolderQuery getCreateFolderQuery() throws ModelException { |
| return BuildpathModifierQueries.getDefaultCreateFolderQuery(getShell(), fCurrJProject); |
| } |
| |
| /** |
| * Get a query to create a linked source folder. |
| * |
| * @see IRemoveLinkedFolderQuery |
| */ |
| @Override |
| public IRemoveLinkedFolderQuery getRemoveLinkedFolderQuery() throws ModelException { |
| return BuildpathModifierQueries.getDefaultRemoveLinkedFolderQuery(getShell()); |
| } |
| |
| /** |
| * Return an <code>IAddArchivesQuery</code>. |
| * |
| * @see BuildpathModifierQueries#getDefaultArchivesQuery(Shell) |
| * @see IBuildpathInformationProvider#getExternalArchivesQuery() |
| */ |
| @Override |
| public IAddArchivesQuery getExternalArchivesQuery(IEnvironment environment) throws ModelException { |
| return BuildpathModifierQueries.getDefaultArchivesQuery(getShell(), environment); |
| } |
| |
| /** |
| * Return an <code>IAddLibrariesQuery</code>. |
| * |
| * @see BuildpathModifierQueries#getDefaultLibrariesQuery(Shell) |
| * @see IBuildpathInformationProvider#getLibrariesQuery() |
| */ |
| @Override |
| public IAddLibrariesQuery getLibrariesQuery() throws ModelException { |
| return BuildpathModifierQueries.getDefaultLibrariesQuery(getShell()); |
| } |
| |
| /** |
| * Delete all newly created folders and files. |
| * Resources that existed before will not be |
| * deleted. |
| */ |
| @Override |
| public void deleteCreatedResources() { |
| Iterator iterator= fNewFolders.iterator(); |
| while (iterator.hasNext()) { |
| Object element= iterator.next(); |
| IFolder folder; |
| try { |
| if (element instanceof IFolder) |
| folder= (IFolder)element; |
| else if (element instanceof IModelElement) |
| folder= fCurrJProject.getProject().getWorkspace().getRoot().getFolder(((IModelElement)element).getPath()); |
| else { |
| ((IFile)element).delete(false, null); |
| continue; |
| } |
| folder.delete(false, null); |
| } catch (CoreException e) { |
| } |
| } |
| |
| fNewFolders= new ArrayList(); |
| } |
| |
| /** |
| * Handle the package explorer action event. |
| * This includes: |
| * <li>Disposing the old composite which contained the labels with the links</li> |
| * <li>Create a new composite</li> |
| * <li>Create new labels with the new actions as child of the new composite. The |
| * information which lables have to be created is contained in the event. |
| * |
| * @see PackageExplorerActionEvent |
| * @see IPackageExplorerActionListener#handlePackageExplorerActionEvent(PackageExplorerActionEvent) |
| */ |
| @Override |
| public void handlePackageExplorerActionEvent(PackageExplorerActionEvent event) { |
| // Get the child composite of the top composite |
| Composite childComposite= (Composite)fTopComposite.getData(); |
| |
| // Dispose old composite (if necessary) |
| if (childComposite != null && childComposite.getParent() != null) |
| childComposite.getParent().dispose(); |
| |
| // Create new composite |
| ScrolledPageContent spc= new ScrolledPageContent(fTopComposite, SWT.V_SCROLL); |
| spc.getVerticalBar().setIncrement(5); |
| spc.setLayoutData(new GridData(GridData.FILL_BOTH)); |
| childComposite= spc.getBody(); |
| TableWrapLayout tableWrapLayout= new TableWrapLayout(); |
| tableWrapLayout.leftMargin= 0; |
| tableWrapLayout.rightMargin= 0; |
| childComposite.setLayout(tableWrapLayout); |
| childComposite.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL)); |
| fTopComposite.setData(childComposite); |
| |
| // Display available actions |
| BuildpathModifierAction[] actions= event.getEnabledActions(); |
| String[] descriptionText= event.getEnabledActionsText(); |
| if (noContextHelpAvailable(actions)) { |
| String noAction= fActionGroup.getNoActionDescription(); |
| createFormText(childComposite, Messages.format(NewWizardMessages.HintTextGroup_NoAction, noAction)); |
| fTopComposite.layout(true); |
| return; |
| } |
| |
| for (int j= 0; j < ACTION_ORDER.length; j++) { |
| for (int i= 0; i < actions.length; i++) { |
| int id= Integer.parseInt(actions[i].getId()); |
| if (id == ACTION_ORDER[j]) { |
| createLabel(childComposite, descriptionText[i], actions[i], fRunnableContext); |
| break; |
| } |
| } |
| } |
| |
| fTopComposite.layout(true); |
| } |
| |
| /** |
| * Check if for the current type of selection, no context specific actions can |
| * be applied. Note: this does not mean, that there are NO actions available at all.<p> |
| * |
| * For example: if the default package is selected, there is no specific action for this kind |
| * of selection as no operations are allowed on the default package. Nevertheless, the |
| * <code>PackageExplorerActionEvent</code> will return at least one action that allows to |
| * link to an existing folder in the file system, but this operation is always available |
| * and does not add any supporting information to the current selection. Therefore, |
| * it can be filtered and the correct answer to the user is that there is no specific |
| * action for the default package. |
| * |
| * @param actions an array of provided actions |
| * @return <code>true</code> if there is at least one action that allows context |
| * sensitive operations, <code>false</code> otherwise. |
| */ |
| private boolean noContextHelpAvailable(BuildpathModifierAction[] actions) { |
| if (actions.length == 0) |
| return true; |
| if (actions.length == 1) { |
| int id= Integer.parseInt(actions[0].getId()); |
| if (id == IBuildpathInformationProvider.CREATE_LINK) |
| return true; |
| } |
| if (actions.length == 2) { |
| int idLink= Integer.parseInt(actions[0].getId()); |
| int idReset= Integer.parseInt(actions[1].getId()); |
| if (idReset == IBuildpathInformationProvider.RESET_ALL && |
| idLink == IBuildpathInformationProvider.CREATE_LINK) |
| return true; |
| } |
| return false; |
| } |
| } |