| /******************************************************************************* |
| * Copyright (c) 2003, 2018 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.ui.internal.navigator; |
| |
| import org.eclipse.jface.viewers.ILabelProvider; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.TreeEditor; |
| import org.eclipse.swt.events.FocusAdapter; |
| import org.eclipse.swt.events.FocusEvent; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Text; |
| import org.eclipse.swt.widgets.Tree; |
| import org.eclipse.swt.widgets.TreeItem; |
| import org.eclipse.ui.internal.navigator.extensions.INavigatorSiteEditor; |
| import org.eclipse.ui.navigator.CommonViewer; |
| |
| |
| /** |
| * A NavigatorSiteEditor is used to edit (i.e., rename) elements in a Navigator view. It displays a |
| * text editor box overlay on the Navigator tree widget. |
| * |
| * @since 3.2 |
| */ |
| public class NavigatorSiteEditor implements INavigatorSiteEditor { |
| |
| private Tree navigatorTree; |
| private TreeEditor treeEditor; |
| private Text textEditor; |
| private Composite textEditorParent; |
| private TextActionHandler textActionHandler; |
| private String text; // the text being edited |
| private CommonViewer commonViewer; |
| |
| |
| /** |
| * Creates an instance of a NavigatorSiteEditor. |
| * |
| * @param aCommonViewer |
| * the viewer this editor applies to |
| * @param navigatorTree |
| * the tree that is being edited |
| */ |
| public NavigatorSiteEditor(CommonViewer aCommonViewer, Tree navigatorTree) { |
| commonViewer = aCommonViewer; |
| this.navigatorTree = navigatorTree; |
| treeEditor = new TreeEditor(navigatorTree); |
| } |
| |
| /** |
| * Creates the parent composite for the editor overlay. |
| * |
| * @return the parent composite for the editor overlay |
| */ |
| Composite createParent() { |
| Composite result = new Composite(navigatorTree, SWT.NONE); |
| TreeItem[] selectedItems = navigatorTree.getSelection(); |
| treeEditor.horizontalAlignment = SWT.LEFT; |
| treeEditor.grabHorizontal = true; |
| treeEditor.setEditor(result, selectedItems[0]); |
| return result; |
| } |
| |
| /** |
| * Creates the text editor widget. |
| * |
| * @param runnable |
| * the Runnable to execute when editing ends by the user pressing enter or clicking |
| * outside the text editor box. |
| */ |
| void createTextEditor(final Runnable runnable) { |
| // Create text editor parent. This draws a nice bounding rect. |
| textEditorParent = createParent(); |
| textEditorParent.setVisible(false); |
| textEditorParent.addListener(SWT.Paint, e -> { |
| Point textSize = textEditor.getSize(); |
| Point parentSize = textEditorParent.getSize(); |
| e.gc.drawRectangle(0, 0, Math.min(textSize.x + 4, parentSize.x - 1), parentSize.y - 1); |
| }); |
| |
| // Create inner text editor. |
| textEditor = new Text(textEditorParent, SWT.NONE); |
| textEditorParent.setBackground(textEditor.getBackground()); |
| textEditor.addListener(SWT.Modify, e -> { |
| Point textSize = textEditor.computeSize(SWT.DEFAULT, SWT.DEFAULT); |
| textSize.x += textSize.y; // Add extra space for new characters. |
| Point parentSize = textEditorParent.getSize(); |
| textEditor.setBounds(2, 1, Math.min(textSize.x, parentSize.x - 4), parentSize.y - 2); |
| textEditorParent.redraw(); |
| }); |
| textEditor.addListener(SWT.Traverse, event -> { |
| // Workaround for Bug 20214 due to extra |
| // traverse events |
| switch (event.detail) { |
| case SWT.TRAVERSE_ESCAPE: |
| // Do nothing in this case |
| disposeTextWidget(); |
| event.doit = true; |
| event.detail = SWT.TRAVERSE_NONE; |
| break; |
| case SWT.TRAVERSE_RETURN: |
| saveChangesAndDispose(runnable); |
| event.doit = true; |
| event.detail = SWT.TRAVERSE_NONE; |
| break; |
| } |
| }); |
| textEditor.addFocusListener(new FocusAdapter() { |
| @Override |
| public void focusLost(FocusEvent fe) { |
| saveChangesAndDispose(runnable); |
| } |
| }); |
| |
| if (textActionHandler != null) { |
| textActionHandler.addText(textEditor); |
| } |
| } |
| |
| /** |
| * Closes the text editor widget. |
| */ |
| void disposeTextWidget() { |
| if (textActionHandler != null) { |
| textActionHandler.removeText(textEditor); |
| } |
| if (textEditorParent != null) { |
| textEditorParent.dispose(); |
| textEditorParent = null; |
| textEditor = null; |
| treeEditor.setEditor(null, null); |
| } |
| } |
| |
| /** |
| * Displays a text editor overlay on the tree widget. |
| * |
| * @param runnable |
| * Runnable to execute when editing ends either by the user pressing enter or |
| * clicking outside the editor box. |
| */ |
| @Override |
| public void edit(Runnable runnable) { |
| IStructuredSelection selection = commonViewer.getStructuredSelection(); |
| |
| if (selection.size() != 1) { |
| return; |
| } |
| text = getLabel(selection.getFirstElement()); |
| if (text == null) { |
| return; |
| } |
| // Make sure text editor is created only once. Simply reset text |
| // editor when action is executed more than once. Fixes bug 22269. |
| if (textEditorParent == null) { |
| createTextEditor(runnable); |
| } |
| textEditor.setText(text); |
| // Open text editor with initial size. |
| textEditorParent.setVisible(true); |
| Point textSize = textEditor.computeSize(SWT.DEFAULT, SWT.DEFAULT); |
| textSize.x += textSize.y; // Add extra space for new characters. |
| Point parentSize = textEditorParent.getSize(); |
| textEditor.setBounds(2, 1, Math.min(textSize.x, parentSize.x - 4), parentSize.y - 2); |
| textEditorParent.redraw(); |
| textEditor.selectAll(); |
| textEditor.setFocus(); |
| } |
| |
| /** |
| * Returns the displayed label of the given element. |
| * |
| * @param element |
| * the element that is displayed in the navigator |
| * @return the displayed label of the given element. |
| */ |
| String getLabel(Object element) { |
| return ((ILabelProvider) commonViewer.getLabelProvider()).getText(element); |
| } |
| |
| |
| @Override |
| public String getText() { |
| return text; |
| } |
| |
| /** |
| * Saves the changes and disposes of the text widget. |
| * |
| * @param runnable |
| * Runnable to execute |
| */ |
| void saveChangesAndDispose(final Runnable runnable) { |
| final String newText = textEditor.getText(); |
| // Run this in an async to make sure that the operation that triggered |
| // this action is completed. Otherwise this leads to problems when the |
| // icon of the item being renamed is clicked (i.e., which causes the rename |
| // text widget to lose focus and trigger this method). |
| Runnable editRunnable = () -> { |
| disposeTextWidget(); |
| if (newText.length() > 0 && newText.equals(text) == false) { |
| text = newText; |
| runnable.run(); |
| } |
| text = null; |
| }; |
| navigatorTree.getShell().getDisplay().asyncExec(editRunnable); |
| } |
| |
| |
| @Override |
| public void setTextActionHandler(TextActionHandler actionHandler) { |
| textActionHandler = actionHandler; |
| } |
| |
| } |