view dwtx/jface/text/AbstractInformationControl.d @ 156:a9566845f1cb

...
author Frank Benoit <benoit@tionex.de>
date Mon, 25 Aug 2008 19:03:46 +0200
parents 75302ef3f92f
children f8d52b926852
line wrap: on
line source

/*******************************************************************************
 * 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 dwtx.jface.text.IDocumentPartitioningListener; // packageimport
import dwtx.jface.text.DefaultTextHover; // packageimport
import dwtx.jface.text.TextUtilities; // packageimport
import dwtx.jface.text.IInformationControlCreatorExtension; // packageimport
import dwtx.jface.text.AbstractInformationControlManager; // packageimport
import dwtx.jface.text.ITextViewerExtension2; // packageimport
import dwtx.jface.text.IDocumentPartitioner; // packageimport
import dwtx.jface.text.DefaultIndentLineAutoEditStrategy; // packageimport
import dwtx.jface.text.ITextSelection; // packageimport
import dwtx.jface.text.Document; // packageimport
import dwtx.jface.text.FindReplaceDocumentAdapterContentProposalProvider; // packageimport
import dwtx.jface.text.ITextListener; // packageimport
import dwtx.jface.text.BadPartitioningException; // packageimport
import dwtx.jface.text.ITextViewerExtension5; // packageimport
import dwtx.jface.text.IDocumentPartitionerExtension3; // packageimport
import dwtx.jface.text.IUndoManager; // packageimport
import dwtx.jface.text.ITextHoverExtension2; // packageimport
import dwtx.jface.text.IRepairableDocument; // packageimport
import dwtx.jface.text.IRewriteTarget; // packageimport
import dwtx.jface.text.DefaultPositionUpdater; // packageimport
import dwtx.jface.text.RewriteSessionEditProcessor; // packageimport
import dwtx.jface.text.TextViewerHoverManager; // packageimport
import dwtx.jface.text.DocumentRewriteSession; // packageimport
import dwtx.jface.text.TextViewer; // packageimport
import dwtx.jface.text.ITextViewerExtension8; // packageimport
import dwtx.jface.text.RegExMessages; // packageimport
import dwtx.jface.text.IDelayedInputChangeProvider; // packageimport
import dwtx.jface.text.ITextOperationTargetExtension; // packageimport
import dwtx.jface.text.IWidgetTokenOwner; // packageimport
import dwtx.jface.text.IViewportListener; // packageimport
import dwtx.jface.text.GapTextStore; // packageimport
import dwtx.jface.text.MarkSelection; // packageimport
import dwtx.jface.text.IDocumentPartitioningListenerExtension; // packageimport
import dwtx.jface.text.IDocumentAdapterExtension; // packageimport
import dwtx.jface.text.IInformationControlExtension; // packageimport
import dwtx.jface.text.IDocumentPartitioningListenerExtension2; // packageimport
import dwtx.jface.text.DefaultDocumentAdapter; // packageimport
import dwtx.jface.text.ITextViewerExtension3; // packageimport
import dwtx.jface.text.IInformationControlCreator; // packageimport
import dwtx.jface.text.TypedRegion; // packageimport
import dwtx.jface.text.ISynchronizable; // packageimport
import dwtx.jface.text.IMarkRegionTarget; // packageimport
import dwtx.jface.text.TextViewerUndoManager; // packageimport
import dwtx.jface.text.IRegion; // packageimport
import dwtx.jface.text.IInformationControlExtension2; // packageimport
import dwtx.jface.text.IDocumentExtension4; // packageimport
import dwtx.jface.text.IDocumentExtension2; // packageimport
import dwtx.jface.text.IDocumentPartitionerExtension2; // packageimport
import dwtx.jface.text.Assert; // packageimport
import dwtx.jface.text.DefaultInformationControl; // packageimport
import dwtx.jface.text.IWidgetTokenOwnerExtension; // packageimport
import dwtx.jface.text.DocumentClone; // packageimport
import dwtx.jface.text.DefaultUndoManager; // packageimport
import dwtx.jface.text.IFindReplaceTarget; // packageimport
import dwtx.jface.text.IAutoEditStrategy; // packageimport
import dwtx.jface.text.ILineTrackerExtension; // packageimport
import dwtx.jface.text.IUndoManagerExtension; // packageimport
import dwtx.jface.text.TextSelection; // packageimport
import dwtx.jface.text.DefaultAutoIndentStrategy; // packageimport
import dwtx.jface.text.IAutoIndentStrategy; // packageimport
import dwtx.jface.text.IPainter; // packageimport
import dwtx.jface.text.IInformationControl; // packageimport
import dwtx.jface.text.IInformationControlExtension3; // packageimport
import dwtx.jface.text.ITextViewerExtension6; // packageimport
import dwtx.jface.text.IInformationControlExtension4; // packageimport
import dwtx.jface.text.DefaultLineTracker; // packageimport
import dwtx.jface.text.IDocumentInformationMappingExtension; // packageimport
import dwtx.jface.text.IRepairableDocumentExtension; // packageimport
import dwtx.jface.text.ITextHover; // packageimport
import dwtx.jface.text.FindReplaceDocumentAdapter; // packageimport
import dwtx.jface.text.ILineTracker; // packageimport
import dwtx.jface.text.Line; // packageimport
import dwtx.jface.text.ITextViewerExtension; // packageimport
import dwtx.jface.text.IDocumentAdapter; // packageimport
import dwtx.jface.text.TextEvent; // packageimport
import dwtx.jface.text.BadLocationException; // packageimport
import dwtx.jface.text.AbstractDocument; // packageimport
import dwtx.jface.text.AbstractLineTracker; // packageimport
import dwtx.jface.text.TreeLineTracker; // packageimport
import dwtx.jface.text.ITextPresentationListener; // packageimport
import dwtx.jface.text.Region; // packageimport
import dwtx.jface.text.ITextViewer; // packageimport
import dwtx.jface.text.IDocumentInformationMapping; // packageimport
import dwtx.jface.text.MarginPainter; // packageimport
import dwtx.jface.text.IPaintPositionManager; // packageimport
import dwtx.jface.text.TextPresentation; // packageimport
import dwtx.jface.text.IFindReplaceTargetExtension; // packageimport
import dwtx.jface.text.ISlaveDocumentManagerExtension; // packageimport
import dwtx.jface.text.ISelectionValidator; // packageimport
import dwtx.jface.text.IDocumentExtension; // packageimport
import dwtx.jface.text.PropagatingFontFieldEditor; // packageimport
import dwtx.jface.text.ConfigurableLineTracker; // packageimport
import dwtx.jface.text.SlaveDocumentEvent; // packageimport
import dwtx.jface.text.IDocumentListener; // packageimport
import dwtx.jface.text.PaintManager; // packageimport
import dwtx.jface.text.IFindReplaceTargetExtension3; // packageimport
import dwtx.jface.text.ITextDoubleClickStrategy; // packageimport
import dwtx.jface.text.IDocumentExtension3; // packageimport
import dwtx.jface.text.Position; // packageimport
import dwtx.jface.text.TextMessages; // packageimport
import dwtx.jface.text.CopyOnWriteTextStore; // packageimport
import dwtx.jface.text.WhitespaceCharacterPainter; // packageimport
import dwtx.jface.text.IPositionUpdater; // packageimport
import dwtx.jface.text.DefaultTextDoubleClickStrategy; // packageimport
import dwtx.jface.text.ListLineTracker; // packageimport
import dwtx.jface.text.ITextInputListener; // packageimport
import dwtx.jface.text.BadPositionCategoryException; // packageimport
import dwtx.jface.text.IWidgetTokenKeeperExtension; // packageimport
import dwtx.jface.text.IInputChangedListener; // packageimport
import dwtx.jface.text.ITextOperationTarget; // packageimport
import dwtx.jface.text.IDocumentInformationMappingExtension2; // packageimport
import dwtx.jface.text.ITextViewerExtension7; // packageimport
import dwtx.jface.text.IInformationControlExtension5; // packageimport
import dwtx.jface.text.IDocumentRewriteSessionListener; // packageimport
import dwtx.jface.text.JFaceTextUtil; // packageimport
import dwtx.jface.text.AbstractReusableInformationControlCreator; // packageimport
import dwtx.jface.text.TabsToSpacesConverter; // packageimport
import dwtx.jface.text.CursorLinePainter; // packageimport
import dwtx.jface.text.ITextHoverExtension; // packageimport
import dwtx.jface.text.IEventConsumer; // packageimport
import dwtx.jface.text.IDocument; // packageimport
import dwtx.jface.text.IWidgetTokenKeeper; // packageimport
import dwtx.jface.text.DocumentCommand; // packageimport
import dwtx.jface.text.TypedPosition; // packageimport
import dwtx.jface.text.IEditingSupportRegistry; // packageimport
import dwtx.jface.text.IDocumentPartitionerExtension; // packageimport
import dwtx.jface.text.AbstractHoverInformationControlManager; // packageimport
import dwtx.jface.text.IEditingSupport; // packageimport
import dwtx.jface.text.IMarkSelection; // packageimport
import dwtx.jface.text.ISlaveDocumentManager; // packageimport
import dwtx.jface.text.DocumentEvent; // packageimport
import dwtx.jface.text.DocumentPartitioningChangedEvent; // packageimport
import dwtx.jface.text.ITextStore; // packageimport
import dwtx.jface.text.JFaceTextMessages; // packageimport
import dwtx.jface.text.DocumentRewriteSessionEvent; // packageimport
import dwtx.jface.text.SequentialRewriteTextStore; // packageimport
import dwtx.jface.text.DocumentRewriteSessionType; // packageimport
import dwtx.jface.text.TextAttribute; // packageimport
import dwtx.jface.text.ITextViewerExtension4; // packageimport
import dwtx.jface.text.ITypedRegion; // packageimport


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 const Shell fShell;
    /** Composite containing the content created by subclasses. */
    private const Composite fContentComposite;
    /** Whether the information control is resizable. */
    private const 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 const 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 this(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 this(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 this(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
     */
    this(Shell parentShell, int shellStyle, String statusFieldText, 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(String statusFieldText, 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(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) {
        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(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();
        bool isWin= platform.equals("win32"); //$NON-NLS-1$
        if (!isWin && !platform.equals("gtk")) //$NON-NLS-1$
            return;

        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 class(isWin,resizer)  PaintListener {
            bool isWin_;
            Canvas resizer_;
            this(bool a, Canvas b ){
                isWin_=a;
                resizer_=b;
            }
            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 class(resizer)  MouseAdapter {
            Canvas resizer_;
            this(Canvas a){
                resizer_=a;
            }
            private MouseMoveListener fResizeListener;

            public void mouseDown(MouseEvent e) {
                Point shellSize= fShell.getSize();
                int shellX= shellSize.x;
                int shellY= shellSize.y;
                Point mouseLoc= resizer_.toDisplay(e.x, e.y);
                int mouseX= mouseLoc.x;
                int mouseY= mouseLoc.y;
                fResizeListener= new class(shellX,shellY,mouseX,mouseY)  MouseMoveListener {
                    int shellX_;
                    int shellY_;
                    int mouseX_;
                    int mouseY_;
                    this(int a, int b, int c, int d ){
                        shellX_=a;
                        shellY_=b;
                        mouseX_=c;
                        mouseY_=d;
                    }
                    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(Control control) {
        MouseAdapter moveSupport= new class(control)  MouseAdapter {
            private MouseMoveListener fMoveListener;
            Control control_;
            this(Control a){
                control_=a;
            }
            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 class()  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(FocusListener listener) {
        if (fFocusListeners.isEmpty()) {
            fShellListener= new class()  Listener {

                public void handleEvent(Event event) {
                    Object[] listeners= fFocusListeners.getListeners();
                    for (int i= 0; i < listeners.length; i++) {
                        FocusListener focusListener= cast(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 (cast(Shell)control )
                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);
    }

}