Java tutorial
/****************************************************************************** * Copyright (c) 2002, 2007 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.gmf.runtime.common.ui.util; import java.util.List; import org.eclipse.core.runtime.IStatus; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.text.contentassist.IContentAssistProcessor; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TreeViewer; 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.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.ui.IActionBars; import org.eclipse.ui.contentassist.ContentAssistHandler; import org.eclipse.gmf.runtime.common.ui.contentassist.ContentAssistantHelper; import org.eclipse.gmf.runtime.common.ui.internal.l10n.CommonUIMessages; /** * A class that enables inline-text editing for tree nodes * * @author Yasser Lulu * */ public class TreeInlineTextEditor { /** * a tree editor used to aid in editing the nodes */ private TreeEditor treeEditor; /** * the tree whose nodes are being edited */ private Tree tree; /** * a text widget displayed to enable user string input */ private Text textEditor; /** * a composite parent for the text widget */ private Composite textEditorParent; /** * a zero size rectangle to force the text widget to disappear from screen * when ending editing */ private static final Rectangle nullRectangle = new Rectangle(0, 0, 0, 0); /** * the final text entered and commited by the user */ private String finalText; /** * the initial string displayed when editing started */ private String initialText; /** * the edit string source and sink */ private IEditStringProvider editStringProvider; /** * the tree item currently being edited */ private TreeItem treeItem; /** * the viewer encapsulating the tree to edit */ private TreeViewer viewer; /** * the text action hanndler */ private IInlineTextActionHandler textActionHandler; /** * flag for disabling F2 */ private boolean isF2disabled; /** * A semaphore that prevents re-entrance into the endEdit() behavior. */ private volatile boolean inEndEdit; /** * content assistant handler */ private ContentAssistHandler contentAssistHandler = null; /** * content assist background color */ private Color proposalPopupBackgroundColor; /** * content assist foreground color */ private Color proposalPopupForegroundColor; /** * Returns the isF2disabled. * * @return boolean */ private boolean isF2disabled() { return isF2disabled; } /** * Sets the isF2disabled. * * @param isF2disabled * The isF2disabled to set */ private void setIsF2disabled(boolean isF2disabled) { this.isF2disabled = isF2disabled; } /** * returns the tree viewer * * @return TreeViewer */ private TreeViewer getTreeViewer() { return viewer; } /** * sets the tree viewer * * @param viewer * the TreeViewer to set */ private void setTreeViewer(TreeViewer viewer) { this.viewer = viewer; } /** * return the currently edited tree item, null if none * * @return TreeItem the tree-item currently being edited or null if none */ private TreeItem getTreeItem() { return treeItem; } /** * returns the edit string provider * * @return IEditStringProvider theedit string provider */ private IEditStringProvider getEditStringProvider() { return editStringProvider; } /** * return the tree-editor * * @return TreeEditor the tree-editor */ private TreeEditor getTreeEditor() { return treeEditor; } /** * sets the tree-editor * * @param treeEditor * The TreeEditor */ private void setTreeEditor(TreeEditor treeEditor) { this.treeEditor = treeEditor; } /** * Constructor for TreeInlineTextEditor. */ public TreeInlineTextEditor(TreeViewer treeViewer, IActionBars actionBars, List disableActionsIds, IEditStringProvider editStringProvider) { this(treeViewer, editStringProvider, false); initTextActionHandler(actionBars, disableActionsIds); } /** * Constructor for TreeInlineTextEditor. * * @param treeViewer the tree viewer * @param editStringProvider * @param isF2disabled boolean flag indicating whether F2 is disabled */ public TreeInlineTextEditor(TreeViewer treeViewer, IEditStringProvider editStringProvider, boolean isF2disabled) { setTreeViewer(treeViewer); setTree(treeViewer.getTree()); setIsF2disabled(isF2disabled); setEditStringProvider(editStringProvider); createControl(); init(); } /** * answers whether this inline-editor has been disposed * * @return boolean indicating its dispose status */ public boolean isDisposed() { return ((getTextEditorParent() == null) || (getTextEditorParent().isDisposed())); } /** * answers if we can start editing * * @return boolean indicating if we can start editing */ public boolean canEdit() { return ((isDisposed() == false) && (getTree().isDisposed() == false) && (getTree().getEnabled()) && (getTree().getVisible()) && (isSelectedItemEditable())); } /** * Answers whether the currently selected tree-item meets all the editablity * criteria * * @return boolean true if editable, false otherwise */ private boolean isSelectedItemEditable() { return ((getTree().getSelection().length == 1) && (getTree().getSelection()[0].isDisposed() == false) && (getTree().getSelection()[0].getData() != null) && getEditStringProvider().canEdit(getTree().getSelection()[0].getData())); } /** * starts the editing process */ public void startEdit() { while (Display.getCurrent().readAndDispatch()) { // process handler.setEnabled(false) queued in // hide() before re-entering content assist mode } if (canEdit()) { cancelEdit(); setTreeItem(getTree().getSelection()[0]); setInitialText(getEditStringProvider().getEditString(getTreeItem().getData())); getTextEditor().setText(getInitialText()); getTreeEditor().setItem(getTreeItem()); show(); } } /** * cancels the editing process */ public void cancelEdit() { if (canProceed()) { hide(); } } /** * ends the editing process */ public void endEdit() { if (inEndEdit) { // prevent re-entrance into this method when we are already // ending the edit. This prevents, in particular, the focus // listener from ending the edit while we are ending it return; } inEndEdit = true; try { if (canProceed()) { setFinalText(getCurrentText()); if (getFinalText().equals(getInitialText()) == false) { final Object obj = getTreeItem().getData(); getEditStringProvider().setEditString(obj, getFinalText()); // Hide first so that the focus change when the dialog opens // doesn't attempt to re-enter "endEdit()". //RATLC00529737 //ask the label-provider to the update tree-node's label in order to //display the newly entered name getTreeItem().getDisplay().asyncExec(new Runnable() { public void run() { if (!isDisposed()) { getTreeViewer().update(obj, null); } } }); hide(); } else { hide(); } } } finally { inEndEdit = false; } } /** * Opens an error dialog for the specified status object. * * @param status * The status object for which to open an error dialog. * */ protected void openErrorDialog(IStatus status) { ErrorDialog.openError(getShell(), CommonUIMessages.TreeInlineTextEditor_errorDialogTitle, null, status); } private Shell getShell() { return getTree().getShell(); } /** * answers if it is ok to continue operation * * @return boolean indicating if it is ok to continue operation */ private boolean canProceed() { return isDisposed() == false && getTreeItem() != null && !getTreeItem().isDisposed(); } /** * displays the text editing widget */ private void show() { uninstallContentAssist(false); if (getTreeItem() != null) { IContentAssistProcessor processor = getEditStringProvider() .getCompletionProcessor(getTreeItem().getData()); if (processor != null) { // install content assist contentAssistHandler = ContentAssistantHelper.createTextContentAssistant(getTextEditor(), proposalPopupForegroundColor, proposalPopupBackgroundColor, processor); } } getTextEditorParent().setEnabled(true); getTextEditorParent().setVisible(true); getTextEditor().setEnabled(true); getTextEditor().setVisible(true); adjustTextEditorBounds(); getTextEditorParent().redraw(); getTextEditor().selectAll(); getTextEditor().setFocus(); if (getTextActionHandler() != null) { getTextActionHandler().hookHandlers(); } } /** * hides the text editing widget */ private void hide() { setTreeItem(null); getTreeEditor().setItem(null); getTextEditor().setVisible(false); getTextEditor().setEnabled(false); getTextEditorParent().setBounds(getNullRectangle()); getTextEditorParent().setVisible(false); getTextEditorParent().setEnabled(false); if (getTextActionHandler() != null) { getTextActionHandler().unHookHandlers(); } uninstallContentAssist(true); } /** * Uninstalls content assist on the text widget, if installed * * @param fork * whether to queue the uninstall or not */ private void uninstallContentAssist(boolean fork) { if (contentAssistHandler != null) { // uninstall content assist final ContentAssistHandler localHandler = contentAssistHandler; contentAssistHandler = null; if (fork) { Display.getCurrent().asyncExec(new Runnable() { public void run() { // Content Assist hack - queue disablement, otherwise // cleanup on focus lost won't happen localHandler.setEnabled(false); } }); } else { localHandler.setEnabled(false); } } } /** * Disposes the text widget and reset the editorText field. * It becomes unreusable afterwards */ public void dispose() { if (getTextEditorParent() != null) { if (getTextActionHandler() != null) { getTextActionHandler().dispose(); } setTextActionHandler(null); setTextEditorParent(null); setTextEditor(null); getTreeEditor().setEditor(null, null); setTreeEditor(null); setTree(null); proposalPopupBackgroundColor.dispose(); proposalPopupForegroundColor.dispose(); } } /** * creates the text widget and its parent composite */ private void createControl() { setTextEditorParent(new Composite(getTree(), SWT.NONE)); setTreeEditor(new TreeEditor(getTree())); getTreeEditor().horizontalAlignment = SWT.LEFT; getTreeEditor().grabHorizontal = true; getTreeEditor().setEditor(getTextEditorParent(), null); getTextEditorParent().setVisible(false); setTextEditor(new Text(getTextEditorParent(), SWT.NONE)); getTextEditorParent().setBackground(getTextEditor().getBackground()); proposalPopupBackgroundColor = new Color(getShell().getDisplay(), new RGB(254, 241, 233)); proposalPopupForegroundColor = new Color(getShell().getDisplay(), new RGB(0, 0, 0)); } /** * initializes the controls and listeners needed to manage the editing * process */ private void init() { getTextEditorParent().addListener(SWT.Paint, new Listener() { public void handleEvent(Event e) { Point textSize = getTextEditor().getSize(); Point parentSize = getTextEditorParent().getSize(); e.gc.drawRectangle(0, 0, Math.min(textSize.x + 4, parentSize.x - 1), parentSize.y - 1); } }); getTextEditor().addListener(SWT.Modify, new Listener() { public void handleEvent(Event e) { adjustTextEditorBounds(); getTextEditorParent().redraw(); } }); getTextEditor().addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent event) { if (event.character == SWT.CR) { endEdit(); } else if (event.character == SWT.ESC) { cancelEdit(); } } }); getTextEditor().addFocusListener(new FocusAdapter() { public void focusGained(FocusEvent e) { return; } public void focusLost(FocusEvent fe) { Shell activeShell = fe.display.getActiveShell(); if (activeShell != null && getTextEditor().getShell().equals(activeShell.getParent())) { /* * CONTENT ASSIST: focus is lost to the content assist pop * up - stay in focus */ return; } if ((getTreeViewer().getSelection().isEmpty() == false) && canProceed()) { final Object obj = getTreeItem().getData(); getTreeItem().getDisplay().asyncExec(new Runnable() { public void run() { if (!isDisposed()) { getTreeViewer().update(obj, null); } } }); } endEdit(); } }); getTreeViewer().addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { cancelEdit(); } }); if (!isF2disabled()) { getTree().addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent event) { if (event.keyCode == SWT.F2) { startEdit(); } } }); } } /** * initializes the text action handlers * * @param actionBars * The IActionBars for the view-site to retarget global * edit-cut-copy events for the text box * @param disableActionsIds * a List of global actions ids that are non-Eclipse and which * we'll have to disable */ private void initTextActionHandler(IActionBars actionBars, List disableActionsIds) { if (actionBars == null) { return; } actionBars.getMenuManager().addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { cancelEdit(); } }); setTextActionHandler(new InlineTextActionHandler(actionBars, getTextEditor(), disableActionsIds)); } /** * adjusts the bounds of the text widget */ private void adjustTextEditorBounds() { Point textSize = getTextEditor().computeSize(SWT.DEFAULT, SWT.DEFAULT); textSize.x += textSize.y; // increase width a little Point parentSize = getTextEditorParent().getSize(); getTextEditor().setBounds(2, 1, Math.min(textSize.x, parentSize.x - 4), parentSize.y - 2); } /** * Get the Tree being edited. * * @returnTree */ private Tree getTree() { return tree; } /** * return the current text * * @return String the text currently in the text widget, or null if it is * disposed already */ public String getCurrentText() { return (canProceed()) ? getTextEditor().getText() : null; } /** * returns the initial value when editing started * * @return String the last initial value used when editing last started */ public String getInitialText() { return initialText; } /** * returns the comitted string by the user when editing ended (not-canclled) * * @return String the comitted string by the user */ public String getFinalText() { return finalText; } /** * returns the text widget * * @return Text the text widget used for editing */ private Text getTextEditor() { return textEditor; } /** * returns the text widget parent composite * * @return Composite text widget parent composite */ private Composite getTextEditorParent() { return textEditorParent; } /** * Sets the editStringProvider. * * @param editStringProvider * The editStringProvider to set */ private void setEditStringProvider(IEditStringProvider editStringProvider) { this.editStringProvider = editStringProvider; } /** * Sets the textEditor. * * @param textEditor * The textEditor to set */ private void setTextEditor(Text textEditor) { this.textEditor = textEditor; } /** * Sets the textEditorParent. * * @param textEditorParent * The textEditorParent to set */ private void setTextEditorParent(Composite textEditorParent) { this.textEditorParent = textEditorParent; } /** * Sets the tree. * * @param tree * The tree to set */ private void setTree(Tree tree) { this.tree = tree; } /** * Sets the treeItem. * * @param treeItem * The treeItem to set */ private void setTreeItem(TreeItem treeItem) { this.treeItem = treeItem; } /** * Returns the nullRectangle. * * @return Rectangle */ private Rectangle getNullRectangle() { return nullRectangle; } /** * Sets the finalText. * * @param finalText * The finalText to set */ private void setFinalText(String finalText) { this.finalText = finalText; } /** * Sets the initialText. * * @param initialText * The initialText to set */ private void setInitialText(String initialText) { this.initialText = initialText; } /** * Returns the textActionHandler. * * @return TextActionHandler */ private IInlineTextActionHandler getTextActionHandler() { return textActionHandler; } /** * Sets the textActionHandler. * * @param textActionHandler * The textActionHandler to set */ private void setTextActionHandler(IInlineTextActionHandler textActionHandler) { this.textActionHandler = textActionHandler; } }