Package com.scriptographer.adm

Source Code of com.scriptographer.adm.Item

/*
* Scriptographer
*
* This file is part of Scriptographer, a Scripting Plugin for Adobe Illustrator
* http://scriptographer.org/
*
* Copyright (c) 2002-2010, Juerg Lehni
* http://scratchdisk.com/
*
* All rights reserved. See LICENSE file for details.
*
* File created on 22.12.2004.
*/

package com.scriptographer.adm;

import java.awt.Dimension;

import com.scratchdisk.script.Callable;
import com.scratchdisk.util.IntegerEnumUtils;
import com.scriptographer.ScriptographerEngine;
import com.scriptographer.ScriptographerException;

/**
* @author lehni
*/
public abstract class Item extends Component {
 
  protected ItemType type;

  protected Dialog dialog;

  // The native bounds of the native item, including margin fixes for faulty
  // size handling by ADM (e.g. TextEditItem)
  protected Rectangle nativeBounds = null;
  // The bounds of the items, excluding any margins.
  protected Rectangle bounds;
  // The visual margins, containing both user set margins and the values
  // returned by getNativeMargin() which was introduced to easily compensate
  // for any native margins required by controls.
  protected Border margin;

  private String toolTip;

  protected java.awt.Component component = null;
  private Size minSize = null;
  private Size maxSize = null;
  private Size prefSize = null;
  private boolean sizeSet = false;
  private boolean isChild = false;

  protected Item() {
    // Call function as it is overridden by Button, where it sets
    // margin according to platform
    setMargin(0, 0, 0, 0);
  }

  /**
   * Constructor for newly created Items
   *
   * @param dialog
   * @param type
   * @param options
   */
  protected Item(Dialog dialog, ItemType type, int options) {
    // Tell the dialog to ignore notifications during item creation, as on
    // some versions of Illustrator (CS2 and below), window activation
    // notifications seem to be triggered by item creation, confusing
    // dialog.initialize() calls.
    dialog.ignoreNotifications = true;
    int handle = nativeCreate(dialog, type.name, options);
    dialog.ignoreNotifications = false;
    init(dialog, handle, type);
    setFont(dialog.getFont());
  }

  protected Item(Dialog dialog, ItemType type) {
    this(dialog, type, 0);
  }

  /**
   * Constructor for already existing Items that get wrapped,
   * e.g. PopupMenu
   *
   * @param dialog
   * @param handle
   * @param isChild
   */
  protected Item(Dialog dialog, int handle, boolean isChild) {
    this.isChild = isChild;
    init(dialog, handle, ItemType.get(nativeInit(handle, isChild)));
  }

  protected void init(Dialog dialog, int handle, ItemType type) {
    this.dialog = dialog;
    this.handle = handle;
    this.type = type;
    // Set margin only after type was set, as getNativeMargin() sometimes
    // depends on it to be set.
    setMargin(0, 0, 0, 0);
    dialog.items.add(this);
    nativeBounds = nativeGetBounds();
    // nativeSize and nativeBounds are set by the native environment
    // size and bounds need to be updated depending on margins and
    // internalInsets
    bounds = new Rectangle(nativeBounds).add(margin);
  }

  protected void initBounds() {
    if (!sizeSet)
      setSize(getBestSize());
    // This is used to fix ADM bugs on CS4 where an item does not update its
    // native bounds in certain situations (hidden window?) even if it was
    // asked to do so.
    Rectangle bounds = nativeGetBounds();
    if (!bounds.equals(nativeBounds))
      setNativeBounds(nativeBounds.x, nativeBounds.y,
            nativeBounds.width, nativeBounds.height);
  }

  public void destroy() {
    if (handle != 0) {
      nativeDestroy(handle);
      handle = 0;
      dialog.removeItem(this);
      dialog = null;
    }
  }
 
  public Dialog getDialog() {
    return dialog;
  }
 
  /*
   * Callback functions:
   */

  private Callable onInitialize = null;

  public Callable getOnInitialize() {
    return onInitialize;
  }

  public void setOnInitialize(Callable onInitialize) {
    this.onInitialize = onInitialize;
  }

  protected void onInitialize() {
    if (onInitialize != null)
      ScriptographerEngine.invoke(onInitialize, this);
  }

  private Callable onDestroy = null;
 
  public Callable getOnDestroy() {
    return onDestroy;
  }

  public void setOnDestroy(Callable onDestroy) {
    this.onDestroy = onDestroy;
  }

  protected void onDestroy() {
    // retrieve through getter so it can be overridden by subclasses,
    // e.g. HierarchyListBox
    Callable onDestroy = this.getOnDestroy();
    if (onDestroy != null)
      ScriptographerEngine.invoke(onDestroy, this);
  }

  protected void onNotify(Notifier notifier) {
    switch (notifier) {
    case INITIALIZE:
      onInitialize();
      break;
    case DESTROY:
      onDestroy();
      break;
    }
  }

  /*
   * ADM stuff:
   */

  /*
   * item creation/destruction
   *
   */

  /**
   * sets size and bounds to valid values
   */
  private native int nativeCreate(Dialog dialog, String type, int options);
 
  /**
   * sets size and bounds to valid values
   */
  private native String nativeInit(int handle, boolean isChild);
 
  private native void nativeDestroy(int handle);

  /*
   * Handler activation / deactivation
   */
  protected native void nativeSetTrackCallback(boolean enabled);
  protected native void nativeSetDrawCallback(boolean enabled);

  public native boolean defaultTrack(Tracker tracker);
  public native void defaultDraw(Drawer drawer);

  public native int getTrackMask();
  public native void setTrackMask(int mask);

  /*
   * item timer
   *
   */
  /*
  public native ADMTimerRef createTimer(ADMUInt32 inMilliseconds,
        ADMActionMask inAbortMask, ADMItemTimerProc inTimerProc,
        ADMItemTimerAbortProc inTimerAbortProc, ADMInt32 inOptions);

  public native void abortTimer(ADMTimerRef inTimer);
  */

  /*
   * item information accessors
   *
   */

  protected native void nativeSetStyle(int style);
  protected native int nativeGetStyle();
 
  protected native int getChildItemHandle(int itemID);

  /*
   * item state accessors
   *
   */
 
  public native boolean isVisible();
  public native void setVisible(boolean visible);

  public native boolean isEnabled();
  public native void setEnabled(boolean enabled);
 
  public native boolean isActive();
  public native void setActive(boolean active);

  public native boolean isKnown();
  public native void setKnown(boolean known);

  /*
   * others...
   *
   */
 
  public native boolean wantsFocus();
  public native void setWantsFocus(boolean wantsFocus);

  /*
   * item bounds accessors
   *
   */

  protected native Rectangle nativeGetBounds();
  protected native void nativeSetBounds(int x, int y, int width, int height);
 
  protected native void nativeSetSize(int width, int height);

  protected final void setNativeBounds(int x, int y, int width, int height) {
    nativeBounds.set(x, y, width, height);
    // Do not change location for children as this messes things up e.g.
    // for SpinEdit.
    if (isChild)
      nativeSetSize(width, height);
    else
      nativeSetBounds(x, y, width, height);
  }

  public Rectangle getBounds() {
    return new Rectangle(bounds);
  }

  /**
   * @jshide
   */
  public void setBounds(int x, int y, int width, int height) {
    updateBounds(x, y, width, height, true);
  }

  public final void setBounds(Rectangle bounds) {
    setBounds(bounds.x, bounds.y, bounds.width, bounds.height);
  }

  protected void updateBounds(int x, int y, int width, int height,
      boolean sizeChanged) {
    if (sizeChanged) {
      // Set prefSize so getPreferredSize does not return results from
      // getBestSize()
      prefSize = new Size(width, height);
      // Set minSize if it is not set yet, so getBestSize() is not used
      // anymore.
      if (minSize == null)
        minSize = prefSize;
      sizeSet = true;
    }
    // Update bounds
    bounds.set(x, y, width, height);
    // Update bounds in AWT proxy:
    updateAWTBounds(bounds);
    // updateNativeBounds does all the work with margins.
    updateNativeBounds(x, y, width, height);
  }

  protected Size getSizeCorrection() {
    // Allow subclasses to correct the native size, to fix ADM issues with
    // layouts. Used in getSizeCorrection.
    return null;
  }

  protected void updateNativeBounds(int x, int y, int width, int height) {
    // Calculate native values
    int nativeX = x + margin.left;
    int nativeY = y + margin.top;
    int nativeWidth = width - margin.left - margin.right;
    int nativeHeight = height - margin.top - margin.bottom;

    Size fix = getSizeCorrection();
    if (fix != null) {
      nativeWidth += fix.width;
      nativeHeight += fix.height;
    }

    int deltaX = nativeWidth - nativeBounds.width;
    int deltaY = nativeHeight - nativeBounds.height;

    boolean sizeChanged = deltaX != 0 || deltaY != 0;
    if (sizeChanged || nativeBounds.x != nativeX
        || nativeBounds.y != nativeY) {
      // Same as setNativeBounds here, only TextEditItem overrides this.
      setNativeBounds(nativeX, nativeY, nativeWidth, nativeHeight);
    }
    if (sizeChanged) {
      try {
        onResize(deltaX, deltaY);
      } catch (Exception e) {
        throw new ScriptographerException(e);
      }
    }
  }

  protected void updateAWTBounds(Rectangle bounds) {
    if (component != null) {
      if (component instanceof AWTItemComponent)
        ((AWTItemComponent) component).updateBounds(bounds);
      else if (component instanceof AWTComponentGroupContainer)
        ((AWTComponentGroupContainer) component).updateBounds(bounds);
    }
  }

  protected void updateAWTMargin(Border margin) {
    if (component != null && this instanceof ComponentGroup)
      this.getAWTContainer(true).setInsets(margin.top, margin.left,
          margin.bottom, margin.right);
  }

  /**
   * @jshide
   */
  public void setPosition(int x, int y) {
    updateBounds(x, y, bounds.width, bounds.height, false);
  }

  public final void setPosition(Point loc) {
    setPosition(loc.x, loc.y);
  }
 
  public Point getPosition() {
    return new Point(bounds.x, bounds.y);
  }

  public Size getSize() {
    return bounds.getSize();
  }

  /**
   * @jshide
   */
  public void setSize(int width, int height) {
    updateBounds(bounds.x, bounds.y, width, height, true);
  }

  public final void setSize(Size size) {
    setSize(size.width, size.height);
  }

  public int getWidth() {
    return getSize().width;
  }

  public void setWidth(int width) {
    Size size = sizeSet ? getSize() : getBestSize();
    size.width = width;
    setSize(size);
  }

  public int getHeight() {
    return getSize().height;
  }

  public void setHeight(int height) {
    Size size = sizeSet ? getSize() : getBestSize();
    size.height = height;
    setSize(size);
  }

  public Size getBestSize() {
    int maxWidth = maxSize != null
      ? maxSize.width - margin.left - margin.right
      : -1;
    // TODO: verify for which items nativeGetBestSize really works!
    Size size = null;
    double factor = isSmall() ? 0.75 : 1;
    switch (type) {
    case PICTURE_STATIC:
    case PICTURE_CHECKBOX:
    case PICTURE_PUSHBUTTON:
    case PICTURE_RADIOBUTTON:
      Image image = null;
      if (this instanceof ImagePane)
        image = ((ImagePane) this).getImage();
      else if (this instanceof Button)
        image = ((Button) this).getImage();
      if (image != null)
        size = image.getSize();
      else // Default size for image buttons
        size = new Size(32, 24).multiply(factor);
      break;
    case SLIDER: // Default size for slider
      size = new Size(isSmall() ? 120 : 160, 8);
      break;
    case POPUP_LIST:
    case SCROLLING_POPUP_LIST:
      Size textSize = getTextSize(" ", -1, true);
      // Calculate the required size of the popup list based on the
      // space required by the native UI element around the selected text.
      Size addSize = ScriptographerEngine.isMacintosh()
          ? new Size(isSmall() ? 32 : 38, 8)
          : new Size(26, 6);
      PopupList list = (PopupList) this;
      if (list.size() > 0) {
        size = new Size(0, 0);
        for (int i = 0, l = list.size(); i < l; i++) {
          ListEntry entry = list.get(i);
          String text = entry.getText();
          Size entrySize = getTextSize(text, maxWidth != -1
              ? maxWidth - addSize.width : -1, true);
          size.width = Math.max(size.width, entrySize.width);
          size.height = Math.max(size.height, entrySize.height);
        }
      } else {
        // Empty list, make sure height is at least set for text
        // and it has some width for future content (32 * space)
        size = textSize.multiply(32, 1);
      }
      size = size.add(addSize);
      break;
    default:
      String text = null;
      boolean multiline = false;
      if (this instanceof TextValueItem) {
        text = ((TextValueItem) this).getText();
        multiline = ((TextValueItem) this).isMultiline();
      } else if (this instanceof TextItem) {
        text = ((TextItem) this).getText();
      }
      if (text != null) {
        if (text.equals(""))
          text = " ";
        size = getTextSize(text, maxWidth, !multiline);
        if (size != null) {
          if (this instanceof Button) {
            size.width += size.height * 2;
            size.height += 6;
          } else if (this instanceof TextEditItem) {
            // Ignore the text width for a TextEdit, just use the
            // text height and use a default width across
            // Scriptographer for text edits, based on the height.
            size.width = size.height * 5;
            size.height += 6;
            // Spin Edits seem to need 1px more on Windows...
            if (ScriptographerEngine.isWindows()
                && this instanceof SpinEdit)
              size.height += 1;
        } else if (this instanceof TextPane) {
            size.width += 4;
            size.height += 4;
          }
        }
      }
    }
    if (size == null) {
      // If it's not a button, use the current size of the object.
      // This is needed e.g. for Spacers, where its current size
      // is the preferred size too.
      size = this instanceof Button
          ? new Size(120, 20).multiply(factor)
          : getSize();
    }
    // Add margins
    size.width += margin.left + margin.right;
    size.height += margin.top + margin.bottom;
    if (minSize != null) {
      if (size.width < minSize.width)
        size.width = minSize.width;
      if (size.height < minSize.height)
        size.height = minSize.height;
    }
    if (maxSize != null) {
      if (maxSize.width >= 0 && size.width > maxSize.width)
        size.width = maxSize.width;
      if (maxSize.height >= 0 && size.height > maxSize.height)
        size.height = maxSize.height;
    }
    return size;
  }

  /**
   * @jshide
   */
  public void setPreferredSize(int width, int height) {
    prefSize = new Size(width, height);
  }

  public void setPreferredSize(Size size) {
    setPreferredSize(size.width, size.height);
  }

  public Size getPreferredSize() {
    return prefSize != null ? prefSize : getBestSize();
  }

  /**
   * @jshide
   */
  public void setMinimumSize(int width, int height) {
    minSize = new Size(width, height);
  }

  public void setMinimumSize(Size size) {
    setMinimumSize(size.width, size.height);
  }

  public Size getMinimumSize() {
    return minSize != null ? minSize : getBestSize();
  }

  /**
   * @jshide
   */
  public void setMaximumSize(int width, int height) {
    maxSize = new Size(width, height);
  }

  public void setMaximumSize(Size size) {
    setMaximumSize(size.width, size.height);
  }

  public Size getMaximumSize() {
    return maxSize != null ? maxSize : getSize();
  }

  // top, right, bottom, left
  protected static final Border MARGIN_NONE = new Border(0, 0, 0, 0);

  protected Border getNativeMargin() {
    return MARGIN_NONE;
  }

  public Border getVisualMargin() {
    return (Border) margin.clone();
  }

  public Border getMargin() {
    return margin.subtract(getNativeMargin());
  }

  public void setMargin(int top, int right, int bottom, int left) {
    margin = new Border(top, right, bottom, left).add(getNativeMargin());
    if (nativeBounds != null)
      updateBounds(bounds.x, bounds.y, bounds.width, bounds.height, false);
    // Update the margins in the AWT proxy as well
    updateAWTMargin(margin);
  }

  /*
   * Coordinate system transformations
   *
   */
 
  /**
   * @jshide
   */
  public native Point localToScreen(int x, int y);

  /**
   * @jshide
   */
  public native Point screenToLocal(int x, int y);

  /**
   * @jshide
   */
  public native Rectangle localToScreen(int x, int y, int width, int height);

  /**
   * @jshide
   */
  public native Rectangle screenToLocal(int x, int y, int width, int height);

  public Point localToScreen(Point pt) {
    return localToScreen(pt.x, pt.y);
  }

  public Point screenToLocal(Point pt) {
    return screenToLocal(pt.x, pt.y);
  }

  public Rectangle localToScreen(Rectangle rt) {
    return localToScreen(rt.x, rt.y, rt.width, rt.height);
  }

  public Rectangle screenToLocal(Rectangle rt) {
    return screenToLocal(rt.x, rt.y, rt.width, rt.height);
  }

  /*
   * Item display
   *
   */

  public native void invalidate();

  /**
   * @jshide
   */
  public native void invalidate(int x, int y, int width, int height);

  public final void invalidate(Rectangle rt) {
    invalidate(rt.x, rt.y, rt.width, rt.height);
  }
 
  public native void update();

  protected native int nativeGetFont();
 
  protected native void nativeSetFont(int font);

  private native void nativeSetBackgroundColor(int color);
  private native int nativeGetBackgroundColor();

  public void setBackgroundColor(DialogColor color) {
    if (color != null)
      nativeSetBackgroundColor(color.value);
  }

  public DialogColor getBackgroundColor() {
    return IntegerEnumUtils.get(DialogColor.class,
        nativeGetBackgroundColor());
  }
  /*
   * Cursor accessors
   *
   */
 
  private native int nativeGetCursor();
 
  private native void nativeSetCursor(int cursor);

  public Cursor getCursor() {
    return IntegerEnumUtils.get(Cursor.class, nativeGetCursor());
  }

  public void setCursor(Cursor cursor) {
    if (cursor != null)
      nativeSetCursor(cursor.value);
  }
 
  /*
   * Tooltips
   *
   */
 
  private native void nativeSetTooltip(String tooltip);
  public native boolean isToolTipEnabled();
  public native void setToolTipEnabled(boolean enabled);

  /**
   * @jshide
   */
  public native void showToolTip(int x, int y);

  public final void showToolTip(Point pt) {
    showToolTip(pt.x, pt.y);
  }

  public native void hideToolTip();

  public String getToolTip() {
    return toolTip;
  }
 
  public void setToolTip(String tooltip) {
    this.toolTip = tooltip;
    nativeSetTooltip(tooltip);
  }

  /*
   * AWT LayoutManager integration:
   */

  protected java.awt.Component getAWTComponent(boolean create) {
    if (component == null && create) {
      if (this instanceof ComponentGroup) {
        component = new AWTComponentGroupContainer();
      } else {
        component = new AWTItemComponent();
      }
      // Take over margin and bounds from the item.
      updateAWTMargin(margin);
      updateAWTBounds(bounds);
    }
    return component;
  }

  /*
   * Calculates the absolute origin of the AWT component.
   */
  protected Point getOrigin(java.awt.Component component) {
    Point delta = new Point();
    java.awt.Container parent = component.getParent();
    while (true) {
      java.awt.Container next = parent.getParent();
      if (next == null)
        break;
      java.awt.Point loc = parent.getLocation();
      delta.x += loc.x;
      delta.y += loc.y;
      parent = next;
    }
    return delta;
  }

  /**
   * AWTComponent wraps an ADM Item and pretends it is a AWT Component, in
   * order to take advantage of all the nice LayoutManagers in AWT.
   *
   * @author lehni
   */
  class AWTItemComponent extends java.awt.Component implements ComponentWrapper {

    public Component getComponent() {
      return Item.this;
    }

    public void doLayout() {
      // Do nothing here...
    }

    public void updateBounds(Rectangle bounds) {
      // Call the setBounds version in super that directly sets the
      // internal values. setBounds(Rectangle) would call the
      // overridden setBounds(int, int, int, int) which would change the
      // underlying Item.
      super.setBounds(bounds.x, bounds.y, bounds.width, bounds.height);
    }

    public Dimension getMaximumSize() {
      Size size = Item.this.getMaximumSize();
      return new Dimension(size.width, size.height);
    }

    public Dimension getMinimumSize() {
      Size size = Item.this.getMinimumSize();
      return new Dimension(size.width, size.height);
    }

    public Dimension getPreferredSize() {
      Size size = Item.this.getPreferredSize();
      return new Dimension(size.width, size.height);
    }

    public void setBounds(int x, int y, int width, int height) {
      super.setBounds(x, y, width, height);
      Point origin = Item.this.getOrigin(this);
      Item.this.setBounds(x + origin.x, y + origin.y, width, height);
    }

    public void setBounds(java.awt.Rectangle r) {
      setBounds(r.x, r.y, r.width, r.height);
    }

    public void setSize(int width, int height) {
      super.setSize(width, height);
      Item.this.setSize(width, height);
    }

    public void setSize(Dimension d) {
      setSize(d.width, d.height);
    }

    public void setLocation(int x, int y) {
      super.setLocation(x, y);
      Point origin = Item.this.getOrigin(this);
      Item.this.setPosition(x + origin.x, y + origin.y);
    }

    public void setLocation(java.awt.Point p) {
      setLocation(p.x, p.y);
    }

    public boolean isVisible() {
      return Item.this.isVisible();
    }
  }

  /**
   * The actual AWT class for ComponentGroup sthat does the work of collecting
   * wrap items or other ComponentGroups and redirecting doLayout calls to its
   * children.
   *
   * @author lehni
   */
  class AWTComponentGroupContainer extends AWTContainer {

    public Component getComponent() {
      return Item.this;
    }

    public void updateBounds(Rectangle bounds) {
      // Call the setBounds version in super that directly sets the
      // internal values. setBounds(Rectangle) would call the
      // overridden setBounds(int, int, int, int) which would change the
      // underlying Item.
      super.setBounds(bounds.x, bounds.y, bounds.width, bounds.height);
    }

    public Dimension getMinimumSize() {
      // If this is a group item such as Frame or ItemGroup, do not
      // use the native items's min size
      if (Item.this instanceof ComponentGroup)
        return super.getMinimumSize();
      Size size = Item.this.getMinimumSize();
      return new Dimension(size.width, size.height);
    }

    public Dimension getMaximumSize() {
      // If this is a group item such as Frame or ItemGroup, do not
      // use the native items's max size
      if (Item.this instanceof ComponentGroup)
        return super.getMaximumSize();
      Size size = Item.this.getMaximumSize();
      return new Dimension(size.width, size.height);
    }

    public Dimension getPreferredSize() {
      // If this is a group item such as Frame or ItemGroup, do not
      // use the native items's preferred size
      if (Item.this instanceof ComponentGroup)
        return super.getPreferredSize();
      Size size = Item.this.getPreferredSize();
      return new Dimension(size.width, size.height);
    }

    public void setBounds(int x, int y, int width, int height) {
      super.setBounds(x, y, width, height);
      Point origin = Item.this.getOrigin(this);
      Item.this.setBounds(x + origin.x, y + origin.y, width, height);
    }

    public void setBounds(java.awt.Rectangle r) {
      setBounds(r.x, r.y, r.width, r.height);
    }

    public void setSize(int width, int height) {
      super.setSize(width, height);
      Item.this.setSize(width, height);
    }

    public void setSize(Dimension d) {
      setSize(d.width, d.height);
    }

    public void setLocation(int x, int y) {
      super.setLocation(x, y);
      Point origin = Item.this.getOrigin(this);
      Item.this.setPosition(x + origin.x, y + origin.y);
    }

    public void setLocation(java.awt.Point p) {
      setLocation(p.x, p.y);
    }

    public boolean isVisible() {
      return Item.this.isVisible();
    }
  }
}
TOP

Related Classes of com.scriptographer.adm.Item

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.