/******************************************************************************* * Copyright (c) 2007, 2010 Intel Corporation, QNX Software Systems, 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: * Intel Corporation - Initial API and implementation * Miwako Tokugawa (Intel Corporation) - Fixed-location tooltip support * QNX Software Systems - [269571] Apply button failure on tool changes *******************************************************************************/ package org.eclipse.cdt.managedbuilder.ui.properties; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.cdt.core.settings.model.ICResourceDescription; import org.eclipse.cdt.managedbuilder.core.BuildException; import org.eclipse.cdt.managedbuilder.core.IConfiguration; import org.eclipse.cdt.managedbuilder.core.IFileInfo; import org.eclipse.cdt.managedbuilder.core.IFolderInfo; import org.eclipse.cdt.managedbuilder.core.IHoldsOptions; import org.eclipse.cdt.managedbuilder.core.IOption; import org.eclipse.cdt.managedbuilder.core.IOptionCategory; import org.eclipse.cdt.managedbuilder.core.IResourceConfiguration; import org.eclipse.cdt.managedbuilder.core.IResourceInfo; import org.eclipse.cdt.managedbuilder.core.ITool; import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager; import org.eclipse.cdt.managedbuilder.internal.core.MultiConfiguration; import org.eclipse.cdt.managedbuilder.internal.macros.BuildMacroProvider; import org.eclipse.cdt.managedbuilder.internal.ui.Messages; import org.eclipse.cdt.ui.newui.CDTPrefUtil; import org.eclipse.cdt.ui.newui.PageLayout; import org.eclipse.core.resources.IResource; import org.eclipse.jface.preference.IPreferencePageContainer; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.Point; 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.ScrollBar; /** * Tool Settings Tab in project properties Build Settings * * @noextend This class is not intended to be subclassed by clients. * @noinstantiate This class is not intended to be instantiated by clients. */ public class ToolSettingsTab extends AbstractCBuildPropertyTab implements IPreferencePageContainer { private static ToolListElement selectedElement; /* * Dialog widgets */ private TreeViewer optionList; private StyledText tipText; private StyleRange styleRange; private SashForm sashForm; private SashForm sashForm2; private Composite settingsPageContainer; private ScrolledComposite containerSC; /* * Bookeeping variables */ private Map<String, List<AbstractToolSettingUI>> configToPageListMap; private IPreferenceStore settingsStore; private AbstractToolSettingUI currentSettingsPage; private ToolListContentProvider listprovider; private Object propertyObject; private IResourceInfo fInfo; private boolean displayFixedTip = CDTPrefUtil.getBool(CDTPrefUtil.KEY_TIPBOX); private int[] defaultWeights = new int[] {4, 1}; private int[] hideTipBoxWeights = new int[] {1, 0}; @Override public void createControls(Composite par) { super.createControls(par); usercomp.setLayout(new GridLayout()); configToPageListMap = new HashMap<String, List<AbstractToolSettingUI>>(); settingsStore = ToolSettingsPrefStore.getDefault(); // Create the sash form sashForm = new SashForm(usercomp, SWT.NONE); sashForm.setOrientation(SWT.HORIZONTAL); sashForm.setLayoutData(new GridData(GridData.FILL_BOTH)); GridLayout layout = new GridLayout(); layout.numColumns = 2; layout.marginHeight = 5; sashForm.setLayout(layout); if (displayFixedTip==false) { createSelectionArea(sashForm); createEditArea(sashForm); } else { createSelectionArea(sashForm); sashForm2 = new SashForm(sashForm, SWT.NONE); sashForm2.setOrientation(SWT.VERTICAL); createEditArea(sashForm2); createTipArea(sashForm2); sashForm2.setWeights(defaultWeights); } usercomp.addControlListener(new ControlAdapter() { @Override public void controlResized(ControlEvent e) { specificResize(); }}); propertyObject = page.getElement(); setValues(); specificResize(); } private void specificResize() { Point p1 = optionList.getTree().computeSize(SWT.DEFAULT, SWT.DEFAULT); Point p2 = optionList.getTree().getSize(); Point p3 = usercomp.getSize(); p1.x += calcExtra(); if (p3.x >= p1.x && (p1.x < p2.x || (p2.x * 2 < p3.x))) { optionList.getTree().setSize(p1.x , p2.y); sashForm.setWeights(new int[] {p1.x, (p3.x - p1.x)}); } } private int calcExtra() { int x = optionList.getTree().getBorderWidth() * 2; ScrollBar sb = optionList.getTree().getVerticalBar(); if (sb != null && sb.isVisible()) x += sb.getSize().x; return x; } protected void createSelectionArea (Composite parent) { optionList = new TreeViewer(parent, SWT.SINGLE|SWT.H_SCROLL|SWT.V_SCROLL|SWT.BORDER); optionList.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { handleOptionSelection(); }}); optionList.getControl().setLayoutData(new GridData(GridData.FILL_BOTH)); optionList.setLabelProvider(new ToolListLabelProvider()); optionList.addFilter(new ViewerFilter() { @Override public boolean select(Viewer viewer, Object parent, Object element) { if(parent instanceof IResourceConfiguration && element instanceof ITool) { return !((ITool)element).getCustomBuildStep(); } else { return true; } } }); } /** * @param name - header of the tooltip help * @param tip - tooltip text * @since 7.0 */ protected void updateTipText(String name, String tip) { if (tipText==null) { return; } tipText.setText(name+"\n\n"+tip); //$NON-NLS-1$ styleRange.length = name.length(); tipText.setStyleRange(styleRange); tipText.update(); } /* (non-Javadoc) * Method resetTipText * @since 7.0 */ private void resetTipText() { if (tipText==null) { return; } tipText.setText(Messages.ToolSettingsTab_0); tipText.update(); } /* (non-Javadoc) * Method displayOptionsForCategory * @param category */ private void displayOptionsForCategory(ToolListElement toolListElement) { selectedElement = toolListElement; IOptionCategory category = toolListElement.getOptionCategory(); IHoldsOptions optionHolder = toolListElement.getHoldOptions(); AbstractToolSettingUI oldPage = currentSettingsPage; currentSettingsPage = null; // Create a new settings page if necessary List<AbstractToolSettingUI> pages = getPagesForConfig(); for (AbstractToolSettingUI page : pages) { if (page.isFor(optionHolder, category)) { currentSettingsPage = page; break; } } if (currentSettingsPage == null) { currentSettingsPage = new BuildOptionSettingsUI( this, fInfo, optionHolder, category, displayFixedTip); boolean needToolTipBox = false; if (displayFixedTip==true) { needToolTipBox = ((BuildOptionSettingsUI)currentSettingsPage).needToolTipBox(optionHolder,category); } pages.add(currentSettingsPage); currentSettingsPage.setContainer(this); currentSettingsPage.setToolTipBoxNeeded(needToolTipBox); if (currentSettingsPage.getControl() == null) { currentSettingsPage.createControl(settingsPageContainer); } } // Make all the other pages invisible Control[] children = settingsPageContainer.getChildren(); Control currentControl = currentSettingsPage.getControl(); for (Control element : children) { if (element != currentControl) element.setVisible(false); } if (displayFixedTip==true) { if (currentSettingsPage.isToolTipBoxNeeded()==false) { // eliminate the option tip box sashForm2.setWeights(hideTipBoxWeights); sashForm2.layout(); } else { // display the option tip box sashForm2.setWeights(defaultWeights); sashForm2.layout(); } } currentSettingsPage.setVisible(true); currentSettingsPage.updateFields(); if (oldPage != null && oldPage != currentSettingsPage) { oldPage.setVisible(false); resetTipText(); } // Set the size of the scrolled area containerSC.setMinSize(currentSettingsPage.computeSize()); settingsPageContainer.layout(); } /* (non-Javadoc) * Method displayOptionsForTool * @param tool */ private void displayOptionsForTool(ToolListElement toolListElement) { selectedElement = toolListElement; ITool tool = toolListElement.getTool(); // Cache the current build setting page AbstractToolSettingUI oldPage = currentSettingsPage; currentSettingsPage = null; // Create a new page if we need one List<AbstractToolSettingUI> pages = getPagesForConfig(); for (AbstractToolSettingUI page : pages) { if (page.isFor(tool, null)) { currentSettingsPage = page; break; } } if (currentSettingsPage == null) { currentSettingsPage = new BuildToolSettingUI(this, fInfo, tool); pages.add(currentSettingsPage); currentSettingsPage.setContainer(this); if (currentSettingsPage.getControl() == null) { currentSettingsPage.createControl(settingsPageContainer); } } // Make all the other pages invisible Control[] children = settingsPageContainer.getChildren(); Control currentControl = currentSettingsPage.getControl(); for (Control element : children) { if (element != currentControl) element.setVisible(false); } if (displayFixedTip==true) { // eliminate the tool tip area sashForm2.setWeights(hideTipBoxWeights); sashForm2.layout(); } // Make the current page visible currentSettingsPage.setVisible(true); // Save the last page build options. if (oldPage != null && oldPage != currentSettingsPage){ oldPage.storeSettings(); } currentSettingsPage.setValues(); if (oldPage != null && oldPage != currentSettingsPage) oldPage.setVisible(false); // Set the size of the scrolled area containerSC.setMinSize(currentSettingsPage.computeSize()); settingsPageContainer.layout(); } /* (non-Javadoc) * Add the fixed-location tool tip box. */ private void createTipArea (Composite parent) { tipText = new StyledText(parent, SWT.V_SCROLL|SWT.BORDER | SWT.READ_ONLY | SWT.MULTI | SWT.WRAP); tipText.setLayoutData(new GridData(GridData.FILL_BOTH)); tipText.setText(Messages.ToolSettingsTab_0); styleRange = new StyleRange(); styleRange.start = 0; FontData data = new FontData(); data.setHeight(10); //data.setName("sans"); data.setStyle(SWT.BOLD); Font font = new Font(parent.getDisplay(),data); styleRange.font = font; } /* (non-Javadoc) * Add the tabs relevant to the project to edit area tab folder. */ protected void createEditArea(Composite parent) { int style = (SWT.H_SCROLL | SWT.V_SCROLL); if (displayFixedTip) { style |= SWT.BORDER; } containerSC = new ScrolledComposite(parent, style); containerSC.setExpandHorizontal(true); containerSC.setExpandVertical(true); // Add a container for the build settings page settingsPageContainer = new Composite(containerSC, SWT.NULL); settingsPageContainer.setLayout(new PageLayout()); containerSC.setContent(settingsPageContainer); containerSC.setMinSize(settingsPageContainer.computeSize(SWT.DEFAULT, SWT.DEFAULT)); settingsPageContainer.layout(); } @Override public void setVisible(boolean visible){ if(visible){ updateData(page.getResDesc()); } super.setVisible(visible); } protected void setValues() { /* * This method updates the context of the build property pages * - Which configuration/resource configuration is selected * - Which tool/option category is selected * * It is called: * - When a property page becomes visible * - When the user changes the configuration selection * - When the user changes the "exclude" setting for a resource */ // Create the Tree Viewer content provider if first time if (listprovider == null) { IResource resource = (IResource) propertyObject; listprovider = new ToolListContentProvider(resource.getType()); optionList.setContentProvider(listprovider); } // Update the selected configuration and the Tree Viewer ToolListElement[] newElements; optionList.setInput(fInfo); newElements = (ToolListElement[])listprovider.getElements(fInfo); optionList.expandAll(); // Determine what the selection in the tree should be // If the saved selection is not null, try to match the saved selection // with an object in the new element list. // Otherwise, select the first tool in the tree Object primaryObject = null; if (selectedElement != null && newElements != null) { selectedElement = matchSelectionElement(selectedElement, newElements); } if (selectedElement == null) { selectedElement = (newElements != null && newElements.length > 0 ? newElements[0] : null); } if (selectedElement != null) { primaryObject = selectedElement.getTool(); if (primaryObject == null) { primaryObject = selectedElement.getOptionCategory(); } if (primaryObject != null) { if (primaryObject instanceof IOptionCategory) { ((ToolSettingsPrefStore)settingsStore).setSelection(getResDesc(), selectedElement, (IOptionCategory)primaryObject); } optionList.setSelection(new StructuredSelection(selectedElement), true); } } } private ToolListElement matchSelectionElement(ToolListElement currentElement, ToolListElement[] elements) { // First, look for an exact match ToolListElement match = exactMatchSelectionElement(currentElement, elements); if (match == null) // Else, look for the same tool/category in the new set of elements match = equivalentMatchSelectionElement(currentElement, elements); return match; } private ToolListElement exactMatchSelectionElement(ToolListElement currentElement, ToolListElement[] elements) { for (ToolListElement e : elements) { if (e == currentElement) { return currentElement; } e = exactMatchSelectionElement(currentElement, e.getChildElements()); if (e != null) return e; } return null; } private ToolListElement equivalentMatchSelectionElement(ToolListElement currentElement, ToolListElement[] elements) { for (ToolListElement e : elements) { if (e.isEquivalentTo(currentElement)) { return e; } e = equivalentMatchSelectionElement(currentElement, e.getChildElements()); if (e != null) return e; } return null; } private void handleOptionSelection() { // Get the selection from the tree list if (optionList == null) return; IStructuredSelection selection = (IStructuredSelection) optionList.getSelection(); // Set the option page based on the selection ToolListElement toolListElement = (ToolListElement)selection.getFirstElement(); if (toolListElement != null) { IOptionCategory cat = toolListElement.getOptionCategory(); if (cat == null) cat = (IOptionCategory)toolListElement.getTool(); if (cat != null) ((ToolSettingsPrefStore)settingsStore).setSelection(getResDesc(), toolListElement, cat); cat = toolListElement.getOptionCategory(); if (cat != null) { displayOptionsForCategory(toolListElement); } else { displayOptionsForTool(toolListElement); } } } /* * (non-Javadoc) * @see org.eclipse.cdt.ui.dialogs.ICOptionPage#performDefaults() */ @Override protected void performDefaults() { if (page.isForProject()) { ManagedBuildManager.resetConfiguration(page.getProject(), getCfg()); } else { ManagedBuildManager.resetOptionSettings(fInfo); } ITool tools[]; if (page.isForProject()) tools = getCfg().getFilteredTools(); else tools = getResCfg(getResDesc()).getTools(); for( int i = 0; i < tools.length; i++ ){ if(!tools[i].getCustomBuildStep()) { tools[i].setToolCommand(null); tools[i].setCommandLinePattern(null); } } // Reset the category or tool selection and run selection event handler selectedElement = null; setDirty(true); fInfo = getResCfg(getResDesc()); setValues(); handleOptionSelection(); } /* * (non-Javadoc) * @see org.eclipse.cdt.ui.dialogs.ICOptionPage#performApply(IProgressMonitor) */ private void copyHoldsOptions(IHoldsOptions src, IHoldsOptions dst, IResourceInfo res){ if(src instanceof ITool) { ITool t1 = (ITool)src; ITool t2 = (ITool)dst; if (t1.getCustomBuildStep()) return; t2.setToolCommand(t1.getToolCommand()); t2.setCommandLinePattern(t1.getCommandLinePattern()); } IOption op1[] = src.getOptions(); IOption op2[] = dst.getOptions(); for(int i = 0; i < op1.length; i++) { setOption(op1[i], op2[i], dst, res); } } /** * @param filter - a viewer filter * @see StructuredViewer#addFilter(ViewerFilter) * * @since 5.1 */ protected void addFilter(ViewerFilter filter) { optionList.addFilter(filter); } /** * Copy the value of an option to another option for a given resource. * @param op1 - option to copy the value from * @param op2 - option to copy the value to * @param dst - the holder/parent of the option * @param res - the resource configuration the option belongs to * * @since 5.1 */ protected void setOption(IOption op1, IOption op2, IHoldsOptions dst, IResourceInfo res){ try { switch (op1.getValueType()) { case IOption.BOOLEAN : boolean boolVal = op1.getBooleanValue(); ManagedBuildManager.setOption(res, dst, op2, boolVal); break; case IOption.ENUMERATED : String enumVal = op1.getStringValue(); String enumId = op1.getEnumeratedId(enumVal); String out = (enumId != null && enumId.length() > 0) ? enumId : enumVal; ManagedBuildManager.setOption(res, dst, op2, out); break; case IOption.STRING : ManagedBuildManager.setOption(res, dst, op2, op1.getStringValue()); break; case IOption.STRING_LIST : case IOption.INCLUDE_PATH : case IOption.PREPROCESSOR_SYMBOLS : case IOption.LIBRARIES : case IOption.OBJECTS : case IOption.INCLUDE_FILES: case IOption.LIBRARY_PATHS: case IOption.LIBRARY_FILES: case IOption.MACRO_FILES: case IOption.UNDEF_INCLUDE_PATH: case IOption.UNDEF_PREPROCESSOR_SYMBOLS: case IOption.UNDEF_INCLUDE_FILES: case IOption.UNDEF_LIBRARY_PATHS: case IOption.UNDEF_LIBRARY_FILES: case IOption.UNDEF_MACRO_FILES: @SuppressWarnings("unchecked") String[] data = ((List<String>)op1.getValue()).toArray(new String[0]); ManagedBuildManager.setOption(res, dst, op2, data); break; default : break; } } catch (BuildException e) { } catch (ClassCastException e) { } } protected boolean containsDefaults(){ IConfiguration parentCfg = fInfo.getParent().getParent(); ITool tools[] = fInfo.getParent().getTools(); for (ITool tool : tools) { if(!tool.getCustomBuildStep()){ ITool cfgTool = parentCfg.getToolChain().getTool(tool.getSuperClass().getId()); // Check for a non-default command or command-line-pattern if(cfgTool != null){ if (!(tool.getToolCommand().equals(cfgTool.getToolCommand()))) return false; if (!(tool.getCommandLinePattern().equals(cfgTool.getCommandLinePattern()))) return false; } // Check for a non-default option IOption options[] = tool.getOptions(); for (IOption option : options) { if(option.getParent() == tool){ IOption ext = option; do{ if(ext.isExtensionElement()) break; } while((ext = ext.getSuperClass()) != null); if(ext != null){ if(cfgTool != null){ IOption defaultOpt = cfgTool.getOptionBySuperClassId(ext.getId()); try { if(defaultOpt != null && defaultOpt.getValueType() == option.getValueType()){ Object value = option.getValue(); Object defaultVal = defaultOpt.getValue(); if(value.equals(defaultVal)) continue; //TODO: check list also } }catch (BuildException e) { } } } return false; } } } } return true; } /* (non-Javadoc) * Answers the list of settings pages for the selected configuration */ private List<AbstractToolSettingUI> getPagesForConfig() { if (getCfg() == null) return null; List<AbstractToolSettingUI> pages = configToPageListMap.get(getCfg().getId()); if (pages == null) { pages = new ArrayList<AbstractToolSettingUI>(); configToPageListMap.put(getCfg().getId(), pages); } return pages; } public IPreferenceStore getPreferenceStore() { return settingsStore; } /** * Sets the "dirty" state * @param b - the new dirty state, {@code true} or {@code false} */ public void setDirty(boolean b) { List<AbstractToolSettingUI> pages = getPagesForConfig(); if (pages == null) return; for (AbstractToolSettingUI page : pages) { if (page == null) continue; page.setDirty(b); } } /** * @return the "dirty" state */ public boolean isDirty() { // Check each settings page List<AbstractToolSettingUI> pages = getPagesForConfig(); // Make sure we have something to work on if (pages == null) { // Nothing to do return false; } for (AbstractToolSettingUI page : pages) { if (page == null) continue; if (page.isDirty()) return true; } return false; } /** * @return the build macro provider to be used for macro resolution * In case the "Build Macros" tab is available, returns the BuildMacroProvider * supplied by that tab. * Unlike the default provider, that provider also contains * the user-modified macros that are not applied yet * If the "Build Macros" tab is not available, returns the default BuildMacroProvider * * @noreference This method is not intended to be referenced by clients. */ public BuildMacroProvider obtainMacroProvider(){ return (BuildMacroProvider)ManagedBuildManager.getBuildMacroProvider(); } @Override public void updateData(ICResourceDescription cfgd) { fInfo = getResCfg(cfgd); setValues(); specificResize(); handleOptionSelection(); } @Override protected void performApply(ICResourceDescription src, ICResourceDescription dst) { IResourceInfo ri1 = getResCfg(src); IResourceInfo ri2 = getResCfg(dst); copyHoldsOptions(ri1.getParent().getToolChain(), ri2.getParent().getToolChain(), ri2); ITool[] t1, t2; if (ri1 instanceof IFolderInfo){ t1 = ((IFolderInfo)ri1).getFilteredTools(); t2 = ((IFolderInfo)ri2).getFilteredTools(); } else if (ri1 instanceof IFileInfo) { t1 = ((IFileInfo)ri1).getToolsToInvoke(); t2 = ((IFileInfo)ri2).getToolsToInvoke(); } else return; // get the corresponding pairs of tools for which we can copy settings // and do the copy for (Map.Entry<ITool, ITool> pair : getToolCorrespondence(t1, t2).entrySet()) { copyHoldsOptions(pair.getKey(), pair.getValue(), ri2); } setDirty(false); updateData(getResDesc()); } /** * Computes the correspondence of tools in the copy-from set (<tt>t1</tt>) and the * copy-to set (<tt>t2</tt>) in an apply operation. The resulting pairs are in the order * of the <tt>t2</tt> array. Note that tools that have no correspondence do not appear in * the result, and that order is not significant. Also, in case of replication of tools * in a chain (?) they are matched one-for one in the order in which they are found in * each chain. * * @param t1 - first group of tools. May not be <code>null</code> * @param t2 - second group of tools. May not be <code>null</code> * @return the one-for-one correspondence of tools, in order of <tt>t2</tt> */ private Map<ITool, ITool> getToolCorrespondence(ITool[] t1, ITool[] t2) { Map<ITool, ITool> result = new java.util.LinkedHashMap<ITool, ITool>(); Map<ITool, List<ITool>> realT1Tools = new java.util.LinkedHashMap<ITool, List<ITool>>(); for (ITool next : t1) { ITool real = ManagedBuildManager.getRealTool(next); List<ITool> list = realT1Tools.get(real); if (list == null) { // the immutable singleton list is efficient in storage realT1Tools.put(real, Collections.singletonList(next)); } else { if (list.size() == 1) { // make the list mutable list = new java.util.ArrayList<ITool>(list); realT1Tools.put(real, list); } list.add(next); } } for (ITool next : t2) { ITool real = ManagedBuildManager.getRealTool(next); List<ITool> correspondents = realT1Tools.get(real); if (correspondents != null) { result.put(correspondents.get(0), next); // consume the correspondent if (correspondents.size() == 1) { // remove the list; no more entries to consume realT1Tools.remove(real); } else { // cost of removal in array-list is not a concern // considering that this is a UI Apply button and // replication of tools is a fringe case correspondents.remove(0); } } } return result; } // IPreferencePageContainer methods @Override public void updateButtons() {} public void updateMessage() {} public void updateTitle() {} @Override public boolean canBeVisible() { IConfiguration cfg = getCfg(); if (cfg instanceof MultiConfiguration) return ((MultiConfiguration)cfg).isManagedBuildOn(); else return cfg.getBuilder().isManagedBuildOn(); } }