/*
* Copyright (c) 2011, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.tools.ui.internal.util;
import com.google.common.collect.Maps;
import com.google.dart.tools.internal.corext.refactoring.util.ReflectionUtils;
import com.google.dart.tools.ui.DartToolsPlugin;
import com.google.dart.tools.ui.DartUI;
import com.google.dart.tools.ui.internal.preferences.FontPreferencePage;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.layout.PixelConverter;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.resource.FontRegistry;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Caret;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.swt.widgets.Scrollable;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.texteditor.AbstractTextEditor;
import java.util.Map;
/**
* Utility class to simplify access to some SWT resources.
*/
public class SWTUtil {
private static final int COMBO_VISIBLE_ITEM_COUNT = 30;
/**
* Font cache. We use it to don't create multiple copies of the font with the same properties.
*/
private static Map<String, Font> fontCache = Maps.newHashMap();
/**
* Propagates changes in {@link JFaceResources} font to the given {@link Control}.
*/
public static void bindJFaceResourcesFontToControl(final Control control) {
updateFontSizeFromJFaceResources(control);
final FontRegistry fontRegistry = JFaceResources.getFontRegistry();
final IPropertyChangeListener propertyListener = new IPropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
if (!control.isDisposed()) {
if (FontPreferencePage.VIEW_BASE_FONT_KEY.equals(event.getProperty())) {
updateFontSizeFromJFaceResources(control);
}
}
}
};
control.addListener(SWT.Dispose, new Listener() {
@Override
public void handleEvent(Event event) {
fontRegistry.removeListener(propertyListener);
}
});
fontRegistry.addListener(propertyListener);
}
/**
* Create a new font with the same attributes as the given <code>oldFont</code> except that its
* size is taken from the <code>newFont</code> Font.
*
* @param oldFont The font whose size is to be changed (not null)
* @param newFont The Font that defines the new font size (may be null)
* @return A new Font like the original but with a new size
*/
public static Font changeFontSize(Font oldFont, Font newFont) {
if (newFont == null) {
return oldFont;
}
FontData[] fontData = newFont.getFontData();
return changeFontSize(oldFont, fontData);
}
/**
* Create a new font with the same attributes as the given <code>oldFont</code> except that its
* size is taken from the <code>sizedFontData</code> FontData.
*
* @param oldFont The font whose size is to be changed (not null)
* @param sizedFontData The FontData[] containing the new font size (may be null or zero length)
* @return A new Font like the original but with a new size
*/
public static Font changeFontSize(Font oldFont, FontData[] sizedFontData) {
if (sizedFontData == null || sizedFontData.length == 0) {
return oldFont;
}
int height = sizedFontData[0].getHeight();
return changeFontSize(oldFont, height);
}
/**
* Create a new font with the same attributes as the given <code>oldFont</code> except that its
* size is given by <code>height</code>.
*
* @param oldFont The font whose size is to be changed (not null)
* @param height The new font height
* @return A new Font like the original but with a new size
*/
public static Font changeFontSize(Font oldFont, int height) {
FontData[] data = oldFont.getFontData();
FontData newData = new FontData(data[0].getName(), height, data[0].getStyle());
return getFont(oldFont.getDevice(), new FontData[] {newData});
}
/**
* Make a FontData array similar to the given <code>data</code> but having font height determined
* by <code>height</code>.
*
* @param height New font height
* @return A font data array with the new height
*/
public static FontData[] changeFontSize(FontData[] data, int height) {
FontData newData = new FontData(data[0].getName(), height, data[0].getStyle());
return new FontData[] {newData};
}
public static void eraseSelection(Event event, Scrollable control, IPreferenceStore prefs) {
event.detail &= ~SWT.HOT; // do not draw hover background natively
if ((event.detail & SWT.SELECTED) == 0) {
return; // item not selected
}
int clientWidth = control.getClientArea().width;
GC gc = event.gc;
Color oldFG = gc.getForeground();
Color oldBG = gc.getBackground();
Color fgColor = DartUI.getViewerSelectionForeground(prefs, control.getDisplay());
if (fgColor != null) {
gc.setForeground(fgColor);
}
Color bgColor = DartUI.getViewerSelectionBackground(prefs, control.getDisplay());
if (bgColor != null) {
gc.setBackground(bgColor);
}
gc.fillRectangle(0, event.y, clientWidth, event.height);
gc.setForeground(oldFG);
gc.setBackground(oldBG);
event.detail &= ~SWT.SELECTED;
}
/**
* Returns a width hint for a button control.
*/
public static int getButtonWidthHint(Button button) {
button.setFont(JFaceResources.getDialogFont());
PixelConverter converter = new PixelConverter(button);
int widthHint = converter.convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
return Math.max(widthHint, button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x);
}
/**
* Get a font for the given <code>device</code> as specified by the given <code>data</code>.
* <p>
* This implementation maintains a cache of fonts.
*
* @param device The device the font applies to.
* @param data The array of font data that defines the characteristics of the desired font.
* @return The font
*/
public static Font getFont(Device device, FontData[] data) {
String name = data[0].getName();
int style = data[0].getStyle();
int height = data[0].getHeight();
StringBuilder keyBuilder = new StringBuilder();
keyBuilder.append(name).append(';');
keyBuilder.append(height).append(';');
keyBuilder.append(style);
String key = keyBuilder.toString();
Font font = fontCache.get(key);
if (font == null || font.isDisposed()) {
FontData newData = new FontData(name, height, style);
font = new Font(device, newData);
fontCache.put(key, font);
}
return font;
}
/**
* Returns the shell for the given widget. If the widget doesn't represent a SWT object that
* manage a shell, <code>null</code> is returned.
*
* @return the shell for the given widget
*/
public static Shell getShell(Widget widget) {
if (widget instanceof Control) {
return ((Control) widget).getShell();
}
if (widget instanceof Caret) {
return ((Caret) widget).getParent().getShell();
}
if (widget instanceof DragSource) {
return ((DragSource) widget).getControl().getShell();
}
if (widget instanceof DropTarget) {
return ((DropTarget) widget).getControl().getShell();
}
if (widget instanceof Menu) {
return ((Menu) widget).getParent().getShell();
}
if (widget instanceof ScrollBar) {
return ((ScrollBar) widget).getParent().getShell();
}
return null;
}
/**
* Returns the standard display to be used. The method first checks, if the thread calling this
* method has an associated display. If so, this display is returned. Otherwise the method returns
* the default display.
*/
public static Display getStandardDisplay() {
Display display = Display.getCurrent();
if (display == null) {
display = Display.getDefault();
}
return display;
}
public static int getTableHeightHint(Table table, int rows) {
if (table.getFont().equals(JFaceResources.getDefaultFont())) {
table.setFont(JFaceResources.getDialogFont());
}
int result = table.getItemHeight() * rows + table.getHeaderHeight();
if (table.getLinesVisible()) {
result += table.getGridLineWidth() * (rows - 1);
}
return result;
}
/**
* Runs the given {@link Runnable} in the UI thread.
* <p>
* If the current thread in the UI thread, run immediately.
* <p>
* Otherwise, run asynchronously.
*/
public static void runUI(final Runnable runnable) {
Display display = Display.getCurrent();
if (display != null) {
try {
runnable.run();
} catch (Throwable e) {
DartToolsPlugin.log(e);
}
} else {
display = Display.getDefault();
display.asyncExec(new Runnable() {
@Override
public void run() {
try {
runnable.run();
} catch (Throwable e) {
DartToolsPlugin.log(e);
}
}
});
}
}
/**
* Sets width and height hint for the button control. <b>Note:</b> This is a NOP if the button's
* layout data is not an instance of <code>GridData</code> .
*
* @param button the button for which to set the dimension hint
*/
public static void setButtonDimensionHint(Button button) {
Assert.isNotNull(button);
Object gd = button.getLayoutData();
if (gd instanceof GridData) {
((GridData) gd).widthHint = getButtonWidthHint(button);
((GridData) gd).horizontalAlignment = GridData.FILL;
}
}
public static void setColors(Control ctl, IPreferenceStore store) {
Color color = store.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT)
? null : createColor(store, AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND);
ctl.setForeground(color);
color = store.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT) ? null
: createColor(store, AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND);
ctl.setBackground(color);
}
public static void setColors(CTabFolder ctl, IPreferenceStore store) {
Color fgColor = store.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT)
? null : createColor(store, AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND);
ctl.setForeground(fgColor);
Color bgColor = store.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT)
? null : createColor(store, AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND);
ctl.setBackground(bgColor);
Color sfgColor = store.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_SELECTION_FOREGROUND_SYSTEM_DEFAULT)
? null : createColor(store, AbstractTextEditor.PREFERENCE_COLOR_SELECTION_FOREGROUND);
ctl.setSelectionForeground(sfgColor);
Color sbgColor = store.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_SELECTION_BACKGROUND_SYSTEM_DEFAULT)
? null : createColor(store, AbstractTextEditor.PREFERENCE_COLOR_SELECTION_BACKGROUND);
ctl.setSelectionBackground(sbgColor);
}
public static void setColors(List ctl, IPreferenceStore store) {
Color color = store.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT)
? null : createColor(store, AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND);
ctl.setForeground(color);
color = store.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT) ? null
: createColor(store, AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND);
ctl.setBackground(color);
// RGB rgb = PreferenceConverter.getColor(store, AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND);
// ctl.setForeground(DartUI.getColorManager().getColor(rgb));
// rgb = PreferenceConverter.getColor(store, AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND);
// ctl.setBackground(DartUI.getColorManager().getColor(rgb));
}
public static void setColors(StyledText ctl, IPreferenceStore store) {
Color color = store.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT)
? null : createColor(store, AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND);
ctl.setForeground(color);
color = store.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT) ? null
: createColor(store, AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND);
ctl.setBackground(color);
color = store.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_SELECTION_FOREGROUND_SYSTEM_DEFAULT)
? null : createColor(store, AbstractTextEditor.PREFERENCE_COLOR_SELECTION_FOREGROUND);
ctl.setSelectionForeground(color);
color = store.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_SELECTION_BACKGROUND_SYSTEM_DEFAULT)
? null : createColor(store, AbstractTextEditor.PREFERENCE_COLOR_SELECTION_BACKGROUND);
ctl.setSelectionBackground(color);
}
public static void setColors(Table ctl, IPreferenceStore store) {
Color color = store.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT)
? null : createColor(store, AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND);
ctl.setForeground(color);
color = store.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT) ? null
: createColor(store, AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND);
ctl.setBackground(color);
}
public static void setColors(Tree ctl, IPreferenceStore store) {
Color color = store.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND_SYSTEM_DEFAULT)
? null : createColor(store, AbstractTextEditor.PREFERENCE_COLOR_FOREGROUND);
ctl.setForeground(color);
color = store.getBoolean(AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT) ? null
: createColor(store, AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND);
ctl.setBackground(color);
}
/**
* Sets the default visible item count for {@link Combo}s. Workaround for
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=7845 .
*
* @param combo the combo
* @see Combo#setVisibleItemCount(int)
* @see #COMBO_VISIBLE_ITEM_COUNT
*/
public static void setDefaultVisibleItemCount(Combo combo) {
combo.setVisibleItemCount(COMBO_VISIBLE_ITEM_COUNT);
}
/**
* Changes enablement of the given {@link Control} and all its children.
*/
public static void setEnabledHierarchy(Control control, boolean enable) {
if (control.getEnabled() != enable) {
control.setEnabled(enable);
}
if (control instanceof Composite) {
Composite composite = (Composite) control;
Control[] children = composite.getChildren();
if (children != null) {
for (Control child : children) {
setEnabledHierarchy(child, enable);
}
}
}
}
/**
* Sets the size of the item to correspond size of the font.
*/
public static void setItemHeightForFont(Control container) {
GC gc = new GC(container);
try {
Point size = gc.textExtent("A");
ReflectionUtils.invokeMethod(container, "setItemHeight(int)", Math.max(16, size.y));
} catch (Throwable e) {
} finally {
gc.dispose();
}
}
private static Color createColor(IPreferenceStore store, String key) {
RGB rgb = null;
if (store.contains(key)) {
if (store.isDefault(key)) {
rgb = PreferenceConverter.getDefaultColor(store, key);
} else {
rgb = PreferenceConverter.getColor(store, key);
}
if (rgb != null) {
return DartUI.getColorManager().getColor(rgb);
}
}
return null;
}
/**
* Sets size of the font in the given {@link Control} to the size specified in
* {@link JFaceResources}.
*/
private static void updateFontSizeFromJFaceResources(Control control) {
Font newFont = JFaceResources.getFont(FontPreferencePage.VIEW_BASE_FONT_KEY);
Font oldFont = control.getFont();
Font font = SWTUtil.changeFontSize(oldFont, newFont);
control.setFont(font);
// set Table/Tree item height
if (control instanceof Table || control instanceof Tree) {
setItemHeightForFont(control);
}
// process Composite children
if (control instanceof Composite) {
Composite composite = (Composite) control;
Control[] children = composite.getChildren();
if (children != null) {
for (Control child : children) {
updateFontSizeFromJFaceResources(child);
}
}
composite.layout();
}
}
private SWTUtil() {
}
}