KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > actions > RenameResourceAction


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.ui.actions;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.List JavaDoc;
15
16 import org.eclipse.core.commands.ExecutionException;
17 import org.eclipse.core.resources.IResource;
18 import org.eclipse.core.resources.IWorkspace;
19 import org.eclipse.core.resources.IWorkspaceRoot;
20 import org.eclipse.core.resources.ResourceAttributes;
21 import org.eclipse.core.runtime.CoreException;
22 import org.eclipse.core.runtime.IPath;
23 import org.eclipse.core.runtime.IProgressMonitor;
24 import org.eclipse.core.runtime.IStatus;
25 import org.eclipse.core.runtime.Status;
26 import org.eclipse.jface.dialogs.IInputValidator;
27 import org.eclipse.jface.dialogs.InputDialog;
28 import org.eclipse.jface.dialogs.MessageDialog;
29 import org.eclipse.jface.operation.IRunnableWithProgress;
30 import org.eclipse.jface.viewers.IStructuredSelection;
31 import org.eclipse.jface.window.Window;
32 import org.eclipse.swt.SWT;
33 import org.eclipse.swt.custom.TreeEditor;
34 import org.eclipse.swt.events.FocusAdapter;
35 import org.eclipse.swt.events.FocusEvent;
36 import org.eclipse.swt.graphics.Point;
37 import org.eclipse.swt.widgets.Composite;
38 import org.eclipse.swt.widgets.Control;
39 import org.eclipse.swt.widgets.Event;
40 import org.eclipse.swt.widgets.Listener;
41 import org.eclipse.swt.widgets.Shell;
42 import org.eclipse.swt.widgets.Text;
43 import org.eclipse.swt.widgets.Tree;
44 import org.eclipse.swt.widgets.TreeItem;
45 import org.eclipse.ui.PlatformUI;
46 import org.eclipse.ui.ide.undo.MoveResourcesOperation;
47 import org.eclipse.ui.ide.undo.WorkspaceUndoUtil;
48 import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
49 import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
50 import org.eclipse.ui.internal.ide.IIDEHelpContextIds;
51
52 import com.ibm.icu.text.MessageFormat;
53
54 /**
55  * Standard action for renaming the selected resources.
56  * <p>
57  * This class may be instantiated; it is not intended to be subclassed.
58  * </p>
59  */

60 public class RenameResourceAction extends WorkspaceAction {
61
62     /*
63      * The tree editing widgets. If treeEditor is null then edit using the
64      * dialog. We keep the editorText around so that we can close it if a new
65      * selection is made.
66      */

67     private TreeEditor treeEditor;
68
69     private Tree navigatorTree;
70
71     private Text textEditor;
72
73     private Composite textEditorParent;
74
75     private TextActionHandler textActionHandler;
76
77     // The resource being edited if this is being done inline
78
private IResource inlinedResource;
79
80     private boolean saving = false;
81
82     /**
83      * The id of this action.
84      */

85     public static final String JavaDoc ID = PlatformUI.PLUGIN_ID
86             + ".RenameResourceAction";//$NON-NLS-1$
87

88     /**
89      * The new path.
90      */

91     private IPath newPath;
92
93     private String JavaDoc[] modelProviderIds;
94
95     private static final String JavaDoc CHECK_RENAME_TITLE = IDEWorkbenchMessages.RenameResourceAction_checkTitle;
96
97     private static final String JavaDoc CHECK_RENAME_MESSAGE = IDEWorkbenchMessages.RenameResourceAction_readOnlyCheck;
98
99     private static String JavaDoc RESOURCE_EXISTS_TITLE = IDEWorkbenchMessages.RenameResourceAction_resourceExists;
100
101     private static String JavaDoc RESOURCE_EXISTS_MESSAGE = IDEWorkbenchMessages.RenameResourceAction_overwriteQuestion;
102
103     private static String JavaDoc PROJECT_EXISTS_MESSAGE = IDEWorkbenchMessages.RenameResourceAction_overwriteProjectQuestion;
104     
105     private static String JavaDoc PROJECT_EXISTS_TITLE = IDEWorkbenchMessages.RenameResourceAction_projectExists;
106
107     /**
108      * Creates a new action. Using this constructor directly will rename using a
109      * dialog rather than the inline editor of a ResourceNavigator.
110      *
111      * @param shell
112      * the shell for any dialogs
113      */

114     public RenameResourceAction(Shell shell) {
115         super(shell, IDEWorkbenchMessages.RenameResourceAction_text);
116         setToolTipText(IDEWorkbenchMessages.RenameResourceAction_toolTip);
117         setId(ID);
118         PlatformUI.getWorkbench().getHelpSystem().setHelp(this,
119                 IIDEHelpContextIds.RENAME_RESOURCE_ACTION);
120     }
121
122     /**
123      * Creates a new action.
124      *
125      * @param shell
126      * the shell for any dialogs
127      * @param tree
128      * the tree
129      */

130     public RenameResourceAction(Shell shell, Tree tree) {
131         this(shell);
132         this.navigatorTree = tree;
133         this.treeEditor = new TreeEditor(tree);
134     }
135
136     /**
137      * Check if the user wishes to overwrite the supplied resource
138      *
139      * @returns true if there is no collision or delete was successful
140      * @param shell
141      * the shell to create the dialog in
142      * @param destination -
143      * the resource to be overwritten
144      */

145     private boolean checkOverwrite(final Shell shell,
146             final IResource destination) {
147
148         final boolean[] result = new boolean[1];
149
150         // Run it inside of a runnable to make sure we get to parent off of the
151
// shell as we are not in the UI thread.
152

153         Runnable JavaDoc query = new Runnable JavaDoc() {
154             public void run() {
155                 String JavaDoc pathName = destination.getFullPath().makeRelative()
156                         .toString();
157                 String JavaDoc message = RESOURCE_EXISTS_MESSAGE;
158                 String JavaDoc title = RESOURCE_EXISTS_TITLE;
159                 if (destination.getType() == IResource.PROJECT) {
160                     message = PROJECT_EXISTS_MESSAGE;
161                     title = PROJECT_EXISTS_TITLE;
162                 }
163                 result[0] = MessageDialog.openQuestion(shell,
164                         title, MessageFormat.format(message,
165                                 new Object JavaDoc[] { pathName }));
166             }
167
168         };
169
170         shell.getDisplay().syncExec(query);
171         return result[0];
172     }
173
174     /**
175      * Check if the supplied resource is read only or null. If it is then ask
176      * the user if they want to continue. Return true if the resource is not
177      * read only or if the user has given permission.
178      *
179      * @return boolean
180      */

181     private boolean checkReadOnlyAndNull(IResource currentResource) {
182         // Do a quick read only and null check
183
if (currentResource == null) {
184             return false;
185         }
186
187         // Do a quick read only check
188
final ResourceAttributes attributes = currentResource
189                 .getResourceAttributes();
190         if (attributes != null && attributes.isReadOnly()) {
191             return MessageDialog.openQuestion(getShell(), CHECK_RENAME_TITLE,
192                     MessageFormat.format(CHECK_RENAME_MESSAGE,
193                             new Object JavaDoc[] { currentResource.getName() }));
194         }
195
196         return true;
197     }
198
199     Composite createParent() {
200         Tree tree = getTree();
201         Composite result = new Composite(tree, SWT.NONE);
202         TreeItem[] selectedItems = tree.getSelection();
203         treeEditor.horizontalAlignment = SWT.LEFT;
204         treeEditor.grabHorizontal = true;
205         treeEditor.setEditor(result, selectedItems[0]);
206         return result;
207     }
208
209     /**
210      * Get the inset used for cell editors
211      * @param c the Control
212      * @return int
213      */

214     private static int getCellEditorInset(Control c) {
215         return 1; // one pixel wide black border
216
}
217
218     /**
219      * Create the text editor widget.
220      *
221      * @param resource
222      * the resource to rename
223      */

224     private void createTextEditor(final IResource resource) {
225         // Create text editor parent. This draws a nice bounding rect.
226
textEditorParent = createParent();
227         textEditorParent.setVisible(false);
228         final int inset = getCellEditorInset(textEditorParent);
229         if (inset > 0) {
230             textEditorParent.addListener(SWT.Paint, new Listener() {
231                 public void handleEvent(Event e) {
232                     Point textSize = textEditor.getSize();
233                     Point parentSize = textEditorParent.getSize();
234                     e.gc.drawRectangle(0, 0, Math.min(textSize.x + 4,
235                             parentSize.x - 1), parentSize.y - 1);
236                 }
237             });
238         }
239         // Create inner text editor.
240
textEditor = new Text(textEditorParent, SWT.NONE);
241         textEditor.setFont(navigatorTree.getFont());
242         textEditorParent.setBackground(textEditor.getBackground());
243         textEditor.addListener(SWT.Modify, new Listener() {
244             public void handleEvent(Event e) {
245                 Point textSize = textEditor.computeSize(SWT.DEFAULT,
246                         SWT.DEFAULT);
247                 textSize.x += textSize.y; // Add extra space for new
248
// characters.
249
Point parentSize = textEditorParent.getSize();
250                 textEditor.setBounds(2, inset, Math.min(textSize.x,
251                         parentSize.x - 4), parentSize.y - 2 * inset);
252                 textEditorParent.redraw();
253             }
254         });
255         textEditor.addListener(SWT.Traverse, new Listener() {
256             public void handleEvent(Event event) {
257
258                 // Workaround for Bug 20214 due to extra
259
// traverse events
260
switch (event.detail) {
261                 case SWT.TRAVERSE_ESCAPE:
262                     // Do nothing in this case
263
disposeTextWidget();
264                     event.doit = true;
265                     event.detail = SWT.TRAVERSE_NONE;
266                     break;
267                 case SWT.TRAVERSE_RETURN:
268                     saveChangesAndDispose(resource);
269                     event.doit = true;
270                     event.detail = SWT.TRAVERSE_NONE;
271                     break;
272                 }
273             }
274         });
275         textEditor.addFocusListener(new FocusAdapter() {
276             public void focusLost(FocusEvent fe) {
277                 saveChangesAndDispose(resource);
278             }
279         });
280
281         if (textActionHandler != null) {
282             textActionHandler.addText(textEditor);
283         }
284     }
285
286     /**
287      * Close the text widget and reset the editorText field.
288      */

289     private void disposeTextWidget() {
290         if (textActionHandler != null) {
291             textActionHandler.removeText(textEditor);
292         }
293
294         if (textEditorParent != null) {
295             textEditorParent.dispose();
296             textEditorParent = null;
297             textEditor = null;
298             treeEditor.setEditor(null, null);
299         }
300     }
301
302     /**
303      * Returns the elements that the action is to be performed on. Return the
304      * resource cached by the action as we cannot rely on the selection being
305      * correct for inlined text.
306      *
307      * @return list of resource elements (element type: <code>IResource</code>)
308      */

309     protected List JavaDoc getActionResources() {
310         if (inlinedResource == null) {
311             return super.getActionResources();
312         }
313
314         List JavaDoc actionResources = new ArrayList JavaDoc();
315         actionResources.add(inlinedResource);
316         return actionResources;
317     }
318
319     /*
320      * (non-Javadoc) Method declared on WorkspaceAction.
321      */

322     protected String JavaDoc getOperationMessage() {
323         return IDEWorkbenchMessages.RenameResourceAction_progress;
324     }
325
326     /*
327      * (non-Javadoc) Method declared on WorkspaceAction.
328      */

329     protected String JavaDoc getProblemsMessage() {
330         return IDEWorkbenchMessages.RenameResourceAction_problemMessage;
331     }
332
333     /*
334      * (non-Javadoc) Method declared on WorkspaceAction.
335      */

336     protected String JavaDoc getProblemsTitle() {
337         return IDEWorkbenchMessages.RenameResourceAction_problemTitle;
338     }
339
340     /**
341      * Get the Tree being edited.
342      *
343      * @returnTree
344      */

345     private Tree getTree() {
346         return this.navigatorTree;
347     }
348
349     /*
350      * (non-Javadoc) Method declared on WorkspaceAction. Since 3.3, this method
351      * is not used, but an implementation is still provided for compatibility.
352      * All work is now done in the operation created in
353      * createOperation(IStatus[]).
354      */

355     protected void invokeOperation(IResource resource, IProgressMonitor monitor) {
356     }
357
358     /**
359      * Return the new name to be given to the target resource.
360      *
361      * @return java.lang.String
362      * @param resource
363      * the resource to query status on
364      */

365     protected String JavaDoc queryNewResourceName(final IResource resource) {
366         final IWorkspace workspace = IDEWorkbenchPlugin.getPluginWorkspace();
367         final IPath prefix = resource.getFullPath().removeLastSegments(1);
368         IInputValidator validator = new IInputValidator() {
369             public String JavaDoc isValid(String JavaDoc string) {
370                 if (resource.getName().equals(string)) {
371                     return IDEWorkbenchMessages.RenameResourceAction_nameMustBeDifferent;
372                 }
373                 IStatus status = workspace.validateName(string, resource
374                         .getType());
375                 if (!status.isOK()) {
376                     return status.getMessage();
377                 }
378                 if (workspace.getRoot().exists(prefix.append(string))) {
379                     return IDEWorkbenchMessages.RenameResourceAction_nameExists;
380                 }
381                 return null;
382             }
383         };
384
385         InputDialog dialog = new InputDialog(getShell(),
386                 IDEWorkbenchMessages.RenameResourceAction_inputDialogTitle,
387                 IDEWorkbenchMessages.RenameResourceAction_inputDialogMessage,
388                 resource.getName(), validator);
389         dialog.setBlockOnOpen(true);
390         int result = dialog.open();
391         if (result == Window.OK)
392             return dialog.getValue();
393         return null;
394     }
395
396     /**
397      * Return the new name to be given to the target resource or
398      * <code>null<code>
399      * if the query was canceled. Rename the currently selected resource using the table editor.
400      * Continue the action when the user is done.
401      *
402      * @param resource the resource to rename
403      */

404     private void queryNewResourceNameInline(final IResource resource) {
405         // Make sure text editor is created only once. Simply reset text
406
// editor when action is executed more than once. Fixes bug 22269.
407
if (textEditorParent == null) {
408             createTextEditor(resource);
409         }
410         textEditor.setText(resource.getName());
411
412         // Open text editor with initial size.
413
textEditorParent.setVisible(true);
414         Point textSize = textEditor.computeSize(SWT.DEFAULT, SWT.DEFAULT);
415         textSize.x += textSize.y; // Add extra space for new characters.
416
Point parentSize = textEditorParent.getSize();
417         int inset = getCellEditorInset(textEditorParent);
418         textEditor.setBounds(2, inset, Math.min(textSize.x, parentSize.x - 4),
419                 parentSize.y - 2 * inset);
420         textEditorParent.redraw();
421         textEditor.selectAll();
422         textEditor.setFocus();
423     }
424
425     /*
426      * (non-Javadoc) Method declared on IAction; overrides method on
427      * WorkspaceAction.
428      */

429     public void run() {
430
431         if (this.navigatorTree == null) {
432             IResource currentResource = getCurrentResource();
433             if (currentResource == null || !currentResource.exists()) {
434                 return;
435             }
436             // Do a quick read only and null check
437
if (!checkReadOnlyAndNull(currentResource)) {
438                 return;
439             }
440             String JavaDoc newName = queryNewResourceName(currentResource);
441             if (newName == null || newName.equals("")) { //$NON-NLS-1$
442
return;
443             }
444             newPath = currentResource.getFullPath().removeLastSegments(1)
445                     .append(newName);
446             super.run();
447         } else {
448             runWithInlineEditor();
449         }
450     }
451
452     /*
453      * Run the receiver using an inline editor from the supplied navigator. The
454      * navigator will tell the action when the path is ready to run.
455      */

456     private void runWithInlineEditor() {
457         IResource currentResource = getCurrentResource();
458         if (!checkReadOnlyAndNull(currentResource)) {
459             return;
460         }
461         queryNewResourceNameInline(currentResource);
462     }
463
464     /**
465      * Return the currently selected resource. Only return an IResouce if there
466      * is one and only one resource selected.
467      *
468      * @return IResource or <code>null</code> if there is zero or more than
469      * one resources selected.
470      */

471     private IResource getCurrentResource() {
472         List JavaDoc resources = getSelectedResources();
473         if (resources.size() == 1) {
474             return (IResource) resources.get(0);
475         }
476         return null;
477
478     }
479
480     /**
481      * @param path
482      * the path
483      * @param resource
484      * the resource
485      */

486     protected void runWithNewPath(IPath path, IResource resource) {
487         this.newPath = path;
488         super.run();
489     }
490
491     /**
492      * Save the changes and dispose of the text widget.
493      *
494      * @param resource -
495      * the resource to move.
496      */

497     private void saveChangesAndDispose(IResource resource) {
498         if (saving == true) {
499             return;
500         }
501
502         saving = true;
503         // Cache the resource to avoid selection loss since a selection of
504
// another item can trigger this method
505
inlinedResource = resource;
506         final String JavaDoc newName = textEditor.getText();
507         // Run this in an async to make sure that the operation that triggered
508
// this action is completed. Otherwise this leads to problems when the
509
// icon of the item being renamed is clicked (i.e., which causes the
510
// rename
511
// text widget to lose focus and trigger this method).
512
Runnable JavaDoc query = new Runnable JavaDoc() {
513             public void run() {
514                 try {
515                     if (!newName.equals(inlinedResource.getName())) {
516                         IWorkspace workspace = IDEWorkbenchPlugin
517                                 .getPluginWorkspace();
518                         IStatus status = workspace.validateName(newName,
519                                 inlinedResource.getType());
520                         if (!status.isOK()) {
521                             displayError(status.getMessage());
522                         } else {
523                             IPath newPath = inlinedResource.getFullPath()
524                                     .removeLastSegments(1).append(newName);
525                             runWithNewPath(newPath, inlinedResource);
526                         }
527                     }
528                     inlinedResource = null;
529                     // Dispose the text widget regardless
530
disposeTextWidget();
531                     // Ensure the Navigator tree has focus, which it may not if
532
// the
533
// text widget previously had focus.
534
if (navigatorTree != null && !navigatorTree.isDisposed()) {
535                         navigatorTree.setFocus();
536                     }
537                 } finally {
538                     saving = false;
539                 }
540             }
541         };
542         getTree().getShell().getDisplay().asyncExec(query);
543     }
544
545     /**
546      * The <code>RenameResourceAction</code> implementation of this
547      * <code>SelectionListenerAction</code> method ensures that this action is
548      * disabled if any of the selections are not resources or resources that are
549      * not local.
550      */

551     protected boolean updateSelection(IStructuredSelection selection) {
552         disposeTextWidget();
553
554         if (selection.size() > 1) {
555             return false;
556         }
557         if (!super.updateSelection(selection)) {
558             return false;
559         }
560
561         IResource currentResource = getCurrentResource();
562         if (currentResource == null || !currentResource.exists()) {
563             return false;
564         }
565
566         return true;
567     }
568
569     /**
570      * Set the text action handler.
571      *
572      * @param actionHandler
573      * the action handler
574      */

575     public void setTextActionHandler(TextActionHandler actionHandler) {
576         textActionHandler = actionHandler;
577     }
578
579     /**
580      * Returns the model provider ids that are known to the client that
581      * instantiated this operation.
582      *
583      * @return the model provider ids that are known to the client that
584      * instantiated this operation.
585      * @since 3.2
586      */

587     public String JavaDoc[] getModelProviderIds() {
588         return modelProviderIds;
589     }
590
591     /**
592      * Sets the model provider ids that are known to the client that
593      * instantiated this operation. Any potential side effects reported by these
594      * models during validation will be ignored.
595      *
596      * @param modelProviderIds
597      * the model providers known to the client who is using this
598      * operation.
599      * @since 3.2
600      */

601     public void setModelProviderIds(String JavaDoc[] modelProviderIds) {
602         this.modelProviderIds = modelProviderIds;
603     }
604
605     /*
606      * (non-Javadoc)
607      *
608      * @see org.eclipse.ui.actions.WorkspaceAction#createOperation(org.eclipse.core.runtime.IStatus[])
609      *
610      * Overridden to create and execute an undoable operation that performs the
611      * rename.
612      * @since 3.3
613      */

614     protected IRunnableWithProgress createOperation(final IStatus[] errorStatus) {
615         return new IRunnableWithProgress() {
616             public void run(IProgressMonitor monitor) {
617                 IResource[] resources = (IResource[]) getActionResources()
618                         .toArray(new IResource[getActionResources().size()]);
619                 // Rename is only valid for a single resource. This has already
620
// been validated.
621
if (resources.length == 1) {
622                     // check for overwrite
623
IWorkspaceRoot workspaceRoot = resources[0].getWorkspace()
624                             .getRoot();
625                     IResource newResource = workspaceRoot.findMember(newPath);
626                     boolean go = true;
627                     if (newResource != null) {
628                         go = checkOverwrite(getShell(), newResource);
629                     }
630                     if (go) {
631                         MoveResourcesOperation op = new MoveResourcesOperation(
632                                 resources[0],
633                                 newPath,
634                                 IDEWorkbenchMessages.RenameResourceAction_operationTitle);
635                         op.setModelProviderIds(getModelProviderIds());
636                         try {
637                             PlatformUI
638                                     .getWorkbench()
639                                     .getOperationSupport()
640                                     .getOperationHistory()
641                                     .execute(
642                                             op,
643                                             monitor,
644                                             WorkspaceUndoUtil
645                                                     .getUIInfoAdapter(getShell()));
646                         } catch (ExecutionException e) {
647                             if (e.getCause() instanceof CoreException) {
648                                 errorStatus[0] = ((CoreException) e.getCause())
649                                         .getStatus();
650                             } else {
651                                 errorStatus[0] = new Status(IStatus.ERROR,
652                                         PlatformUI.PLUGIN_ID,
653                                         getProblemsMessage(), e);
654                             }
655                         }
656                     }
657                 }
658             }
659         };
660     }
661 }
662
Popular Tags