blob: 36f8995c9f734d1a9811782baa9771940b9e7a1f [file] [log] [blame]
/*******************************************************************************
* 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.IMarker;
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.swt.SWT;
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.jface.action.IMenuManager;
import org.eclipse.jface.dialogs.Dialog;
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.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.WorkbenchMessages;
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)
try {
processDelta(affectedElement);
} catch (CoreException exception) {
//Failed so close the receiver
getSite().getPage().closeEditor(OleEditor.this, true);
}
}
/*
* Process the delta for the receiver
*/
private boolean processDelta(final IResourceDelta delta) throws CoreException {
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 =
WorkbenchMessages.getString("OleEditor.errorSaving"); //$NON-NLS-1$
private static final String OLE_EXCEPTION_TITLE =
WorkbenchMessages.getString("OleEditor.oleExceptionTitle"); //$NON-NLS-1$
private static final String OLE_EXCEPTION_MESSAGE =
WorkbenchMessages.getString("OleEditor.oleExceptionMessage"); //$NON-NLS-1$
private static final String SAVE_ERROR_TITLE =
WorkbenchMessages.getString("OleEditor.savingTitle"); //$NON-NLS-1$
private static final String SAVE_ERROR_MESSAGE =
WorkbenchMessages.getString("OleEditor.savingMessage"); //$NON-NLS-1$
/**
* Return a new ole editor.
*/
public OleEditor() {
}
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.
clientSite = new OleClientSite(clientFrame, SWT.NONE, source);
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.
*/
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() {
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) {
}
return;
} else {
displayErrorDialog(
OLE_EXCEPTION_TITLE,
OLE_EXCEPTION_MESSAGE + String.valueOf(result));
return;
}
}
}
if (saveFile(source)) {
try {
resource.refreshLocal(IResource.DEPTH_ZERO, monitor);
} catch (CoreException ex) {
}
} 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;
}
/* (non-Javadoc)
* Sets the cursor and selection state for this editor to the passage defined
* by the given marker.
*
* @see IEditorPart
*/
public void gotoMarker(IMarker marker) {
}
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(
WorkbenchMessages.format("OleEditor.invalidInput", new Object[] { input })); //$NON-NLS-1$
//$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(
WorkbenchMessages.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)
* Returns whether the contents of this editor have changed since the last save
* operation. As this is an external editor and we have no way of knowing return true
* if there is something to save to.
*
* @see IEditorPart
*/
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)
* Returns whether the "save as" operation is supported by this editor. We assume we
* can always save a file whether it will be via OLE or not.
*
* @see IEditorPart
*/
public boolean isSaveAsAllowed() {
return true;
}
/**
* Since we don't know when a change has been made, always answer true
*/
public boolean isSaveNeeded() {
//Answer false if it was not opened and true only if it is dirty
return getClientSite() != null && isDirty();
}
/**
* Save the supplied file using the SWT API.
* @param file java.io.File
*/
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;
} else {
// save failed so restore the backup
tempFile.renameTo(file);
return false;
}
}
/**
* Save the new File using the client site.
*/
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() == Dialog.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;
}
}
}
};
}
/**
* Asks the part to take focus within the workbench.
*/
public void setFocus() {}
/**
* 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
*/
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.
*/
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
*/
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();
}
}