| /******************************************************************************* |
| * Copyright (c) 2003, 2016 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Benjamin Cabe <benjamin.cabe@anyware-tech.com> - bug 251339 |
| * Martin Karpisek <martin.karpisek@gmail.com> - Bug 507831 |
| *******************************************************************************/ |
| package org.eclipse.pde.internal.ui.editor; |
| |
| import java.io.*; |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.jface.action.*; |
| import org.eclipse.jface.dialogs.Dialog; |
| import org.eclipse.jface.resource.JFaceResources; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.pde.core.IBaseModel; |
| import org.eclipse.pde.internal.ui.PDEPluginImages; |
| import org.eclipse.pde.internal.ui.PDEUIMessages; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.CCombo; |
| import org.eclipse.swt.custom.CTabFolder; |
| import org.eclipse.swt.dnd.Clipboard; |
| import org.eclipse.swt.events.FocusEvent; |
| import org.eclipse.swt.events.FocusListener; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.*; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.actions.ActionFactory; |
| import org.eclipse.ui.dialogs.FilteredTree; |
| import org.eclipse.ui.forms.*; |
| import org.eclipse.ui.forms.editor.FormEditor; |
| import org.eclipse.ui.forms.editor.FormPage; |
| import org.eclipse.ui.forms.widgets.*; |
| |
| public abstract class PDEFormPage extends FormPage { |
| |
| private Control fLastFocusControl; |
| |
| public PDEFormPage(FormEditor editor, String id, String title) { |
| super(editor, id, title); |
| fLastFocusControl = null; |
| } |
| |
| @Override |
| public void dispose() { |
| Control c = getPartControl(); |
| if (c != null && !c.isDisposed()) { |
| Menu menu = c.getMenu(); |
| if (menu != null) |
| resetMenu(menu, c); |
| } |
| super.dispose(); |
| } |
| |
| private void resetMenu(Menu menu, Control c) { |
| if (c instanceof Composite) { |
| Composite comp = (Composite) c; |
| Control[] children = comp.getChildren(); |
| for (Control child : children) { |
| resetMenu(menu, child); |
| } |
| } |
| Menu cmenu = c.getMenu(); |
| if (cmenu != null && cmenu.equals(menu)) { |
| c.setMenu(null); |
| } |
| } |
| |
| @Override |
| protected void createFormContent(IManagedForm managedForm) { |
| final ScrolledForm form = managedForm.getForm(); |
| FormToolkit toolkit = managedForm.getToolkit(); |
| toolkit.decorateFormHeading(form.getForm()); |
| |
| IToolBarManager manager = form.getToolBarManager(); |
| |
| getPDEEditor().contributeToToolbar(manager); |
| |
| final String helpContextID = getHelpResource(); |
| if (helpContextID != null) { |
| Action helpAction = new Action("help") { //$NON-NLS-1$ |
| @Override |
| public void run() { |
| PlatformUI.getWorkbench().getHelpSystem().displayHelp(helpContextID); |
| } |
| }; |
| helpAction.setToolTipText(PDEUIMessages.PDEFormPage_help); |
| helpAction.setImageDescriptor(PDEPluginImages.DESC_HELP); |
| manager.add(helpAction); |
| } |
| //check to see if our form parts are contributing actions |
| IFormPart[] parts = managedForm.getParts(); |
| for (IFormPart part : parts) { |
| if (part instanceof IAdaptable) { |
| IAdaptable adapter = (IAdaptable) part; |
| IAction[] actions = adapter.getAdapter(IAction[].class); |
| if (actions != null) { |
| for (IAction action : actions) { |
| form.getToolBarManager().add(action); |
| } |
| } |
| } |
| } |
| form.updateToolBar(); |
| } |
| |
| public PDEFormEditor getPDEEditor() { |
| return (PDEFormEditor) getEditor(); |
| } |
| |
| /** |
| * Return a help context id to have the help button at the top right of the form page open |
| * the dynamic help for the page. |
| * |
| * @return a help context id or <code>null</code> |
| */ |
| protected String getHelpResource() { |
| return null; |
| } |
| |
| public IBaseModel getModel() { |
| return getPDEEditor().getAggregateModel(); |
| } |
| |
| public void contextMenuAboutToShow(IMenuManager menu) { |
| } |
| |
| protected Control getFocusControl() { |
| IManagedForm form = getManagedForm(); |
| if (form == null) |
| return null; |
| Control control = form.getForm(); |
| if (control == null || control.isDisposed()) |
| return null; |
| Display display = control.getDisplay(); |
| Control focusControl = display.getFocusControl(); |
| if (focusControl == null || focusControl.isDisposed()) |
| return null; |
| return focusControl; |
| } |
| |
| public boolean performGlobalAction(String actionId) { |
| Control focusControl = getFocusControl(); |
| if (focusControl == null) |
| return false; |
| |
| if (canPerformDirectly(actionId, focusControl)) |
| return true; |
| AbstractFormPart focusPart = getFocusSection(); |
| if (focusPart != null) { |
| if (focusPart instanceof PDESection) |
| return ((PDESection) focusPart).doGlobalAction(actionId); |
| if (focusPart instanceof PDEDetails) |
| return ((PDEDetails) focusPart).doGlobalAction(actionId); |
| } |
| return false; |
| } |
| |
| public boolean canPaste(Clipboard clipboard) { |
| AbstractFormPart focusPart = getFocusSection(); |
| if (focusPart != null) { |
| if (focusPart instanceof PDESection) { |
| return ((PDESection) focusPart).canPaste(clipboard); |
| } |
| if (focusPart instanceof PDEDetails) { |
| return ((PDEDetails) focusPart).canPaste(clipboard); |
| } |
| } |
| return false; |
| } |
| |
| public boolean canCopy(ISelection selection) { |
| AbstractFormPart focusPart = getFocusSection(); |
| if (focusPart != null) { |
| if (focusPart instanceof PDESection) { |
| return ((PDESection) focusPart).canCopy(selection); |
| } |
| if (focusPart instanceof PDEDetails) { |
| return ((PDEDetails) focusPart).canCopy(selection); |
| } |
| } |
| return false; |
| } |
| |
| public boolean canCut(ISelection selection) { |
| AbstractFormPart focusPart = getFocusSection(); |
| if (focusPart != null) { |
| if (focusPart instanceof PDESection) { |
| return ((PDESection) focusPart).canCut(selection); |
| } |
| if (focusPart instanceof PDEDetails) { |
| return ((PDEDetails) focusPart).canCut(selection); |
| } |
| } |
| return false; |
| } |
| |
| private AbstractFormPart getFocusSection() { |
| Control focusControl = getFocusControl(); |
| if (focusControl == null) |
| return null; |
| Composite parent = focusControl.getParent(); |
| AbstractFormPart targetPart = null; |
| while (parent != null) { |
| Object data = parent.getData("part"); //$NON-NLS-1$ |
| if (data != null && data instanceof AbstractFormPart) { |
| targetPart = (AbstractFormPart) data; |
| break; |
| } |
| parent = parent.getParent(); |
| } |
| return targetPart; |
| } |
| |
| protected boolean canPerformDirectly(String id, Control control) { |
| if (control instanceof Text) { |
| Text text = (Text) control; |
| if (id.equals(ActionFactory.CUT.getId())) { |
| text.cut(); |
| return true; |
| } |
| if (id.equals(ActionFactory.COPY.getId())) { |
| text.copy(); |
| return true; |
| } |
| if (id.equals(ActionFactory.PASTE.getId())) { |
| text.paste(); |
| return true; |
| } |
| if (id.equals(ActionFactory.SELECT_ALL.getId())) { |
| text.selectAll(); |
| return true; |
| } |
| if (id.equals(ActionFactory.DELETE.getId())) { |
| int count = text.getSelectionCount(); |
| if (count == 0) { |
| int caretPos = text.getCaretPosition(); |
| text.setSelection(caretPos, caretPos + 1); |
| } |
| text.insert(""); //$NON-NLS-1$ |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public void cancelEdit() { |
| IFormPart[] parts = getManagedForm().getParts(); |
| for (IFormPart part : parts) { |
| if (part instanceof IContextPart) |
| ((IContextPart) part).cancelEdit(); |
| } |
| } |
| |
| @Override |
| public void createPartControl(Composite parent) { |
| super.createPartControl(parent); |
| // Dynamically add focus listeners to all the forms children in order |
| // to track the last focus control |
| IManagedForm managedForm = getManagedForm(); |
| if (managedForm != null) { |
| addLastFocusListeners(managedForm.getForm()); |
| } |
| } |
| |
| /** |
| * Programatically and recursively add focus listeners to the specified |
| * composite and its children that track the last control to have focus |
| * before a page change or the editor lost focus |
| * |
| * @param composite |
| */ |
| public void addLastFocusListeners(Composite composite) { |
| Control[] controls = composite.getChildren(); |
| for (Control control : controls) { |
| // Add a focus listener if the control is any one of the below types |
| // Note that the controls listed below represent all the controls |
| // currently in use by all form pages in PDE. In the future, |
| // more controls will have to be added. |
| // Could not add super class categories of controls because it |
| // would include things like tool bars that we don't want to track |
| // focus for. |
| if ((control instanceof Text) || (control instanceof Button) || (control instanceof Combo) || (control instanceof CCombo) || (control instanceof Tree) || (control instanceof Table) || (control instanceof Spinner) || (control instanceof Link) || (control instanceof List) || (control instanceof TabFolder) || (control instanceof CTabFolder) || (control instanceof Hyperlink) || (control instanceof FilteredTree)) { |
| addLastFocusListener(control); |
| } |
| if (control instanceof Composite) { |
| // Recursively add focus listeners to this composites children |
| addLastFocusListeners((Composite) control); |
| } |
| } |
| } |
| |
| /** |
| * Add a focus listener to the specified control that tracks the last |
| * control to have focus on this page. |
| * When focus is gained by this control, it registers itself as the last |
| * control to have focus. The last control to have focus is stored in order |
| * to be restored after a page change or editor loses focus. |
| * |
| * @param control |
| */ |
| private void addLastFocusListener(final Control control) { |
| control.addFocusListener(new FocusListener() { |
| @Override |
| public void focusGained(FocusEvent e) { |
| // NO-OP |
| } |
| |
| @Override |
| public void focusLost(FocusEvent e) { |
| fLastFocusControl = control; |
| } |
| }); |
| } |
| |
| /** |
| * Set the focus on the last control to have focus before a page change |
| * or the editor lost focus. |
| */ |
| public void updateFormSelection() { |
| if ((fLastFocusControl != null) && (fLastFocusControl.isDisposed() == false)) { |
| Control lastControl = fLastFocusControl; |
| // Set focus on the control |
| lastControl.forceFocus(); |
| // If the control is a Text widget, select its contents |
| if (lastControl instanceof Text) { |
| Text text = (Text) lastControl; |
| text.setSelection(0, text.getText().length()); |
| } |
| } else { |
| // No focus control set |
| // Fallback on managed form selection mechanism by setting the |
| // focus on this page itself. |
| // The managed form will set focus on the first managed part. |
| // Most likely this will turn out to be a section. |
| // In order for this to work properly, we must override the |
| // sections setFocus() method and set focus on a child control |
| // (preferrably first) that can practically take focus. |
| setFocus(); |
| } |
| } |
| |
| public Control getLastFocusControl() { |
| return fLastFocusControl; |
| } |
| |
| /** |
| * @param control |
| */ |
| public void setLastFocusControl(Control control) { |
| fLastFocusControl = control; |
| } |
| |
| protected void createFormErrorContent(IManagedForm managedForm, String errorTitle, String errorMessage, Exception e) { |
| |
| ScrolledForm form = managedForm.getForm(); |
| FormToolkit toolkit = managedForm.getToolkit(); |
| toolkit.decorateFormHeading(form.getForm()); |
| |
| Composite parent = form.getBody(); |
| GridLayout layout = new GridLayout(); |
| GridData data2 = new GridData(GridData.FILL_BOTH); |
| layout.marginWidth = 7; |
| layout.marginHeight = 7; |
| parent.setLayout(layout); |
| parent.setLayoutData(data2); |
| // Set the title and image of the form |
| form.setText(errorTitle); |
| form.setImage(JFaceResources.getImage(Dialog.DLG_IMG_MESSAGE_ERROR)); |
| |
| int sectionStyle = Section.DESCRIPTION | ExpandableComposite.TITLE_BAR; |
| // Create the message section |
| Section messageSection = createUISection(parent, PDEUIMessages.PDEFormPage_titleMessage, errorMessage, sectionStyle); |
| Composite messageClient = createUISectionContainer(messageSection, 1); |
| // Bind the widgets |
| toolkit.paintBordersFor(messageClient); |
| messageSection.setClient(messageClient); |
| // Ensure the exception was defined |
| if (e == null) { |
| return; |
| } |
| // Create the details section |
| Section detailsSection = createUISection(parent, PDEUIMessages.PDEFormPage_titleDetails, e.getMessage(), sectionStyle); |
| Composite detailsClient = createUISectionContainer(detailsSection, 1); |
| // Create text widget holding the exception trace |
| int style = SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL | SWT.READ_ONLY; |
| Text text = toolkit.createText(detailsClient, getStackTrace(e), style); |
| GridData data = new GridData(GridData.FILL_HORIZONTAL); |
| data.heightHint = 160; |
| data.widthHint = 200; |
| text.setLayoutData(data); |
| // Bind the widgets |
| toolkit.paintBordersFor(detailsClient); |
| detailsSection.setClient(detailsClient); |
| // Note: The veritical scrollbar fails to appear when text widget is |
| // not entirely shown |
| } |
| |
| public Section createUISection(Composite parent, String text, String description, int style) { |
| Section section = getManagedForm().getToolkit().createSection(parent, style); |
| section.clientVerticalSpacing = FormLayoutFactory.SECTION_HEADER_VERTICAL_SPACING; |
| section.setLayout(FormLayoutFactory.createClearGridLayout(false, 1)); |
| section.setText(text); |
| section.setDescription(description); |
| GridData data = new GridData(GridData.FILL_HORIZONTAL); |
| section.setLayoutData(data); |
| return section; |
| } |
| |
| public Composite createUISectionContainer(Composite parent, int columns) { |
| Composite container = getManagedForm().getToolkit().createComposite(parent); |
| container.setLayout(FormLayoutFactory.createSectionClientGridLayout(false, columns)); |
| return container; |
| } |
| |
| private String getStackTrace(Throwable throwable) { |
| try (StringWriter swriter = new StringWriter(); PrintWriter pwriter = new PrintWriter(swriter)) { |
| throwable.printStackTrace(pwriter); |
| pwriter.flush(); |
| pwriter.close(); |
| return swriter.toString(); |
| } catch (IOException e) { |
| return ""; //$NON-NLS-1$ |
| } |
| |
| } |
| |
| /** |
| * Used to align the section client / decriptions of two section headers |
| * horizontally adjacent to each other. The misalignment is caused by one |
| * section header containing toolbar icons and the other not. |
| * |
| * @param masterSection |
| * @param detailsSection |
| */ |
| public void alignSectionHeaders(Section masterSection, Section detailsSection) { |
| detailsSection.descriptionVerticalSpacing += masterSection.getTextClientHeightDifference(); |
| } |
| |
| } |