view dwtx/jface/internal/text/StickyHoverManager.d @ 143:53b889547456

instanceof after &&
author Frank Benoit <benoit@tionex.de>
date Sun, 24 Aug 2008 21:32:37 +0200
parents 26688fec6d23
children 75302ef3f92f
line wrap: on
line source

/*******************************************************************************
 * Copyright (c) 2007, 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.internal.text.StickyHoverManager;

import dwtx.jface.internal.text.NonDeletingPositionUpdater; // packageimport
import dwtx.jface.internal.text.InternalAccessor; // packageimport
import dwtx.jface.internal.text.InformationControlReplacer; // packageimport
import dwtx.jface.internal.text.TableOwnerDrawSupport; // packageimport
import dwtx.jface.internal.text.DelayedInputChangeListener; // packageimport


import dwt.dwthelper.utils;



import dwt.DWT;
import dwt.events.ControlEvent;
import dwt.events.ControlListener;
import dwt.events.FocusEvent;
import dwt.events.FocusListener;
import dwt.events.KeyEvent;
import dwt.events.KeyListener;
import dwt.events.MouseEvent;
import dwt.events.MouseListener;
import dwt.graphics.Point;
import dwt.graphics.Rectangle;
import dwt.widgets.Control;
import dwt.widgets.Display;
import dwt.widgets.Event;
import dwt.widgets.Listener;
import dwtx.jface.text.IInformationControl;
import dwtx.jface.text.IInformationControlExtension3;
import dwtx.jface.text.IInformationControlExtension5;
import dwtx.jface.text.IViewportListener;
import dwtx.jface.text.IWidgetTokenKeeper;
import dwtx.jface.text.IWidgetTokenKeeperExtension;
import dwtx.jface.text.IWidgetTokenOwner;
import dwtx.jface.text.TextViewer;
import dwtx.jface.util.Geometry;


/**
 * Implements a sticky hover control, i.e. a control that replaces a hover
 * with an enriched and focusable control.
 * <p>
 * The information control is made visible on request by calling
 * {@link #showInformationControl(Rectangle)}.
 * </p>
 * <p>
 * Clients usually instantiate and configure this class before using it. The configuration
 * must be consistent: This means the used {@link dwtx.jface.text.IInformationControlCreator}
 * must create an information control expecting information in the same format the configured
 * {@link dwtx.jface.text.information.IInformationProvider}s use to encode the information they provide.
 * </p>
 *
 * @since 3.4
 */
public class StickyHoverManager : InformationControlReplacer , IWidgetTokenKeeper, IWidgetTokenKeeperExtension {

    /**
     * Priority of the info controls managed by this sticky hover manager.
     * <p>
     * Note: Only applicable when info control does not have focus.
     * -5 as value has been chosen in order to be beaten by the hovers of TextViewerHoverManager.
     * </p>
     */
    private static final int WIDGET_PRIORITY= -5;


    /**
     * Internal information control closer. Listens to several events issued by its subject control
     * and closes the information control when necessary.
     */
    class Closer : IInformationControlCloser, ControlListener, MouseListener, IViewportListener, KeyListener, FocusListener, Listener {
        //TODO: Catch 'Esc' key in fInformationControlToClose: Don't dispose, just hideInformationControl().
        // This would allow to reuse the information control also when the user explicitly closes it.
        
        //TODO: if subject control is a Scrollable, should add selection listeners to both scroll bars
        // (and remove the ViewPortListener, which only listens to vertical scrolling)

        /** The subject control. */
        private Control fSubjectControl;
        /** Indicates whether this closer is active. */
        private bool fIsActive= false;
        /** The display. */
        private Display fDisplay;

        /*
         * @see IInformationControlCloser#setSubjectControl(Control)
         */
        public void setSubjectControl(Control control) {
            fSubjectControl= control;
        }

        /*
         * @see IInformationControlCloser#setInformationControl(IInformationControl)
         */
        public void setInformationControl(IInformationControl control) {
            // NOTE: we use getCurrentInformationControl2() from the outer class
        }

        /*
         * @see IInformationControlCloser#start(Rectangle)
         */
        public void start(Rectangle informationArea) {

            if (fIsActive)
                return;
            fIsActive= true;

            if (fSubjectControl !is null && !fSubjectControl.isDisposed()) {
                fSubjectControl.addControlListener(this);
                fSubjectControl.addMouseListener(this);
                fSubjectControl.addKeyListener(this);
            }

            fTextViewer.addViewportListener(this);
            
            IInformationControl fInformationControlToClose= getCurrentInformationControl2();
            if (fInformationControlToClose !is null)
                fInformationControlToClose.addFocusListener(this);

            fDisplay= fSubjectControl.getDisplay();
            if (!fDisplay.isDisposed()) {
                fDisplay.addFilter(DWT.MouseMove, this);
                fDisplay.addFilter(DWT.FocusOut, this);
            }
        }

        /*
         * @see IInformationControlCloser#stop()
         */
        public void stop() {

            if (!fIsActive)
                return;
            fIsActive= false;

            fTextViewer.removeViewportListener(this);

            if (fSubjectControl !is null && !fSubjectControl.isDisposed()) {
                fSubjectControl.removeControlListener(this);
                fSubjectControl.removeMouseListener(this);
                fSubjectControl.removeKeyListener(this);
            }
            
            IInformationControl fInformationControlToClose= getCurrentInformationControl2();
            if (fInformationControlToClose !is null)
                fInformationControlToClose.removeFocusListener(this);
            
            if (fDisplay !is null && !fDisplay.isDisposed()) {
                fDisplay.removeFilter(DWT.MouseMove, this);
                fDisplay.removeFilter(DWT.FocusOut, this);
            }

            fDisplay= null;
        }

        /*
         * @see ControlListener#controlResized(ControlEvent)
         */
         public void controlResized(ControlEvent e) {
             hideInformationControl();
        }

        /*
         * @see ControlListener#controlMoved(ControlEvent)
         */
         public void controlMoved(ControlEvent e) {
             hideInformationControl();
        }

        /*
         * @see MouseListener#mouseDown(MouseEvent)
         */
         public void mouseDown(MouseEvent e) {
             hideInformationControl();
        }

        /*
         * @see MouseListener#mouseUp(MouseEvent)
         */
        public void mouseUp(MouseEvent e) {
        }

        /*
         * @see MouseListener#mouseDoubleClick(MouseEvent)
         */
        public void mouseDoubleClick(MouseEvent e) {
            hideInformationControl();
        }

        /*
         * @see IViewportListenerListener#viewportChanged(int)
         */
        public void viewportChanged(int topIndex) {
            hideInformationControl();
        }

        /*
         * @see KeyListener#keyPressed(KeyEvent)
         */
        public void keyPressed(KeyEvent e) {
            hideInformationControl();
        }

        /*
         * @see KeyListener#keyReleased(KeyEvent)
         */
        public void keyReleased(KeyEvent e) {
        }
        
        /*
         * @see dwt.events.FocusListener#focusGained(dwt.events.FocusEvent)
         */
        public void focusGained(FocusEvent e) {
        }
        
        /*
         * @see dwt.events.FocusListener#focusLost(dwt.events.FocusEvent)
         */
        public void focusLost(FocusEvent e) {
            if cast(DEBUG) System.out_.println("StickyHoverManager.Closer.focusLost(): " + e); //$NON-NLS-1$
            Display d= fSubjectControl.getDisplay();
            d.asyncExec(new class()  Runnable {
                // Without the asyncExec, mouse clicks to the workbench window are swallowed.
                public void run() {
                    hideInformationControl();
                }
            });
        }
        
        /*
         * @see dwt.widgets.Listener#handleEvent(dwt.widgets.Event)
         */
        public void handleEvent(Event event) {
            if (event.type is DWT.MouseMove) {
                if (!(event.widget instanceof Control) || event.widget.isDisposed())
                    return;
                
                IInformationControl infoControl= getCurrentInformationControl2();
                if (infoControl !is null && !infoControl.isFocusControl() && cast(IInformationControlExtension3)infoControl ) {
//                  if cast(DEBUG) System.out_.println("StickyHoverManager.Closer.handleEvent(): activeShell= " + fDisplay.getActiveShell()); //$NON-NLS-1$
                    IInformationControlExtension3 iControl3= cast(IInformationControlExtension3) infoControl;
                    Rectangle controlBounds= iControl3.getBounds();
                    if (controlBounds !is null) {
                        Point mouseLoc= event.display.map(cast(Control) event.widget, null, event.x, event.y);
                        int margin= getKeepUpMargin();
                        Geometry.expand(controlBounds, margin, margin, margin, margin);
                        if (!controlBounds.contains(mouseLoc)) {
                            hideInformationControl();
                        }
                    }
                    
                } else {
                    /*
                     * TODO: need better understanding of why/if this is needed.
                     * Looks like the same panic code we have in dwtx.jface.text.AbstractHoverInformationControlManager.Closer.handleMouseMove(Event)
                     */
                    if (fDisplay !is null && !fDisplay.isDisposed())
                        fDisplay.removeFilter(DWT.MouseMove, this);
                }
                
            } else if (event.type is DWT.FocusOut) {
                if cast(DEBUG) System.out_.println("StickyHoverManager.Closer.handleEvent(): focusOut: " + event); //$NON-NLS-1$
                IInformationControl iControl= getCurrentInformationControl2();
                if (iControl !is null && ! iControl.isFocusControl())
                    hideInformationControl();
            }
        }
    }

    
    private final TextViewer fTextViewer;

    
    /**
     * Creates a new sticky hover manager.
     * 
     * @param textViewer the text viewer
     */
    public this(TextViewer textViewer) {
        super(new DefaultInformationControlCreator());
        
        fTextViewer= textViewer;
        setCloser(new Closer());
        
        install(fTextViewer.getTextWidget());
    }

    /*
     * @see AbstractInformationControlManager#showInformationControl(Rectangle)
     */
    protected void showInformationControl(Rectangle subjectArea) {
        if (fTextViewer !is null && fTextViewer.requestWidgetToken(this, WIDGET_PRIORITY))
            super.showInformationControl(subjectArea);
        else
            if cast(DEBUG)
                System.out_.println("cancelled StickyHoverManager.showInformationControl(..): did not get widget token (with prio)"); //$NON-NLS-1$
    }

    /*
     * @see AbstractInformationControlManager#hideInformationControl()
     */
    public void hideInformationControl() {
        try {
            super.hideInformationControl();
        } finally {
            if (fTextViewer !is null)
                fTextViewer.releaseWidgetToken(this);
        }
    }

    /*
     * @see AbstractInformationControlManager#handleInformationControlDisposed()
     */
    protected void handleInformationControlDisposed() {
        try {
            super.handleInformationControlDisposed();
        } finally {
            if (fTextViewer !is null)
                fTextViewer.releaseWidgetToken(this);
        }
    }

    /*
     * @see dwtx.jface.text.IWidgetTokenKeeper#requestWidgetToken(IWidgetTokenOwner)
     */
    public bool requestWidgetToken(IWidgetTokenOwner owner) {
        hideInformationControl();
        if cast(DEBUG)
            System.out_.println("StickyHoverManager gave up widget token (no prio)"); //$NON-NLS-1$
        return true;
    }

    /*
     * @see dwtx.jface.text.IWidgetTokenKeeperExtension#requestWidgetToken(dwtx.jface.text.IWidgetTokenOwner, int)
     */
    public bool requestWidgetToken(IWidgetTokenOwner owner, int priority) {
        if (getCurrentInformationControl2() !is null) {
            if (getCurrentInformationControl2().isFocusControl()) {
                if cast(DEBUG)
                    System.out_.println("StickyHoverManager kept widget token (focused)"); //$NON-NLS-1$
                return false;
            } else if (priority > WIDGET_PRIORITY) {
                hideInformationControl();
                if cast(DEBUG)
                    System.out_.println("StickyHoverManager gave up widget token (prio)"); //$NON-NLS-1$
                return true;
            } else {
                if cast(DEBUG)
                    System.out_.println("StickyHoverManager kept widget token (prio)"); //$NON-NLS-1$
                return false;
            }
        }
        if cast(DEBUG)
            System.out_.println("StickyHoverManager gave up widget token (no iControl)"); //$NON-NLS-1$
        return true;
    }

    /*
     * @see dwtx.jface.text.IWidgetTokenKeeperExtension#setFocus(dwtx.jface.text.IWidgetTokenOwner)
     */
    public bool setFocus(IWidgetTokenOwner owner) {
        IInformationControl iControl= getCurrentInformationControl2();
        if ( cast(IInformationControlExtension5)iControl ) {
            IInformationControlExtension5 iControl5= cast(IInformationControlExtension5) iControl;
            if (iControl5.isVisible()) {
                iControl.setFocus();
                return iControl.isFocusControl();
            }
            return false;
        }
        iControl.setFocus();
        return iControl.isFocusControl();
    }
    
}