/* ******************************************************************************
* Copyright (c) 2006-2016 XMind Ltd. and others.
*
* This file is a part of XMind 3. XMind releases 3 and
* above are dual-licensed under the Eclipse Public License (EPL),
* which is available at http://www.eclipse.org/legal/epl-v10.html
* and the GNU Lesser General Public License (LGPL),
* which is available at http://www.gnu.org/licenses/lgpl.html
* See http://www.xmind.net/license.html for details.
*
* Contributors:
* XMind Ltd. - initial API and implementation
*******************************************************************************/
/**
*
*/
package org.xmind.ui.internal.e4handlers;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import javax.inject.Named;
import org.eclipse.core.expressions.IEvaluationContext;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.e4.core.di.annotations.Execute;
import org.eclipse.e4.core.di.annotations.Optional;
import org.eclipse.e4.ui.services.IServiceConstants;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.IFilter;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.progress.IProgressService;
import org.eclipse.ui.services.IEvaluationService;
import org.eclipse.ui.services.IServiceLocator;
import org.xmind.core.Core;
import org.xmind.core.CoreException;
import org.xmind.core.IWorkbook;
import org.xmind.gef.ui.editor.IEditable;
import org.xmind.ui.internal.MindMapMessages;
import org.xmind.ui.internal.MindMapUIPlugin;
import org.xmind.ui.internal.dialogs.SaveWizardDialog;
import org.xmind.ui.internal.editor.DecryptionDialog;
import org.xmind.ui.internal.editor.IEncryptable;
import org.xmind.ui.internal.editor.SaveWizardManager.SaveWizardDescriptor;
import org.xmind.ui.mindmap.IWorkbookRef;
import org.xmind.ui.mindmap.IWorkbookRefFactory;
import org.xmind.ui.prefs.PrefConstants;
import org.xmind.ui.wizards.ISaveContext;
import org.xmind.ui.wizards.ISaveWizard;
import org.xmind.ui.wizards.ISaveWizard.SaveWizardNotAvailable;
import org.xmind.ui.wizards.SaveOptions;
/**
* @author Frank Shaka
* @since 3.6.50
*/
public class SaveWorkbookAsHandler {
@Execute
public void execute(
@Named(IServiceConstants.ACTIVE_SELECTION) Object selection,
@Optional IProgressService progressProvider,
final @Optional IServiceLocator serviceLocator)
throws InvocationTargetException {
if (selection instanceof IStructuredSelection) {
selection = ((IStructuredSelection) selection).getFirstElement();
}
if (selection instanceof IWorkbookRef) {
saveWorkbookAs(new ISaveContext() {
@Override
public Object getContextVariable(String key) {
IEvaluationService service = serviceLocator
.getService(IEvaluationService.class);
Assert.isNotNull(service);
Object variable = service.getCurrentState()
.getVariable(key);
return variable == IEvaluationContext.UNDEFINED_VARIABLE
? null : variable;
}
@Override
public <T> T getContextVariable(Class<T> key) {
return serviceLocator.getService(key);
}
}, (IWorkbookRef) selection, progressProvider, null, false);
}
}
/**
* @param context
* the context where this operation happens
* @param workbookRef
* the source workbook ref to be saved
* @param runner
* an {@link IRunnableContext} to run the job
* @param optionFilter
* <code>null</code> to accept all options at beginning, or an
* {@link IFilter} instance that selects available option ids
* @param onlyToLocal
* is only save to local
* @return a new workbook ref that is filled with contents from the source
* workbook ref
* @throws InvocationTargetException
*/
public static IWorkbookRef saveWorkbookAs(ISaveContext context,
final IWorkbookRef workbookRef, IRunnableContext runner,
IFilter optionFilter, boolean onlyToLocal)
throws InvocationTargetException {
Assert.isLegal(context != null);
Shell shell = context.getContextVariable(Shell.class);
// list all available location options
List<SaveWizardDescriptor> wizards = MindMapUIPlugin.getDefault()
.getSaveWizardManager().getWizards();
if (wizards.isEmpty()) {
// no location options available....
// should not happen?
// no need to extract strings
MessageDialog.openWarning(shell, "Save As", //$NON-NLS-1$
"No 'Save As' options available in this application. " //$NON-NLS-1$
+ "Please contact the software provider for this issue."); //$NON-NLS-1$
return null;
}
// make a new array so that we can remove unavailable ones
wizards = new ArrayList<SaveWizardDescriptor>(wizards);
if (optionFilter != null) {
Iterator<SaveWizardDescriptor> locationProviderIt = wizards
.iterator();
while (locationProviderIt.hasNext()) {
SaveWizardDescriptor locationProvider = locationProviderIt
.next();
if (!optionFilter.select(locationProvider.getId())) {
locationProviderIt.remove();
}
}
}
sortLocationProviders(wizards);
// ensure runnable context available
if (runner == null) {
runner = new ProgressMonitorDialog(shell);
}
// prepare save options
String oldName = workbookRef.getName();
if (oldName == null) {
IWorkbook workbook = workbookRef.getWorkbook();
if (workbook != null) {
oldName = workbook.getPrimarySheet().getRootTopic()
.getTitleText();
}
if (oldName == null)
oldName = MindMapMessages.SaveWorkbookAsHandler_oldName_default;
}
while (true) {
if (wizards.isEmpty()) {
// tried out all possible location options....
// should not happen?
// no need to extract strings
MessageDialog.openError(shell, "Save As", //$NON-NLS-1$
"No 'Save As' options available now for this workbook. " //$NON-NLS-1$
+ "Please contact the software provider for this issue."); //$NON-NLS-1$
return null;
}
SaveOptions options = SaveOptions.getDefault() //
.proposalName(oldName) //
.oldURI(workbookRef.getURI());
SaveWizardDescriptor wizard;
if (onlyToLocal || wizards.size() == 1) {
wizard = wizards.get(0);
} else {
String oldWizardId = workbookRef.getSaveWizardId();
SaveWizardDescriptor defaultWizard = oldWizardId == null ? null
: findSaveWizard(wizards, oldWizardId, false);
if (defaultWizard == null) {
int maxPriority = 0;
for (SaveWizardDescriptor wizardDescriptor : wizards) {
/// sort by priority
/// choose highest priority
/// exclude those whose priority < 0
int priority = wizardDescriptor.getWizard()
.getPriorityFor(context, options);
if (priority > maxPriority) {
maxPriority = priority;
defaultWizard = wizardDescriptor;
}
if (priority < 0)
wizards.remove(wizardDescriptor);
}
}
if (defaultWizard == null) {
defaultWizard = wizards.get(0);
}
SaveWizardDialog dialog = new SaveWizardDialog(shell, wizards,
defaultWizard, options);
if (dialog.open() != SaveWizardDialog.OK)
// canceled
return null;
wizard = dialog.getTargetWizard();
options = dialog.getTargetOptions();
}
ISaveWizard wizardImpl = wizard.getWizard();
Assert.isNotNull(wizardImpl);
URI newURI;
try {
newURI = wizardImpl.askForTargetURI(context, options);
} catch (SaveWizardNotAvailable na) {
wizards.remove(wizard);
String saveText;
if (wizards.size() == 0) {
MessageDialog.openInformation(shell, "No options", //$NON-NLS-1$
na.getMessage());
return null;
} else if (wizards.size() == 1) {
saveText = NLS.bind(
MindMapMessages.SaveWorkbookAsHandler_saveToOtherDialog_saveTo_text,
wizards.get(0).getName());
} else {
saveText = MindMapMessages.SaveWorkbookAsHandler_saveToOtherDialog_saveToAnother_text;
}
MessageDialog dialog = new MessageDialog(
shell,
MindMapMessages.SaveWorkbookAsHandler_saveToOtherDialog_title,
null,
na.getMessage(),
MessageDialog.CONFIRM,
new String[] {
saveText,
IDialogConstants.CANCEL_LABEL
}, 0
);
if (dialog.open() != MessageDialog.OK)
return null;
continue;
}
if (newURI == null)
// canceled
return null;
IWorkbookRefFactory workbookRefFactory = MindMapUIPlugin
.getDefault().getWorkbookRefFactory();
final IWorkbookRef newWorkbookRef = workbookRefFactory
.createWorkbookRef(newURI, null);
Assert.isNotNull(newWorkbookRef);
if (!newWorkbookRef.isInState(IEditable.CLOSED)) {
MessageDialog.openWarning(shell,
MindMapMessages.SaveWorkbookAsHandler_warningDialog_title,
MindMapMessages.SaveWorkbookAsHandler_warningDialog_description);
continue;
}
newWorkbookRef.setActiveContext(workbookRef.getActiveContext());
if (newWorkbookRef.equals(workbookRef)) {
// same location
// just save the old workbook ref
if (!workbookRef.isInState(IWorkbookRef.CLOSED)) {
try {
runner.run(true, true, new IRunnableWithProgress() {
@Override
public void run(IProgressMonitor monitor)
throws InvocationTargetException,
InterruptedException {
workbookRef.save(monitor);
}
});
} catch (InterruptedException e) {
// canceled
return null;
}
}
return newWorkbookRef;
}
if (!newWorkbookRef.canImportFrom(workbookRef)) {
MessageDialog.openError(shell,
MindMapMessages.SaveWorkbookAsHandler_saveAsDialog_title,
MindMapMessages.SaveWorkbookAsHandler_saveAsDialog_description);
wizards.remove(wizard);
continue;
}
try {
runner.run(true, true, new IRunnableWithProgress() {
public void run(IProgressMonitor monitor)
throws InvocationTargetException,
InterruptedException {
doSaveAs(monitor, workbookRef, newWorkbookRef, 0);
}
});
} catch (InterruptedException e) {
// canceled
return null;
}
// should return here in normal cases
return newWorkbookRef;
}
}
private static void doSaveAs(final IProgressMonitor monitor,
final IWorkbookRef oldWorkbookRef,
final IWorkbookRef newWorkbookRef, int times) {
SubMonitor subMonitor = SubMonitor.convert(monitor, 100);
try {
oldWorkbookRef.open(subMonitor.newChild(15));
newWorkbookRef.importFrom(subMonitor.newChild(80), oldWorkbookRef);
} catch (final InvocationTargetException e) {
CoreException coreEx = getCoreException(e);
if (coreEx != null) {
int errType = coreEx.getType();
if (errType == Core.ERROR_WRONG_PASSWORD) {
openDecryptionDialog(oldWorkbookRef, newWorkbookRef,
monitor,
MindMapMessages.MindMapEditor_passwordPrompt_message2,
times);
return;
}
return;
}
} catch (InterruptedException e) {
return;
} finally {
subMonitor.setWorkRemaining(5);
try {
oldWorkbookRef.close(subMonitor.newChild(5));
} catch (InvocationTargetException e) {
} catch (InterruptedException e) {
}
}
}
private static CoreException getCoreException(Throwable e) {
if (e == null)
return null;
if (e instanceof CoreException)
return (CoreException) e;
return getCoreException(e.getCause());
}
private static void openDecryptionDialog(final IWorkbookRef oldWorkbookRef,
final IWorkbookRef newWorkbookRef, final IProgressMonitor monitor,
String message, final int times) {
final int nextTime = times + 1;
final IEncryptable encryptable = oldWorkbookRef
.getAdapter(IEncryptable.class);
Display.getDefault().asyncExec(new Runnable() {
public void run() {
new DecryptionDialog(Display.getDefault().getActiveShell(),
oldWorkbookRef.getName(), encryptable.getPasswordHint(),
times) {
protected void okPressed() {
super.okPressed();
encryptable.setPassword(getPassword());
doSaveAs(monitor, oldWorkbookRef, newWorkbookRef,
nextTime);
};
protected void cancelPressed() {
super.cancelPressed();
};
}.open();
}
});
}
private static void sortLocationProviders(List<SaveWizardDescriptor> list) {
IPreferenceStore pref = MindMapUIPlugin.getDefault()
.getPreferenceStore();
List<SaveWizardDescriptor> preferredWizards = new ArrayList<SaveWizardDescriptor>();
List<SaveWizardDescriptor> nameSortedWizards = new ArrayList<SaveWizardDescriptor>();
String[] preferredWizardIds = getPreferredSaveWizardIds(pref);
if (preferredWizardIds != null) {
for (String wizardId : preferredWizardIds) {
SaveWizardDescriptor wizard = findSaveWizard(list, wizardId,
true);
if (wizard != null) {
preferredWizards.add(wizard);
}
}
}
nameSortedWizards.addAll(list);
if (!nameSortedWizards.isEmpty()) {
Collections.sort(nameSortedWizards,
new Comparator<SaveWizardDescriptor>() {
@Override
public int compare(SaveWizardDescriptor w1,
SaveWizardDescriptor w2) {
return w1.getName().compareTo(w2.getName());
}
});
}
list.clear();
list.addAll(preferredWizards);
list.addAll(nameSortedWizards);
}
/**
* @param list
* @param id
* @param remove
* @return
*/
private static SaveWizardDescriptor findSaveWizard(
List<SaveWizardDescriptor> list, String id, boolean remove) {
Iterator<SaveWizardDescriptor> it = list.iterator();
while (it.hasNext()) {
SaveWizardDescriptor wizard = it.next();
if (id.equals(wizard.getId())) {
if (remove)
it.remove();
return wizard;
}
}
return null;
}
private static String[] getPreferredSaveWizardIds(IPreferenceStore pref) {
String preferredWizardIds = pref.getString(PrefConstants.SAVE_WIZARDS);
if (preferredWizardIds != null && !"".equals(preferredWizardIds)) { //$NON-NLS-1$
return preferredWizardIds.split(","); //$NON-NLS-1$
}
return null;
}
}