/***************************************************************************** * Copyright (c) 2009 CEA LIST. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Remi Schnekenburger (CEA LIST) remi.schnekenburger@cea.fr - Initial API and implementation * *****************************************************************************/ package org.eclipse.papyrus.uml.diagram.common.part; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.eclipse.gef.palette.PaletteContainer; import org.eclipse.gef.palette.PaletteDrawer; import org.eclipse.gef.palette.PaletteEntry; import org.eclipse.gef.palette.PaletteRoot; import org.eclipse.gef.ui.palette.customize.DefaultEntryPage; import org.eclipse.gef.ui.palette.customize.DrawerEntryPage; import org.eclipse.gef.ui.palette.customize.EntryPage; import org.eclipse.gmf.runtime.gef.ui.palette.customize.IPaletteState; import org.eclipse.gmf.runtime.gef.ui.palette.customize.PaletteCustomizerEx; import org.eclipse.gmf.runtime.gef.ui.palette.customize.PaletteDrawerState; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.papyrus.uml.diagram.common.Activator; import org.eclipse.ui.IMemento; import org.eclipse.ui.WorkbenchException; import org.eclipse.ui.XMLMemento; /** * Customizer for the palette viewer */ public class PapyrusPaletteCustomizer extends PaletteCustomizerEx { /** maps that stores the modification of parents */ protected Map<PaletteEntry, PaletteContainer> changedParents = new HashMap<PaletteEntry, PaletteContainer>(); /** * Creates a new PapyrusPaletteCustomizer. * * @param preferenceStore * the preference store associated to this palette customizer */ public PapyrusPaletteCustomizer(IPreferenceStore preferenceStore) { super(preferenceStore); this.preferences = preferenceStore; } /** * Returns the change parents * * @return the change parents */ public Map<PaletteEntry, PaletteContainer> getChangedParents() { return changedParents; } /** * {@inheritDoc} */ @Override public void applyCustomizationsToPalette(PaletteRoot paletteRoot) { XMLMemento rootMemento = getExistingCustomizations(); if(rootMemento != null) { applyCustomizations(rootMemento, paletteRoot, rootMemento); } // must now clean the palette, as the element whose parent container is // changed is not // removed from its old container... // 1. ContainerA -< PaletteA, ContainerB // 2. apply customization => ContainerA -< PaletteA, ContainerB -< // PaletteA (setParent = // ContainerB) // 3. clean => remove PaletteA from ContainerA. // issue => cannot modify the list of children of containerA in // applyCustomization because // of the iterator system (concurrent modification) // so, clean palette using a specific method // 4. goal: ContainerA, ContainerB -< PaletteA // FIXME made by a new palette definition now // cleanPalette(paletteRoot); } /** * Cleans the given palette root from its entries * * @param paletteRoot */ protected void cleanPalette(PaletteContainer paletteContainer) { // copy children list, then iterate, so the original children list can // be modified... List<PaletteEntry> children = new ArrayList<PaletteEntry>(paletteContainer.getChildren().size()); for(Object o : paletteContainer.getChildren()) { children.add((PaletteEntry)o); } for(PaletteEntry entry : children) { // if parent is not the correct one, delete this child from the // children list if(entry.getParent() != paletteContainer) { paletteContainer.getChildren().remove(entry); } // call for sub-containers if(entry instanceof PaletteContainer) { cleanPalette((PaletteContainer)entry); } } } /** * Creation factory method for the <code>IPaletteState</code>. Clients may * override to provide custom <code>IPaletteStates</code>. * * @param entry * the palette entry * @return a new <code>IPaletteState</code> instance. */ protected IPaletteState createPaletteState(PaletteEntry entry) { if(entry instanceof PaletteDrawer) { return new PaletteDrawerState((PaletteDrawer)entry); } else { // specific papyrus entry state. It manages at least the parent // customization return new PapyrusPaletteEntryState(entry); } } /** * id used in the workspace preference store for palette customizations */ private static final String PALETTE_CUSTOMIZATIONS_ID = "org.eclipse.gmf.runtime.diagram.ui.paletteCustomizations"; //$NON-NLS-1$ /** * Matches each palette entry with a palette state. */ private HashMap<PaletteEntry, IPaletteState> paletteStates = new HashMap<PaletteEntry, IPaletteState>(); /** * the preference store in which the palette customizations are to be saved * and retrieved from */ private IPreferenceStore preferences; public EntryPage getPropertiesPage(PaletteEntry entry) { if(entry.getType().equals(PaletteDrawer.PALETTE_TYPE_DRAWER)) { return new PapyrusDrawerEntryPage(); } return new PapyrusDefaultEntryPage(); } /** * Stores the palette state for a given palette entry if the state has not * yet been stored. * * @param entry * the palette entry */ public void storePaletteState(PaletteEntry entry) { if(paletteStates.get(entry) != null) { // the palette state has already been stored return; } IPaletteState paletteState = createPaletteState(entry); paletteState.storeState(); paletteStates.put(entry, paletteState); } public void revertToSaved() { for(Iterator<Entry<PaletteEntry, IPaletteState>> iterator = paletteStates.entrySet().iterator(); iterator.hasNext();) { Entry<PaletteEntry, IPaletteState> entry = iterator.next(); entry.getValue().rollback(); } paletteStates.clear(); } public void save() { if(paletteStates.isEmpty()) { return; } // If there are already existing palette customizations we will add to // them, otherwise, create a new XML memento which makes it easy to save // the customizations in a tree format. XMLMemento rootMemento = getExistingCustomizations(); if(rootMemento == null) { rootMemento = XMLMemento.createWriteRoot(PALETTE_CUSTOMIZATIONS_ID); } for(Iterator<Entry<PaletteEntry, IPaletteState>> iterator = paletteStates.entrySet().iterator(); iterator.hasNext();) { Entry<PaletteEntry, IPaletteState> entry = iterator.next(); IMemento memento = getMementoForEntry(rootMemento, entry.getKey()); if(memento != null) { entry.getValue().storeChangesInMemento(memento); } } StringWriter writer = new StringWriter(); try { rootMemento.save(writer); if(preferences != null) { preferences.setValue(PALETTE_CUSTOMIZATIONS_ID, writer.toString()); } } catch (IOException e) { Activator.getDefault().logError("Error while saving preferences for palette", e); } paletteStates.clear(); } /** * Given the rootMemento, gets the memento that already exists for the * palette entry or creates a new one in the rootMemento (and the necessary * palette container mementos) if one does not exist yet. The root memento's * tree structure matches that of the palette root. If a palette entry in * stack A, in drawer B is customized, the root memento will have a child * memento for drawer B which has a child memento for stack A which has a * child memento for the entry. The memento's use the palette entry's id. * * @param rootMemento * the root memento representing the palette root * @param paletteEntry * the palette entry for which a memento should be retrieved or * created * @return returns the memento that already exists for the palette entry or * creates a new one in the rootMemento if one does not exist yet or * null if the memento could not be created (most likely because the * palete id is not acceptable). */ private IMemento getMementoForEntry(IMemento rootMemento, PaletteEntry paletteEntry) { ArrayList<String> idList = new ArrayList<String>(); idList.add(paletteEntry.getId()); // modification of GMF code to have the parent. takes the default parent // instead of the // actual one... PaletteContainer parent = changedParents.get(paletteEntry); if(parent == null) { parent = paletteEntry.getParent(); } while(parent != null && !PaletteRoot.PALETTE_TYPE_ROOT.equals(parent.getType())) { idList.add(parent.getId()); parent = parent.getParent(); } // go through ids in reverse order and create the mementos as necessary IMemento containerMemento = rootMemento; for(int i = idList.size() - 1; i >= 0; i--) { String id = idList.get(i); IMemento memento = containerMemento.getChild(id); if(memento == null) { try { memento = containerMemento.createChild(id); } catch (Exception e) { Activator.getDefault().logError("Error while reading preferences for palette", e); return null; } } containerMemento = memento; } return containerMemento; } /** * Recursive helper method to apply the palette customizations in a memento * to a palette container. * * @param containerMemento * the mememto where the container's customizations are stored * @param paletteContainer * the palette container on which to apply the customizations */ private void applyCustomizations(IMemento containerMemento, PaletteContainer paletteContainer, IMemento rootMemento) { for(Iterator iterator = paletteContainer.getChildren().iterator(); iterator.hasNext();) { PaletteEntry entry = (PaletteEntry)iterator.next(); IMemento childMemento = containerMemento.getChild(entry.getId()); if(childMemento != null) { // check that the memento does not change the parent of the // entry if(isChangingParent(entry, childMemento)) { changedParents.put(entry, entry.getParent()); } IPaletteState state = createPaletteState(entry); state.applyChangesFromMemento(childMemento); if(entry instanceof PaletteContainer) { applyCustomizations(childMemento, (PaletteContainer)entry, rootMemento); } } } } protected boolean isChangingParent(PaletteEntry entry, IMemento childMemento) { String parentId = childMemento.getString(PapyrusPaletteEntryState.PARENT_ID_KEY); if(parentId == null) { return false; } return !(entry.getParent().getId().equals(parentId)); } /** * Retrieves the root memento from the workspace preferences if there were * existing palette customizations. * * @return the root memento if there were existing customizations; null * otherwise */ private XMLMemento getExistingCustomizations() { if(preferences != null) { String sValue = preferences.getString(PALETTE_CUSTOMIZATIONS_ID); if(sValue != null && !sValue.equals("")) { //$NON-NLS-1$ try { XMLMemento rootMemento = XMLMemento.createReadRoot(new StringReader(sValue)); return rootMemento; } catch (WorkbenchException e) { Activator.getDefault().logError("Error while reading preferences for palette", e); } } } return null; } /** * Customized so that the palette state can be saved before the change is * made to the palette model so that: <li>when the cancel button is pressed, the stored state can be restored</li> <li>when the save button is * pressed, the customizations made since the state was stored can be written to the workspace preferences</li> */ private class PapyrusDefaultEntryPage extends DefaultEntryPage { protected void handleDescriptionChanged(String text) { storePaletteState(getEntry()); super.handleDescriptionChanged(text); } protected void handleHiddenSelected(boolean isChecked) { storePaletteState(getEntry()); super.handleHiddenSelected(isChecked); } protected void handleNameChanged(String text) { storePaletteState(getEntry()); super.handleNameChanged(text); } } /** * Customized so that the palette state can be saved before the change is * made to the palette model so that: <li>when the cancel button is pressed, the stored state can be restored</li> <li>when the save button is * pressed, the customizations made since the state was stored can be written to the workspace preferences</li> */ public class PapyrusDrawerEntryPage extends DrawerEntryPage { protected void handleOpenSelected(boolean selection) { storePaletteState(getEntry()); super.handleOpenSelected(selection); } protected void handlePinSelected(boolean selection) { storePaletteState(getEntry()); super.handlePinSelected(selection); } protected void handleDescriptionChanged(String text) { storePaletteState(getEntry()); super.handleDescriptionChanged(text); } protected void handleHiddenSelected(boolean isChecked) { storePaletteState(getEntry()); super.handleHiddenSelected(isChecked); } protected void handleNameChanged(String text) { storePaletteState(getEntry()); super.handleNameChanged(text); } } }