blob: e3056aca8072ce3b0157dde124516926516c177e [file] [log] [blame]
/*********************************************************************
* Copyright (c) 2009, 2012 SpringSource, a division of VMware, Inc.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/
package org.eclipse.virgo.ide.ui.editors.text;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IInformationControlExtension;
import org.eclipse.jface.text.IInformationControlExtension2;
import org.eclipse.jface.text.IInformationControlExtension3;
import org.eclipse.jface.text.IInformationControlExtension4;
import org.eclipse.jface.util.Geometry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
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.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
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.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Slider;
import org.eclipse.swt.widgets.ToolBar;
/**
* An abstract information control that can show content inside a shell. The information control can be created in two
* styles:
* <ul>
* <li>non-resizable tooltip with optional status</li>
* <li>resizable tooltip with optional tool bar</li>
* </ul>
* Additionally it can present either a status line containing a status text or a toolbar containing toolbar buttons.
* <p>
* Subclasses must either override {@link IInformationControl#setInformation(String)} or implement
* {@link IInformationControlExtension2}. They should also extend {@link #computeTrim()} if they create a content area
* with additional trim (e.g. scrollbars) and override {@link #getInformationPresenterControlCreator()}.
* </p>
*
* @author Christian Dupuis
* @since 3.4
*/
public abstract class JFaceAbstractInformationControl implements IInformationControl, IInformationControlExtension, IInformationControlExtension3,
IInformationControlExtension4, JFaceIInformationControlExtension5 {
/** The information control's shell. */
private final Shell fShell;
/** Composite containing the content created by subclasses. */
private final Composite fContentComposite;
/** Whether the information control is resizable. */
private final boolean fResizable;
/**
* Composite containing the status line content or <code>null</code> if none.
*/
private Composite fStatusComposite;
/** Separator between content and status line or <code>null</code> if none. */
private Label fSeparator;
/** Label in the status line or <code>null</code> if none. */
private Label fStatusLabel;
/** The toolbar manager used by the toolbar or <code>null</code> if none. */
private final ToolBarManager fToolBarManager;
/** Status line toolbar or <code>null</code> if none. */
private ToolBar fToolBar;
/** Listener for shell activation and deactivation. */
private Listener fShellListener;
/** All focus listeners registered to this information control. */
private final ListenerList fFocusListeners = new ListenerList(ListenerList.IDENTITY);
/**
* Size constraints, x is the maxWidth and y is the maxHeight, or <code>null</code> if not set.
*/
private Point fSizeConstraints;
/** The size of the resize handle if already set, -1 otherwise */
private int fResizeHandleSize;
/**
* Creates an abstract information control with the given shell as parent. The control will not be resizable and
* optionally show a status line with the given status field text.
* <p>
* <em>Important: Subclasses are required to call {@link #create()} at the end of their constructor.</em>
* </p>
*
* @param parentShell the parent of this control's shell
* @param statusFieldText the text to be used in the status field or <code>null</code> to hide the status field
*/
public JFaceAbstractInformationControl(Shell parentShell, String statusFieldText) {
this(parentShell, SWT.TOOL | SWT.ON_TOP, statusFieldText, null);
}
/**
* Creates an abstract information control with the given shell as parent. The control will be resizable and
* optionally show a tool bar managed by the given tool bar manager.
* <p>
* <em>Important: Subclasses are required to call {@link #create()} at the end of their constructor.</em>
* </p>
*
* @param parentShell the parent of this control's shell
* @param toolBarManager the manager or <code>null</code> if toolbar is not desired
*/
public JFaceAbstractInformationControl(Shell parentShell, ToolBarManager toolBarManager) {
this(parentShell, SWT.TOOL | SWT.ON_TOP | SWT.RESIZE, null, toolBarManager);
}
/**
* Creates an abstract information control with the given shell as parent.
* <p>
* <em>Important: Subclasses are required to call {@link #create()} at the end of their constructor.</em>
* </p>
*
* @param parentShell the parent of this control's shell
* @param isResizable <code>true</code> if the control should be resizable
*/
public JFaceAbstractInformationControl(Shell parentShell, boolean isResizable) {
this(parentShell, SWT.TOOL | SWT.ON_TOP | (isResizable ? SWT.RESIZE : 0), null, null);
}
/**
* Creates an abstract information control with the given shell as parent. The given shell style is used for the
* shell (NO_TRIM will be removed to make sure there's a border).
* <p>
* The control will optionally show either a status line or a tool bar. At most one of <code>toolBarManager</code>
* or <code>statusFieldText</code> can be non-null.
* </p>
* <p>
* <strong>Important:</strong>: Subclasses are required to call {@link #create()} at the end of their constructor.
* </p>
*
* @param parentShell the parent of this control's shell
* @param shellStyle style of this control's shell
* @param statusFieldText the text to be used in the status field or <code>null</code> to hide the status field
* @param toolBarManager the manager or <code>null</code> if toolbar is not desired
* @deprecated clients should use one of the public constructors
*/
@Deprecated
JFaceAbstractInformationControl(Shell parentShell, int shellStyle, final String statusFieldText, final ToolBarManager toolBarManager) {
Assert.isTrue(statusFieldText == null || toolBarManager == null);
this.fResizeHandleSize = -1;
this.fToolBarManager = toolBarManager;
if ((shellStyle & SWT.NO_TRIM) != 0) {
shellStyle &= ~(SWT.NO_TRIM | SWT.SHELL_TRIM); // make sure we get
// the OS border but
// no other trims
}
this.fResizable = (shellStyle & SWT.RESIZE) != 0; // on GTK, Shell removes
// SWT.RESIZE if SWT.ON_TOP
// is set
this.fShell = new Shell(parentShell, shellStyle);
Display display = this.fShell.getDisplay();
Color foreground = display.getSystemColor(SWT.COLOR_INFO_FOREGROUND);
Color background = display.getSystemColor(SWT.COLOR_INFO_BACKGROUND);
setColor(this.fShell, foreground, background);
GridLayout layout = new GridLayout(1, false);
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.verticalSpacing = 0;
this.fShell.setLayout(layout);
this.fContentComposite = new Composite(this.fShell, SWT.NONE);
this.fContentComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
this.fContentComposite.setLayout(new FillLayout());
setColor(this.fContentComposite, foreground, background);
createStatusComposite(statusFieldText, toolBarManager, foreground, background);
}
private void createStatusComposite(final String statusFieldText, final ToolBarManager toolBarManager, Color foreground, Color background) {
if (toolBarManager == null && statusFieldText == null) {
return;
}
this.fStatusComposite = new Composite(this.fShell, SWT.NONE);
GridData gridData = new GridData(SWT.FILL, SWT.BOTTOM, true, false);
this.fStatusComposite.setLayoutData(gridData);
GridLayout statusLayout = new GridLayout(1, false);
statusLayout.marginHeight = 0;
statusLayout.marginWidth = 0;
statusLayout.verticalSpacing = 1;
this.fStatusComposite.setLayout(statusLayout);
this.fSeparator = new Label(this.fStatusComposite, SWT.SEPARATOR | SWT.HORIZONTAL);
this.fSeparator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
if (statusFieldText != null) {
createStatusLabel(statusFieldText, foreground, background);
} else {
createToolBar(toolBarManager);
}
}
private void createStatusLabel(final String statusFieldText, Color foreground, Color background) {
this.fStatusLabel = new Label(this.fStatusComposite, SWT.RIGHT);
this.fStatusLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
this.fStatusLabel.setText(statusFieldText);
FontData[] fontDatas = JFaceResources.getDialogFont().getFontData();
for (FontData element : fontDatas) {
element.setHeight(element.getHeight() * 9 / 10);
}
this.fStatusLabel.setFont(new Font(this.fStatusLabel.getDisplay(), fontDatas));
this.fStatusLabel.setForeground(this.fStatusLabel.getDisplay().getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW));
this.fStatusLabel.setBackground(background);
setColor(this.fStatusComposite, foreground, background);
}
private void createToolBar(ToolBarManager toolBarManager) {
final Composite bars = new Composite(this.fStatusComposite, SWT.NONE);
bars.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
GridLayout layout = new GridLayout(3, false);
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.horizontalSpacing = 0;
layout.verticalSpacing = 0;
bars.setLayout(layout);
this.fToolBar = toolBarManager.createControl(bars);
GridData gd = new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false);
this.fToolBar.setLayoutData(gd);
Composite spacer = new Composite(bars, SWT.NONE);
gd = new GridData(SWT.FILL, SWT.FILL, true, true);
gd.widthHint = 0;
gd.heightHint = 0;
spacer.setLayoutData(gd);
addMoveSupport(spacer);
addResizeSupportIfNecessary(bars);
}
private void addResizeSupportIfNecessary(final Composite bars) {
// XXX: workarounds for
// - https://bugs.eclipse.org/bugs/show_bug.cgi?id=219139 : API to add
// resize grip / grow box in lower right corner of shell
// - https://bugs.eclipse.org/bugs/show_bug.cgi?id=23980 : platform
// specific shell resize behavior
String platform = SWT.getPlatform();
final boolean isWin = platform.equals("win32"); //$NON-NLS-1$
if (!isWin && !platform.equals("gtk")) {
return;
}
final Canvas resizer = new Canvas(bars, SWT.NONE);
int size = getResizeHandleSize(bars);
GridData data = new GridData(SWT.END, SWT.END, false, true);
data.widthHint = size;
data.heightHint = size;
resizer.setLayoutData(data);
resizer.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
Point s = resizer.getSize();
int x = s.x - 2;
int y = s.y - 2;
int min = Math.min(x, y);
if (isWin) {
// draw dots
e.gc.setBackground(resizer.getDisplay().getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW));
int end = min - 1;
for (int i = 0; i <= 2; i++) {
for (int j = 0; j <= 2 - i; j++) {
e.gc.fillRectangle(end - 4 * i, end - 4 * j, 2, 2);
}
}
end--;
e.gc.setBackground(resizer.getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW));
for (int i = 0; i <= 2; i++) {
for (int j = 0; j <= 2 - i; j++) {
e.gc.fillRectangle(end - 4 * i, end - 4 * j, 2, 2);
}
}
} else {
// draw diagonal lines
e.gc.setForeground(resizer.getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW));
for (int i = 1; i < min; i += 4) {
e.gc.drawLine(i, y, x, i);
}
e.gc.setForeground(resizer.getDisplay().getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW));
for (int i = 2; i < min; i += 4) {
e.gc.drawLine(i, y, x, i);
}
}
}
});
resizer.setCursor(new Cursor(resizer.getDisplay(), SWT.CURSOR_SIZESE));
MouseAdapter resizeSupport = new MouseAdapter() {
private MouseMoveListener fResizeListener;
@Override
public void mouseDown(MouseEvent e) {
Point shellSize = JFaceAbstractInformationControl.this.fShell.getSize();
final int shellX = shellSize.x;
final int shellY = shellSize.y;
Point mouseLoc = resizer.toDisplay(e.x, e.y);
final int mouseX = mouseLoc.x;
final int mouseY = mouseLoc.y;
this.fResizeListener = new MouseMoveListener() {
public void mouseMove(MouseEvent e2) {
Point mouseLoc2 = resizer.toDisplay(e2.x, e2.y);
int dx = mouseLoc2.x - mouseX;
int dy = mouseLoc2.y - mouseY;
setSize(shellX + dx, shellY + dy);
}
};
resizer.addMouseMoveListener(this.fResizeListener);
}
@Override
public void mouseUp(MouseEvent e) {
resizer.removeMouseMoveListener(this.fResizeListener);
this.fResizeListener = null;
}
};
resizer.addMouseListener(resizeSupport);
}
private int getResizeHandleSize(Composite parent) {
if (this.fResizeHandleSize == -1) {
Slider sliderV = new Slider(parent, SWT.VERTICAL);
Slider sliderH = new Slider(parent, SWT.HORIZONTAL);
int width = sliderV.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
int height = sliderH.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
sliderV.dispose();
sliderH.dispose();
this.fResizeHandleSize = Math.min(width, height);
}
return this.fResizeHandleSize;
}
/**
* Adds support to move the shell by dragging the given control.
*
* @param control the control that can be used to move the shell
*/
private void addMoveSupport(final Control control) {
MouseAdapter moveSupport = new MouseAdapter() {
private MouseMoveListener fMoveListener;
@Override
public void mouseDown(MouseEvent e) {
Point shellLoc = JFaceAbstractInformationControl.this.fShell.getLocation();
final int shellX = shellLoc.x;
final int shellY = shellLoc.y;
Point mouseLoc = control.toDisplay(e.x, e.y);
final int mouseX = mouseLoc.x;
final int mouseY = mouseLoc.y;
this.fMoveListener = new MouseMoveListener() {
public void mouseMove(MouseEvent e2) {
Point mouseLoc2 = control.toDisplay(e2.x, e2.y);
int dx = mouseLoc2.x - mouseX;
int dy = mouseLoc2.y - mouseY;
JFaceAbstractInformationControl.this.fShell.setLocation(shellX + dx, shellY + dy);
}
};
control.addMouseMoveListener(this.fMoveListener);
}
@Override
public void mouseUp(MouseEvent e) {
control.removeMouseMoveListener(this.fMoveListener);
this.fMoveListener = null;
}
};
control.addMouseListener(moveSupport);
}
/**
* Utility to set the foreground and the background color of the given control
*
* @param control the control to modify
* @param foreground the color to use for the foreground
* @param background the color to use for the background
*/
private static void setColor(Control control, Color foreground, Color background) {
control.setForeground(foreground);
control.setBackground(background);
}
/**
* The shell of the popup window.
*
* @return the shell used for the popup window
*/
protected final Shell getShell() {
return this.fShell;
}
/**
* The toolbar manager used to manage the toolbar, or <code>null</code> if no toolbar is shown.
*
* @return the tool bar manager or <code>null</code>
*/
protected final ToolBarManager getToolBarManager() {
return this.fToolBarManager;
}
/**
* Creates the content of this information control. Subclasses must call this method at the end of their
* constructor(s).
*/
protected final void create() {
createContent(this.fContentComposite);
}
/**
* Creates the content of the popup window.
* <p>
* Implementors will usually take over {@link Composite#getBackground()} and {@link Composite#getForeground()} from
* <code>parent</code>.
* </p>
* <p>
* Implementors are expected to consider {@link #isResizable()}: If <code>true</code>, they should show scrollbars
* if their content may exceed the size of the information control. If <code>false</code>, they should never show
* scrollbars.
* </p>
* <p>
* The given <code>parent</code> comes with a {@link FillLayout}. Subclasses may set a different layout.
* </p>
*
* @param parent the container of the content
*/
protected abstract void createContent(Composite parent);
/**
* Sets the information to be presented by this information control.
* <p>
* The default implementation does nothing. Subclasses must either override this method or implement
* {@link IInformationControlExtension2}.
*
* @param information the information to be presented
* @see org.eclipse.jface.text.IInformationControl#setInformation(java.lang.String)
*/
public void setInformation(String information) {
}
/**
* Returns whether the information control is resizable.
*
* @return <code>true</code> if the information control is resizable, <code>false</code> if it is not resizable.
*/
public boolean isResizable() {
return this.fResizable;
}
/*
* @see IInformationControl#setVisible(boolean)
*/
public void setVisible(boolean visible) {
if (this.fShell.isVisible() == visible) {
return;
}
this.fShell.setVisible(visible);
}
/*
* @see IInformationControl#dispose()
*/
public void dispose() {
if (this.fShell != null && !this.fShell.isDisposed()) {
this.fShell.dispose();
}
}
/*
* @see IInformationControl#setSize(int, int)
*/
public void setSize(int width, int height) {
this.fShell.setSize(width, height);
}
/*
* @see IInformationControl#setLocation(Point)
*/
public void setLocation(Point location) {
this.fShell.setLocation(location);
}
/*
* @see IInformationControl#setSizeConstraints(int, int)
*/
public void setSizeConstraints(int maxWidth, int maxHeight) {
this.fSizeConstraints = new Point(maxWidth, maxHeight);
}
/**
* Returns the size constraints.
*
* @return the size constraints or <code>null</code> if not set
* @see #setSizeConstraints(int, int)
*/
protected final Point getSizeConstraints() {
return this.fSizeConstraints != null ? Geometry.copy(this.fSizeConstraints) : null;
}
/*
* @see IInformationControl#computeSizeHint()
*/
public Point computeSizeHint() {
// XXX: Verify whether this is a good default implementation. If yes,
// document it.
Point constrains = getSizeConstraints();
if (constrains == null) {
return this.fShell.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
}
return this.fShell.computeSize(constrains.x, constrains.y, true);
}
/**
* Computes the trim (status text and tool bar are considered as trim). Subclasses can extend this method to add
* additional trim (e.g. scroll bars for resizable information controls).
*
* @see org.eclipse.jface.text.IInformationControlExtension3#computeTrim()
*/
public Rectangle computeTrim() {
Rectangle trim = this.fShell.computeTrim(0, 0, 0, 0);
if (this.fStatusComposite != null) {
trim.height += this.fStatusComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
}
return trim;
}
/*
* @see org.eclipse.jface.text.IInformationControlExtension3#getBounds()
*/
public Rectangle getBounds() {
return this.fShell.getBounds();
}
/**
* {@inheritDoc}
* <p>
* The default implementation always returns <code>false</code>.
* </p>
*
* @see org.eclipse.jface.text.IInformationControlExtension3#restoresLocation()
*/
public boolean restoresLocation() {
return false;
}
/**
* {@inheritDoc}
* <p>
* The default implementation always returns <code>false</code>.
* </p>
*
* @see org.eclipse.jface.text.IInformationControlExtension3#restoresSize()
*/
public boolean restoresSize() {
return false;
}
/*
* @see IInformationControl#addDisposeListener(DisposeListener)
*/
public void addDisposeListener(DisposeListener listener) {
this.fShell.addDisposeListener(listener);
}
/*
* @see IInformationControl#removeDisposeListener(DisposeListener)
*/
public void removeDisposeListener(DisposeListener listener) {
this.fShell.removeDisposeListener(listener);
}
/*
* @see IInformationControl#setForegroundColor(Color)
*/
public void setForegroundColor(Color foreground) {
this.fContentComposite.setForeground(foreground);
}
/*
* @see IInformationControl#setBackgroundColor(Color)
*/
public void setBackgroundColor(Color background) {
this.fContentComposite.setBackground(background);
}
/**
* {@inheritDoc} This method is not intended to be overridden by subclasses.
*/
public boolean isFocusControl() {
return this.fShell.getDisplay().getActiveShell() == this.fShell;
}
/**
* This default implementation sets the focus on the popup shell. Subclasses can override or extend.
*
* @see IInformationControl#setFocus()
*/
public void setFocus() {
boolean focusTaken = this.fShell.setFocus();
if (!focusTaken) {
this.fShell.forceFocus();
}
}
/**
* {@inheritDoc} This method is not intended to be overridden by subclasses.
*/
public void addFocusListener(final FocusListener listener) {
if (this.fFocusListeners.isEmpty()) {
this.fShellListener = new Listener() {
public void handleEvent(Event event) {
Object[] listeners = JFaceAbstractInformationControl.this.fFocusListeners.getListeners();
for (Object element : listeners) {
FocusListener focusListener = (FocusListener) element;
if (event.type == SWT.Activate) {
focusListener.focusGained(new FocusEvent(event));
} else {
focusListener.focusLost(new FocusEvent(event));
}
}
}
};
this.fShell.addListener(SWT.Deactivate, this.fShellListener);
this.fShell.addListener(SWT.Activate, this.fShellListener);
}
this.fFocusListeners.add(listener);
}
/**
* {@inheritDoc} This method is not intended to be overridden by subclasses.
*/
public void removeFocusListener(FocusListener listener) {
this.fFocusListeners.remove(listener);
if (this.fFocusListeners.isEmpty()) {
this.fShell.removeListener(SWT.Activate, this.fShellListener);
this.fShell.removeListener(SWT.Deactivate, this.fShellListener);
this.fShellListener = null;
}
}
/**
* Sets the text of the status field.
* <p>
* The default implementation currently only updates the status field when the popup shell is not visible. The
* status field can currently only be shown if the information control has been created with a non-null status field
* text.
* </p>
*
* @param statusFieldText the text to be used in the optional status field or <code>null</code> if the status field
* should be hidden
* @see org.eclipse.jface.text.IInformationControlExtension4#setStatusText(java.lang.String)
*/
public void setStatusText(String statusFieldText) {
if (this.fStatusLabel != null && !getShell().isVisible()) {
if (statusFieldText == null) {
this.fStatusComposite.setVisible(false);
} else {
this.fStatusLabel.setText(statusFieldText);
this.fStatusComposite.setVisible(true);
}
}
}
/*
* @see org.eclipse.jface.text.IInformationControlExtension5#containsControl( org.eclipse.swt.widgets.Control)
*/
public boolean containsControl(Control control) {
do {
if (control == this.fShell) {
return true;
}
if (control instanceof Shell) {
return false;
}
control = control.getParent();
} while (control != null);
return false;
}
/*
* @see org.eclipse.jface.text.IInformationControlExtension5#isVisible()
*/
public boolean isVisible() {
return this.fShell != null && !this.fShell.isDisposed() && this.fShell.isVisible();
}
/**
* {@inheritDoc} This default implementation returns <code>null</code>. Subclasses may override.
*/
public IInformationControlCreator getInformationPresenterControlCreator() {
return null;
}
/**
* Computes the size constraints based on the {@link JFaceResources#getDialogFont() dialog font}. Subclasses can
* override or extend.
*
* @see org.eclipse.jface.text.IInformationControlExtension5#computeSizeConstraints(int, int)
*/
public Point computeSizeConstraints(int widthInChars, int heightInChars) {
GC gc = new GC(this.fContentComposite);
gc.setFont(JFaceResources.getDialogFont());
int width = gc.getFontMetrics().getAverageCharWidth();
int height = gc.getFontMetrics().getHeight();
gc.dispose();
return new Point(widthInChars * width, heightInChars * height);
}
}