package org.xmind.ui.internal.wizards; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.xml.parsers.DocumentBuilderFactory; import org.eclipse.core.runtime.Adapters; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; import org.eclipse.jface.resource.ResourceManager; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IColorProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.jface.wizard.IWizardNode; import org.eclipse.jface.wizard.IWizardPage; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchWizard; import org.eclipse.ui.activities.ITriggerPoint; import org.eclipse.ui.activities.WorkbenchActivityHelper; import org.eclipse.ui.dialogs.FilteredTree; import org.eclipse.ui.dialogs.PatternFilter; import org.eclipse.ui.internal.WorkbenchMessages; import org.eclipse.ui.internal.dialogs.DialogUtil; import org.eclipse.ui.internal.dialogs.WizardActivityFilter; import org.eclipse.ui.internal.dialogs.WizardContentProvider; import org.eclipse.ui.internal.dialogs.WizardPatternFilter; import org.eclipse.ui.internal.dialogs.WorkbenchWizardElement; import org.eclipse.ui.internal.dialogs.WorkbenchWizardNode; import org.eclipse.ui.internal.dialogs.WorkbenchWizardSelectionPage; import org.eclipse.ui.model.AdaptableList; import org.eclipse.ui.model.IWorkbenchAdapter; import org.eclipse.ui.wizards.IWizardCategory; import org.eclipse.ui.wizards.IWizardDescriptor; import org.osgi.framework.Bundle; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xmind.core.io.BundleResource; import org.xmind.core.util.DOMUtils; import org.xmind.ui.internal.MindMapUIPlugin; import org.xmind.ui.mindmap.MindMapUI; import org.xmind.ui.resources.ColorUtils; /** * Abstract wizard page class from which an import or export wizard can be * chosen. * * @since 3.2 */ public abstract class ImportExportPage extends WorkbenchWizardSelectionPage { public static final String RECENTLY_USED_CATEGORY_ID = "recentlyUsed"; //$NON-NLS-1$ protected static final String DIALOG_SETTING_SECTION_NAME = "ImportExportPage."; //$NON-NLS-1$ private static final String WIZARDS_ORDER_XML_PATH = "src/org/xmind/ui/internal/wizards/" //$NON-NLS-1$ + "wizards_order.xml"; //$NON-NLS-1$ protected static class Category { private String id; private List<String> descriptorIds; public Category(String id, List<String> descriptorIds) { this.id = id; this.descriptorIds = descriptorIds; } public String getId() { return id; } public List<String> getDescriptorIds() { return descriptorIds; } } private static class DataTransferWizardCollectionComparator extends ViewerComparator { public final static DataTransferWizardCollectionComparator INSTANCE = new DataTransferWizardCollectionComparator(); private List<Object> wizardsOrder; private DataTransferWizardCollectionComparator() { super(); } public void setWizardsOrder(List<Object> wizardsOrder) { this.wizardsOrder = wizardsOrder; } @Override public int compare(Viewer viewer, Object e1, Object e2) { if (e1 instanceof IWizardCategory && e2 instanceof IWizardCategory) { return indexOf((IWizardCategory) e1) - indexOf((IWizardCategory) e2); } else if (e1 instanceof IWizardDescriptor && e2 instanceof IWizardDescriptor) { //sort recently used if (RECENTLY_USED_CATEGORY_ID .equals(((IWizardDescriptor) e1).getCategory().getId()) && RECENTLY_USED_CATEGORY_ID .equals(((IWizardDescriptor) e2).getCategory() .getId())) { return 1; } else { return indexOf((IWizardDescriptor) e1) - indexOf((IWizardDescriptor) e2); } } return super.compare(viewer, e1, e2); } private int indexOf(Object object) { if (object instanceof IWizardCategory) { IWizardCategory wizardCategory = (IWizardCategory) object; for (Object category : wizardsOrder) { if (((Category) category).getId() .equals(wizardCategory.getId())) { return wizardsOrder.indexOf(category); } } } else if (object instanceof IWizardDescriptor) { IWizardCategory wizardCategory = ((IWizardDescriptor) object) .getCategory(); for (Object category : wizardsOrder) { if (((Category) category).getId() .equals(wizardCategory.getId())) { return ((Category) category).getDescriptorIds() .indexOf(((IWizardDescriptor) object).getId()); } } } return -1; } } private static class InternalFilteredTree extends FilteredTree { private ResourceManager resources; private Label messageLabel; public InternalFilteredTree(Composite parent, int treeStyle, PatternFilter filter, String message) { super(parent, treeStyle, filter, true); messageLabel.setText(message); } protected void createControl(Composite parent, int treeStyle) { GridLayout layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; layout.verticalSpacing = 10; setLayout(layout); if (parent.getLayout() instanceof GridLayout) { setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); } if (showFilterControls) { Composite composite = new Composite(this, SWT.NONE); resources = new LocalResourceManager( JFaceResources.getResources(), composite); composite.setBackground(parent.getBackground()); composite.setLayoutData( new GridData(SWT.FILL, SWT.TOP, true, false)); composite.setFont(parent.getFont()); GridLayout layout2 = new GridLayout(2, false); layout2.marginWidth = 0; layout2.marginHeight = 0; layout2.horizontalSpacing = 32; composite.setLayout(layout2); messageLabel = new Label(composite, SWT.NONE); messageLabel.setLayoutData( new GridData(SWT.BEGINNING, SWT.CENTER, false, true)); messageLabel.setFont(composite.getFont()); messageLabel.setBackground(composite.getBackground()); messageLabel.setForeground((Color) resources .get(ColorUtils.toDescriptor("#323232"))); //$NON-NLS-1$ filterComposite = new Composite(composite, SWT.BORDER); filterComposite.setBackground( getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND)); GridLayout filterLayout = new GridLayout(2, false); filterLayout.marginHeight = 0; filterLayout.marginWidth = 0; filterComposite.setLayout(filterLayout); filterComposite.setFont(composite.getFont()); createFilterControls(filterComposite); filterComposite.setLayoutData( new GridData(SWT.FILL, SWT.BEGINNING, true, false)); } treeComposite = new Composite(this, SWT.NONE); GridLayout treeCompositeLayout = new GridLayout(); treeCompositeLayout.marginHeight = 0; treeCompositeLayout.marginWidth = 0; treeComposite.setLayout(treeCompositeLayout); GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); treeComposite.setLayoutData(data); createTreeControl(treeComposite, treeStyle); } @Override protected void createFilterText(Composite parent) { super.createFilterText(parent); filterText.setForeground( (Color) resources.get(ColorUtils.toDescriptor("#b2b2b2"))); //$NON-NLS-1$ } @Override protected void textChanged() { super.textChanged(); if (getFilterString() == null || getFilterString().equals("")) { //$NON-NLS-1$ Display.getCurrent().timerExec( (int) getRefreshJobDelay() * 3 / 2, new Runnable() { @Override public void run() { getViewer().expandAll(); } }); } } } private static class ImportExportLabelProvider extends LabelProvider implements IColorProvider { private ResourceManager resources; public ImportExportLabelProvider(ResourceManager resources) { this.resources = resources; } @Override public Color getForeground(Object element) { return null; } @Override public Color getBackground(Object element) { return null; } @Override public String getText(Object element) { //query the element for its label IWorkbenchAdapter adapter = getAdapter(element); if (adapter == null) { return ""; //$NON-NLS-1$ } String label = adapter.getLabel(element); //return the decorated label return decorateText(label, element); } private IWorkbenchAdapter getAdapter(Object o) { return Adapters.adapt(o, IWorkbenchAdapter.class); } private String decorateText(String input, Object element) { return input; } @Override public Image getImage(Object element) { if (element instanceof IWizardCategory) { return (Image) resources.get( MindMapUI.getImages().get("icons/impexp/folder.png")); //$NON-NLS-1$ } //obtain the base image by querying the element IWorkbenchAdapter adapter = getAdapter(element); if (adapter == null) { return null; } ImageDescriptor descriptor = adapter.getImageDescriptor(element); if (descriptor == null) { return null; } //add any annotations to the image descriptor descriptor = decorateImage(descriptor, element); return (Image) resources.get(descriptor); } private ImageDescriptor decorateImage(ImageDescriptor input, Object element) { return input; } } /* * Class to create a control that shows a categorized tree of wizard types. */ protected class CategorizedWizardSelectionTree { private final static int SIZING_LISTS_HEIGHT = 200; private IWizardCategory wizardCategories; private String message; private TreeViewer viewer; /** * Constructor for CategorizedWizardSelectionTree * * @param categories * root wizard category for the wizard type * @param msg * message describing what the user should choose from the * tree. */ protected CategorizedWizardSelectionTree(IWizardCategory categories, String msg) { this.wizardCategories = categories; this.message = msg; } /** * Create the tree viewer and a message describing what the user should * choose from the tree. * * @param parent * Composite on which the tree viewer is to be created * @return Comoposite with all widgets */ protected Composite createControl(Composite parent) { Font font = parent.getFont(); // create composite for page. Composite outerContainer = new Composite(parent, SWT.NONE); outerContainer.setLayout(new GridLayout()); outerContainer.setLayoutData(new GridData(GridData.FILL_BOTH)); outerContainer.setFont(font); createFilteredTree(outerContainer); layoutTopControl(viewer.getControl()); return outerContainer; } /** * Create the categorized tree viewer. * * @param parent */ @SuppressWarnings("unchecked") private void createFilteredTree(Composite parent) { // Create a FilteredTree for the categories and wizards InternalFilteredTree filteredTree = new InternalFilteredTree(parent, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER | SWT.FULL_SELECTION, new WizardPatternFilter(), message); viewer = filteredTree.getViewer(); filteredTree.setFont(parent.getFont()); filteredTree.setQuickSelectionMode(true); viewer.setContentProvider(new WizardContentProvider()); viewer.setLabelProvider(new ImportExportLabelProvider(resources)); DataTransferWizardCollectionComparator comparator = DataTransferWizardCollectionComparator.INSTANCE; comparator.setWizardsOrder(wizardsOrder); viewer.setComparator(comparator); ArrayList inputArray = new ArrayList(); if (wizardCategories != null) { if (wizardCategories.getParent() == null) { IWizardCategory[] children = wizardCategories .getCategories(); for (int i = 0; i < children.length; i++) { inputArray.add(children[i]); } } else { inputArray.add(wizardCategories); } } viewer.setAutoExpandLevel(TreeViewer.ALL_LEVELS); AdaptableList input = new AdaptableList(inputArray); // filter wizard list according to capabilities that are enabled viewer.addFilter(new WizardActivityFilter()); viewer.setInput(input); } /** * @return the categorized tree viewer */ protected TreeViewer getViewer() { return viewer; } /** * Layout for the given control. * * @param control */ private void layoutTopControl(Control control) { GridData data = new GridData(GridData.FILL_BOTH); int availableRows = DialogUtil.availableRows(control.getParent()); //Only give a height hint if the dialog is going to be too small if (availableRows > 50) { data.heightHint = SIZING_LISTS_HEIGHT; } else { data.heightHint = availableRows * 3; } control.setLayoutData(data); } } protected ResourceManager resources; private TreeViewer treeViewer; protected List<Object> wizardsOrder; /** * Constructor for import/export wizard page. * * @param aWorkbench * current workbench * @param currentSelection * current selection */ protected ImportExportPage(IWorkbench aWorkbench, IStructuredSelection currentSelection) { super("importExportPage", aWorkbench, currentSelection, null, null); //$NON-NLS-1$ setTitle(WorkbenchMessages.Select); } @Override public void createControl(Composite parent) { Font font = parent.getFont(); // create composite for page. Composite outerContainer = new Composite(parent, SWT.NONE); resources = new LocalResourceManager(JFaceResources.getResources(), outerContainer); outerContainer.setLayout(new GridLayout()); outerContainer.setLayoutData(new GridData(GridData.FILL_BOTH)); outerContainer.setFont(font); Composite comp = createTreeViewer(outerContainer); Dialog.applyDialogFont(comp); restoreWidgetValues(); setControl(outerContainer); initialize(); } /** * Create the tree viewer from which a wizard is selected. */ protected abstract Composite createTreeViewer(Composite parent); /** * Method to call when an item in one of the lists is double-clicked. Shows * the first page of the selected wizard or expands a collapsed tree. * * @param event */ protected void treeDoubleClicked(DoubleClickEvent event) { ISelection selection = event.getViewer().getSelection(); IStructuredSelection ss = (IStructuredSelection) selection; listSelectionChanged(ss); Object element = ss.getFirstElement(); TreeViewer v = (TreeViewer) event.getViewer(); if (v.isExpandable(element)) { v.setExpandedState(element, !v.getExpandedState(element)); } else if (element instanceof WorkbenchWizardElement) { if (canFlipToNextPage()) { getContainer().showPage(getNextPage()); return; } } getContainer().showPage(getNextPage()); } /* * Update the wizard's message based on the given (selected) wizard element. */ private void updateSelectedNode(WorkbenchWizardElement wizardElement) { setErrorMessage(null); if (wizardElement == null) { updateMessage(); setSelectedNode(null); return; } setSelectedNode(createWizardNode(wizardElement)); setMessage(wizardElement.getDescription()); } /* * Update the wizard's message based on the currently selected tab and the * selected wizard on that tab. */ protected void updateMessage() { TreeViewer viewer = getTreeViewer(); if (viewer != null) { ISelection selection = viewer.getSelection(); IStructuredSelection ss = (IStructuredSelection) selection; Object sel = ss.getFirstElement(); if (sel instanceof WorkbenchWizardElement) { updateSelectedNode((WorkbenchWizardElement) sel); } else { setSelectedNode(null); } } else { setMessage(null); } } /* * Method to call whenever the selection in one of the lists has changed. * Updates the wizard's message to relect the description of the currently * selected wizard. */ protected void listSelectionChanged(ISelection selection) { setErrorMessage(null); IStructuredSelection ss = (IStructuredSelection) selection; Object sel = ss.getFirstElement(); if (sel instanceof WorkbenchWizardElement) { WorkbenchWizardElement currentWizardSelection = (WorkbenchWizardElement) sel; updateSelectedNode(currentWizardSelection); } else { updateSelectedNode(null); } } /* * Create a wizard node given a wizard's descriptor. */ private IWizardNode createWizardNode(IWizardDescriptor element) { return new WorkbenchWizardNode(this, element) { @Override public IWorkbenchWizard createWizard() throws CoreException { return wizardElement.createWizard(); } }; } /** * Uses the dialog store to restore widget values to the values that they * held last time this wizard was used to completion. */ protected void restoreWidgetValues() { updateMessage(); } /** * Expands the wizard categories in this page's category viewer that were * expanded last time this page was used. If a category that was previously * expanded no longer exists then it is ignored. */ @SuppressWarnings("unchecked") protected void expandPreviouslyExpandedCategories(String setting, IWizardCategory wizardCategories, TreeViewer viewer) { String[] expandedCategoryPaths = getDialogSettings().getArray(setting); if (expandedCategoryPaths == null || expandedCategoryPaths.length == 0) { return; } List categoriesToExpand = new ArrayList(expandedCategoryPaths.length); if (wizardCategories != null) { for (int i = 0; i < expandedCategoryPaths.length; i++) { IWizardCategory category = wizardCategories .findCategory(new Path(expandedCategoryPaths[i])); if (category != null) { categoriesToExpand.add(category); } } } if (!categoriesToExpand.isEmpty()) { viewer.setExpandedElements(categoriesToExpand.toArray()); } } /** * Selects the wizard category and wizard in this page that were selected * last time this page was used. If a category or wizard that was previously * selected no longer exists then it is ignored. */ protected void selectPreviouslySelected(String setting, IWizardCategory wizardCategories, final TreeViewer viewer) { String selectedId = getDialogSettings().get(setting); if (selectedId == null) { return; } if (wizardCategories == null) { return; } Object selected = wizardCategories.findCategory(new Path(selectedId)); if (selected == null) { selected = wizardCategories.findWizard(selectedId); if (selected == null) { // if we cant find either a category or a wizard, abort. return; } } viewer.setSelection(new StructuredSelection(selected), true); } /** * Stores the collection of currently-expanded categories in this page's * dialog store, in order to recreate this page's state in the next instance * of this page. */ @SuppressWarnings("unchecked") protected void storeExpandedCategories(String setting, TreeViewer viewer) { Object[] expandedElements = viewer.getExpandedElements(); List expandedElementPaths = new ArrayList(expandedElements.length); for (int i = 0; i < expandedElements.length; ++i) { if (expandedElements[i] instanceof IWizardCategory) { expandedElementPaths.add(((IWizardCategory) expandedElements[i]) .getPath().toString()); } } getDialogSettings().put(setting, (String[]) expandedElementPaths .toArray(new String[expandedElementPaths.size()])); } /** * Stores the currently-selected element in this page's dialog store, in * order to recreate this page's state in the next instance of this page. */ protected void storeSelectedCategoryAndWizard(String setting, TreeViewer viewer) { Object selected = ((IStructuredSelection) viewer.getSelection()) .getFirstElement(); if (selected != null) { if (selected instanceof IWizardCategory) { getDialogSettings().put(setting, ((IWizardCategory) selected).getPath().toString()); } else { // else its a wizard getDialogSettings().put(setting, ((IWizardDescriptor) selected).getId()); } } } /** * When Finish is pressed, write widget values to the dialog store so that * they will persist into the next invocation of the wizard page. */ public void saveWidgetValues() { // do nothing by default - subclasses should override } @Override public IWizardPage getNextPage() { ITriggerPoint triggerPoint = getTriggerPoint(); if (triggerPoint == null || WorkbenchActivityHelper .allowUseOf(triggerPoint, getSelectedNode())) { return super.getNextPage(); } return null; } /** * Get the trigger point for the wizard type, if one exists. * * @return the wizard's trigger point */ protected ITriggerPoint getTriggerPoint() { return null; // default implementation } /** * Set the tree viewer that is used for this wizard selection page. * * @param viewer */ protected void setTreeViewer(TreeViewer viewer) { treeViewer = viewer; } /** * Get the tree viewer that is used for this wizard selection page. * * @return tree viewer used for this wizard's selection page */ protected TreeViewer getTreeViewer() { return treeViewer; } /** * Perform any initialization of the wizard page that needs to be done after * widgets are created and main control is set. */ protected void initialize() { // do nothing by default } protected static List<Object> getWizardsOrder(String tag) { List<Object> wizardsOrder = new ArrayList<Object>(); loadWizardsOrder(wizardsOrder, tag); return wizardsOrder; } private static void loadWizardsOrder(List<Object> wizardsOrder, String tag) { Bundle bundle = Platform.getBundle(MindMapUIPlugin.PLUGIN_ID); if (bundle == null) { return; } BundleResource xmLResource = new BundleResource(bundle, new Path(WIZARDS_ORDER_XML_PATH)).resolve(); if (xmLResource == null) { MindMapUIPlugin.getDefault().getLog() .log(new Status(IStatus.ERROR, MindMapUIPlugin.PLUGIN_ID, "Failed to locate wizards order xml: " //$NON-NLS-1$ + bundle.getSymbolicName() + "/" //$NON-NLS-1$ + WIZARDS_ORDER_XML_PATH)); return; } URL listXMLURL = xmLResource.toPlatformURL(); Element element = getWizardsOrderElement(listXMLURL); if (element == null) { return; } Element orderEle = DOMUtils.getFirstChildElementByTag(element, tag); Iterator<Element> it = DOMUtils.childElementIterByTag(orderEle, "category"); //$NON-NLS-1$ while (it.hasNext()) { Element categoryEle = it.next(); String categoryId = categoryEle.getAttribute("id"); //$NON-NLS-1$ List<String> wizardIds = new ArrayList<String>(); for (Element wizardEle : DOMUtils.getChildElementsByTag(categoryEle, "wizard")) { //$NON-NLS-1$ wizardIds.add(wizardEle.getAttribute("id")); //$NON-NLS-1$ } wizardsOrder.add(new Category(categoryId, wizardIds)); } } private static Element getWizardsOrderElement(URL xmlURL) { xmlURL = FileLocator.find(xmlURL); try { InputStream is = xmlURL.openStream(); if (is != null) { try { Document doc = DocumentBuilderFactory.newInstance() .newDocumentBuilder().parse(is); if (doc != null) return doc.getDocumentElement(); } finally { is.close(); } } } catch (Throwable e) { MindMapUIPlugin.getDefault().getLog() .log(new Status(IStatus.WARNING, MindMapUIPlugin.PLUGIN_ID, "Failed to load wizards order list from " //$NON-NLS-1$ + xmlURL.toExternalForm(), e)); } return null; } }