/******************************************************************************* * Copyright (c) 2008, 2011 Thomas Holland (thomas@innot.de) and others. * 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: * Thomas Holland - initial API and implementation *******************************************************************************/ package de.innot.avreclipse.ui.propertypages; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICResourceDescription; import org.eclipse.cdt.managedbuilder.core.IConfiguration; import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager; import org.eclipse.cdt.ui.newui.CDTPropertyManager; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.widgets.Widget; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.dialogs.PropertyPage; import org.osgi.service.prefs.BackingStoreException; import de.innot.avreclipse.AVRPlugin; import de.innot.avreclipse.core.properties.AVRProjectProperties; import de.innot.avreclipse.core.properties.ProjectPropertyManager; /** * Manages the list of {@link AbstractAVRPage} for a property dialog. * <p> * This manager keeps track of all open property pages. Each page registers itself by calling the * {@link #getPropertyManager(PropertyPage, IProject)} method. * </p> * <p> * All data and all methods are static. This is no problem, because the property dialog is modal, so * only one property dialog = one session can be open at a time. * </p> * <p> * This class is very similar to and supplements the * <code>CDTPropertyManager</class>, which manages the list of all CDT <code>AbstractPage</code>s.</p> * * @see CDTPropertyManager * * @author Thomas Holland * @since 2.2 * */ public class AVRPropertyPageManager { /** List of all open Property Pages */ private static List<PropertyPage> fPages = new ArrayList<PropertyPage>(); /** The Project for which the properties are edited */ private static IProject fProject; /** The Project Property Manager for the current project */ private static ProjectPropertyManager fPropertiesManager; /** The Project Properties for the current project */ private static AVRProjectProperties fProjectProps; /** Cache of build config Properties for the current project. */ private static Map<String, AVRProjectProperties> fConfigPropertiesMap; /** * Gets the the {@link ProjectPropertyManager} for the project and registers the given page in * the list of property pages. * <p> * On the first call to this method for a new or a different project, a new session is * initiated. * </p> * * @param page * <code>AbstractAVRPropertyPage</code> to register in this manager. * @param project * The current project. * @return The <code>ProjectPropertyManager</code> for the given project. */ public static ProjectPropertyManager getPropertyManager(PropertyPage page, IProject project) { // If no pages registered start a new static session if (fPages.size() == 0) { fProject = null; fPropertiesManager = null; } // Remember the page and add dispose listener to the page so we know // when it is closed if (!fPages.contains(page)) { fPages.add(page); page.getControl().addDisposeListener(fDisposeListener); } // Check if a new project has been selected if (fProject == null || !project.equals(fProject)) { fProject = project; fPropertiesManager = null; } // Check if a new properties object is required if (fPropertiesManager == null) { fPropertiesManager = ProjectPropertyManager.getPropertyManager(project); fProjectProps = null; fConfigPropertiesMap = new HashMap<String, AVRProjectProperties>(); } return fPropertiesManager; } /** * Save all modifications to the properties to the properties storage and remove the page from * the manager. * <p> * Also the list of "per config" properties is synchronized with the list of existing build * configurations, so AVR properties for deleted build configurations will be deleted as well. * </p> * * @param page * Originating page. * @param allconfigs * Array with all build configuration description objects. */ public static void performOK(PropertyPage page, ICConfigurationDescription[] allconfigs) { try { fPropertiesManager.save(); // saves the perConfig flag if (fProjectProps != null) { fProjectProps.save(); // Saves the global project properties } // and the config properties if (fConfigPropertiesMap != null) { for (AVRProjectProperties props : fConfigPropertiesMap.values()) { props.save(); } } if (allconfigs != null) { // Convert the given array of ConfigurationDescriptions into a list of configuration // id values and call the ProjectPropertyManager.sync() method to remove // configuration properties for deleted build configurations. List<String> allcfgids = new ArrayList<String>(allconfigs.length); for (ICConfigurationDescription cfgd : allconfigs) { IConfiguration cfg = ManagedBuildManager.getConfigurationForDescription(cfgd); allcfgids.add(cfg.getId()); } fPropertiesManager.sync(allcfgids); } } catch (BackingStoreException e) { IStatus status = new Status(IStatus.ERROR, AVRPlugin.PLUGIN_ID, "Could not write project properties to the preferences.", e); ErrorDialog.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), "AVR Properties Error", null, status); // We continue, even if the props could not be saved, because we still need to remove // the page. } removePage(page); } /** * Cancel all modifications and remove the page from the manager. * * @param page * Originating page. */ public static void performCancel(PropertyPage page) { if (fConfigPropertiesMap != null) { fConfigPropertiesMap.clear(); } fProjectProps = null; removePage(page); } /** * Remove the given <code>AbstractAVRPage</code> from the manager. * <p> * Once the last page has been removed from this manager, the current session is closed. * </p> * * @param page * Page to remove from the manager. */ private static void removePage(PropertyPage page) { if (fPages.contains(page)) { fPages.remove(page); } if (fPages.size() == 0) { // all pages have been disposed fPropertiesManager = null; fProject = null; fProjectProps = null; fConfigPropertiesMap = null; } } /** * @return A <code>List</code> of all managed pages. */ public static List<PropertyPage> getPages() { return fPages; } /** * Gets the project properties for a given <code>ICResourceDescription</code>. * <p> * The avr project properties object is cached so that it can be retrieved multiple times * without being sync'd with the underlying preferencestore. The first call to this method will * return a freshly sync'd object. * </p> * * @param resdesc * @return */ public static AVRProjectProperties getConfigProperties(ICResourceDescription resdesc) { IConfiguration buildcfg = getConfigFromConfigDesc(resdesc); AVRProjectProperties props = null; // Added the cache in response to Bug 2050945: Read Back of "Enable Individual Settings ..." // The ProjectPropertyManager will now always sync any property objects it hands out. // Syncing means that all changes made are lost, so we cache the objects in order to keep // all modifications until either the performOK() or the performCancel() method is // called. if (fConfigPropertiesMap != null) { if (fConfigPropertiesMap.containsKey(buildcfg.getId())) { props = fConfigPropertiesMap.get(buildcfg.getId()); } else { props = fPropertiesManager.getConfigurationProperties(buildcfg); fConfigPropertiesMap.put(buildcfg.getId(), props); } } return props; } public static AVRProjectProperties getConfigPropertiesNoCache(ICResourceDescription resdesc) { // This method is used in the performApply() methods of the Tabs. // In order to save only the contents of a single tab it gets a new Properties object // directly from the preference store. The tab can then modify its individual settings, and // saves it immediately. IConfiguration buildcfg = getConfigFromConfigDesc(resdesc); return fPropertiesManager.getConfigurationProperties(buildcfg, false); } /** * Get the current per project properties. * * @return */ public static AVRProjectProperties getProjectProperties() { if (fProjectProps == null) { fProjectProps = fPropertiesManager.getProjectProperties(); } return fProjectProps; } /** * Convenience method to get an <code>IConfiguration</code> from an * <code>ICResourceDescription</code> * * @param resdesc * An <code>ICResourceDescription</code> * @return <code>IConfiguration</code> associated with the given Description. */ private static IConfiguration getConfigFromConfigDesc(ICResourceDescription resdesc) { ICConfigurationDescription cfgDes = resdesc.getConfiguration(); IConfiguration conf = ManagedBuildManager.getConfigurationForDescription(cfgDes); return conf; } /** * Listener to remove disposed pages from the manager. */ private static DisposeListener fDisposeListener = new MyDisposeListener(); private static class MyDisposeListener implements DisposeListener { public void widgetDisposed(DisposeEvent e) { Widget w = e.widget; for (PropertyPage page : fPages) { if (page.getControl().equals(w)) { fPages.remove(page); break; } } if (fPages.size() == 0) { // all pages have been disposed fPropertiesManager = null; fProject = null; fProjectProps = null; fConfigPropertiesMap = null; } } }; }