0001: /*******************************************************************************
0002: * Copyright (c) 2003, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.ui.internal.themes;
0011:
0012: import com.ibm.icu.text.MessageFormat;
0013: import java.util.ArrayList;
0014: import java.util.Arrays;
0015: import java.util.HashMap;
0016: import java.util.HashSet;
0017: import java.util.Iterator;
0018: import java.util.List;
0019: import java.util.Map;
0020: import java.util.ResourceBundle;
0021: import java.util.Set;
0022:
0023: import org.eclipse.core.runtime.CoreException;
0024: import org.eclipse.core.runtime.IStatus;
0025: import org.eclipse.jface.preference.ColorSelector;
0026: import org.eclipse.jface.preference.PreferenceConverter;
0027: import org.eclipse.jface.preference.PreferencePage;
0028: import org.eclipse.jface.resource.JFaceResources;
0029: import org.eclipse.jface.resource.StringConverter;
0030: import org.eclipse.jface.util.IPropertyChangeListener;
0031: import org.eclipse.jface.util.PropertyChangeEvent;
0032: import org.eclipse.jface.viewers.AbstractTreeViewer;
0033: import org.eclipse.jface.viewers.DoubleClickEvent;
0034: import org.eclipse.jface.viewers.IDoubleClickListener;
0035: import org.eclipse.jface.viewers.IFontProvider;
0036: import org.eclipse.jface.viewers.ISelectionChangedListener;
0037: import org.eclipse.jface.viewers.IStructuredSelection;
0038: import org.eclipse.jface.viewers.ITreeContentProvider;
0039: import org.eclipse.jface.viewers.LabelProvider;
0040: import org.eclipse.jface.viewers.LabelProviderChangedEvent;
0041: import org.eclipse.jface.viewers.SelectionChangedEvent;
0042: import org.eclipse.jface.viewers.StructuredSelection;
0043: import org.eclipse.jface.viewers.Viewer;
0044: import org.eclipse.jface.viewers.ViewerComparator;
0045: import org.eclipse.swt.SWT;
0046: import org.eclipse.swt.custom.StackLayout;
0047: import org.eclipse.swt.events.DisposeEvent;
0048: import org.eclipse.swt.events.DisposeListener;
0049: import org.eclipse.swt.events.SelectionAdapter;
0050: import org.eclipse.swt.events.SelectionEvent;
0051: import org.eclipse.swt.graphics.Color;
0052: import org.eclipse.swt.graphics.Font;
0053: import org.eclipse.swt.graphics.FontData;
0054: import org.eclipse.swt.graphics.GC;
0055: import org.eclipse.swt.graphics.Image;
0056: import org.eclipse.swt.graphics.RGB;
0057: import org.eclipse.swt.layout.FillLayout;
0058: import org.eclipse.swt.layout.GridData;
0059: import org.eclipse.swt.layout.GridLayout;
0060: import org.eclipse.swt.widgets.Button;
0061: import org.eclipse.swt.widgets.Composite;
0062: import org.eclipse.swt.widgets.Control;
0063: import org.eclipse.swt.widgets.Display;
0064: import org.eclipse.swt.widgets.FontDialog;
0065: import org.eclipse.swt.widgets.Label;
0066: import org.eclipse.swt.widgets.Text;
0067: import org.eclipse.ui.IWorkbench;
0068: import org.eclipse.ui.IWorkbenchPreferencePage;
0069: import org.eclipse.ui.PlatformUI;
0070: import org.eclipse.ui.dialogs.FilteredTree;
0071: import org.eclipse.ui.dialogs.PatternFilter;
0072: import org.eclipse.ui.internal.IWorkbenchGraphicConstants;
0073: import org.eclipse.ui.internal.IWorkbenchHelpContextIds;
0074: import org.eclipse.ui.internal.Workbench;
0075: import org.eclipse.ui.internal.WorkbenchMessages;
0076: import org.eclipse.ui.internal.WorkbenchPlugin;
0077: import org.eclipse.ui.internal.misc.StatusUtil;
0078: import org.eclipse.ui.internal.util.PrefUtil;
0079: import org.eclipse.ui.internal.util.Util;
0080: import org.eclipse.ui.themes.ITheme;
0081: import org.eclipse.ui.themes.IThemeManager;
0082: import org.eclipse.ui.themes.IThemePreview;
0083:
0084: /**
0085: * Preference page for management of system colors, gradients and fonts.
0086: *
0087: * @since 3.0
0088: */
0089: public final class ColorsAndFontsPreferencePage extends PreferencePage
0090: implements IWorkbenchPreferencePage {
0091:
0092: private static final String SELECTED_ELEMENT_PREF = "ColorsAndFontsPreferencePage.selectedElement"; //$NON-NLS-1$
0093: /**
0094: * The preference that stores the expanded state.
0095: */
0096: private static final String EXPANDED_ELEMENTS_PREF = "ColorsAndFontsPreferencePage.expandedCategories"; //$NON-NLS-1$
0097: /**
0098: * The token that seperates expanded elements in EXPANDED_ELEMENTS_PREF.
0099: */
0100: private static final String EXPANDED_ELEMENTS_TOKEN = "\t"; //$NON-NLS-1$
0101:
0102: /**
0103: * Marks category tokens in EXPANDED_ELEMENTS_PREF and SELECTED_ELEMENT_PREF.
0104: */
0105: private static final char MARKER_CATEGORY = 'T';
0106:
0107: /**
0108: * Marks color tokens in EXPANDED_ELEMENTS_PREF and SELECTED_ELEMENT_PREF.
0109: */
0110: private static final char MARKER_COLOR = 'C';
0111:
0112: /**
0113: * Marks font tokens in EXPANDED_ELEMENTS_PREF and SELECTED_ELEMENT_PREF.
0114: */
0115: private static final char MARKER_FONT = 'F';
0116:
0117: private class ThemeContentProvider implements ITreeContentProvider {
0118:
0119: private IThemeRegistry registry;
0120:
0121: /* (non-Javadoc)
0122: * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
0123: */
0124: public Object[] getChildren(Object parentElement) {
0125: if (parentElement instanceof ThemeElementCategory) {
0126: String categoryId = ((ThemeElementCategory) parentElement)
0127: .getId();
0128: Object[] defintions = (Object[]) categoryMap
0129: .get(categoryId);
0130: if (defintions == null) {
0131: defintions = getCategoryChildren(categoryId);
0132: categoryMap.put(categoryId, defintions);
0133: }
0134: return defintions;
0135: }
0136:
0137: ArrayList list = new ArrayList();
0138: IHierarchalThemeElementDefinition def = (IHierarchalThemeElementDefinition) parentElement;
0139: String id = def.getId();
0140: IHierarchalThemeElementDefinition[] defs;
0141: if (def instanceof ColorDefinition) {
0142: defs = registry.getColors();
0143: } else {
0144: defs = registry.getFonts();
0145: }
0146:
0147: for (int i = 0; i < defs.length; i++) {
0148: if (id.equals(defs[i].getDefaultsTo())
0149: && ColorsAndFontsPreferencePage
0150: .equals(
0151: ((ICategorizedThemeElementDefinition) def)
0152: .getCategoryId(),
0153: ((ICategorizedThemeElementDefinition) defs[i])
0154: .getCategoryId())) {
0155: list.add(defs[i]);
0156: }
0157: }
0158: return list.toArray();
0159: }
0160:
0161: private Object[] getCategoryChildren(String categoryId) {
0162: ArrayList list = new ArrayList();
0163:
0164: if (categoryId != null) {
0165: ThemeElementCategory[] categories = registry
0166: .getCategories();
0167: for (int i = 0; i < categories.length; i++) {
0168: if (categoryId.equals(categories[i].getParentId())) {
0169: Set bindings = themeRegistry
0170: .getPresentationsBindingsFor(categories[i]);
0171: if (bindings == null
0172: || bindings.contains(workbench
0173: .getPresentationId())) {
0174: list.add(categories[i]);
0175: }
0176: }
0177: }
0178: }
0179: {
0180: ColorDefinition[] colorDefinitions = themeRegistry
0181: .getColorsFor(currentTheme.getId());
0182: for (int i = 0; i < colorDefinitions.length; i++) {
0183: if (!colorDefinitions[i].isEditable()) {
0184: continue;
0185: }
0186: String catId = colorDefinitions[i].getCategoryId();
0187: if ((catId == null && categoryId == null)
0188: || (catId != null && categoryId != null && categoryId
0189: .equals(catId))) {
0190: if (colorDefinitions[i].getDefaultsTo() != null
0191: && parentIsInSameCategory(colorDefinitions[i])) {
0192: continue;
0193: }
0194: list.add(colorDefinitions[i]);
0195: }
0196: }
0197: }
0198: {
0199: FontDefinition[] fontDefinitions = themeRegistry
0200: .getFontsFor(currentTheme.getId());
0201: for (int i = 0; i < fontDefinitions.length; i++) {
0202: if (!fontDefinitions[i].isEditable()) {
0203: continue;
0204: }
0205: String catId = fontDefinitions[i].getCategoryId();
0206: if ((catId == null && categoryId == null)
0207: || (catId != null && categoryId != null && categoryId
0208: .equals(catId))) {
0209: if (fontDefinitions[i].getDefaultsTo() != null
0210: && parentIsInSameCategory(fontDefinitions[i])) {
0211: continue;
0212: }
0213: list.add(fontDefinitions[i]);
0214: }
0215: }
0216: }
0217: return list.toArray(new Object[list.size()]);
0218: }
0219:
0220: /**
0221: * @param definition
0222: * @return
0223: */
0224: private boolean parentIsInSameCategory(
0225: ColorDefinition definition) {
0226: String defaultsTo = definition.getDefaultsTo();
0227: ColorDefinition[] defs = registry.getColors();
0228: for (int i = 0; i < defs.length; i++) {
0229: if (defs[i].getId().equals(defaultsTo)
0230: && ColorsAndFontsPreferencePage.equals(defs[i]
0231: .getCategoryId(), definition
0232: .getCategoryId())) {
0233: return true;
0234: }
0235: }
0236: return false;
0237: }
0238:
0239: /**
0240: * @param definition
0241: * @return
0242: */
0243: private boolean parentIsInSameCategory(FontDefinition definition) {
0244: String defaultsTo = definition.getDefaultsTo();
0245: FontDefinition[] defs = registry.getFonts();
0246: for (int i = 0; i < defs.length; i++) {
0247: if (defs[i].getId().equals(defaultsTo)
0248: && ColorsAndFontsPreferencePage.equals(defs[i]
0249: .getCategoryId(), definition
0250: .getCategoryId())) {
0251: return true;
0252: }
0253: }
0254: return false;
0255: }
0256:
0257: /* (non-Javadoc)
0258: * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
0259: */
0260: public Object getParent(Object element) {
0261: return null;
0262: }
0263:
0264: /* (non-Javadoc)
0265: * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
0266: */
0267: public boolean hasChildren(Object element) {
0268: if (element instanceof ThemeElementCategory) {
0269: return true;
0270: }
0271:
0272: IHierarchalThemeElementDefinition def = (IHierarchalThemeElementDefinition) element;
0273: String id = def.getId();
0274: IHierarchalThemeElementDefinition[] defs;
0275: if (def instanceof ColorDefinition) {
0276: defs = registry.getColors();
0277: } else {
0278: defs = registry.getFonts();
0279: }
0280:
0281: for (int i = 0; i < defs.length; i++) {
0282: if (id.equals(defs[i].getDefaultsTo())
0283: && ColorsAndFontsPreferencePage
0284: .equals(
0285: ((ICategorizedThemeElementDefinition) def)
0286: .getCategoryId(),
0287: ((ICategorizedThemeElementDefinition) defs[i])
0288: .getCategoryId())) {
0289: return true;
0290: }
0291: }
0292:
0293: return false;
0294: }
0295:
0296: /*
0297: * (non-Javadoc)
0298: *
0299: * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
0300: */
0301: public Object[] getElements(Object inputElement) {
0302: ArrayList list = new ArrayList();
0303: Object[] uncatChildren = getCategoryChildren(null);
0304: list.addAll(Arrays.asList(uncatChildren));
0305: ThemeElementCategory[] categories = ((IThemeRegistry) inputElement)
0306: .getCategories();
0307: for (int i = 0; i < categories.length; i++) {
0308: if (categories[i].getParentId() == null) {
0309: Set bindings = themeRegistry
0310: .getPresentationsBindingsFor(categories[i]);
0311: if (bindings == null
0312: || bindings.contains(workbench
0313: .getPresentationId())) {
0314: list.add(categories[i]);
0315: }
0316: }
0317: }
0318: return list.toArray(new Object[list.size()]);
0319: }
0320:
0321: /* (non-Javadoc)
0322: * @see org.eclipse.jface.viewers.IContentProvider#dispose()
0323: */
0324: public void dispose() {
0325: categoryMap.clear();
0326: }
0327:
0328: /* (non-Javadoc)
0329: * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
0330: */
0331: public void inputChanged(Viewer viewer, Object oldInput,
0332: Object newInput) {
0333: categoryMap.clear();
0334: registry = (IThemeRegistry) newInput;
0335: }
0336:
0337: }
0338:
0339: private class PresentationLabelProvider extends LabelProvider
0340: implements IFontProvider {
0341:
0342: private HashMap fonts = new HashMap();
0343:
0344: private HashMap images = new HashMap();
0345:
0346: private int imageSize = -1;
0347:
0348: private int usableImageSize = -1;
0349:
0350: private IPropertyChangeListener listener = new IPropertyChangeListener() {
0351: public void propertyChange(PropertyChangeEvent event) {
0352: fireLabelProviderChanged(new LabelProviderChangedEvent(
0353: PresentationLabelProvider.this ));
0354: }
0355: };
0356:
0357: private Image emptyImage;
0358:
0359: /**
0360: *
0361: */
0362: public PresentationLabelProvider() {
0363: hookListeners();
0364: }
0365:
0366: /**
0367: * Hook the listeners onto the various registries.
0368: */
0369: public void hookListeners() {
0370: colorRegistry.addListener(listener);
0371: fontRegistry.addListener(listener);
0372: }
0373:
0374: /* (non-Javadoc)
0375: * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
0376: */
0377: public void dispose() {
0378: super .dispose();
0379: colorRegistry.removeListener(listener);
0380: fontRegistry.removeListener(listener);
0381: for (Iterator i = images.values().iterator(); i.hasNext();) {
0382: ((Image) i.next()).dispose();
0383: }
0384: images.clear();
0385:
0386: if (emptyImage != null) {
0387: emptyImage.dispose();
0388: emptyImage = null;
0389: }
0390:
0391: //clear the fonts.
0392: clearFontCache();
0393: }
0394:
0395: /**
0396: * Clears and disposes all fonts.
0397: */
0398: public void clearFontCache() {
0399: for (Iterator i = fonts.values().iterator(); i.hasNext();) {
0400: ((Font) i.next()).dispose();
0401: }
0402: fonts.clear();
0403: }
0404:
0405: /**
0406: * Clears and disposes all fonts and fires a label update.
0407: */
0408: public void clearFontCacheAndUpdate() {
0409: clearFontCache();
0410: fireLabelProviderChanged(new LabelProviderChangedEvent(
0411: PresentationLabelProvider.this ));
0412: }
0413:
0414: /* (non-Javadoc)
0415: * @see org.eclipse.jface.viewers.IFontProvider#getFont(java.lang.Object)
0416: */
0417: public Font getFont(Object element) {
0418: Display display = tree.getDisplay();
0419: if (element instanceof FontDefinition) {
0420: int parentHeight = tree.getViewer().getControl()
0421: .getFont().getFontData()[0].getHeight();
0422: Font baseFont = fontRegistry
0423: .get(((FontDefinition) element).getId());
0424: Font font = (Font) fonts.get(baseFont);
0425: if (font == null) {
0426: FontData[] data = baseFont.getFontData();
0427: for (int i = 0; i < data.length; i++) {
0428: data[i].setHeight(parentHeight);
0429: }
0430: font = new Font(display, data);
0431:
0432: fonts.put(baseFont, font);
0433: }
0434: return font;
0435: }
0436:
0437: return JFaceResources.getDialogFont();
0438: }
0439:
0440: /* (non-Javadoc)
0441: * @see org.eclipse.jface.viewers.ILabelProvider#getImage(java.lang.Object)
0442: */
0443: public Image getImage(Object element) {
0444: if (element instanceof ColorDefinition) {
0445: Color c = colorRegistry.get(((ColorDefinition) element)
0446: .getId());
0447: Image image = (Image) images.get(c);
0448: if (image == null) {
0449: Display display = tree.getDisplay();
0450: ensureImageSize(display);
0451: //int size = presentationList.getControl().getFont().getFontData()[0].getHeight();
0452: image = new Image(display, imageSize, imageSize);
0453:
0454: GC gc = new GC(image);
0455: gc.setBackground(tree.getViewer().getControl()
0456: .getBackground());
0457: gc.setForeground(tree.getViewer().getControl()
0458: .getBackground());
0459: gc
0460: .drawRectangle(0, 0, imageSize - 1,
0461: imageSize - 1);
0462:
0463: gc.setForeground(tree.getViewer().getControl()
0464: .getForeground());
0465: gc.setBackground(c);
0466:
0467: int offset = (imageSize - usableImageSize) / 2;
0468: gc.drawRectangle(offset, offset, usableImageSize
0469: - offset, usableImageSize - offset);
0470: gc.fillRectangle(offset + 1, offset + 1,
0471: usableImageSize - offset - 1,
0472: usableImageSize - offset - 1);
0473: gc.dispose();
0474:
0475: images.put(c, image);
0476: }
0477: return image;
0478:
0479: } else if (element instanceof FontDefinition) {
0480: return workbench.getSharedImages().getImage(
0481: IWorkbenchGraphicConstants.IMG_OBJ_FONT);
0482: } else {
0483: return workbench
0484: .getSharedImages()
0485: .getImage(
0486: IWorkbenchGraphicConstants.IMG_OBJ_THEME_CATEGORY);
0487: }
0488: }
0489:
0490: /**
0491: * @param display
0492: * @return
0493: */
0494: private void ensureImageSize(Display display) {
0495: if (imageSize == -1) {
0496: imageSize = tree.getViewer().getTree().getItemHeight();
0497: usableImageSize = Math.max(1, imageSize - 4);
0498: }
0499: }
0500:
0501: /* (non-Javadoc)
0502: * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object)
0503: */
0504: public String getText(Object element) {
0505: if (element instanceof IHierarchalThemeElementDefinition) {
0506: IHierarchalThemeElementDefinition themeElement = (IHierarchalThemeElementDefinition) element;
0507: if (themeElement.getDefaultsTo() != null) {
0508: String myCategory = ((ICategorizedThemeElementDefinition) themeElement)
0509: .getCategoryId();
0510: ICategorizedThemeElementDefinition def;
0511: if (element instanceof ColorDefinition) {
0512: def = themeRegistry.findColor(themeElement
0513: .getDefaultsTo());
0514: } else {
0515: def = themeRegistry.findFont(themeElement
0516: .getDefaultsTo());
0517: }
0518:
0519: if (!ColorsAndFontsPreferencePage.equals(def
0520: .getCategoryId(), myCategory)) {
0521: if (isDefault(themeElement)) {
0522: return MessageFormat
0523: .format(
0524: RESOURCE_BUNDLE
0525: .getString("defaultFormat_default"), new Object[] { themeElement.getName(), def.getName() }); //$NON-NLS-1$
0526: }
0527:
0528: return MessageFormat
0529: .format(
0530: RESOURCE_BUNDLE
0531: .getString("defaultFormat_override"), new Object[] { themeElement.getName(), def.getName() }); //$NON-NLS-1$
0532: }
0533: }
0534: }
0535: return ((IThemeElementDefinition) element).getName();
0536: }
0537:
0538: /**
0539: * Return whether the element is set to default.
0540: *
0541: * @param def the definition
0542: * @return whether the element is set to default
0543: * @since 3.2
0544: */
0545: private boolean isDefault(IThemeElementDefinition def) {
0546: if (def instanceof FontDefinition) {
0547: return ColorsAndFontsPreferencePage.this
0548: .isDefault((FontDefinition) def);
0549: } else if (def instanceof ColorDefinition) {
0550: return ColorsAndFontsPreferencePage.this
0551: .isDefault((ColorDefinition) def);
0552: }
0553: return false;
0554: }
0555: }
0556:
0557: /**
0558: * The translation bundle in which to look up internationalized text.
0559: */
0560: private final static ResourceBundle RESOURCE_BUNDLE = ResourceBundle
0561: .getBundle(ColorsAndFontsPreferencePage.class.getName());
0562:
0563: /**
0564: * Map to precalculate category color lists.
0565: */
0566: private Map categoryMap = new HashMap(7);
0567:
0568: private Font appliedDialogFont;
0569:
0570: /**
0571: * The composite containing all color-specific controls.
0572: */
0573: private Composite colorControls;
0574:
0575: /**
0576: * Map of defintion id->RGB objects that map to changes expressed in this
0577: * UI session. These changes should be made in preferences and the
0578: * registry.
0579: */
0580: private Map colorPreferencesToSet = new HashMap(7);
0581:
0582: private CascadingColorRegistry colorRegistry;
0583:
0584: private Button colorResetButton;
0585:
0586: private ColorSelector colorSelector;
0587:
0588: /**
0589: * Map of defintion id->RGB objects that map to changes expressed in this
0590: * UI session. These changes should be made in the registry.
0591: */
0592: private Map colorValuesToSet = new HashMap(7);
0593:
0594: /**
0595: * The composite that contains the font or color controls (or none).
0596: */
0597: private Composite controlArea;
0598:
0599: /**
0600: * The layout for the controlArea.
0601: */
0602: private StackLayout controlAreaLayout;
0603:
0604: /**
0605: * The composite to use when no preview is available.
0606: */
0607: private Composite defaultPreviewControl;
0608:
0609: private Text descriptionText;
0610:
0611: private List dialogFontWidgets = new ArrayList();
0612:
0613: private Button fontChangeButton;
0614:
0615: /**
0616: * The composite containing all font-specific controls.
0617: */
0618: private Composite fontControls;
0619:
0620: private Map fontPreferencesToSet = new HashMap(7);
0621:
0622: private CascadingFontRegistry fontRegistry;
0623:
0624: private Button fontResetButton;
0625:
0626: private Button fontSystemButton;
0627:
0628: /**
0629: * Map of defintion id->FontData[] objects that map to changes expressed in
0630: * this UI session. These changes should be made in preferences and the
0631: * registry.
0632: */
0633: private Map fontValuesToSet = new HashMap(7);
0634:
0635: /**
0636: * The list of fonts and colors.
0637: */
0638: //private TreeViewer presentationList;
0639: /**
0640: * The composite that is parent to all previews.
0641: */
0642: private Composite previewComposite;
0643:
0644: /**
0645: * A mapping from PresentationCategory->Composite for the created previews.
0646: */
0647: private Map previewMap = new HashMap(7);
0648:
0649: /**
0650: * Set containing all IPresentationPreviews created.
0651: */
0652: private Set previewSet = new HashSet(7);
0653:
0654: /**
0655: * The layout for the previewComposite.
0656: */
0657: private StackLayout stackLayout;
0658:
0659: private final IThemeRegistry themeRegistry;
0660:
0661: private ITheme currentTheme;
0662:
0663: private PresentationLabelProvider labelProvider;
0664:
0665: private CascadingTheme cascadingTheme;
0666:
0667: private IPropertyChangeListener themeChangeListener;
0668:
0669: private Workbench workbench;
0670:
0671: private FilteredTree tree;
0672:
0673: /**
0674: * Create a new instance of the receiver.
0675: */
0676: public ColorsAndFontsPreferencePage() {
0677: themeRegistry = WorkbenchPlugin.getDefault().getThemeRegistry();
0678: //no-op
0679: }
0680:
0681: /**
0682: * @param string
0683: * @param string2
0684: * @return
0685: */
0686: private static boolean equals(String string, String string2) {
0687: if ((string == null && string2 == null)) {
0688: return true;
0689: }
0690: if (string == null || string2 == null) {
0691: return false;
0692: }
0693: if (string.equals(string2)) {
0694: return true;
0695: }
0696:
0697: return false;
0698: }
0699:
0700: /**
0701: * Create a button for the preference page.
0702: * @param parent
0703: * @param label
0704: */
0705: private Button createButton(Composite parent, String label) {
0706: Button button = new Button(parent, SWT.PUSH | SWT.CENTER);
0707: button.setText(label);
0708: myApplyDialogFont(button);
0709: setButtonLayoutData(button);
0710: button.setEnabled(false);
0711: return button;
0712: }
0713:
0714: /**
0715: * Create the color selection control.
0716: */
0717: private void createColorControl() {
0718: Composite composite = new Composite(colorControls, SWT.NONE);
0719: GridLayout layout = new GridLayout(2, false);
0720: layout.marginHeight = 0;
0721: layout.marginWidth = 0;
0722: composite.setLayout(layout);
0723:
0724: colorSelector = new ColorSelector(composite);
0725: colorSelector.getButton().setLayoutData(new GridData());
0726: myApplyDialogFont(colorSelector.getButton());
0727: colorSelector.setEnabled(false);
0728:
0729: colorResetButton = createButton(composite, RESOURCE_BUNDLE
0730: .getString("reset")); //$NON-NLS-1$
0731: }
0732:
0733: /* (non-Javadoc)
0734: * @see org.eclipse.jface.preference.PreferencePage#createContents(org.eclipse.swt.widgets.Composite)
0735: */
0736: protected Control createContents(Composite parent) {
0737:
0738: PlatformUI.getWorkbench().getHelpSystem().setHelp(parent,
0739: IWorkbenchHelpContextIds.FONTS_PREFERENCE_PAGE);
0740:
0741: parent.addDisposeListener(new DisposeListener() {
0742: public void widgetDisposed(DisposeEvent e) {
0743: if (appliedDialogFont != null) {
0744: appliedDialogFont.dispose();
0745: }
0746: }
0747: });
0748: Composite mainColumn = new Composite(parent, SWT.NONE);
0749: GridLayout layout = new GridLayout();
0750: layout.marginWidth = 0;
0751: layout.marginHeight = 0;
0752: mainColumn.setFont(parent.getFont());
0753: mainColumn.setLayout(layout);
0754:
0755: GridData data = new GridData(GridData.BEGINNING);
0756: Label label = new Label(mainColumn, SWT.LEFT);
0757: label.setText(RESOURCE_BUNDLE.getString("colorsAndFonts")); //$NON-NLS-1$
0758: myApplyDialogFont(label);
0759: label.setLayoutData(data);
0760:
0761: Composite controlRow = new Composite(mainColumn, SWT.NONE);
0762: layout = new GridLayout();
0763: layout.numColumns = 2;
0764: layout.marginHeight = 0;
0765: layout.marginWidth = 0;
0766: controlRow.setLayout(layout);
0767: data = new GridData(GridData.FILL_HORIZONTAL);
0768: controlRow.setLayoutData(data);
0769:
0770: createTree(controlRow);
0771: Composite controlColumn = new Composite(controlRow, SWT.NONE);
0772: data = new GridData(GridData.FILL_VERTICAL);
0773: controlColumn.setLayoutData(data);
0774: layout = new GridLayout();
0775: layout.marginHeight = 0;
0776: layout.marginWidth = 0;
0777: controlColumn.setLayout(layout);
0778:
0779: controlArea = new Composite(controlColumn, SWT.NONE);
0780: controlAreaLayout = new StackLayout();
0781: controlArea.setLayout(controlAreaLayout);
0782:
0783: colorControls = new Composite(controlArea, SWT.NONE);
0784: colorControls.setLayout(new FillLayout());
0785: createColorControl();
0786:
0787: fontControls = new Composite(controlArea, SWT.NONE);
0788: fontControls.setLayout(new FillLayout());
0789: createFontControl();
0790:
0791: createDescriptionControl(mainColumn);
0792:
0793: createPreviewControl(mainColumn);
0794:
0795: hookListeners();
0796:
0797: return mainColumn;
0798: }
0799:
0800: /**
0801: * Create the text box that will contain the current color/font description
0802: * text (if any).
0803: *
0804: * @param parent the parent <code>Composite</code>.
0805: */
0806: private void createDescriptionControl(Composite parent) {
0807: Composite composite = new Composite(parent, SWT.NONE);
0808: GridLayout layout = new GridLayout();
0809: layout.marginWidth = 0;
0810: layout.marginHeight = 0;
0811: composite.setLayout(layout);
0812: GridData data = new GridData(GridData.FILL_BOTH);
0813: data.heightHint = convertHeightInCharsToPixels(5);
0814: composite.setLayoutData(data);
0815:
0816: Label label = new Label(composite, SWT.LEFT);
0817: label.setText(RESOURCE_BUNDLE.getString("description")); //$NON-NLS-1$
0818: myApplyDialogFont(label);
0819:
0820: descriptionText = new Text(composite, SWT.H_SCROLL
0821: | SWT.V_SCROLL | SWT.READ_ONLY | SWT.BORDER | SWT.WRAP);
0822: data = new GridData(GridData.FILL_BOTH);
0823: descriptionText.setLayoutData(data);
0824: myApplyDialogFont(descriptionText);
0825: }
0826:
0827: private void createFontControl() {
0828: Composite composite = new Composite(fontControls, SWT.NONE);
0829: GridLayout layout = new GridLayout(1, false);
0830: layout.marginHeight = 0;
0831: layout.marginWidth = 0;
0832: composite.setLayout(layout);
0833:
0834: fontSystemButton = createButton(composite,
0835: WorkbenchMessages.FontsPreference_useSystemFont);
0836:
0837: fontChangeButton = createButton(composite, JFaceResources
0838: .getString("openChange")); //$NON-NLS-1$
0839:
0840: fontResetButton = createButton(composite, RESOURCE_BUNDLE
0841: .getString("reset")); //$NON-NLS-1$
0842: }
0843:
0844: /**
0845: * Create the <code>ListViewer</code> that will contain all color
0846: * definitions as defined in the extension point.
0847: *
0848: * @param parent the parent <code>Composite</code>.
0849: */
0850: private void createTree(Composite parent) {
0851: labelProvider = new PresentationLabelProvider();
0852: // create a new tree with a custom pattern matcher that will allow
0853: // non-category elements to be returned in the event that their children
0854: // do not
0855: tree = new FilteredTree(parent, SWT.SINGLE | SWT.H_SCROLL
0856: | SWT.V_SCROLL | SWT.BORDER, new PatternFilter() {
0857:
0858: /* (non-Javadoc)
0859: * @see org.eclipse.ui.dialogs.PatternFilter#isParentMatch(org.eclipse.jface.viewers.Viewer, java.lang.Object)
0860: */
0861: protected boolean isParentMatch(Viewer viewer,
0862: Object element) {
0863: Object[] children = ((ITreeContentProvider) ((AbstractTreeViewer) viewer)
0864: .getContentProvider()).getChildren(element);
0865: if (children.length > 0
0866: && element instanceof ThemeElementCategory) {
0867: return filter(viewer, element, children).length > 0;
0868: }
0869: return false;
0870: }
0871: });
0872:
0873: GridData data = new GridData(GridData.FILL_HORIZONTAL
0874: | GridData.VERTICAL_ALIGN_FILL);
0875: data.heightHint = Math.max(175,
0876: convertHeightInCharsToPixels(10));
0877: tree.setLayoutData(data);
0878: myApplyDialogFont(tree.getViewer().getControl());
0879: Text filterText = tree.getFilterControl();
0880: if (filterText != null) {
0881: myApplyDialogFont(filterText);
0882: }
0883:
0884: tree.getViewer().setLabelProvider(labelProvider);
0885: tree.getViewer().setContentProvider(new ThemeContentProvider());
0886: tree.getViewer().setComparator(new ViewerComparator() {
0887: /* (non-Javadoc)
0888: * @see org.eclipse.jface.viewers.ViewerComparator#category(java.lang.Object)
0889: */
0890: public int category(Object element) {
0891: if (element instanceof ThemeElementCategory) {
0892: return 0;
0893: }
0894: return 1;
0895: }
0896: });
0897: tree.getViewer().setInput(
0898: WorkbenchPlugin.getDefault().getThemeRegistry());
0899: tree.getViewer().addDoubleClickListener(
0900: new IDoubleClickListener() {
0901: /*
0902: * (non-Javadoc)
0903: *
0904: * @see org.eclipse.jface.viewers.IDoubleClickListener#doubleClick(org.eclipse.jface.viewers.DoubleClickEvent)
0905: */
0906: public void doubleClick(DoubleClickEvent event) {
0907: IStructuredSelection s = (IStructuredSelection) event
0908: .getSelection();
0909: Object element = s.getFirstElement();
0910: if (tree.getViewer().isExpandable(element)) {
0911: tree.getViewer().setExpandedState(
0912: element,
0913: !tree.getViewer().getExpandedState(
0914: element));
0915: }
0916:
0917: if (element instanceof FontDefinition) {
0918: editFont(tree.getDisplay());
0919: } else if (element instanceof ColorDefinition) {
0920: colorSelector.open();
0921: }
0922: }
0923: });
0924:
0925: restoreTreeExpansion();
0926: restoreTreeSelection();
0927: }
0928:
0929: /**
0930: * @param mainColumn
0931: */
0932: private void createPreviewControl(Composite mainColumn) {
0933: Composite composite = new Composite(mainColumn, SWT.NONE);
0934: GridData data = new GridData(GridData.FILL_BOTH);
0935: data.heightHint = 175;
0936: composite.setLayoutData(data);
0937: GridLayout layout = new GridLayout(1, true);
0938: layout.marginHeight = 0;
0939: layout.marginWidth = 0;
0940: composite.setLayout(layout);
0941:
0942: Label label = new Label(composite, SWT.LEFT);
0943: label.setText(RESOURCE_BUNDLE.getString("preview")); //$NON-NLS-1$
0944: myApplyDialogFont(label);
0945: previewComposite = new Composite(composite, SWT.NONE);
0946: data = new GridData(GridData.FILL_BOTH);
0947: previewComposite.setLayoutData(data);
0948: stackLayout = new StackLayout();
0949: previewComposite.setLayout(stackLayout);
0950: }
0951:
0952: /* (non-Javadoc)
0953: * @see org.eclipse.jface.dialogs.IDialogPage#dispose()
0954: */
0955: public void dispose() {
0956: super .dispose();
0957:
0958: workbench.getThemeManager().removePropertyChangeListener(
0959: themeChangeListener);
0960:
0961: clearPreviews();
0962:
0963: colorRegistry.dispose();
0964: fontRegistry.dispose();
0965:
0966: }
0967:
0968: /**
0969: * Clear all previews.
0970: */
0971: private void clearPreviews() {
0972: if (cascadingTheme != null) {
0973: cascadingTheme.dispose();
0974: }
0975:
0976: for (Iterator i = previewSet.iterator(); i.hasNext();) {
0977: IThemePreview preview = (IThemePreview) i.next();
0978: try {
0979: preview.dispose();
0980: } catch (RuntimeException e) {
0981: WorkbenchPlugin
0982: .log(
0983: RESOURCE_BUNDLE
0984: .getString("errorDisposePreviewLog"), StatusUtil.newStatus(IStatus.ERROR, e.getMessage(), e)); //$NON-NLS-1$
0985: }
0986: }
0987:
0988: previewSet.clear();
0989: }
0990:
0991: /**
0992: * Get the ancestor of the given color, if any.
0993: *
0994: * @param definition the descendant <code>ColorDefinition</code>.
0995: * @return the ancestror <code>ColorDefinition</code>, or <code>null</code>
0996: * if none.
0997: */
0998: private ColorDefinition getColorAncestor(ColorDefinition definition) {
0999: String defaultsTo = definition.getDefaultsTo();
1000: if (defaultsTo == null) {
1001: return null;
1002: }
1003:
1004: return themeRegistry.findColor(defaultsTo);
1005: }
1006:
1007: /**
1008: * Get the RGB value of the given colors ancestor, if any.
1009: *
1010: * @param definition the descendant <code>ColorDefinition</code>.
1011: * @return the ancestror <code>RGB</code>, or <code>null</code> if none.
1012: */
1013: private RGB getColorAncestorValue(ColorDefinition definition) {
1014: ColorDefinition ancestor = getColorAncestor(definition);
1015: if (ancestor == null) {
1016: return null;
1017: }
1018:
1019: return getColorValue(ancestor);
1020: }
1021:
1022: /**
1023: * Get the RGB value for the specified definition. Cascades through
1024: * preferenceToSet, valuesToSet and finally the registry.
1025: *
1026: * @param definition the <code>ColorDefinition</code>.
1027: * @return the <code>RGB</code> value.
1028: */
1029: private RGB getColorValue(ColorDefinition definition) {
1030: String id = definition.getId();
1031: RGB updatedRGB = (RGB) colorPreferencesToSet.get(id);
1032: if (updatedRGB == null) {
1033: updatedRGB = (RGB) colorValuesToSet.get(id);
1034: if (updatedRGB == null) {
1035: updatedRGB = currentTheme.getColorRegistry().getRGB(id);
1036: }
1037: }
1038: return updatedRGB;
1039: }
1040:
1041: /**
1042: * @return Return the default "No preview available." preview.
1043: */
1044: private Composite getDefaultPreviewControl() {
1045: if (defaultPreviewControl == null) {
1046: defaultPreviewControl = new Composite(previewComposite,
1047: SWT.NONE);
1048: defaultPreviewControl.setLayout(new FillLayout());
1049: Label l = new Label(defaultPreviewControl, SWT.LEFT);
1050: l.setText(RESOURCE_BUNDLE.getString("noPreviewAvailable")); //$NON-NLS-1$
1051: myApplyDialogFont(l);
1052: }
1053: return defaultPreviewControl;
1054: }
1055:
1056: /**
1057: * Get colors that descend from the provided color.
1058: *
1059: * @param definition the ancestor <code>ColorDefinition</code>.
1060: * @return the ColorDefinitions that have the provided definition as their
1061: * defaultsTo attribute.
1062: */
1063: private ColorDefinition[] getDescendantColors(
1064: ColorDefinition definition) {
1065: List list = new ArrayList(5);
1066: String id = definition.getId();
1067:
1068: ColorDefinition[] colors = themeRegistry.getColors();
1069: ColorDefinition[] sorted = new ColorDefinition[colors.length];
1070: System.arraycopy(colors, 0, sorted, 0, sorted.length);
1071:
1072: Arrays.sort(sorted, new IThemeRegistry.HierarchyComparator(
1073: colors));
1074:
1075: for (int i = 0; i < sorted.length; i++) {
1076: if (id.equals(sorted[i].getDefaultsTo())) {
1077: list.add(sorted[i]);
1078: }
1079: }
1080:
1081: return (ColorDefinition[]) list
1082: .toArray(new ColorDefinition[list.size()]);
1083: }
1084:
1085: /**
1086: * @param definition
1087: * @return
1088: */
1089: private FontDefinition[] getDescendantFonts(
1090: FontDefinition definition) {
1091: List list = new ArrayList(5);
1092: String id = definition.getId();
1093:
1094: FontDefinition[] fonts = themeRegistry.getFonts();
1095: FontDefinition[] sorted = new FontDefinition[fonts.length];
1096: System.arraycopy(fonts, 0, sorted, 0, sorted.length);
1097:
1098: Arrays.sort(sorted, new IThemeRegistry.HierarchyComparator(
1099: fonts));
1100:
1101: for (int i = 0; i < sorted.length; i++) {
1102: if (id.equals(sorted[i].getDefaultsTo())) {
1103: list.add(sorted[i]);
1104: }
1105: }
1106:
1107: return (FontDefinition[]) list.toArray(new FontDefinition[list
1108: .size()]);
1109: }
1110:
1111: /**
1112: * @param definition
1113: * @return
1114: */
1115: private FontDefinition getFontAncestor(FontDefinition definition) {
1116: String defaultsTo = definition.getDefaultsTo();
1117: if (defaultsTo == null) {
1118: return null;
1119: }
1120:
1121: return themeRegistry.findFont(defaultsTo);
1122: }
1123:
1124: /**
1125: * @param definition
1126: * @return
1127: */
1128: private FontData[] getFontAncestorValue(FontDefinition definition) {
1129: FontDefinition ancestor = getFontAncestor(definition);
1130: if (ancestor == null) {
1131: return PreferenceConverter.getDefaultFontDataArray(
1132: getPreferenceStore(), ThemeElementHelper
1133: .createPreferenceKey(currentTheme,
1134: definition.getId()));
1135: }
1136:
1137: return getFontValue(ancestor);
1138: }
1139:
1140: /**
1141: * @param definition
1142: * @return
1143: */
1144: protected FontData[] getFontValue(FontDefinition definition) {
1145: String id = definition.getId();
1146: FontData[] updatedFD = (FontData[]) fontPreferencesToSet
1147: .get(id);
1148: if (updatedFD == null) {
1149: updatedFD = (FontData[]) fontValuesToSet.get(id);
1150: if (updatedFD == null) {
1151: updatedFD = currentTheme.getFontRegistry().getFontData(
1152: id);
1153: }
1154: }
1155: return updatedFD;
1156: }
1157:
1158: /**
1159: * @return
1160: */
1161: protected ColorDefinition getSelectedColorDefinition() {
1162: Object o = ((IStructuredSelection) tree.getViewer()
1163: .getSelection()).getFirstElement();
1164: if (o instanceof ColorDefinition) {
1165: return (ColorDefinition) o;
1166: }
1167: return null;
1168: }
1169:
1170: /**
1171: * @return
1172: */
1173: protected FontDefinition getSelectedFontDefinition() {
1174: Object o = ((IStructuredSelection) tree.getViewer()
1175: .getSelection()).getFirstElement();
1176: if (o instanceof FontDefinition) {
1177: return (FontDefinition) o;
1178: }
1179: return null;
1180: }
1181:
1182: /**
1183: * Hook all control listeners.
1184: */
1185: private void hookListeners() {
1186: colorSelector.addListener(new IPropertyChangeListener() {
1187:
1188: /* (non-Javadoc)
1189: * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
1190: */
1191: public void propertyChange(PropertyChangeEvent event) {
1192: ColorDefinition definition = getSelectedColorDefinition();
1193:
1194: RGB newRGB = (RGB) event.getNewValue();
1195: if (definition != null && newRGB != null
1196: && !newRGB.equals(event.getOldValue())) {
1197: setColorPreferenceValue(definition, newRGB);
1198: setRegistryValue(definition, newRGB);
1199: }
1200:
1201: updateColorControls(definition);
1202: }
1203: });
1204:
1205: tree.getViewer().addSelectionChangedListener(
1206: new ISelectionChangedListener() {
1207:
1208: /* (non-Javadoc)
1209: * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
1210: */
1211: public void selectionChanged(
1212: SelectionChangedEvent event) {
1213: if (event.getSelection().isEmpty()) {
1214: swapNoControls();
1215: updateColorControls(null);
1216: updateCategorySelection(null);
1217: } else {
1218: Object element = ((IStructuredSelection) event
1219: .getSelection()).getFirstElement();
1220: if (element instanceof ThemeElementCategory) {
1221: swapNoControls();
1222: String description = ((ThemeElementCategory) element)
1223: .getDescription();
1224: descriptionText
1225: .setText(description == null ? "" : description); //$NON-NLS-1$
1226: updateCategorySelection((ThemeElementCategory) element);
1227: } else if (element instanceof ColorDefinition) {
1228: updateColorControls((ColorDefinition) element);
1229: swapColorControls();
1230: updateCategorySelection(WorkbenchPlugin
1231: .getDefault()
1232: .getThemeRegistry()
1233: .findCategory(
1234: ((ColorDefinition) element)
1235: .getCategoryId()));
1236: } else if (element instanceof FontDefinition) {
1237: updateFontControls((FontDefinition) element);
1238: swapFontControls();
1239: updateCategorySelection(WorkbenchPlugin
1240: .getDefault()
1241: .getThemeRegistry()
1242: .findCategory(
1243: ((FontDefinition) element)
1244: .getCategoryId()));
1245: }
1246: }
1247: }
1248: });
1249:
1250: colorResetButton.addSelectionListener(new SelectionAdapter() {
1251:
1252: /* (non-Javadoc)
1253: * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
1254: */
1255: public void widgetSelected(SelectionEvent e) {
1256: ColorDefinition definition = getSelectedColorDefinition();
1257: if (resetColor(definition)) {
1258: updateColorControls(definition);
1259: }
1260: }
1261: });
1262:
1263: fontResetButton.addSelectionListener(new SelectionAdapter() {
1264:
1265: /* (non-Javadoc)
1266: * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
1267: */
1268: public void widgetSelected(SelectionEvent e) {
1269: FontDefinition definition = getSelectedFontDefinition();
1270: if (resetFont(definition)) {
1271: updateFontControls(definition);
1272: }
1273: }
1274: });
1275:
1276: fontChangeButton.addSelectionListener(new SelectionAdapter() {
1277: public void widgetSelected(SelectionEvent event) {
1278: Display display = event.display;
1279: editFont(display);
1280: }
1281: });
1282:
1283: fontSystemButton.addSelectionListener(new SelectionAdapter() {
1284: public void widgetSelected(SelectionEvent event) {
1285: FontDefinition definition = getSelectedFontDefinition();
1286: if (definition != null) {
1287: FontData[] defaultFontData = JFaceResources
1288: .getDefaultFont().getFontData();
1289: setFontPreferenceValue(definition, defaultFontData);
1290: setRegistryValue(definition, defaultFontData);
1291:
1292: updateFontControls(definition);
1293: }
1294: }
1295: });
1296: }
1297:
1298: /* (non-Javadoc)
1299: * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench)
1300: */
1301: public void init(IWorkbench aWorkbench) {
1302: this .workbench = (Workbench) aWorkbench;
1303: setPreferenceStore(PrefUtil.getInternalPreferenceStore());
1304:
1305: final IThemeManager themeManager = aWorkbench.getThemeManager();
1306:
1307: themeChangeListener = new IPropertyChangeListener() {
1308:
1309: /* (non-Javadoc)
1310: * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
1311: */
1312: public void propertyChange(PropertyChangeEvent event) {
1313: if (event.getProperty().equals(
1314: IThemeManager.CHANGE_CURRENT_THEME)) {
1315: updateThemeInfo(themeManager);
1316: refreshCategory();
1317: tree.getViewer().refresh(); // refresh all the labels in the tree
1318: }
1319: }
1320: };
1321: themeManager.addPropertyChangeListener(themeChangeListener);
1322:
1323: updateThemeInfo(themeManager);
1324: }
1325:
1326: private void updateThemeInfo(IThemeManager manager) {
1327: clearPreviews();
1328: categoryMap.clear();
1329:
1330: if (labelProvider != null) {
1331: labelProvider.dispose(); // nuke the old cache
1332: }
1333:
1334: currentTheme = manager.getCurrentTheme();
1335:
1336: if (colorRegistry != null) {
1337: colorRegistry.dispose();
1338: }
1339: if (fontRegistry != null) {
1340: fontRegistry.dispose();
1341: }
1342:
1343: currentTheme = manager.getCurrentTheme();
1344:
1345: colorRegistry = new CascadingColorRegistry(currentTheme
1346: .getColorRegistry());
1347: fontRegistry = new CascadingFontRegistry(currentTheme
1348: .getFontRegistry());
1349:
1350: fontPreferencesToSet.clear();
1351: fontValuesToSet.clear();
1352:
1353: colorPreferencesToSet.clear();
1354: colorValuesToSet.clear();
1355:
1356: if (labelProvider != null) {
1357: labelProvider.hookListeners(); // rehook the listeners
1358: }
1359: }
1360:
1361: /**
1362: * Answers whether the definition is currently set to the default value.
1363: *
1364: * @param definition the <code>ColorDefinition</code> to check.
1365: * @return Return whether the definition is currently mapped to the default
1366: * value, either in the preference store or in the local change record
1367: * of this preference page.
1368: */
1369: private boolean isDefault(ColorDefinition definition) {
1370: String id = definition.getId();
1371:
1372: if (colorPreferencesToSet.containsKey(id)) {
1373: if (definition.getValue() != null) { // value-based color
1374: if (colorPreferencesToSet.get(id).equals(
1375: definition.getValue())) {
1376: return true;
1377: }
1378: } else {
1379: if (colorPreferencesToSet.get(id).equals(
1380: getColorAncestorValue(definition))) {
1381: return true;
1382: }
1383: }
1384: } else {
1385: if (definition.getValue() != null) { // value-based color
1386: if (getPreferenceStore().isDefault(
1387: ThemeElementHelper.createPreferenceKey(
1388: currentTheme, id))) {
1389: return true;
1390: }
1391: } else {
1392: // a descendant is default if it's the same value as its ancestor
1393: if (getColorValue(definition).equals(
1394: getColorAncestorValue(definition))) {
1395: return true;
1396: }
1397: }
1398: }
1399: return false;
1400: }
1401:
1402: /**
1403: * @param definition
1404: * @return
1405: */
1406: private boolean isDefault(FontDefinition definition) {
1407: String id = definition.getId();
1408:
1409: if (fontPreferencesToSet.containsKey(id)) {
1410: if (definition.getValue() != null) { // value-based font
1411: if (Arrays.equals((FontData[]) fontPreferencesToSet
1412: .get(id), definition.getValue())) {
1413: return true;
1414: }
1415: } else {
1416: FontData[] ancestor = getFontAncestorValue(definition);
1417: if (Arrays.equals((FontData[]) fontPreferencesToSet
1418: .get(id), ancestor)) {
1419: return true;
1420: }
1421: }
1422: } else {
1423: if (definition.getValue() != null) { // value-based font
1424: if (getPreferenceStore().isDefault(
1425: ThemeElementHelper.createPreferenceKey(
1426: currentTheme, id))) {
1427: return true;
1428: }
1429: } else {
1430: FontData[] ancestor = getFontAncestorValue(definition);
1431: if (ancestor == null) {
1432: return true;
1433: }
1434:
1435: // a descendant is default if it's the same value as its ancestor
1436: if (Arrays.equals(getFontValue(definition), ancestor)) {
1437: return true;
1438: }
1439: }
1440: }
1441: return false;
1442: }
1443:
1444: /**
1445: * Apply the dialog font to the control and store
1446: * it for later so that it can be used for a later
1447: * update.
1448: * @param control
1449: */
1450: private void myApplyDialogFont(Control control) {
1451: control.setFont(JFaceResources.getDialogFont());
1452: dialogFontWidgets.add(control);
1453: }
1454:
1455: /**
1456: * @see org.eclipse.jface.preference.PreferencePage#performApply()
1457: */
1458: protected void performApply() {
1459: super .performApply();
1460:
1461: //Apply the default font to the dialog.
1462: Font oldFont = appliedDialogFont;
1463:
1464: FontDefinition fontDefinition = themeRegistry
1465: .findFont(JFaceResources.DIALOG_FONT);
1466: if (fontDefinition == null) {
1467: return;
1468: }
1469:
1470: FontData[] newData = getFontValue(fontDefinition);
1471:
1472: appliedDialogFont = new Font(getControl().getDisplay(), newData);
1473:
1474: updateForDialogFontChange(appliedDialogFont);
1475: getApplyButton().setFont(appliedDialogFont);
1476: getDefaultsButton().setFont(appliedDialogFont);
1477:
1478: if (oldFont != null) {
1479: oldFont.dispose();
1480: }
1481: }
1482:
1483: /**
1484: *
1485: */
1486: private void performColorDefaults() {
1487: ColorDefinition[] definitions = themeRegistry.getColors();
1488:
1489: // apply defaults in depth-order.
1490: ColorDefinition[] definitionsCopy = new ColorDefinition[definitions.length];
1491: System.arraycopy(definitions, 0, definitionsCopy, 0,
1492: definitions.length);
1493:
1494: Arrays.sort(definitionsCopy,
1495: new IThemeRegistry.HierarchyComparator(definitions));
1496:
1497: for (int i = 0; i < definitionsCopy.length; i++) {
1498: resetColor(definitionsCopy[i]);
1499: }
1500:
1501: updateColorControls(getSelectedColorDefinition());
1502: }
1503:
1504: /**
1505: * @return
1506: */
1507: private boolean performColorOk() {
1508: for (Iterator i = colorPreferencesToSet.keySet().iterator(); i
1509: .hasNext();) {
1510: String id = (String) i.next();
1511: String key = ThemeElementHelper.createPreferenceKey(
1512: currentTheme, id);
1513: RGB rgb = (RGB) colorPreferencesToSet.get(id);
1514: String rgbString = StringConverter.asString(rgb);
1515: String storeString = getPreferenceStore().getString(key);
1516:
1517: if (!rgbString.equals(storeString)) {
1518: getPreferenceStore().setValue(key, rgbString);
1519: }
1520: }
1521:
1522: colorValuesToSet.clear();
1523: colorPreferencesToSet.clear();
1524:
1525: return true;
1526: }
1527:
1528: /* (non-Javadoc)
1529: * @see org.eclipse.jface.preference.PreferencePage#performDefaults()
1530: */
1531: protected void performDefaults() {
1532: performColorDefaults();
1533: performFontDefaults();
1534: }
1535:
1536: /**
1537: *
1538: */
1539: private void performFontDefaults() {
1540: FontDefinition[] definitions = themeRegistry.getFonts();
1541:
1542: // apply defaults in depth-order.
1543: FontDefinition[] definitionsCopy = new FontDefinition[definitions.length];
1544: System.arraycopy(definitions, 0, definitionsCopy, 0,
1545: definitions.length);
1546:
1547: Arrays.sort(definitionsCopy,
1548: new IThemeRegistry.HierarchyComparator(definitions));
1549:
1550: for (int i = 0; i < definitionsCopy.length; i++) {
1551: resetFont(definitionsCopy[i]);
1552: }
1553:
1554: updateFontControls(getSelectedFontDefinition());
1555: }
1556:
1557: /**
1558: * @return
1559: */
1560: private boolean performFontOk() {
1561: for (Iterator i = fontPreferencesToSet.keySet().iterator(); i
1562: .hasNext();) {
1563: String id = (String) i.next();
1564: String key = ThemeElementHelper.createPreferenceKey(
1565: currentTheme, id);
1566: FontData[] fd = (FontData[]) fontPreferencesToSet.get(id);
1567:
1568: String fdString = PreferenceConverter
1569: .getStoredRepresentation(fd);
1570: String storeString = getPreferenceStore().getString(key);
1571:
1572: if (!fdString.equals(storeString)) {
1573: getPreferenceStore().setValue(key, fdString);
1574: }
1575: }
1576:
1577: fontValuesToSet.clear();
1578: fontPreferencesToSet.clear();
1579: return true;
1580: }
1581:
1582: /* (non-Javadoc)
1583: * @see org.eclipse.jface.preference.IPreferencePage#performOk()
1584: */
1585: public boolean performOk() {
1586: saveTreeExpansion();
1587: saveTreeSelection();
1588: boolean result = performColorOk() && performFontOk();
1589: if (result) {
1590: PrefUtil.savePrefs();
1591: }
1592: return result;
1593: }
1594:
1595: /**
1596: * Refreshes the category.
1597: */
1598: private void refreshCategory() {
1599: updateColorControls(null);
1600: updateFontControls(null);
1601: }
1602:
1603: /**
1604: * Resets the supplied definition to its default value.
1605: *
1606: * @param definition the <code>ColorDefinition</code> to reset.
1607: * @return whether any change was made.
1608: */
1609: private boolean resetColor(ColorDefinition definition) {
1610: if (!isDefault(definition)) {
1611:
1612: RGB newRGB;
1613: if (definition.getValue() != null) {
1614: newRGB = definition.getValue();
1615: } else {
1616: newRGB = getColorAncestorValue(definition);
1617: }
1618:
1619: if (newRGB != null) {
1620: setColorPreferenceValue(definition, newRGB);
1621: setRegistryValue(definition, newRGB);
1622: return true;
1623: }
1624: }
1625: return false;
1626: }
1627:
1628: /**
1629: * @param definition
1630: * @return
1631: */
1632: protected boolean resetFont(FontDefinition definition) {
1633: if (!isDefault(definition)) {
1634:
1635: FontData[] newFD;
1636: if (definition.getDefaultsTo() != null) {
1637: newFD = getFontAncestorValue(definition);
1638: } else {
1639: newFD = PreferenceConverter.getDefaultFontDataArray(
1640: getPreferenceStore(), ThemeElementHelper
1641: .createPreferenceKey(currentTheme,
1642: definition.getId()));
1643: }
1644:
1645: if (newFD != null) {
1646: setFontPreferenceValue(definition, newFD);
1647: setRegistryValue(definition, newFD);
1648: return true;
1649: }
1650: }
1651: return false;
1652: }
1653:
1654: /**
1655: * Set the value (in preferences) for the given color.
1656: *
1657: * @param definition the <code>ColorDefinition</code> to set.
1658: * @param newRGB the new <code>RGB</code> value for the definitions
1659: * identifier.
1660: */
1661: protected void setColorPreferenceValue(ColorDefinition definition,
1662: RGB newRGB) {
1663: setDescendantRegistryValues(definition, newRGB);
1664: colorPreferencesToSet.put(definition.getId(), newRGB);
1665: }
1666:
1667: /**
1668: * Set the value (in registry) for the given colors children.
1669: *
1670: * @param definition the <code>ColorDefinition</code> whos children should
1671: * be set.
1672: * @param newRGB the new <code>RGB</code> value for the definitions
1673: * identifier.
1674: */
1675: private void setDescendantRegistryValues(
1676: ColorDefinition definition, RGB newRGB) {
1677: ColorDefinition[] children = getDescendantColors(definition);
1678:
1679: for (int i = 0; i < children.length; i++) {
1680: if (isDefault(children[i])) {
1681: setDescendantRegistryValues(children[i], newRGB);
1682: setRegistryValue(children[i], newRGB);
1683: colorValuesToSet.put(children[i].getId(), newRGB);
1684: }
1685: }
1686: }
1687:
1688: /**
1689: * @param definition
1690: * @param datas
1691: */
1692: private void setDescendantRegistryValues(FontDefinition definition,
1693: FontData[] datas) {
1694: FontDefinition[] children = getDescendantFonts(definition);
1695:
1696: for (int i = 0; i < children.length; i++) {
1697: if (isDefault(children[i])) {
1698: setDescendantRegistryValues(children[i], datas);
1699: setRegistryValue(children[i], datas);
1700: fontValuesToSet.put(children[i].getId(), datas);
1701: }
1702: }
1703: }
1704:
1705: /**
1706: * @param definition
1707: * @param datas
1708: */
1709: protected void setFontPreferenceValue(FontDefinition definition,
1710: FontData[] datas) {
1711: setDescendantRegistryValues(definition, datas);
1712: fontPreferencesToSet.put(definition.getId(), datas);
1713: }
1714:
1715: /**
1716: * Updates the working registry.
1717: * @param definition
1718: * @param newRGB
1719: */
1720: protected void setRegistryValue(ColorDefinition definition,
1721: RGB newRGB) {
1722: colorRegistry.put(definition.getId(), newRGB);
1723: }
1724:
1725: /**
1726: * @param definition
1727: * @param datas
1728: */
1729: protected void setRegistryValue(FontDefinition definition,
1730: FontData[] datas) {
1731: fontRegistry.put(definition.getId(), datas);
1732: }
1733:
1734: /**
1735: * Swap in the color selection controls.
1736: */
1737: protected void swapColorControls() {
1738: controlAreaLayout.topControl = colorControls;
1739: controlArea.layout();
1740: }
1741:
1742: /**
1743: * Swap in the font selection controls.
1744: */
1745: protected void swapFontControls() {
1746: controlAreaLayout.topControl = fontControls;
1747: controlArea.layout();
1748: }
1749:
1750: /**
1751: * Swap in no controls (empty the control area)
1752: */
1753: protected void swapNoControls() {
1754: controlAreaLayout.topControl = null;
1755: controlArea.layout();
1756: }
1757:
1758: /**
1759: * Set the color list.
1760: * @param category the category to use.
1761: */
1762: private void updateCategorySelection(ThemeElementCategory category) {
1763:
1764: Composite previewControl = (Composite) previewMap.get(category);
1765: if (previewControl == null) {
1766: if (category != null) {
1767: try {
1768: IThemePreview preview = getThemePreview(category);
1769: if (preview != null) {
1770: previewControl = new Composite(
1771: previewComposite, SWT.NONE);
1772: previewControl.setLayout(new FillLayout());
1773: ITheme theme = getCascadingTheme();
1774: preview.createControl(previewControl, theme);
1775: previewSet.add(preview);
1776: }
1777: } catch (CoreException e) {
1778: previewControl = new Composite(previewComposite,
1779: SWT.NONE);
1780: previewControl.setLayout(new FillLayout());
1781: myApplyDialogFont(previewControl);
1782: Text error = new Text(previewControl, SWT.WRAP
1783: | SWT.READ_ONLY);
1784: error.setText(RESOURCE_BUNDLE
1785: .getString("errorCreatingPreview")); //$NON-NLS-1$
1786: WorkbenchPlugin
1787: .log(
1788: RESOURCE_BUNDLE
1789: .getString("errorCreatePreviewLog"), StatusUtil.newStatus(IStatus.ERROR, e.getMessage(), e)); //$NON-NLS-1$
1790: }
1791: }
1792: }
1793: if (previewControl == null) {
1794: previewControl = getDefaultPreviewControl();
1795: }
1796: previewMap.put(category, previewControl);
1797: stackLayout.topControl = previewControl;
1798: previewComposite.layout();
1799: }
1800:
1801: /**
1802: * @param category the category
1803: * @return the preview for the category, or its ancestors preview if it does not have one.
1804: */
1805: private IThemePreview getThemePreview(ThemeElementCategory category)
1806: throws CoreException {
1807: IThemePreview preview = category.createPreview();
1808: if (preview != null) {
1809: return preview;
1810: }
1811:
1812: if (category.getParentId() != null) {
1813: int idx = Arrays.binarySearch(
1814: themeRegistry.getCategories(), category
1815: .getParentId(),
1816: IThemeRegistry.ID_COMPARATOR);
1817: if (idx >= 0) {
1818: return getThemePreview(themeRegistry.getCategories()[idx]);
1819: }
1820: }
1821:
1822: return null;
1823: }
1824:
1825: /**
1826: * @return
1827: */
1828: private ITheme getCascadingTheme() {
1829: if (cascadingTheme == null) {
1830: cascadingTheme = new CascadingTheme(currentTheme,
1831: colorRegistry, fontRegistry);
1832: }
1833: return cascadingTheme;
1834: }
1835:
1836: /**
1837: * Update the color controls based on the supplied definition.
1838: *
1839: * @param definition The currently selected <code>ColorDefinition</code>.
1840: */
1841: protected void updateColorControls(ColorDefinition definition) {
1842: if (definition == null) {
1843: colorResetButton.setEnabled(false);
1844: colorSelector.setEnabled(false);
1845: descriptionText.setText(""); //$NON-NLS-1$
1846: return;
1847: }
1848:
1849: colorSelector.setColorValue(getColorValue(definition));
1850:
1851: colorResetButton.setEnabled(!isDefault(definition));
1852: colorSelector.setEnabled(true);
1853: String description = definition.getDescription();
1854: descriptionText.setText(description == null ? "" : description); //$NON-NLS-1$
1855: }
1856:
1857: protected void updateFontControls(FontDefinition definition) {
1858: if (definition == null) {
1859: fontSystemButton.setEnabled(false);
1860: fontResetButton.setEnabled(false);
1861: fontChangeButton.setEnabled(false);
1862: descriptionText.setText(""); //$NON-NLS-1$
1863: return;
1864: }
1865:
1866: fontSystemButton.setEnabled(true);
1867: fontResetButton.setEnabled(!isDefault(definition));
1868: fontChangeButton.setEnabled(true);
1869: String description = definition.getDescription();
1870: descriptionText.setText(description == null ? "" : description); //$NON-NLS-1$
1871: }
1872:
1873: /**
1874: * Update for a change in the dialog font.
1875: * @param newFont
1876: */
1877: private void updateForDialogFontChange(Font newFont) {
1878: Iterator iterator = dialogFontWidgets.iterator();
1879: while (iterator.hasNext()) {
1880: ((Control) iterator.next()).setFont(newFont);
1881: }
1882:
1883: //recalculate the fonts for the tree
1884: labelProvider.clearFontCacheAndUpdate();
1885: }
1886:
1887: /**
1888: * Restore the selection state of the tree.
1889: *
1890: * @since 3.1
1891: */
1892: private void restoreTreeSelection() {
1893: String selectedElementString = getPreferenceStore().getString(
1894: SELECTED_ELEMENT_PREF);
1895:
1896: if (selectedElementString == null) {
1897: return;
1898: }
1899:
1900: Object element = findElementFromMarker(selectedElementString);
1901: if (element == null) {
1902: return;
1903: }
1904:
1905: tree.getViewer().setSelection(new StructuredSelection(element),
1906: true);
1907: }
1908:
1909: /**
1910: * Save the selection state of the tree.
1911: *
1912: * @since 3.1
1913: */
1914: private void saveTreeSelection() {
1915: IStructuredSelection selection = (IStructuredSelection) tree
1916: .getViewer().getSelection();
1917: Object element = selection.getFirstElement();
1918: StringBuffer buffer = new StringBuffer();
1919: appendMarkerToBuffer(buffer, element);
1920: if (buffer.length() > 0) {
1921: buffer.append(((IThemeElementDefinition) element).getId());
1922: }
1923: getPreferenceStore().setValue(SELECTED_ELEMENT_PREF,
1924: buffer.toString());
1925: }
1926:
1927: /**
1928: * Restore the expansion state of the tree.
1929: *
1930: * @since 3.1
1931: */
1932: private void restoreTreeExpansion() {
1933: String expandedElementsString = getPreferenceStore().getString(
1934: EXPANDED_ELEMENTS_PREF);
1935: if (expandedElementsString == null) {
1936: return;
1937: }
1938:
1939: String[] expandedElementIDs = Util.getArrayFromList(
1940: expandedElementsString, EXPANDED_ELEMENTS_TOKEN);
1941: if (expandedElementIDs.length == 0) {
1942: return;
1943: }
1944:
1945: List elements = new ArrayList(expandedElementIDs.length);
1946: for (int i = 0; i < expandedElementIDs.length; i++) {
1947: IThemeElementDefinition def = findElementFromMarker(expandedElementIDs[i]);
1948:
1949: if (def != null) {
1950: elements.add(def);
1951: }
1952: }
1953: tree.getViewer().setExpandedElements(elements.toArray());
1954: }
1955:
1956: /**
1957: * Find the theme element from the given string. It will check the first
1958: * character against the known constants and then call the appropriate
1959: * method on the theme registry. If the element does not exist or the string
1960: * is invalid <code>null</code> is returned.
1961: *
1962: * @param string the string to parse
1963: * @return the element, or <code>null</code>
1964: */
1965: private IThemeElementDefinition findElementFromMarker(String string) {
1966: if (string.length() < 2) {
1967: return null;
1968: }
1969:
1970: char marker = string.charAt(0);
1971: String id = string.substring(1);
1972: IThemeElementDefinition def = null;
1973: switch (marker) {
1974: case MARKER_FONT:
1975: def = themeRegistry.findFont(id);
1976: break;
1977: case MARKER_COLOR:
1978: def = themeRegistry.findColor(id);
1979: break;
1980: case MARKER_CATEGORY:
1981: def = themeRegistry.findCategory(id);
1982: break;
1983: }
1984: return def;
1985: }
1986:
1987: /**
1988: * Saves the expansion state of the tree.
1989: *
1990: * @since 3.1
1991: */
1992: private void saveTreeExpansion() {
1993: Object[] elements = tree.getViewer().getExpandedElements();
1994: List elementIds = new ArrayList(elements.length);
1995:
1996: StringBuffer buffer = new StringBuffer();
1997: for (int i = 0; i < elements.length; i++) {
1998: Object object = elements[i];
1999: appendMarkerToBuffer(buffer, object);
2000:
2001: if (buffer.length() != 0) {
2002: buffer.append(((IThemeElementDefinition) object)
2003: .getId());
2004: elementIds.add(buffer.toString());
2005: }
2006: buffer.setLength(0);
2007: }
2008:
2009: for (Iterator i = elementIds.iterator(); i.hasNext();) {
2010: String id = (String) i.next();
2011: buffer.append(id);
2012: if (i.hasNext()) {
2013: buffer.append(EXPANDED_ELEMENTS_TOKEN);
2014: }
2015: }
2016:
2017: getPreferenceStore().setValue(EXPANDED_ELEMENTS_PREF,
2018: buffer.toString());
2019: }
2020:
2021: /**
2022: * @param buffer
2023: * @param object
2024: */
2025: private void appendMarkerToBuffer(StringBuffer buffer, Object object) {
2026: if (object instanceof FontDefinition) {
2027: buffer.append(MARKER_FONT);
2028: } else if (object instanceof ColorDefinition) {
2029: buffer.append(MARKER_COLOR);
2030: } else if (object instanceof ThemeElementCategory) {
2031: buffer.append(MARKER_CATEGORY);
2032: }
2033: }
2034:
2035: /**
2036: * Edit the currently selected font.
2037: *
2038: * @param display the display to open the dialog on
2039: * @since 3.2
2040: */
2041: private void editFont(Display display) {
2042: final FontDefinition definition = getSelectedFontDefinition();
2043: if (definition != null) {
2044: final FontDialog fontDialog = new FontDialog(
2045: fontChangeButton.getShell());
2046: fontDialog.setFontList(getFontValue(definition));
2047: final FontData data = fontDialog.open();
2048:
2049: if (data != null) {
2050: setFontPreferenceValue(definition, fontDialog
2051: .getFontList());
2052: setRegistryValue(definition, fontDialog.getFontList());
2053: }
2054:
2055: updateFontControls(definition);
2056: }
2057: }
2058: }
|