| /******************************************************************************* |
| * Copyright (c) 2000, 2003 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.ui.internal.editorsupport.win32; |
| |
| import java.io.File; |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.Vector; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IResourceChangeEvent; |
| import org.eclipse.core.resources.IResourceChangeListener; |
| import org.eclipse.core.resources.IResourceDelta; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.dialogs.ErrorDialog; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.dialogs.ProgressMonitorDialog; |
| import org.eclipse.jface.resource.ImageDescriptor; |
| import org.eclipse.jface.resource.JFaceColors; |
| import org.eclipse.jface.window.Window; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.SWTException; |
| import org.eclipse.swt.custom.BusyIndicator; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.ole.win32.OLE; |
| import org.eclipse.swt.ole.win32.OleAutomation; |
| import org.eclipse.swt.ole.win32.OleClientSite; |
| import org.eclipse.swt.ole.win32.OleFrame; |
| import org.eclipse.swt.ole.win32.Variant; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.swt.widgets.MenuItem; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorSite; |
| import org.eclipse.ui.IFileEditorInput; |
| import org.eclipse.ui.IPartListener; |
| import org.eclipse.ui.IWorkbench; |
| import org.eclipse.ui.IWorkbenchActionConstants; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.IWorkbenchWindow; |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.actions.WorkspaceModifyOperation; |
| import org.eclipse.ui.dialogs.SaveAsDialog; |
| import org.eclipse.ui.internal.WorkbenchPlugin; |
| import org.eclipse.ui.part.EditorPart; |
| import org.eclipse.ui.part.FileEditorInput; |
| |
| /** |
| */ |
| public class OleEditor extends EditorPart { |
| |
| /** |
| * The resource listener updates the receiver when |
| * a change has occured. |
| */ |
| private IResourceChangeListener resourceListener = new IResourceChangeListener() { |
| |
| /* |
| * @see IResourceChangeListener#resourceChanged(IResourceChangeEvent) |
| */ |
| public void resourceChanged(IResourceChangeEvent event) { |
| IResourceDelta mainDelta = event.getDelta(); |
| if (mainDelta == null) |
| return; |
| IResourceDelta affectedElement = mainDelta.findMember(resource |
| .getFullPath()); |
| if (affectedElement != null) |
| processDelta(affectedElement); |
| } |
| |
| /* |
| * Process the delta for the receiver |
| */ |
| private boolean processDelta(final IResourceDelta delta) { |
| |
| Runnable changeRunnable = null; |
| |
| switch (delta.getKind()) { |
| case IResourceDelta.REMOVED: |
| if ((IResourceDelta.MOVED_TO & delta.getFlags()) != 0) { |
| changeRunnable = new Runnable() { |
| public void run() { |
| IPath path = delta.getMovedToPath(); |
| IFile newFile = delta.getResource().getWorkspace() |
| .getRoot().getFile(path); |
| if (newFile != null) { |
| sourceChanged(newFile); |
| } |
| } |
| }; |
| } else { |
| changeRunnable = new Runnable() { |
| public void run() { |
| sourceDeleted = true; |
| getSite().getPage().closeEditor(OleEditor.this, |
| true); |
| } |
| }; |
| |
| } |
| |
| break; |
| } |
| |
| if (changeRunnable != null) |
| update(changeRunnable); |
| |
| return true; // because we are sitting on files anyway |
| } |
| |
| }; |
| |
| private OleFrame clientFrame; |
| |
| private OleClientSite clientSite; |
| |
| private File source; |
| |
| private IFile resource; |
| |
| private Image oleTitleImage; |
| |
| //The sourceDeleted flag makes sure that the receiver is not |
| //dirty when shutting down |
| boolean sourceDeleted = false; |
| |
| //The sourceChanged flag indicates whether or not the save from the ole component |
| //can be used or if the input changed |
| boolean sourceChanged = false; |
| |
| /** |
| * Keep track of whether we have an active client so we do not |
| * deactivate multiple times |
| */ |
| private boolean clientActive = false; |
| |
| /** |
| * Keep track of whether we have activated OLE or not as some applications |
| * will only allow single activations. |
| */ |
| private boolean oleActivated = false; |
| |
| private IPartListener partListener = new IPartListener() { |
| public void partActivated(IWorkbenchPart part) { |
| activateClient(part); |
| } |
| |
| public void partBroughtToTop(IWorkbenchPart part) { |
| } |
| |
| public void partClosed(IWorkbenchPart part) { |
| } |
| |
| public void partOpened(IWorkbenchPart part) { |
| } |
| |
| public void partDeactivated(IWorkbenchPart part) { |
| deactivateClient(part); |
| } |
| }; |
| |
| private static final String RENAME_ERROR_TITLE = OleMessages |
| .getString("OleEditor.errorSaving"); //$NON-NLS-1$ |
| |
| private static final String OLE_EXCEPTION_TITLE = OleMessages |
| .getString("OleEditor.oleExceptionTitle"); //$NON-NLS-1$ |
| |
| private static final String OLE_EXCEPTION_MESSAGE = OleMessages |
| .getString("OleEditor.oleExceptionMessage"); //$NON-NLS-1$ |
| |
| private static final String OLE_CREATE_EXCEPTION_MESSAGE = OleMessages |
| .getString("OleEditor.oleCreationExceptionMessage"); //$NON-NLS-1$ |
| |
| private static final String SAVE_ERROR_TITLE = OleMessages |
| .getString("OleEditor.savingTitle"); //$NON-NLS-1$ |
| |
| private static final String SAVE_ERROR_MESSAGE = OleMessages |
| .getString("OleEditor.savingMessage"); //$NON-NLS-1$ |
| |
| /** |
| * Return a new ole editor. |
| */ |
| public OleEditor() { |
| //Do nothing |
| } |
| |
| private void activateClient(IWorkbenchPart part) { |
| if (part == this) { |
| oleActivate(); |
| this.clientActive = true; |
| } |
| } |
| |
| /** |
| * createPartControl method comment. |
| */ |
| public void createPartControl(Composite parent) { |
| |
| // Create a frame. |
| clientFrame = new OleFrame(parent, SWT.CLIP_CHILDREN); |
| clientFrame.setBackground(JFaceColors.getBannerBackground(clientFrame |
| .getDisplay())); |
| |
| initializeWorkbenchMenus(); |
| |
| // Set the input file. |
| IEditorInput input = getEditorInput(); |
| if (input instanceof IFileEditorInput) { |
| setResource(((IFileEditorInput) input).getFile()); |
| resource.getWorkspace().addResourceChangeListener(resourceListener); |
| } |
| |
| createClientSite(); |
| } |
| |
| /** |
| * Create the client site for the reciever |
| */ |
| |
| private void createClientSite() { |
| //If there was an OLE Error or nothing has been created yet |
| if (clientFrame == null || clientFrame.isDisposed()) |
| return; |
| // Create a OLE client site. |
| try { |
| clientSite = new OleClientSite(clientFrame, SWT.NONE, source); |
| } catch (SWTException exception) { |
| |
| IStatus errorStatus = new Status(IStatus.ERROR, |
| WorkbenchPlugin.PI_WORKBENCH, IStatus.ERROR, |
| OLE_CREATE_EXCEPTION_MESSAGE, exception); |
| ErrorDialog.openError(null, OLE_EXCEPTION_TITLE, errorStatus |
| .getMessage(), errorStatus); |
| return; |
| } |
| clientSite.setBackground(JFaceColors.getBannerBackground(clientFrame |
| .getDisplay())); |
| |
| } |
| |
| private void deactivateClient(IWorkbenchPart part) { |
| //Check the client active flag. Set it to false when we have deactivated |
| //to prevent multiple deactivations. |
| if (part == this && clientActive) { |
| if (clientSite != null) |
| clientSite.deactivateInPlaceClient(); |
| this.clientActive = false; |
| this.oleActivated = false; |
| } |
| } |
| |
| /** |
| * Display an error dialog with the supplied title and message. |
| * @param title |
| * @param message |
| */ |
| private void displayErrorDialog(String title, String message) { |
| Shell parent = null; |
| if (getClientSite() != null) |
| parent = getClientSite().getShell(); |
| MessageDialog.openError(parent, title, message); |
| } |
| |
| /** |
| * @see IWorkbenchPart#dispose |
| */ |
| public void dispose() { |
| if (resource != null) |
| resource.getWorkspace().removeResourceChangeListener( |
| resourceListener); |
| |
| //can dispose the title image because it was created in init |
| if (oleTitleImage != null) { |
| oleTitleImage.dispose(); |
| oleTitleImage = null; |
| } |
| |
| if (getSite() != null && getSite().getPage() != null) |
| getSite().getPage().removePartListener(partListener); |
| |
| } |
| |
| /** |
| * Print this object's contents |
| */ |
| public void doPrint() { |
| if (clientSite == null) |
| return; |
| BusyIndicator.showWhile(clientSite.getDisplay(), new Runnable() { |
| public void run() { |
| clientSite.exec(OLE.OLECMDID_PRINT, |
| OLE.OLECMDEXECOPT_PROMPTUSER, null, null); |
| // note: to check for success: above == SWTOLE.S_OK |
| } |
| }); |
| } |
| |
| /** |
| * Save the viewer's contents to the source file system file |
| */ |
| public void doSave(final IProgressMonitor monitor) { |
| if (clientSite == null) |
| return; |
| BusyIndicator.showWhile(clientSite.getDisplay(), new Runnable() { |
| |
| /* |
| * (non-Javadoc) |
| * @see java.lang.Runnable#run() |
| */ |
| public void run() { |
| |
| //Do not try and use the component provided save if the source has |
| //changed in Eclipse |
| if (!sourceChanged) { |
| int result = clientSite.queryStatus(OLE.OLECMDID_SAVE); |
| if ((result & OLE.OLECMDF_ENABLED) != 0) { |
| result = clientSite.exec(OLE.OLECMDID_SAVE, |
| OLE.OLECMDEXECOPT_PROMPTUSER, null, null); |
| if (result == OLE.S_OK) { |
| try { |
| resource.refreshLocal(IResource.DEPTH_ZERO, |
| monitor); |
| } catch (CoreException ex) { |
| //Do nothing on a failed refresh |
| } |
| return; |
| } |
| displayErrorDialog(OLE_EXCEPTION_TITLE, |
| OLE_EXCEPTION_MESSAGE + String.valueOf(result)); |
| return; |
| } |
| } |
| if (saveFile(source)) { |
| try { |
| resource.refreshLocal(IResource.DEPTH_ZERO, monitor); |
| } catch (CoreException ex) { |
| //Do nothing on a failed refresh |
| } |
| } else |
| displayErrorDialog(SAVE_ERROR_TITLE, SAVE_ERROR_MESSAGE |
| + source.getName()); |
| } |
| }); |
| } |
| |
| /** |
| * Save the viewer's contents into the provided resource. |
| */ |
| public void doSaveAs() { |
| if (clientSite == null) |
| return; |
| WorkspaceModifyOperation op = saveNewFileOperation(); |
| Shell shell = clientSite.getShell(); |
| try { |
| new ProgressMonitorDialog(shell).run(false, true, op); |
| } catch (InterruptedException interrupt) { |
| //Nothing to reset so do nothing |
| } catch (InvocationTargetException invocationException) { |
| MessageDialog.openError(shell, RENAME_ERROR_TITLE, |
| invocationException.getTargetException().getMessage()); |
| } |
| |
| } |
| |
| /** |
| * Answer self's client site |
| * |
| * @return org.eclipse.swt.ole.win32.OleClientSite |
| */ |
| public OleClientSite getClientSite() { |
| return clientSite; |
| } |
| |
| /** |
| * Answer the file system representation of self's input element |
| * |
| * @return java.io.File |
| */ |
| public File getSourceFile() { |
| return source; |
| } |
| |
| private void handleWord() { |
| OleAutomation dispInterface = new OleAutomation(clientSite); |
| // Get Application |
| int[] appId = dispInterface |
| .getIDsOfNames(new String[] { "Application" }); //$NON-NLS-1$ |
| if (appId != null) { |
| Variant pVarResult = dispInterface.getProperty(appId[0]); |
| if (pVarResult != null) { |
| OleAutomation application = pVarResult.getAutomation(); |
| int[] dispid = application |
| .getIDsOfNames(new String[] { "DisplayScrollBars" }); //$NON-NLS-1$ |
| if (dispid != null) { |
| Variant rgvarg = new Variant(true); |
| application.setProperty(dispid[0], rgvarg); |
| } |
| application.dispose(); |
| } |
| } |
| dispInterface.dispose(); |
| } |
| |
| /* (non-Javadoc) |
| * Initializes the editor when created from scratch. |
| * |
| * This method is called soon after part construction and marks |
| * the start of the extension lifecycle. At the end of the |
| * extension lifecycle <code>shutdown</code> will be invoked |
| * to terminate the lifecycle. |
| * |
| * @param container an interface for communication with the part container |
| * @param input The initial input element for the editor. In most cases |
| * it is an <code>IFile</code> but other types are acceptable. |
| * @see IWorkbenchPart#shutdown |
| */ |
| public void init(IEditorSite site, IEditorInput input) |
| throws PartInitException { |
| // Check input. |
| if (!(input instanceof IFileEditorInput)) |
| throw new PartInitException(OleMessages.format( |
| "OleEditor.invalidInput", new Object[] { input })); //$NON-NLS-1$ |
| |
| IFile file = (((IFileEditorInput) input).getFile()); |
| |
| //Cannot create this with a file and no physical location |
| if (file.getLocation() == null |
| || !(new File(file.getLocation().toOSString()).exists())) |
| throw new PartInitException( |
| OleMessages |
| .format( |
| "OleEditor.noFileInput", new Object[] { file.getLocation() })); //$NON-NLS-1$ |
| |
| // Save input. |
| setSite(site); |
| setInput(input); |
| |
| // Update titles. |
| setTitle(input.getName()); |
| setTitleToolTip(input.getToolTipText()); |
| ImageDescriptor desc = input.getImageDescriptor(); |
| if (desc != null) { |
| oleTitleImage = desc.createImage(); |
| setTitleImage(oleTitleImage); |
| } |
| |
| // Listen for part activation. |
| site.getPage().addPartListener(partListener); |
| |
| } |
| |
| /** |
| * Initialize the workbench menus for proper merging |
| */ |
| protected void initializeWorkbenchMenus() { |
| //If there was an OLE Error or nothing has been created yet |
| if (clientFrame == null || clientFrame.isDisposed()) |
| return; |
| // Get the browser menubar. If one does not exist then |
| // create it. |
| Shell shell = clientFrame.getShell(); |
| Menu menuBar = shell.getMenuBar(); |
| if (menuBar == null) { |
| menuBar = new Menu(shell, SWT.BAR); |
| shell.setMenuBar(menuBar); |
| } |
| |
| // Swap the file and window menus. |
| MenuItem[] windowMenu = new MenuItem[1]; |
| MenuItem[] fileMenu = new MenuItem[1]; |
| Vector containerItems = new Vector(); |
| |
| IWorkbenchWindow window = getSite().getWorkbenchWindow(); |
| |
| for (int i = 0; i < menuBar.getItemCount(); i++) { |
| MenuItem item = menuBar.getItem(i); |
| String id = ""; //$NON-NLS-1$ |
| if (item.getData() instanceof IMenuManager) |
| id = ((IMenuManager) item.getData()).getId(); |
| if (id.equals(IWorkbenchActionConstants.M_FILE)) |
| fileMenu[0] = item; |
| else if (id.equals(IWorkbenchActionConstants.M_WINDOW)) |
| windowMenu[0] = item; |
| else { |
| if (window.isApplicationMenu(id)) { |
| containerItems.addElement(item); |
| } |
| } |
| } |
| MenuItem[] containerMenu = new MenuItem[containerItems.size()]; |
| containerItems.copyInto(containerMenu); |
| clientFrame.setFileMenus(fileMenu); |
| clientFrame.setContainerMenus(containerMenu); |
| clientFrame.setWindowMenus(windowMenu); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * @see org.eclipse.ui.ISaveablePart#isDirty() |
| */ |
| public boolean isDirty() { |
| /*Return only if we have a clientSite which is dirty |
| as this can be asked before anything is opened*/ |
| return this.clientSite != null; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * @see org.eclipse.ui.ISaveablePart#isSaveAsAllowed() |
| */ |
| public boolean isSaveAsAllowed() { |
| return true; |
| } |
| |
| /** |
| *Since we don't know when a change has been made, always answer true |
| * @return <code>false</code> if it was not opened and <code>true</code> |
| * only if it is dirty |
| */ |
| public boolean isSaveNeeded() { |
| return getClientSite() != null && isDirty(); |
| } |
| |
| /** |
| * Save the supplied file using the SWT API. |
| * @param file java.io.File |
| * @return <code>true</code> if the save was successful |
| */ |
| private boolean saveFile(File file) { |
| |
| File tempFile = new File(file.getAbsolutePath() + ".tmp"); //$NON-NLS-1$ |
| file.renameTo(tempFile); |
| boolean saved = false; |
| if (OLE.isOleFile(file) || usesStorageFiles(clientSite.getProgramID())) { |
| saved = clientSite.save(file, true); |
| } else { |
| saved = clientSite.save(file, false); |
| } |
| |
| if (saved) { |
| // save was successful so discard the backup |
| tempFile.delete(); |
| return true; |
| } |
| // save failed so restore the backup |
| tempFile.renameTo(file); |
| return false; |
| } |
| |
| /** |
| * Save the new File using the client site. |
| * @return WorkspaceModifyOperation |
| */ |
| private WorkspaceModifyOperation saveNewFileOperation() { |
| |
| return new WorkspaceModifyOperation() { |
| public void execute(final IProgressMonitor monitor) |
| throws CoreException { |
| SaveAsDialog dialog = new SaveAsDialog(clientFrame.getShell()); |
| IFileEditorInput input = (IFileEditorInput) getEditorInput(); |
| IFile sFile = input.getFile(); |
| dialog.setOriginalFile(sFile); |
| dialog.open(); |
| |
| IPath newPath = dialog.getResult(); |
| if (newPath == null) |
| return; |
| |
| if (dialog.getReturnCode() == Window.OK) { |
| String projectName = newPath.segment(0); |
| newPath = newPath.removeFirstSegments(1); |
| IProject project = resource.getWorkspace().getRoot() |
| .getProject(projectName); |
| newPath = project.getLocation().append(newPath); |
| File newFile = newPath.toFile(); |
| if (saveFile(newFile)) { |
| IFile newResource = resource.getWorkspace().getRoot() |
| .getFileForLocation(newPath); |
| if (newResource != null) { |
| sourceChanged(newResource); |
| newResource.refreshLocal(IResource.DEPTH_ZERO, |
| monitor); |
| } |
| } else { |
| displayErrorDialog(SAVE_ERROR_TITLE, SAVE_ERROR_MESSAGE |
| + newFile.getName()); |
| return; |
| } |
| } |
| } |
| }; |
| |
| } |
| |
| /* |
| * (non-Javadoc) |
| * @see org.eclipse.ui.IWorkbenchPart#setFocus() |
| */ |
| public void setFocus() { |
| //Do not take focus |
| } |
| |
| /** |
| * Make ole active so that the controls are rendered. |
| */ |
| private void oleActivate() { |
| //If there was an OLE Error or nothing has been created yet |
| if (clientSite == null || clientFrame == null |
| || clientFrame.isDisposed()) |
| return; |
| |
| if (!oleActivated) { |
| clientSite.doVerb(OLE.OLEIVERB_SHOW); |
| oleActivated = true; |
| String progId = clientSite.getProgramID(); |
| if (progId != null && progId.startsWith("Word.Document")) { //$NON-NLS-1$ |
| handleWord(); |
| } |
| } |
| } |
| |
| /** |
| * Set the file resource that this object is displaying |
| * @param file |
| */ |
| protected void setResource(IFile file) { |
| resource = file; |
| source = new File(file.getLocation().toOSString()); |
| } |
| |
| /** |
| * See if it is one of the known types that use OLE Storage. |
| * @param progID the type to test |
| * @return <code>true</code> if it is one of the known types |
| */ |
| private static boolean usesStorageFiles(String progID) { |
| return (progID != null && (progID.startsWith("Word.", 0) //$NON-NLS-1$ |
| || progID.startsWith("MSGraph", 0) //$NON-NLS-1$ |
| || progID.startsWith("PowerPoint", 0) //$NON-NLS-1$ |
| || progID.startsWith("Excel", 0))); //$NON-NLS-1$ |
| } |
| |
| /** |
| * The source has changed to the newFile. Update |
| * editors and set any required flags |
| * @param newFile The file to get the new contents from. |
| */ |
| private void sourceChanged(IFile newFile) { |
| |
| FileEditorInput newInput = new FileEditorInput(newFile); |
| setInput(newInput); |
| setResource(newFile); |
| sourceChanged = true; |
| setTitle(newInput.getName()); |
| |
| } |
| |
| /* |
| * See IEditorPart.isSaveOnCloseNeeded() |
| */ |
| public boolean isSaveOnCloseNeeded() { |
| return !sourceDeleted && super.isSaveOnCloseNeeded(); |
| } |
| |
| /** |
| * Posts the update code "behind" the running operation. |
| * |
| * @param runnable the update code |
| */ |
| private void update(Runnable runnable) { |
| IWorkbench workbench = PlatformUI.getWorkbench(); |
| IWorkbenchWindow[] windows = workbench.getWorkbenchWindows(); |
| if (windows != null && windows.length > 0) { |
| Display display = windows[0].getShell().getDisplay(); |
| display.asyncExec(runnable); |
| } else |
| runnable.run(); |
| } |
| |
| } |