Mercurial > projects > dwt2
diff org.eclipse.jface/src/org/eclipse/jface/resource/FontRegistry.d @ 12:bc29606a740c
Added dwt-addons in original directory structure of eclipse.org
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sat, 14 Mar 2009 18:23:29 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface/src/org/eclipse/jface/resource/FontRegistry.d Sat Mar 14 18:23:29 2009 +0100 @@ -0,0 +1,880 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation 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: + * IBM Corporation - initial API and implementation + * Port to the D programming language: + * Frank Benoit <benoit@tionex.de> + *******************************************************************************/ +module org.eclipse.jface.resource.FontRegistry; + +import org.eclipse.jface.resource.ResourceRegistry; +import org.eclipse.jface.resource.FontDescriptor; +import org.eclipse.jface.resource.StringConverter; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.resource.DataFormatException; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.core.runtime.Assert; + +import java.util.ResourceBundle; +import java.util.MissingResourceException; +import java.lang.all; +import java.util.Collections; +import java.util.Collection; +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map; +import java.util.HashMap; +import java.util.Set; +version(Windows) import org.eclipse.swt.internal.win32.OS; + +/** + * A font registry maintains a mapping between symbolic font names + * and SWT fonts. + * <p> + * A font registry owns all of the font objects registered + * with it, and automatically disposes of them when the SWT Display + * that creates the fonts is disposed. Because of this, clients do + * not need to (indeed, must not attempt to) dispose of font + * objects themselves. + * </p> + * <p> + * A special constructor is provided for populating a font registry + * from a property files using the standard Java resource bundle mechanism. + * </p> + * <p> + * Methods are provided for registering listeners that will be kept + * apprised of changes to list of registed fonts. + * </p> + * <p> + * Clients may instantiate this class (it was not designed to be subclassed). + * </p> + * + * Since 3.0 this class extends ResourceRegistry. + * @noextend This class is not intended to be subclassed by clients. + */ +public class FontRegistry : ResourceRegistry { + + /** + * FontRecord is a private helper class that holds onto a font + * and can be used to generate its bold and italic version. + */ + private class FontRecord { + + Font baseFont; + + Font boldFont; + + Font italicFont; + + FontData[] baseData; + + /** + * Create a new instance of the receiver based on the + * plain font and the data for it. + * @param plainFont The base looked up font. + * @param data The data used to look it up. + */ + this(Font plainFont, FontData[] data) { + baseFont = plainFont; + baseData = data; + } + + /** + * Dispose any of the fonts created for this record. + */ + void dispose() { + baseFont.dispose(); + if (boldFont !is null) { + boldFont.dispose(); + } + if (italicFont !is null) { + italicFont.dispose(); + } + } + + /** + * Return the base Font. + * @return Font + */ + public Font getBaseFont() { + return baseFont; + } + + /** + * Return the bold Font. Create a bold version + * of the base font to get it. + * @return Font + */ + public Font getBoldFont() { + if (boldFont !is null) { + return boldFont; + } + + FontData[] boldData = getModifiedFontData(SWT.BOLD); + boldFont = new Font(Display.getCurrent(), boldData); + return boldFont; + } + + /** + * Get a version of the base font data with the specified + * style. + * @param style the new style + * @return the font data with the style {@link FontData#FontData(String, int, int)} + * @see SWT#ITALIC + * @see SWT#NORMAL + * @see SWT#BOLD + * @todo Generated comment + */ + private FontData[] getModifiedFontData(int style) { + FontData[] styleData = new FontData[baseData.length]; + for (int i = 0; i < styleData.length; i++) { + FontData base = baseData[i]; + styleData[i] = new FontData(base.getName(), base.getHeight(), + base.getStyle() | style); + } + + return styleData; + } + + /** + * Return the italic Font. Create an italic version of the + * base font to get it. + * @return Font + */ + public Font getItalicFont() { + if (italicFont !is null) { + return italicFont; + } + + FontData[] italicData = getModifiedFontData(SWT.ITALIC); + italicFont = new Font(Display.getCurrent(), italicData); + return italicFont; + } + + /** + * Add any fonts that were allocated for this record to the + * stale fonts. Anything that matches the default font will + * be skipped. + * @param defaultFont The system default. + */ + void addAllocatedFontsToStale(Font defaultFont) { + //Return all of the fonts allocated by the receiver. + //if any of them are the defaultFont then don't bother. + if (defaultFont !is baseFont && baseFont !is null) { + staleFonts.add(baseFont); + } + if (defaultFont !is boldFont && boldFont !is null) { + staleFonts.add(boldFont); + } + if (defaultFont !is italicFont && italicFont !is null) { + staleFonts.add(italicFont); + } + } + } + + /** + * Table of known fonts, keyed by symbolic font name + * (key type: <code>String</code>, + * value type: <code>FontRecord</code>. + */ + private Map stringToFontRecord; + + /** + * Table of known font data, keyed by symbolic font name + * (key type: <code>String</code>, + * value type: <code>org.eclipse.swt.graphics.FontData[]</code>). + */ + private Map stringToFontData; + + /** + * Collection of Fonts that are now stale to be disposed + * when it is safe to do so (i.e. on shutdown). + * @see List + */ + private List staleFonts; + + /** + * Runnable that cleans up the manager on disposal of the display. + */ + protected Runnable displayRunnable; + + private bool displayDisposeHooked; + + private final bool cleanOnDisplayDisposal; + + /** + * Creates an empty font registry. + * <p> + * There must be an SWT Display created in the current + * thread before calling this method. + * </p> + */ + public this() { + this(Display.getCurrent(), true); + } + + /** + * Creates a font registry and initializes its content from + * a property file. + * <p> + * There must be an SWT Display created in the current + * thread before calling this method. + * </p> + * <p> + * The OS name (retrieved using <code>System.getProperty("os.name")</code>) + * is converted to lowercase, purged of whitespace, and appended + * as suffix (separated by an underscore <code>'_'</code>) to the given + * location string to yield the base name of a resource bundle + * acceptable to <code>ResourceBundle.getBundle</code>. + * The standard Java resource bundle mechanism is then used to locate + * and open the appropriate properties file, taking into account + * locale specific variations. + * </p> + * <p> + * For example, on the Windows 2000 operating system the location string + * <code>"com.example.myapp.Fonts"</code> yields the base name + * <code>"com.example.myapp.Fonts_windows2000"</code>. For the US English locale, + * this further elaborates to the resource bundle name + * <code>"com.example.myapp.Fonts_windows2000_en_us"</code>. + * </p> + * <p> + * If no appropriate OS-specific resource bundle is found, the + * process is repeated using the location as the base bundle name. + * </p> + * <p> + * The property file contains entries that look like this: + * <pre> + * textfont.0=MS Sans Serif-regular-10 + * textfont.1=Times New Roman-regular-10 + * + * titlefont.0=MS Sans Serif-regular-12 + * titlefont.1=Times New Roman-regular-12 + * </pre> + * Each entry maps a symbolic font names (the font registry keys) with + * a "<code>.<it>n</it></code> suffix to standard font names + * on the right. The suffix indicated order of preference: + * "<code>.0</code>" indicates the first choice, + * "<code>.1</code>" indicates the second choice, and so on. + * </p> + * The following example shows how to use the font registry: + * <pre> + * FontRegistry registry = new FontRegistry("com.example.myapp.fonts"); + * Font font = registry.get("textfont"); + * control.setFont(font); + * ... + * </pre> + * + * @param location the name of the resource bundle + * @param loader the ClassLoader to use to find the resource bundle + * @exception MissingResourceException if the resource bundle cannot be found + * @since 2.1 + */ + public this(String location, /+ClassLoader+/Object loader){ + initInstance(); + Display display = Display.getCurrent(); + Assert.isNotNull(display); + // FIXE: need to respect loader + //readResourceBundle(location, loader); + readResourceBundle(location); + + cleanOnDisplayDisposal = true; + hookDisplayDispose(display); + } + + private void initInstance(){ + displayRunnable = new class Runnable { + public void run() { + clearCaches(); + } + }; + stringToFontRecord = new HashMap(7); + stringToFontData = new HashMap(7); + staleFonts = new ArrayList(); + } + + /** + * Load the FontRegistry using the ClassLoader from the PlatformUI + * plug-in + * @param location the location to read the resource bundle from + * @throws MissingResourceException Thrown if a resource is missing + */ + public this(String location) { + // FIXE: + // this(location, WorkbenchPlugin.getDefault().getDescriptor().getPluginClassLoader()); + this(location, null); + } + + /** + * Read the resource bundle at location. Look for a file with the + * extension _os_ws first, then _os then just the name. + * @param location - String - the location of the file. + */ + + private void readResourceBundle(String location) { +// String osname = System.getProperty("os.name").trim(); //$NON-NLS-1$ +// String wsname = SWT.getPlatform(); +// osname = StringConverter.removeWhiteSpaces(osname).toLowerCase(); +// wsname = StringConverter.removeWhiteSpaces(wsname).toLowerCase(); +// String OSLocation = location; +// String WSLocation = location; + ResourceBundle bundle = null; +// if (osname !is null) { +// OSLocation = location ~ "_" ~ osname; //$NON-NLS-1$ +// if (wsname !is null) { +// WSLocation = OSLocation ~ "_" ~ wsname; //$NON-NLS-1$ +// } +// } + String errorLocation = "org.eclipse.jface.resource.FontRegistry properties"; + const char[] prefix = "org.eclipse.jface.resource.jfacefonts_"; + const char[] postfix = ".properties"; + version( linux ){ + ImportData propdata = getImportData!( prefix ~ "linux_gtk" ~ postfix ); + } + else version( Windows ){ + ImportData propdata; + if( OS.IsWin95 && OS.WIN32_VERSION >= OS.VERSION (4, 10 )){ + propdata = getImportData!( prefix ~ "windows98" ~ postfix ); + } + else if( OS.WIN32_VERSION >= OS.VERSION (5, 1 )){ + propdata = getImportData!( prefix ~ "windowsxp" ~ postfix ); + } + else if( OS.WIN32_VERSION >= OS.VERSION (5, 0)){ + propdata = getImportData!( prefix ~ "windows2000" ~ postfix ); + } + else if( OS.WIN32_VERSION >= OS.VERSION (4, 0)){ + propdata = getImportData!( prefix ~ "windowsnt" ~ postfix ); + } + else{ + assert( false, "TODO: detect windows version" ); + } + } + else { + static assert( false, "unknown plattform" ); + } + +// try { + bundle = ResourceBundle.getBundle(propdata); + readResourceBundle(bundle, errorLocation); +// } catch (MissingResourceException wsException) { +// try { +// bundle = ResourceBundle.getBundle(OSLocation); +// readResourceBundle(bundle, WSLocation); +// } catch (MissingResourceException osException) { +// if (location !is OSLocation) { +// bundle = ResourceBundle.getBundle(location); +// readResourceBundle(bundle, WSLocation); +// } else { +// throw osException; +// } +// } +// } + } + + /** + * Creates an empty font registry. + * + * @param display the Display + */ + public this(Display display) { + this(display, true); + } + + /** + * Creates an empty font registry. + * + * @param display + * the <code>Display</code> + * @param cleanOnDisplayDisposal + * whether all fonts allocated by this <code>FontRegistry</code> + * should be disposed when the display is disposed + * @since 3.1 + */ + public this(Display display, bool cleanOnDisplayDisposal) { + initInstance(); + Assert.isNotNull(display); + this.cleanOnDisplayDisposal = cleanOnDisplayDisposal; + if (cleanOnDisplayDisposal) { + hookDisplayDispose(display); + } + } + + /** + * Find the first valid fontData in the provided list. If none are valid + * return the first one regardless. If the list is empty return null. Return + * <code>null</code> if one cannot be found. + * + * @param fonts the font list + * @param display the display used + * @return the font data of the like describe above + * + * @deprecated use bestDataArray in order to support Motif multiple entry + * fonts. + */ + public FontData bestData(FontData[] fonts, Display display) { + for (int i = 0; i < fonts.length; i++) { + FontData fd = fonts[i]; + + if (fd is null) { + break; + } + + FontData[] fixedFonts = display.getFontList(fd.getName(), false); + if (isFixedFont(fixedFonts, fd)) { + return fd; + } + + FontData[] scalableFonts = display.getFontList(fd.getName(), true); + if (scalableFonts.length > 0) { + return fd; + } + } + + //None of the provided datas are valid. Return the + //first one as it is at least the first choice. + if (fonts.length > 0) { + return fonts[0]; + } + + //Nothing specified + return null; + } + + /** + * Find the first valid fontData in the provided list. + * If none are valid return the first one regardless. + * If the list is empty return <code>null</code>. + * + * @param fonts list of fonts + * @param display the display + * @return font data like described above + * @deprecated use filterData in order to preserve + * multiple entry fonts on Motif + */ + public FontData[] bestDataArray(FontData[] fonts, Display display) { + + FontData bestData = bestData(fonts, display); + if (bestData is null) { + return null; + } + + FontData[] datas = new FontData[1]; + datas[0] = bestData; + return datas; + } + + /** + * Removes from the list all fonts that do not exist in this system. + * If none are valid, return the first irregardless. If the list is + * empty return <code>null</code>. + * + * @param fonts the fonts to check + * @param display the display to check against + * @return the list of fonts that have been found on this system + * @since 3.1 + */ + public FontData [] filterData(FontData [] fonts, Display display) { + ArrayList good = new ArrayList(fonts.length); + for (int i = 0; i < fonts.length; i++) { + FontData fd = fonts[i]; + + if (fd is null) { + continue; + } + + FontData[] fixedFonts = display.getFontList(fd.getName(), false); + if (isFixedFont(fixedFonts, fd)) { + good.add(fd); + } + + FontData[] scalableFonts = display.getFontList(fd.getName(), true); + if (scalableFonts.length > 0) { + good.add(fd); + } + } + + + //None of the provided datas are valid. Return the + //first one as it is at least the first choice. + if (good.isEmpty() && fonts.length > 0) { + good.add(fonts[0]); + } + else if (fonts.length is 0) { + return null; + } + + return arraycast!(FontData)(good.toArray()); + } + + + /** + * Creates a new font with the given font datas or <code>null</code> + * if there is no data. + * @return FontRecord for the new Font or <code>null</code>. + */ + private FontRecord createFont(String symbolicName, FontData[] fonts) { + Display display = Display.getCurrent(); + if (display is null) { + return null; + } + if (cleanOnDisplayDisposal && !displayDisposeHooked) { + hookDisplayDispose(display); + } + + FontData[] validData = filterData(fonts, display); + if (validData.length is 0) { + //Nothing specified + return null; + } + + //Do not fire the update from creation as it is not a property change + put(symbolicName, validData, false); + Font newFont = new Font(display, validData); + return new FontRecord(newFont, validData); + } + + /** + * Calculates the default font and returns the result. + * This method creates a font that must be disposed. + */ + Font calculateDefaultFont() { + Display current = Display.getCurrent(); + if (current is null) { + Shell shell = new Shell(); + Font font = new Font(null, shell.getFont().getFontData()); + shell.dispose(); + return font; + } + return new Font(current, current.getSystemFont().getFontData()); + } + + /** + * Returns the default font data. Creates it if necessary. + * @return Font + */ + public Font defaultFont() { + return defaultFontRecord().getBaseFont(); + } + + /** + * Returns the font descriptor for the font with the given symbolic + * font name. Returns the default font if there is no special value + * associated with that name + * + * @param symbolicName symbolic font name + * @return the font descriptor (never null) + * + * @since 3.3 + */ + public FontDescriptor getDescriptor(String symbolicName) { + Assert.isTrue(symbolicName.length > 0); + return FontDescriptor.createFrom(getFontData(symbolicName)); + } + + + + /** + * Returns the default font record. + */ + private FontRecord defaultFontRecord() { + + FontRecord record = cast(FontRecord) stringToFontRecord + .get(JFaceResources.DEFAULT_FONT); + if (record is null) { + Font defaultFont = calculateDefaultFont(); + record = createFont(JFaceResources.DEFAULT_FONT, defaultFont + .getFontData()); + defaultFont.dispose(); + stringToFontRecord.put(stringcast(JFaceResources.DEFAULT_FONT), record); + } + return record; + } + + /** + * Returns the default font data. Creates it if necessary. + */ + private FontData[] defaultFontData() { + return defaultFontRecord().baseData; + } + + /** + * Returns the font data associated with the given symbolic font name. + * Returns the default font data if there is no special value associated + * with that name. + * + * @param symbolicName symbolic font name + * @return the font + */ + public FontData[] getFontData(String symbolicName) { + + Assert.isTrue(symbolicName.length > 0); + Object result = stringToFontData.get(symbolicName); + if (result is null) { + return defaultFontData(); + } + + return arrayFromObject!(FontData)(result); + } + + /** + * Returns the font associated with the given symbolic font name. + * Returns the default font if there is no special value associated + * with that name. + * + * @param symbolicName symbolic font name + * @return the font + */ + public Font get(String symbolicName) { + + return getFontRecord(symbolicName).getBaseFont(); + } + + /** + * Returns the bold font associated with the given symbolic font name. + * Returns the bolded default font if there is no special value associated + * with that name. + * + * @param symbolicName symbolic font name + * @return the font + * @since 3.0 + */ + public Font getBold(String symbolicName) { + + return getFontRecord(symbolicName).getBoldFont(); + } + + /** + * Returns the italic font associated with the given symbolic font name. + * Returns the italic default font if there is no special value associated + * with that name. + * + * @param symbolicName symbolic font name + * @return the font + * @since 3.0 + */ + public Font getItalic(String symbolicName) { + + return getFontRecord(symbolicName).getItalicFont(); + } + + /** + * Return the font record for the key. + * @param symbolicName The key for the record. + * @return FontRecird + */ + private FontRecord getFontRecord(String symbolicName) { + Assert.isNotNull(symbolicName); + auto result = stringToFontRecord.get(symbolicName); + if (result !is null) { + return cast(FontRecord) result; + } + + result = stringToFontData.get(symbolicName); + + FontRecord fontRecord; + + if (result is null) { + fontRecord = defaultFontRecord(); + } else { + fontRecord = createFont(symbolicName, arrayFromObject!(FontData)(result)); + } + + if (fontRecord is null) { + fontRecord = defaultFontRecord(); + } + + stringToFontRecord.put(symbolicName.dup, fontRecord); + return fontRecord; + + } + + /* (non-Javadoc) + * @see org.eclipse.jface.resource.ResourceRegistry#getKeySet() + */ + public override Set getKeySet() { + return Collections.unmodifiableSet(stringToFontData.keySet()); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.resource.ResourceRegistry#hasValueFor(java.lang.String) + */ + public override bool hasValueFor(String fontKey) { + return stringToFontData.containsKey(fontKey); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.resource.ResourceRegistry#clearCaches() + */ + protected override void clearCaches() { + + Iterator iterator = stringToFontRecord.values().iterator(); + while (iterator.hasNext()) { + Object next = iterator.next(); + (cast(FontRecord) next).dispose(); + } + + disposeFonts(staleFonts.iterator()); + stringToFontRecord.clear(); + staleFonts.clear(); + + displayDisposeHooked = false; + } + + /** + * Dispose of all of the fonts in this iterator. + * @param iterator over Collection of Font + */ + private void disposeFonts(Iterator iterator) { + while (iterator.hasNext()) { + Object next = iterator.next(); + (cast(Font) next).dispose(); + } + } + + /** + * Hook a dispose listener on the SWT display. + */ + private void hookDisplayDispose(Display display) { + displayDisposeHooked = true; + display.disposeExec(displayRunnable); + } + + /** + * Checks whether the given font is in the list of fixed fonts. + */ + private bool isFixedFont(FontData[] fixedFonts, FontData fd) { + // Can't use FontData.equals() since some values aren't + // set if a fontdata isn't used. + int height = fd.getHeight(); + String name = fd.getName(); + for (int i = 0; i < fixedFonts.length; i++) { + FontData fixed = fixedFonts[i]; + if (fixed.getHeight() is height && fixed.getName().equals(name)) { + return true; + } + } + return false; + } + + /** + * Converts a String into a FontData object. + */ + private FontData makeFontData(String value) { + try { + return StringConverter.asFontData(value.trim()); + } catch (DataFormatException e) { + throw new MissingResourceException( + "Wrong font data format. Value is: \"" ~ value ~ "\"", this.classinfo.name, value); //$NON-NLS-2$//$NON-NLS-1$ + } + } + + /** + * Adds (or replaces) a font to this font registry under the given + * symbolic name. + * <p> + * A property change event is reported whenever the mapping from + * a symbolic name to a font changes. The source of the event is + * this registry; the property name is the symbolic font name. + * </p> + * + * @param symbolicName the symbolic font name + * @param fontData an Array of FontData + */ + public void put(String symbolicName, FontData[] fontData) { + put(symbolicName, fontData, true); + } + + /** + * Adds (or replaces) a font to this font registry under the given + * symbolic name. + * <p> + * A property change event is reported whenever the mapping from + * a symbolic name to a font changes. The source of the event is + * this registry; the property name is the symbolic font name. + * </p> + * + * @param symbolicName the symbolic font name + * @param fontData an Array of FontData + * @param update - fire a font mapping changed if true. False + * if this method is called from the get method as no setting + * has changed. + */ + private void put(String symbolicName, FontData[] fontData, bool update) { + + Assert.isNotNull(symbolicName); + Assert.isTrue(fontData.length > 0 ); + + FontData[] existing = arrayFromObject!(FontData)( stringToFontData.get(stringcast(symbolicName))); + if (ArrayEquals(existing, fontData)) { + return; + } + + FontRecord oldFont = cast(FontRecord) stringToFontRecord.remove(stringcast(symbolicName)); + stringToFontData.put(symbolicName.dup, new ArrayWrapperObject(fontData)); + if (update) { + fireMappingChanged(symbolicName, new ArrayWrapperT!(FontData)(existing), new ArrayWrapperObject(arraycast!(Object)(fontData))); + } + + if (oldFont !is null) { + oldFont.addAllocatedFontsToStale(defaultFontRecord().getBaseFont()); + } + } + + /** + * Reads the resource bundle. This puts FontData[] objects + * in the mapping table. These will lazily be turned into + * real Font objects when requested. + */ + private void readResourceBundle(ResourceBundle bundle, String bundleName) { + auto keys = bundle.getKeys(); + foreach ( key; keys ) { + int pos = key.lastIndexOf('.'); + if (pos is -1) { + stringToFontData.put(stringcast(key.dup), new ArrayWrapperObject(arraycast!(Object)([ makeFontData(bundle + .getString(key)) ]))); + } else { + String name = key.substring(0, pos); + int i = 0; + try { + i = tango.text.convert.Integer.toInt(key.substring(pos + 1)); + } catch (IllegalArgumentException e) { + //Panic the file can not be parsed. + throw new MissingResourceException( + "Wrong key format ", bundleName, key); //$NON-NLS-1$ + } + FontData[] elements = arrayFromObject!(FontData)( stringToFontData.get(stringcast(name))); + if (elements is null) { + elements = new FontData[8]; + stringToFontData.put(name.dup, new ArrayWrapperObject(elements)); + } + if (i > elements.length) { + FontData[] na = new FontData[i + 8]; + System.arraycopy(elements, 0, na, 0, elements.length); + elements = na; + stringToFontData.put(name.dup, new ArrayWrapperObject(elements)); + } + elements[i] = makeFontData(bundle.getString(key)); + } + } + } + + /** + * Returns the font descriptor for the JFace default font. + * + * @return the font descriptor for the JFace default font + * @since 3.3 + */ + public FontDescriptor defaultFontDescriptor() { + return FontDescriptor.createFrom(defaultFontData()); + } +}