Mercurial > projects > dwt-addons
view dwtx/jface/window/ToolTip.d @ 192:c3583c6ec027
Added missing default cases for switch statements
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Mon, 03 Nov 2008 22:52:26 +0100 |
parents | 862b05e0334a |
children |
line wrap: on
line source
/******************************************************************************* * Copyright (c) 2006, 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: * Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation * bugfix in: 195137, 198089, 225190 * Port to the D programming language: * Frank Benoit <benoit@tionex.de> *******************************************************************************/ module dwtx.jface.window.ToolTip; import tango.util.log.Trace; import dwt.DWT; import dwt.events.DisposeEvent; import dwt.events.DisposeListener; import dwt.graphics.Point; import dwt.graphics.Rectangle; import dwt.layout.FillLayout; import dwt.widgets.Composite; import dwt.widgets.Control; import dwt.widgets.Display; import dwt.widgets.Event; import dwt.widgets.Listener; import dwt.widgets.Monitor; import dwt.widgets.Shell; // import dwtx.jface.viewers.ColumnViewer; // import dwtx.jface.viewers.ViewerCell; import dwt.dwthelper.utils; import dwtx.dwtxhelper.Collection; import dwt.dwthelper.Runnable; import dwtx.dwtxhelper.JThread; /** * This class gives implementors to provide customized tooltips for any control. * * @since 3.3 */ public abstract class ToolTip { private Control control; private int xShift = 3; private int yShift = 0; private int popupDelay = 0; private int hideDelay = 0; private ToolTipOwnerControlListener listener; private HashMap data; // Ensure that only one tooltip is active in time private static Shell CURRENT_TOOLTIP; /** * Recreate the tooltip on every mouse move */ public static const int RECREATE = 1; /** * Don't recreate the tooltip as long the mouse doesn't leave the area * triggering the tooltip creation */ public static const int NO_RECREATE = 1 << 1; private TooltipHideListener hideListener; private Listener shellListener; private bool hideOnMouseDown = true; private bool respectDisplayBounds = true; private bool respectMonitorBounds = true; private int style; private Object currentArea; private static final bool IS_OSX = DWT.getPlatform().equals("carbon"); //$NON-NLS-1$ /** * Create new instance which add TooltipSupport to the widget * * @param control * the control on whose action the tooltip is shown */ public this(Control control) { this(control, RECREATE, false); } /** * @param control * the control to which the tooltip is bound * @param style * style passed to control tooltip behavior * * @param manualActivation * <code>true</code> if the activation is done manually using * {@link #show(Point)} * @see #RECREATE * @see #NO_RECREATE */ public this(Control control, int style, bool manualActivation) { this.control = control; this.style = style; this.hideListener = new TooltipHideListener(); this.control.addDisposeListener(new class DisposeListener { public void widgetDisposed(DisposeEvent e) { this.outer.data = null; this.outer.deactivate(); } }); this.listener = new ToolTipOwnerControlListener(); this.shellListener = dgListener( (Event event_, Control control_, ToolTip tooltip_) { if( control_ !is null && ! control_.isDisposed() ) { control_.getDisplay().asyncExec( dgRunnable( (Event event__, Control control__, ToolTip tooltip__){ // Check if the new active shell is the tooltip // itself if( control__.getDisplay().getActiveShell() !is CURRENT_TOOLTIP) { tooltip__.toolTipHide(CURRENT_TOOLTIP, event__); } }, event_, control_, tooltip_)); } }, control, this); if (!manualActivation) { activate(); } } /** * Restore arbitrary data under the given key * * @param key * the key * @param value * the value */ public void setData(String key, Object value) { if (data is null) { data = new HashMap(); } data.put(stringcast(key), value); } /** * Get the data restored under the key * * @param key * the key * @return data or <code>null</code> if no entry is restored under the key */ public Object getData(String key) { if (data !is null) { return data.get(stringcast(key)); } return null; } /** * Set the shift (from the mouse position triggered the event) used to * display the tooltip. * <p> * By default the tooltip is shifted 3 pixels to the right. * </p> * * @param p * the new shift */ public void setShift(Point p) { xShift = p.x; yShift = p.y; } /** * Activate tooltip support for this control */ public void activate() { deactivate(); control.addListener(DWT.Dispose, listener); control.addListener(DWT.MouseHover, listener); control.addListener(DWT.MouseMove, listener); control.addListener(DWT.MouseExit, listener); control.addListener(DWT.MouseDown, listener); control.addListener(DWT.MouseWheel, listener); } /** * Deactivate tooltip support for the underlying control */ public void deactivate() { control.removeListener(DWT.Dispose, listener); control.removeListener(DWT.MouseHover, listener); control.removeListener(DWT.MouseMove, listener); control.removeListener(DWT.MouseExit, listener); control.removeListener(DWT.MouseDown, listener); control.removeListener(DWT.MouseWheel, listener); } /** * Return whether the tooltip respects bounds of the display. * * @return <code>true</code> if the tooltip respects bounds of the display */ public bool isRespectDisplayBounds() { return respectDisplayBounds; } /** * Set to <code>false</code> if display bounds should not be respected or * to <code>true</code> if the tooltip is should repositioned to not * overlap the display bounds. * <p> * Default is <code>true</code> * </p> * * @param respectDisplayBounds */ public void setRespectDisplayBounds(bool respectDisplayBounds) { this.respectDisplayBounds = respectDisplayBounds; } /** * Return whether the tooltip respects bounds of the monitor. * * @return <code>true</code> if tooltip respects the bounds of the monitor */ public bool isRespectMonitorBounds() { return respectMonitorBounds; } /** * Set to <code>false</code> if monitor bounds should not be respected or * to <code>true</code> if the tooltip is should repositioned to not * overlap the monitors bounds. The monitor the tooltip belongs to is the * same is control's monitor the tooltip is shown for. * <p> * Default is <code>true</code> * </p> * * @param respectMonitorBounds */ public void setRespectMonitorBounds(bool respectMonitorBounds) { this.respectMonitorBounds = respectMonitorBounds; } /** * Should the tooltip displayed because of the given event. * <p> * <b>Subclasses may overwrite this to get custom behavior</b> * </p> * * @param event * the event * @return <code>true</code> if tooltip should be displayed */ protected bool shouldCreateToolTip(Event event) { if ((style & NO_RECREATE) !is 0) { Object tmp = getToolTipArea(event); // No new area close the current tooltip if (tmp is null) { hide(); return false; } bool rv = !tmp.opEquals(currentArea); return rv; } return true; } /** * This method is called before the tooltip is hidden * * @param event * the event trying to hide the tooltip * @return <code>true</code> if the tooltip should be hidden */ private bool shouldHideToolTip(Event event) { if (event !is null && event.type is DWT.MouseMove && (style & NO_RECREATE) !is 0) { Object tmp = getToolTipArea(event); // No new area close the current tooltip if (tmp is null) { hide(); return false; } bool rv = !tmp.opEquals(currentArea); return rv; } return true; } /** * This method is called to check for which area the tooltip is * created/hidden for. In case of {@link #NO_RECREATE} this is used to * decide if the tooltip is hidden recreated. * * <code>By the default it is the widget the tooltip is created for but could be any object. To decide if * the area changed the {@link Object#equals(Object)} method is used.</code> * * @param event * the event * @return the area responsible for the tooltip creation or * <code>null</code> this could be any object describing the area * (e.g. the {@link Control} onto which the tooltip is bound to, a * part of this area e.g. for {@link ColumnViewer} this could be a * {@link ViewerCell}) */ protected Object getToolTipArea(Event event) { return control; } /** * Start up the tooltip programmatically * * @param location * the location relative to the control the tooltip is shown */ public void show(Point location) { Event event = new Event(); event.x = location.x; event.y = location.y; event.widget = control; toolTipCreate(event); } private Shell toolTipCreate(Event event) { if (shouldCreateToolTip(event)) { Shell shell = new Shell(control.getShell(), DWT.ON_TOP | DWT.TOOL | DWT.NO_FOCUS); shell.setLayout(new FillLayout()); toolTipOpen(shell, event); return shell; } return null; } private void toolTipShow(Shell tip, Event event) { if (!tip.isDisposed()) { currentArea = getToolTipArea(event); createToolTipContentArea(event, tip); if (isHideOnMouseDown()) { toolTipHookBothRecursively(tip); } else { toolTipHookByTypeRecursively(tip, true, DWT.MouseExit); } tip.pack(); Point size = tip.getSize(); Point location = fixupDisplayBounds(size, getLocation(size, event)); // Need to adjust a bit more if the mouse cursor.y is tip.y and // the cursor.x is inside the tip Point cursorLocation = tip.getDisplay().getCursorLocation(); if (cursorLocation.y is location.y && location.x < cursorLocation.x && location.x + size.x > cursorLocation.x) { location.y -= 2; } tip.setLocation(location); tip.setVisible(true); } } private Point fixupDisplayBounds(Point tipSize, Point location) { if (respectDisplayBounds || respectMonitorBounds) { Rectangle bounds; Point rightBounds = new Point(tipSize.x + location.x, tipSize.y + location.y); dwt.widgets.Monitor.Monitor[] ms = control.getDisplay().getMonitors(); if (respectMonitorBounds && ms.length > 1) { // By default present in the monitor of the control bounds = control.getMonitor().getBounds(); Point p = new Point(location.x, location.y); // Search on which monitor the event occurred Rectangle tmp; for (int i = 0; i < ms.length; i++) { tmp = ms[i].getBounds(); if (tmp.contains(p)) { bounds = tmp; break; } } } else { bounds = control.getDisplay().getBounds(); } if (!(bounds.contains(location) && bounds.contains(rightBounds))) { if (rightBounds.x > bounds.x + bounds.width) { location.x -= rightBounds.x - (bounds.x + bounds.width); } if (rightBounds.y > bounds.y + bounds.height) { location.y -= rightBounds.y - (bounds.y + bounds.height); } if (location.x < bounds.x) { location.x = bounds.x; } if (location.y < bounds.y) { location.y = bounds.y; } } } return location; } /** * Get the display relative location where the tooltip is displayed. * Subclasses may overwrite to implement custom positioning. * * @param tipSize * the size of the tooltip to be shown * @param event * the event triggered showing the tooltip * @return the absolute position on the display */ public Point getLocation(Point tipSize, Event event) { return control.toDisplay(event.x + xShift, event.y + yShift); } private void toolTipHide(Shell tip, Event event) { assert(this); if (tip !is null && !tip.isDisposed() && shouldHideToolTip(event)) { assert(this); control.getShell().removeListener(DWT.Deactivate, shellListener); currentArea = null; passOnEvent(tip, event); tip.dispose(); CURRENT_TOOLTIP = null; afterHideToolTip(event); } } private void passOnEvent(Shell tip, Event event) { if (control !is null && !control.isDisposed() && event !is null && event.widget !is control && event.type is DWT.MouseDown) { Display display = control.getDisplay(); Point newPt = display.map(tip, null, new Point(event.x, event.y)); Event newEvent = new Event(); newEvent.button = event.button; newEvent.character = event.character; newEvent.count = event.count; newEvent.data = event.data; newEvent.detail = event.detail; newEvent.display = event.display; newEvent.doit = event.doit; newEvent.end = event.end; newEvent.gc = event.gc; newEvent.height = event.height; newEvent.index = event.index; newEvent.item = event.item; newEvent.keyCode = event.keyCode; newEvent.start = event.start; newEvent.stateMask = event.stateMask; newEvent.text = event.text; newEvent.time = event.time; newEvent.type = event.type; newEvent.widget = event.widget; newEvent.width = event.width; newEvent.x = newPt.x; newEvent.y = newPt.y; tip.close(); display.asyncExec(dgRunnable( delegate(Display display_, Event newEvent_) { if (IS_OSX) { try { JThread.sleep(300); } catch (InterruptedException e) { } display_.post(newEvent_); newEvent_.type = DWT.MouseUp; display_.post(newEvent_); } else { display_.post(newEvent_); } }, display,newEvent)); } } private void toolTipOpen(Shell shell, Event event) { // Ensure that only one Tooltip is shown in time if (CURRENT_TOOLTIP !is null) { toolTipHide(CURRENT_TOOLTIP, null); } CURRENT_TOOLTIP = shell; control.getShell().addListener(DWT.Deactivate, shellListener); if (popupDelay > 0) { control.getDisplay().timerExec(popupDelay, dgRunnable( (Shell shell_,Event event_, ToolTip tooltip_){ assert(tooltip_); tooltip_.toolTipShow(shell_, event_); },shell,event, this)); } else { toolTipShow(CURRENT_TOOLTIP, event); } if (hideDelay > 0) { control.getDisplay().timerExec(popupDelay + hideDelay, dgRunnable( (Shell shell_, ToolTip tooltip_){ assert(tooltip_); tooltip_.toolTipHide(shell_, null); }, shell, this )); } } private void toolTipHookByTypeRecursively(Control c, bool add, int type) { if (add) { c.addListener(type, hideListener); } else { c.removeListener(type, hideListener); } if ( auto c2 = cast(Composite)c ) { Control[] children = c2.getChildren(); for (int i = 0; i < children.length; i++) { toolTipHookByTypeRecursively(children[i], add, type); } } } private void toolTipHookBothRecursively(Control c) { c.addListener(DWT.MouseDown, hideListener); c.addListener(DWT.MouseExit, hideListener); if ( auto comp = cast(Composite) c ) { Control[] children = comp.getChildren(); for (int i = 0; i < children.length; i++) { toolTipHookBothRecursively(children[i]); } } } /** * Creates the content area of the the tooltip. * * @param event * the event that triggered the activation of the tooltip * @param parent * the parent of the content area * @return the content area created */ protected abstract Composite createToolTipContentArea(Event event, Composite parent); /** * This method is called after a tooltip is hidden. * <p> * <b>Subclasses may override to clean up requested system resources</b> * </p> * * @param event * event triggered the hiding action (may be <code>null</code> * if event wasn't triggered by user actions directly) */ protected void afterHideToolTip(Event event) { } /** * Set the hide delay. * * @param hideDelay * the delay before the tooltip is hidden. If <code>0</code> * the tooltip is shown until user moves to other item */ public void setHideDelay(int hideDelay) { this.hideDelay = hideDelay; } /** * Set the popup delay. * * @param popupDelay * the delay before the tooltip is shown to the user. If * <code>0</code> the tooltip is shown immediately */ public void setPopupDelay(int popupDelay) { this.popupDelay = popupDelay; } /** * Return if hiding on mouse down is set. * * @return <code>true</code> if hiding on mouse down in the tool tip is on */ public bool isHideOnMouseDown() { return hideOnMouseDown; } /** * If you don't want the tool tip to be hidden when the user clicks inside * the tool tip set this to <code>false</code>. You maybe also need to * hide the tool tip yourself depending on what you do after clicking in the * tooltip (e.g. if you open a new {@link Shell}) * * @param hideOnMouseDown * flag to indicate of tooltip is hidden automatically on mouse * down inside the tool tip */ public void setHideOnMouseDown(bool hideOnMouseDown) { // Only needed if there's currently a tooltip active if (CURRENT_TOOLTIP !is null && !CURRENT_TOOLTIP.isDisposed()) { // Only change if value really changed if (hideOnMouseDown !is this.hideOnMouseDown) { control.getDisplay().syncExec(new class(hideOnMouseDown) Runnable { bool hideOnMouseDown_; this(bool a){ hideOnMouseDown_=a; } public void run() { if (CURRENT_TOOLTIP !is null && CURRENT_TOOLTIP.isDisposed()) { toolTipHookByTypeRecursively(CURRENT_TOOLTIP, hideOnMouseDown_, DWT.MouseDown); } } }); } } this.hideOnMouseDown = hideOnMouseDown; } /** * Hide the currently active tool tip */ public void hide() { toolTipHide(CURRENT_TOOLTIP, null); } private class ToolTipOwnerControlListener : Listener { public void handleEvent(Event event) { switch (event.type) { case DWT.Dispose: case DWT.KeyDown: case DWT.MouseDown: case DWT.MouseMove: case DWT.MouseWheel: toolTipHide(CURRENT_TOOLTIP, event); break; case DWT.MouseHover: toolTipCreate(event); break; case DWT.MouseExit: /* * Check if the mouse exit happened because we move over the * tooltip */ if (CURRENT_TOOLTIP !is null && !CURRENT_TOOLTIP.isDisposed()) { if (CURRENT_TOOLTIP.getBounds().contains( control.toDisplay(event.x, event.y))) { break; } } toolTipHide(CURRENT_TOOLTIP, event); break; default: } } } private class TooltipHideListener : Listener { public void handleEvent(Event event) { if ( auto c = cast(Control)event.widget ) { Shell shell = c.getShell(); switch (event.type) { case DWT.MouseDown: if (isHideOnMouseDown()) { toolTipHide(shell, event); } break; case DWT.MouseExit: /* * Give some insets to ensure we get exit informations from * a wider area ;-) */ Rectangle rect = shell.getBounds(); rect.x += 5; rect.y += 5; rect.width -= 10; rect.height -= 10; if (!rect.contains(c.getDisplay().getCursorLocation())) { toolTipHide(shell, event); } break; default: } } } } }