view dwt/widgets/TrayItem.d @ 149:36ae710173d9

Selection is now working for TrayItems
author Jacob Carlborg <doob@me.com>
date Tue, 09 Jun 2009 21:05:40 +0200
parents c74ba20de292
children
line wrap: on
line source

/*******************************************************************************
 * Copyright (c) 2000, 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:
 *     Jacob Carlborg <doob@me.com>
 *******************************************************************************/
module dwt.widgets.TrayItem;


import dwt.DWT;
import dwt.DWTException;
import dwt.events.MenuDetectListener;
import dwt.events.SelectionEvent;
import dwt.events.SelectionListener;
import dwt.graphics.Image;
import dwt.graphics.Point;
import dwt.internal.cocoa.NSControl;
import dwt.internal.cocoa.NSEvent;
import dwt.internal.cocoa.NSImageView;
import dwt.internal.cocoa.NSPoint;
import dwt.internal.cocoa.NSRect;
import dwt.internal.cocoa.NSStatusBar;
import dwt.internal.cocoa.NSStatusItem;
import dwt.internal.cocoa.NSString;
import dwt.internal.cocoa.OS;
import dwt.internal.cocoa.SWTImageView;

import dwt.dwthelper.utils;
import dwt.internal.cocoa.NSView;
import Carbon = dwt.internal.c.Carbon;
import objc = dwt.internal.objc.runtime;
import dwt.widgets.Item;
import dwt.widgets.ToolTip;
import dwt.widgets.Tray;
import dwt.widgets.TypedListener;

/**
 * Instances of this class represent icons that can be placed on the
 * system tray or task bar status area.
 * <p>
 * <dl>
 * <dt><b>Styles:</b></dt>
 * <dd>(none)</dd>
 * <dt><b>Events:</b></dt>
 * <dd>DefaultSelection, MenuDetect, Selection</dd>
 * </dl>
 * </p><p>
 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
 * </p>
 *
 * @see <a href="http://www.eclipse.org/swt/snippets/#tray">Tray, TrayItem snippets</a>
 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
 * 
 * @since 3.0
 */
public class TrayItem : Item {
    Tray parent;
    ToolTip toolTip;
    String toolTipText;
    bool visible = true, highlight;
    NSStatusItem item;
    NSImageView view;
    
    static const float BORDER = 8f;
    
/**
 * Constructs a new instance of this class given its parent
 * (which must be a <code>Tray</code>) and a style value
 * describing its behavior and appearance. The item is added
 * to the end of the items maintained by its parent.
 * <p>
 * The style value is either one of the style constants defined in
 * class <code>DWT</code> which is applicable to instances of this
 * class, or must be built by <em>bitwise OR</em>'ing together 
 * (that is, using the <code>int</code> "|" operator) two or more
 * of those <code>DWT</code> style constants. The class description
 * lists the style constants that are applicable to the class.
 * Style bits are also inherited from superclasses.
 * </p>
 *
 * @param parent a composite control which will be the parent of the new instance (cannot be null)
 * @param style the style of control to construct
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
 * </ul>
 * @exception DWTException <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
 *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
 * </ul>
 *
 * @see DWT
 * @see Widget#checkSubclass
 * @see Widget#getStyle
 */
public this (Tray parent, int style) {
    super (parent, style);
    this.parent = parent;
    parent.createItem (this, parent.getItemCount ());
    createWidget ();
}

/**
 * Adds the listener to the collection of listeners who will
 * be notified when the platform-specific context menu trigger
 * has occurred, by sending it one of the messages defined in
 * the <code>MenuDetectListener</code> interface.
 *
 * @param listener the listener which should be notified
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * @exception DWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 *
 * @see MenuDetectListener
 * @see #removeMenuDetectListener
 *
 * @since 3.3
 */
public void addMenuDetectListener (MenuDetectListener listener) {
    checkWidget ();
    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
    TypedListener typedListener = new TypedListener (listener);
    addListener (DWT.MenuDetect, typedListener);
}

/**
 * Adds the listener to the collection of listeners who will
 * be notified when the receiver is selected by the user, by sending
 * it one of the messages defined in the <code>SelectionListener</code>
 * interface.
 * <p>
 * <code>widgetSelected</code> is called when the receiver is selected
 * <code>widgetDefaultSelected</code> is called when the receiver is double-clicked
 * </p>
 *
 * @param listener the listener which should be notified when the receiver is selected by the user
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * @exception DWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 *
 * @see SelectionListener
 * @see #removeSelectionListener
 * @see SelectionEvent
 */
public void addSelectionListener(SelectionListener listener) {
    checkWidget ();
    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
    TypedListener typedListener = new TypedListener (listener);
    addListener (DWT.Selection,typedListener);
    addListener (DWT.DefaultSelection,typedListener);
}

protected void checkSubclass () {
    if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
}

void createHandle () {
    NSStatusBar statusBar = NSStatusBar.systemStatusBar();
    item = statusBar.statusItemWithLength(0);
    if (item is null) error (DWT.ERROR_NO_HANDLES);
    item.retain();
    item.setHighlightMode(true);    
    NSRect rect = NSRect();
    view = cast(NSImageView)(new SWTImageView()).alloc();
    if (view is null) error (DWT.ERROR_NO_HANDLES);
    view.initWithFrame(rect);
    item.setView(view);
}

void deregister () {
    super.deregister ();
    display.removeWidget (view);
    display.removeWidget(view.cell());
}

void destroyWidget () {
    parent.destroyItem (this);
    releaseHandle ();
}

Point getLocation () {
    NSRect rect = view.frame();
    NSRect windowRect = view.window().frame();
    NSPoint pt = NSPoint();
    pt.x = rect.width / 2;
    pt.y = rect.height;
    pt = view.convertPoint_fromView_(pt, null);
    pt.x += windowRect.x;
    return new Point (cast(int)pt.x, cast(int)pt.y);
}

/**
 * Returns the receiver's parent, which must be a <code>Tray</code>.
 *
 * @return the receiver's parent
 *
 * @exception DWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 * 
 * @since 3.2
 */
public Tray getParent () {
    checkWidget ();
    return parent;
}

/**
 * Returns the receiver's tool tip, or null if it has
 * not been set.
 *
 * @return the receiver's tool tip text
 *
 * @exception DWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 * 
 * @since 3.2
 */
public ToolTip getToolTip () {
    checkWidget ();
    return toolTip;
}

/**
 * Returns the receiver's tool tip text, or null if it has
 * not been set.
 *
 * @return the receiver's tool tip text
 *
 * @exception DWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public String getToolTipText () {
    checkWidget ();
    return toolTipText;
}

/**
 * Returns <code>true</code> if the receiver is visible and 
 * <code>false</code> otherwise.
 *
 * @return the receiver's visibility
 *
 * @exception DWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public bool getVisible () {
    checkWidget ();
    return visible;
}

void register () {
    super.register ();
    display.addWidget (view, this);
    display.addWidget ((cast(NSControl)view).cell(), this);
}

void releaseHandle () {
    super.releaseHandle ();
    parent = null;
    if (item !is null) item.release();
    if (view !is null) view.release();
    item = null;
    view = null;
}

void releaseWidget () {
    super.releaseWidget ();
    NSStatusBar statusBar = NSStatusBar.systemStatusBar();
    statusBar.removeStatusItem(item);
}

/**
 * Removes the listener from the collection of listeners who will
 * be notified when the platform-specific context menu trigger has
 * occurred.
 *
 * @param listener the listener which should no longer be notified
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * @exception DWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 *
 * @see MenuDetectListener
 * @see #addMenuDetectListener
 *
 * @since 3.3
 */
public void removeMenuDetectListener (MenuDetectListener listener) {
    checkWidget ();
    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
    if (eventTable is null) return;
    eventTable.unhook (DWT.MenuDetect, listener);
}

/**
 * Removes the listener from the collection of listeners who will
 * be notified when the receiver is selected by the user.
 *
 * @param listener the listener which should no longer be notified
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
 * </ul>
 * @exception DWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 *
 * @see SelectionListener
 * @see #addSelectionListener
 */
public void removeSelectionListener (SelectionListener listener) {
    checkWidget ();
    if (listener is null) error (DWT.ERROR_NULL_ARGUMENT);
    if (eventTable is null) return;
    eventTable.unhook (DWT.Selection, listener);
    eventTable.unhook (DWT.DefaultSelection,listener);  
}

/**
 * Sets the receiver's image.
 *
 * @param image the new image
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
 * </ul>
 * @exception DWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void setImage (Image image) {
    checkWidget ();
    if (image !is null && image.isDisposed ()) error (DWT.ERROR_INVALID_ARGUMENT);
    super.setImage (image);
    view.setImage(image !is null ? image.handle : null);
    Carbon.CGFloat width = image !is null && visible ? image.handle.size().width + BORDER : 0;
    item.setLength(width);
}

/**
 * Sets the receiver's tool tip to the argument, which
 * may be null indicating that no tool tip should be shown.
 *
 * @param toolTip the new tool tip (or null)
 *
 * @exception DWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 * 
 * @since 3.2
 */
public void setToolTip (ToolTip toolTip) {
    checkWidget ();
    ToolTip oldTip = this.toolTip, newTip = toolTip;
    if (oldTip !is null) oldTip.item = null;
    this.toolTip = newTip;
    if (newTip !is null) newTip.item = this;
}

/**
 * Sets the receiver's tool tip text to the argument, which
 * may be null indicating that no tool tip text should be shown.
 *
 * @param value the new tool tip text (or null)
 *
 * @exception DWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void setToolTipText (String string) {
    checkWidget ();
    toolTipText = string;
    _setToolTipText (string);
}

void _setToolTipText (String string) {
    NSString str = null;
    if (string !is null) str = NSString.stringWith(string);
    view.setToolTip(str);
}

/**
 * Makes the receiver visible if the argument is <code>true</code>,
 * and makes it invisible otherwise. 
 *
 * @param visible the new visibility state
 *
 * @exception DWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
 * </ul>
 */
public void setVisible (bool visible) {
    checkWidget ();
    if (this.visible is visible) return;
    if (visible) {
        sendEvent (DWT.Show);
        if (isDisposed ()) return;
    }
    this.visible = visible;
    Carbon.CGFloat width = image !is null && visible ? image.handle.size().width + BORDER : 0;
    item.setLength(width);
    if (!visible) sendEvent (DWT.Hide);
}

void showMenu () {
    _setToolTipText (null);
    sendEvent (DWT.MenuDetect);
    if (isDisposed ()) return;
//  display.runPopups ();
    if (isDisposed ()) return;
    _setToolTipText (toolTipText);
}

void mouseDown(objc.id id, objc.SEL sel, objc.id event) {
    NSEvent nsEvent = new NSEvent(event);
    if ((nsEvent.modifierFlags() & OS.NSDeviceIndependentModifierFlagsMask) is OS.NSControlKeyMask) {
        showMenu();
    } else {
        highlight = true;
        (cast(NSView)view).setNeedsDisplay(true);
        postEvent(nsEvent.clickCount() is 2 ? DWT.DefaultSelection : DWT.Selection);
    }
}

void mouseUp(objc.id id, objc.SEL sel, objc.id theEvent) {
    highlight = false;
    (cast(NSView)view).setNeedsDisplay(true);
}

void rightMouseDown(objc.id id, objc.SEL sel, objc.id theEvent) {
    showMenu();
}

void drawRect(objc.id id, objc.SEL sel, NSRect rect) {
    item.drawStatusBarBackgroundInRect(rect, highlight);
    super.drawRect(id, sel, rect);
}
}