/******************************************************************************* * Copyright (c) 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.wst.server.ui.internal.view.servers; import org.eclipse.core.runtime.CoreException; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredSelection; 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.graphics.Point; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; 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.actions.TextActionHandler; import org.eclipse.ui.texteditor.IWorkbenchActionDefinitionIds; import org.eclipse.wst.server.core.IServer; import org.eclipse.wst.server.core.IServerWorkingCopy; import org.eclipse.wst.server.core.internal.ServerPlugin; import org.eclipse.wst.server.ui.internal.Messages; /** * Action to rename a server. */ public class RenameAction extends AbstractServerAction { /* * The tree editing widgets. If treeEditor is null then edit using the * dialog. We keep the editorText around so that we can close it if a new * selection is made. */ protected TreeEditor treeEditor; protected Tree tree; protected Text textEditor; protected Composite textEditorParent; private TextActionHandler textActionHandler; // The server being edited if this is being done inline protected IServer editedServer; protected boolean saving = false; public RenameAction(Shell shell, TreeViewer viewer, ISelectionProvider selectionProvider) { super(shell, selectionProvider, Messages.actionRename); setActionDefinitionId(IWorkbenchActionDefinitionIds.RENAME); this.tree = viewer.getTree(); this.treeEditor = new TreeEditor(tree); try { selectionChanged((IStructuredSelection) selectionProvider.getSelection()); } catch (Exception e) { // ignore } } public void perform(IServer server) { runWithInlineEditor(server); } /* * Run the receiver using an inline editor from the supplied navigator. The * navigator will tell the action when the path is ready to run. */ private void runWithInlineEditor(IServer server) { queryNewServerNameInline(server); } /** * On Mac the text widget already provides a border when it has focus, so * there is no need to draw another one. The value of returned by this * method is usd to control the inset we apply to the text field bound's in * order to get space for drawing a border. A value of 1 means a one-pixel * wide border around the text field. A negative value supresses the border. * However, in M9 the system property * "org.eclipse.swt.internal.carbon.noFocusRing" has been introduced as a * temporary workaround for bug #28842. The existence of the property turns * the native focus ring off if the widget is contained in a main window * (not dialog). The check for the property should be removed after a final * fix for #28842 has been provided. */ private static int getCellEditorInset(Control c) { // special case for MacOS X if ("carbon".equals(SWT.getPlatform())) { //$NON-NLS-1$ if (System .getProperty("org.eclipse.swt.internal.carbon.noFocusRing") == null || c.getShell().getParent() != null) { //$NON-NLS-1$ return -2; // native border } } return 1; // one pixel wide black border } /** * Get the Tree being edited. * * @returnTree */ private Tree getTree() { return tree; } private Composite createParent() { Tree tree2 = getTree(); Composite result = new Composite(tree2, SWT.NONE); TreeItem[] selectedItems = tree2.getSelection(); treeEditor.horizontalAlignment = SWT.LEFT; treeEditor.grabHorizontal = true; treeEditor.setEditor(result, selectedItems[0]); return result; } /** * Return the new name to be given to the target resource or * <code>null<code> * if the query was canceled. Rename the currently selected server using the table editor. * Continue the action when the user is done. * * @param server the server to rename */ private void queryNewServerNameInline(final IServer server) { // 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(server); } textEditor.setText(server.getName()); // 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(); int inset = getCellEditorInset(textEditorParent); textEditor.setBounds(2, inset, Math.min(textSize.x, parentSize.x - 4), parentSize.y - 2 * inset); textEditorParent.redraw(); textEditor.selectAll(); textEditor.setFocus(); } /** * Create the text editor widget. * * @param server the server to rename */ private void createTextEditor(final IServer server) { // Create text editor parent. This draws a nice bounding rect textEditorParent = createParent(); textEditorParent.setVisible(false); final int inset = getCellEditorInset(textEditorParent); if (inset > 0) { textEditorParent.addListener(SWT.Paint, new Listener() { public void handleEvent(Event 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); textEditor.setFont(tree.getFont()); textEditorParent.setBackground(textEditor.getBackground()); textEditor.addListener(SWT.Modify, new Listener() { public void handleEvent(Event 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, inset, Math.min(textSize.x, parentSize.x - 4), parentSize.y - 2 * inset); textEditorParent.redraw(); } }); textEditor.addListener(SWT.Traverse, new Listener() { public void handleEvent(Event 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(server); event.doit = true; event.detail = SWT.TRAVERSE_NONE; break; } } }); textEditor.addFocusListener(new FocusAdapter() { public void focusLost(FocusEvent fe) { saveChangesAndDispose(server); } }); if (textActionHandler != null) { textActionHandler.addText(textEditor); } } /** * Close the text widget and reset the editorText field. */ protected void disposeTextWidget() { if (textActionHandler != null) textActionHandler.removeText(textEditor); if (textEditorParent != null) { textEditorParent.dispose(); textEditorParent = null; textEditor = null; treeEditor.setEditor(null, null); } } /** * Save the changes and dispose of the text widget. * * @param server the server to rename */ protected void saveChangesAndDispose(IServer server) { if (saving == true) return; saving = true; // Cache the resource to avoid selection loss since a selection of // another item can trigger this method editedServer = server; final String newName = 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) getTree().getShell().getDisplay().asyncExec(new Runnable() { public void run() { try { if (!newName.equals(editedServer.getName())) { if (ServerPlugin.isNameInUse(editedServer, newName)) { MessageDialog.openError(shell, Messages.defaultDialogTitle, Messages.errorDuplicateName); } else { try { IServerWorkingCopy wc = editedServer.createWorkingCopy(); wc.setName(newName); wc.save(false, null); } catch (CoreException ce) { // ignore for now } } } editedServer = null; // Dispose the text widget regardless disposeTextWidget(); // Ensure the Navigator tree has focus, which it may not if // the text widget previously had focus if (tree != null && !tree.isDisposed()) { tree.setFocus(); } } finally { saving = false; } } }); } }