diff dwtx/jface/text/AbstractInformationControl.d @ 129:eb30df5ca28b

Added JFace Text sources
author Frank Benoit <benoit@tionex.de>
date Sat, 23 Aug 2008 19:10:48 +0200
parents
children c4fb132a086c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/text/AbstractInformationControl.d	Sat Aug 23 19:10:48 2008 +0200
@@ -0,0 +1,736 @@
+/*******************************************************************************
+ * Copyright (c) 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 dwtx.jface.text.AbstractInformationControl;
+
+import dwt.dwthelper.utils;
+
+
+
+
+import dwt.DWT;
+import dwt.events.DisposeListener;
+import dwt.events.FocusEvent;
+import dwt.events.FocusListener;
+import dwt.events.MouseAdapter;
+import dwt.events.MouseEvent;
+import dwt.events.MouseMoveListener;
+import dwt.events.PaintEvent;
+import dwt.events.PaintListener;
+import dwt.graphics.Color;
+import dwt.graphics.Cursor;
+import dwt.graphics.Font;
+import dwt.graphics.FontData;
+import dwt.graphics.GC;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.layout.FillLayout;
+import dwt.layout.GridData;
+import dwt.layout.GridLayout;
+import dwt.widgets.Canvas;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwt.widgets.Display;
+import dwt.widgets.Event;
+import dwt.widgets.Label;
+import dwt.widgets.Listener;
+import dwt.widgets.Shell;
+import dwt.widgets.Slider;
+import dwt.widgets.ToolBar;
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.ListenerList;
+import dwtx.jface.action.ToolBarManager;
+import dwtx.jface.resource.JFaceResources;
+import dwtx.jface.util.Geometry;
+
+
+/**
+ * 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>
+ * 
+ * @since 3.4
+ */
+public abstract class AbstractInformationControl : IInformationControl, IInformationControlExtension, IInformationControlExtension3, IInformationControlExtension4, IInformationControlExtension5 {
+
+    /** 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 bool 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 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 AbstractInformationControl(Shell parentShell, String statusFieldText) {
+        this(parentShell, DWT.TOOL | DWT.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 AbstractInformationControl(Shell parentShell, ToolBarManager toolBarManager) {
+        this(parentShell, DWT.TOOL | DWT.ON_TOP | DWT.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 AbstractInformationControl(Shell parentShell, bool isResizable) {
+        this(parentShell, DWT.TOOL | DWT.ON_TOP | (isResizable ? DWT.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
+     */
+    AbstractInformationControl(Shell parentShell, int shellStyle, final String statusFieldText, final ToolBarManager toolBarManager) {
+        Assert.isTrue(statusFieldText is null || toolBarManager is null);
+        fResizeHandleSize= -1;
+        fToolBarManager= toolBarManager;
+        
+        if ((shellStyle & DWT.NO_TRIM) !is 0)
+            shellStyle&= ~(DWT.NO_TRIM | DWT.SHELL_TRIM); // make sure we get the OS border but no other trims
+        
+        fResizable= (shellStyle & DWT.RESIZE) !is 0; // on GTK, Shell removes DWT.RESIZE if DWT.ON_TOP is set
+        fShell= new Shell(parentShell, shellStyle);
+        Display display= fShell.getDisplay();
+        Color foreground= display.getSystemColor(DWT.COLOR_INFO_FOREGROUND);
+        Color background= display.getSystemColor(DWT.COLOR_INFO_BACKGROUND);
+        setColor(fShell, foreground, background);
+        
+        GridLayout layout= new GridLayout(1, false);
+        layout.marginHeight= 0;
+        layout.marginWidth= 0;
+        layout.verticalSpacing= 0;
+        fShell.setLayout(layout);
+        
+        fContentComposite= new Composite(fShell, DWT.NONE);
+        fContentComposite.setLayoutData(new GridData(DWT.FILL, DWT.FILL, true, true));
+        fContentComposite.setLayout(new FillLayout());
+        setColor(fContentComposite, foreground, background);
+        
+        createStatusComposite(statusFieldText, toolBarManager, foreground, background);
+    }
+
+    private void createStatusComposite(final String statusFieldText, final ToolBarManager toolBarManager, Color foreground, Color background) {
+        if (toolBarManager is null && statusFieldText is null)
+            return;
+        
+        fStatusComposite= new Composite(fShell, DWT.NONE);
+        GridData gridData= new GridData(DWT.FILL, DWT.BOTTOM, true, false);
+        fStatusComposite.setLayoutData(gridData);
+        GridLayout statusLayout= new GridLayout(1, false);
+        statusLayout.marginHeight= 0;
+        statusLayout.marginWidth= 0;
+        statusLayout.verticalSpacing= 1;
+        fStatusComposite.setLayout(statusLayout);
+        
+        fSeparator= new Label(fStatusComposite, DWT.SEPARATOR | DWT.HORIZONTAL);
+        fSeparator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+        
+        if (statusFieldText !is null) {
+            createStatusLabel(statusFieldText, foreground, background);
+        } else {
+            createToolBar(toolBarManager);
+        }
+    }
+
+    private void createStatusLabel(final String statusFieldText, Color foreground, Color background) {
+        fStatusLabel= new Label(fStatusComposite, DWT.RIGHT);
+        fStatusLabel.setLayoutData(new GridData(DWT.FILL, DWT.CENTER, true, false));
+        fStatusLabel.setText(statusFieldText);
+        
+        FontData[] fontDatas= JFaceResources.getDialogFont().getFontData();
+        for (int i= 0; i < fontDatas.length; i++) {
+            fontDatas[i].setHeight(fontDatas[i].getHeight() * 9 / 10);
+        }
+        fStatusLabel.setFont(new Font(fStatusLabel.getDisplay(), fontDatas));
+        
+        fStatusLabel.setForeground(fStatusLabel.getDisplay().getSystemColor(DWT.COLOR_WIDGET_DARK_SHADOW));
+        fStatusLabel.setBackground(background);
+        setColor(fStatusComposite, foreground, background);
+    }
+    
+    private void createToolBar(ToolBarManager toolBarManager) {
+        final Composite bars= new Composite(fStatusComposite, DWT.NONE);
+        bars.setLayoutData(new GridData(DWT.FILL, DWT.FILL, false, false));
+        
+        GridLayout layout= new GridLayout(3, false);
+        layout.marginHeight= 0;
+        layout.marginWidth= 0;
+        layout.horizontalSpacing= 0;
+        layout.verticalSpacing= 0;
+        bars.setLayout(layout);
+        
+        fToolBar= toolBarManager.createControl(bars);
+        GridData gd= new GridData(DWT.BEGINNING, DWT.BEGINNING, false, false);
+        fToolBar.setLayoutData(gd);
+        
+        Composite spacer= new Composite(bars, DWT.NONE);
+        gd= new GridData(DWT.FILL, DWT.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= DWT.getPlatform();
+        final bool isWin= platform.equals("win32"); //$NON-NLS-1$
+        if (!isWin && !platform.equals("gtk")) //$NON-NLS-1$
+            return;
+        
+        final Canvas resizer= new Canvas(bars, DWT.NONE);
+        
+        int size= getResizeHandleSize(bars);
+        
+        GridData data= new GridData(DWT.END, DWT.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(DWT.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(DWT.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(DWT.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(DWT.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(), DWT.CURSOR_SIZESE));
+        MouseAdapter resizeSupport= new MouseAdapter() {
+            private MouseMoveListener fResizeListener;
+            
+            public void mouseDown(MouseEvent e) {
+                Point shellSize= 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;
+                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(fResizeListener);
+            }
+            
+            public void mouseUp(MouseEvent e) {
+                resizer.removeMouseMoveListener(fResizeListener);
+                fResizeListener= null;
+            }
+        };
+        resizer.addMouseListener(resizeSupport);
+    }
+
+    private int getResizeHandleSize(Composite parent) {
+        if (fResizeHandleSize is -1) {
+            Slider sliderV= new Slider(parent, DWT.VERTICAL);
+            Slider sliderH= new Slider(parent, DWT.HORIZONTAL);
+            int width= sliderV.computeSize(DWT.DEFAULT, DWT.DEFAULT).x;
+            int height= sliderH.computeSize(DWT.DEFAULT, DWT.DEFAULT).y;
+            sliderV.dispose();
+            sliderH.dispose();
+            fResizeHandleSize= Math.min(width, height);
+        }
+        
+        return 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;
+
+            public void mouseDown(MouseEvent e) {
+                Point shellLoc= 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;
+                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;
+                        fShell.setLocation(shellX + dx, shellY + dy);
+                    }
+                };
+                control.addMouseMoveListener(fMoveListener);
+            }
+
+            public void mouseUp(MouseEvent e) {
+                control.removeMouseMoveListener(fMoveListener);
+                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 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 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(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 dwtx.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 bool isResizable() {
+        return fResizable;
+    }
+
+    /*
+     * @see IInformationControl#setVisible(bool)
+     */
+    public void setVisible(bool visible) {
+        if (fShell.isVisible() is visible)
+            return;
+        
+        fShell.setVisible(visible);
+    }
+
+    /*
+     * @see IInformationControl#dispose()
+     */
+    public void dispose() {
+        if (fShell !is null && !fShell.isDisposed())
+            fShell.dispose();
+    }
+
+    /*
+     * @see IInformationControl#setSize(int, int)
+     */
+    public void setSize(int width, int height) {
+        fShell.setSize(width, height);
+    }
+
+    /*
+     * @see IInformationControl#setLocation(Point)
+     */
+    public void setLocation(Point location) {
+        fShell.setLocation(location);
+    }
+
+    /*
+     * @see IInformationControl#setSizeConstraints(int, int)
+     */
+    public void setSizeConstraints(int maxWidth, int maxHeight) {
+        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 fSizeConstraints !is null ? Geometry.copy(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 is null)
+            return fShell.computeSize(DWT.DEFAULT, DWT.DEFAULT, true);
+        
+        return 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 dwtx.jface.text.IInformationControlExtension3#computeTrim()
+     */
+    public Rectangle computeTrim() {
+        Rectangle trim= fShell.computeTrim(0, 0, 0, 0);
+
+        if (fStatusComposite !is null)
+            trim.height+= fStatusComposite.computeSize(DWT.DEFAULT, DWT.DEFAULT).y;
+
+        return trim;
+    }
+
+    /*
+     * @see dwtx.jface.text.IInformationControlExtension3#getBounds()
+     */
+    public Rectangle getBounds() {
+        return fShell.getBounds();
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * The default implementation always returns <code>false</code>.
+     * </p>
+     * @see dwtx.jface.text.IInformationControlExtension3#restoresLocation()
+     */
+    public bool restoresLocation() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * The default implementation always returns <code>false</code>.
+     * </p>
+     * @see dwtx.jface.text.IInformationControlExtension3#restoresSize()
+     */
+    public bool restoresSize() {
+        return false;
+    }
+
+    /*
+     * @see IInformationControl#addDisposeListener(DisposeListener)
+     */
+    public void addDisposeListener(DisposeListener listener) {
+        fShell.addDisposeListener(listener);
+    }
+
+    /*
+     * @see IInformationControl#removeDisposeListener(DisposeListener)
+     */
+    public void removeDisposeListener(DisposeListener listener) {
+        fShell.removeDisposeListener(listener);
+    }
+
+    /*
+     * @see IInformationControl#setForegroundColor(Color)
+     */
+    public void setForegroundColor(Color foreground) {
+        fContentComposite.setForeground(foreground);
+    }
+
+    /*
+     * @see IInformationControl#setBackgroundColor(Color)
+     */
+    public void setBackgroundColor(Color background) {
+        fContentComposite.setBackground(background);
+    }
+
+    /**
+     * {@inheritDoc}
+     * This method is not intended to be overridden by subclasses.
+     */
+    public bool isFocusControl() {
+        return fShell.getDisplay().getActiveShell() is fShell;
+    }
+
+    /**
+     * This default implementation sets the focus on the popup shell.
+     * Subclasses can override or extend.
+     * 
+     * @see IInformationControl#setFocus()
+     */
+    public void setFocus() {
+        bool focusTaken= fShell.setFocus();
+        if (!focusTaken)
+            fShell.forceFocus();
+    }
+
+    /**
+     * {@inheritDoc}
+     * This method is not intended to be overridden by subclasses.
+     */
+    public void addFocusListener(final FocusListener listener) {
+        if (fFocusListeners.isEmpty()) {
+            fShellListener= new Listener() {
+
+                public void handleEvent(Event event) {
+                    Object[] listeners= fFocusListeners.getListeners();
+                    for (int i= 0; i < listeners.length; i++) {
+                        FocusListener focusListener= (FocusListener)listeners[i];
+                        if (event.type is DWT.Activate) {
+                            focusListener.focusGained(new FocusEvent(event));
+                        } else {
+                            focusListener.focusLost(new FocusEvent(event));
+                        }
+                    }
+                }
+            };
+            fShell.addListener(DWT.Deactivate, fShellListener);
+            fShell.addListener(DWT.Activate, fShellListener);
+        }
+        fFocusListeners.add(listener);
+    }
+
+    /**
+     * {@inheritDoc}
+     * This method is not intended to be overridden by subclasses.
+     */
+    public void removeFocusListener(FocusListener listener) {
+        fFocusListeners.remove(listener);
+        if (fFocusListeners.isEmpty()) {
+            fShell.removeListener(DWT.Activate, fShellListener);
+            fShell.removeListener(DWT.Deactivate, fShellListener);
+            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 dwtx.jface.text.IInformationControlExtension4#setStatusText(java.lang.String)
+     */
+    public void setStatusText(String statusFieldText) {
+        if (fStatusLabel !is null && ! getShell().isVisible()) {
+            if (statusFieldText is null ) {
+                fStatusComposite.setVisible(false);
+            } else {
+                fStatusLabel.setText(statusFieldText);
+                fStatusComposite.setVisible(true);
+            }
+        }
+    }
+    
+    /*
+     * @see dwtx.jface.text.IInformationControlExtension5#containsControl(dwt.widgets.Control)
+     */
+    public bool containsControl(Control control) {
+        do {
+            if (control is fShell)
+                return true;
+            if (control instanceof Shell)
+                return false;
+            control= control.getParent();
+        } while (control !is null);
+        return false;
+    }
+
+    /*
+     * @see dwtx.jface.text.IInformationControlExtension5#isVisible()
+     */
+    public bool isVisible() {
+        return fShell !is null && !fShell.isDisposed() && 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 dwtx.jface.text.IInformationControlExtension5#computeSizeConstraints(int, int)
+     */
+    public Point computeSizeConstraints(int widthInChars, int heightInChars) {
+        GC gc= new GC(fContentComposite);
+        gc.setFont(JFaceResources.getDialogFont());
+        int width= gc.getFontMetrics().getAverageCharWidth();
+        int height= gc.getFontMetrics().getHeight();
+        gc.dispose();
+
+        return new Point(widthInChars * width, heightInChars * height);
+    }
+    
+}