/* * 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 03.01.2005. */ package com.scriptographer.adm; import java.util.EnumSet; import com.scriptographer.ScriptographerEngine; import com.scratchdisk.script.Callable; import com.scratchdisk.util.IntegerEnumUtils; /** * @author lehni * * @jshide */ public abstract class TextEditItem<S> extends TextValueItem { private boolean unitInitialized = false; protected TextEditItem(Dialog dialog, int handle, boolean isChild) { super(dialog, handle, isChild); } /** * For subclasses * * @param dialog * @param type * @param options */ protected TextEditItem(Dialog dialog, ItemType type, EnumSet<TextOption> options) { super(dialog, type, IntegerEnumUtils.getFlags(options)); // -1 == Maximum length. setMaxLength(-1); } protected TextEditItem(Dialog dialog, EnumSet<TextOption> options) { // filter out the pseudo styles from the options: // (max. real bit is 3, and the mask is (1 << (max + 1)) - 1 this(dialog, getType(options), options); } private static ItemType getType(EnumSet<TextOption> options) { if (options != null) { // abuse the ADM's password style for creating it as a type... if (options.contains(TextOption.PASSWORD)) { return ItemType.TEXT_EDIT_PASSWORD; } else if (options.contains(TextOption.POPUP)) { return options.contains(TextOption.SCROLLING) ? ItemType.TEXT_EDIT_SCROLLING_POPUP : ItemType.TEXT_EDIT_POPUP; } else { boolean multiline = (options.contains(TextOption.MULTILINE)); if (options.contains(TextOption.READONLY)) { return multiline ? ItemType.TEXT_EDIT_MULTILINE_READONLY : ItemType.TEXT_EDIT_READONLY; } else if (multiline) { return ItemType.TEXT_EDIT_MULTILINE; } } } return ItemType.TEXT_EDIT; } public void setValue(float value) { super.setValue(value); // By default we don't want units to be shown, but this only // works when the text edit is actually used for values, // otherwise it would directly be forced into value mode... // So only set it to false if it is for values, and only once. if (!unitInitialized) { this.setShowUnits(false); unitInitialized = true; } } public abstract S getStyle(); public abstract void setStyle(S style); /* * Callback functions */ // TODO: are all these really needed? private Callable onPreCut = null; public Callable getOnPreCut() { return onPreCut; } public void setOnPreCut(Callable onPreCut) { this.onPreCut = onPreCut; } protected void onPreCut() { if (onPreCut != null) ScriptographerEngine.invoke(onPreCut, this); } private Callable onCut = null; public Callable getOnCut() { return onCut; } public void setOnCut(Callable onCut) { this.onCut = onCut; } protected void onCut() { if (onCut != null) ScriptographerEngine.invoke(onCut, this); } private Callable onPreCopy = null; public Callable getOnPreCopy() { return onPreCopy; } public void setOnPreCopy(Callable onPreCopy) { this.onPreCopy = onPreCopy; } protected void onPreCopy() { if (onPreCopy != null) ScriptographerEngine.invoke(onPreCopy, this); } private Callable onCopy = null; public Callable getOnCopy() { return onCopy; } public void setOnCopy(Callable onCopy) { this.onCopy = onCopy; } protected void onCopy() { if (onCopy != null) ScriptographerEngine.invoke(onCopy, this); } private Callable onPrePaste = null; public Callable getOnPrePaste() { return onPrePaste; } public void setOnPrePaste(Callable onPrePaste) { this.onPrePaste = onPrePaste; } protected void onPrePaste() { if (onPrePaste != null) ScriptographerEngine.invoke(onPrePaste, this); } private Callable onPaste = null; public Callable getOnPaste() { return onPaste; } public void setOnPaste(Callable onPaste) { this.onPaste = onPaste; } protected void onPaste() { if (onPaste != null) ScriptographerEngine.invoke(onPaste, this); } private Callable onPreClear = null; public Callable getOnPreClear() { return onPreClear; } public void setOnPreClear(Callable onPreClear) { this.onPreClear = onPreClear; } protected void onPreClear() { if (onPreClear != null) ScriptographerEngine.invoke(onPreClear, this); } private Callable onClear = null; public Callable getOnClear() { return onClear; } public void setOnClear(Callable onClear) { this.onClear = onClear; } protected void onClear() { if (onClear != null) ScriptographerEngine.invoke(onClear, this); } private Callable onPreSelectionChange = null; public Callable getOnPreSelectionChange() { return onPreSelectionChange; } public void setOnPreSelectionChange(Callable onPreSelectionChange) { this.onPreSelectionChange = onPreSelectionChange; } protected void onPreSelectionChange() { if (onPreSelectionChange != null) ScriptographerEngine.invoke(onPreSelectionChange, this); } private Callable onSelectionChange = null; public Callable getOnSelectionChange() { return onSelectionChange; } public void setOnSelectionChange(Callable onSelectionChange) { this.onSelectionChange = onSelectionChange; } protected void onSelectionChange() { if (onSelectionChange != null) ScriptographerEngine.invoke(onSelectionChange, this); } private Callable onPreRedo = null; public Callable getOnPreRedo() { return onPreRedo; } public void setOnPreRedo(Callable onPreRedo) { this.onPreRedo = onPreRedo; } protected void onPreRedo() { if (onPreRedo != null) ScriptographerEngine.invoke(onPreRedo, this); } private Callable onRedo = null; public Callable getOnRedo() { return onRedo; } public void setOnRedo(Callable onRedo) { this.onRedo = onRedo; } protected void onRedo() { if (onRedo != null) ScriptographerEngine.invoke(onRedo, this); } private Callable onPreUndo = null; public Callable getOnPreUndo() { return onPreUndo; } public void setOnPreUndo(Callable onPreUndo) { this.onPreUndo = onPreUndo; } protected void onPreUndo() { if (onPreUndo != null) ScriptographerEngine.invoke(onPreUndo, this); } private Callable onUndo = null; public Callable getOnUndo() { return onUndo; } public void setOnUndo(Callable onUndo) { this.onUndo = onUndo; } protected void onUndo() { if (onUndo != null) ScriptographerEngine.invoke(onUndo, this); } protected void onNotify(Notifier notifier, ListEntry entry) { switch (notifier) { case PRE_CLIPBOARD_CUT: onPreCut(); break; case POST_CLIPBOARD_CUT: onCut(); break; case PRE_CLIPBOARD_COPY: onPreCopy(); break; case POST_CLIPBOARD_COPY: onCopy(); break; case PRE_CLIPBOARD_PASTE: onPrePaste(); break; case POST_CLIPBOARD_PASTE: onPaste(); break; case PRE_CLIPBOARD_CLEAR: onPreClear(); break; case POST_CLIPBOARD_CLEAR: onClear(); break; case PRE_TEXT_SELECTION_CHANGED: onPreSelectionChange(); break; case TEXT_SELECTION_CHANGED: onSelectionChange(); break; case PRE_CLIPBOARD_REDO: onPreRedo(); break; case POST_CLIPBOARD_REDO: onRedo(); break; case PRE_CLIPBOARD_UNDO: onPreUndo(); break; case POST_CLIPBOARD_UNDO: onUndo(); break; } } /* * text edits * */ public void setStringValue(Object value) { if (value != null) setText(value.toString()); } public String getStringValue() { return getText(); } public native int getFractionDigits(); public native void setFractionDigits(int precision); /** * @jshide * @deprecated */ public int getPrecision() { return getFractionDigits(); } /** * @jshide * @deprecated */ public void setPrecision(int precision) { setFractionDigits(precision); } /** * @param length -1 for maximum allowed value. */ public native void setMaxLength(int length); public native int getMaxLength(); /** * @jshide */ public native void setSelection(int start, int end); public native int[] getSelection(); public native void selectAll(); public void setSelection(int[] range) { setSelection(range[0], range[1]); } public void setSelection(int pos) { setSelection(pos, pos); } /* * Needed for a workaround on CS4, Mac. See native code. */ private int setSelectionTimer = 0; public native void setAllowMath(boolean allowMath); public native boolean getAllowMath(); public native void setAllowUnits(boolean allowUnits); public native boolean getAllowUnits(); /* * child items */ private static final int ITEM_TEXTEDIT = 3, ITEM_POPUP = 4; private TextEdit textEdit; private PopupList popupList; public TextEdit getTextEdit() { if (textEdit == null) { int handle = getChildItemHandle(ITEM_TEXTEDIT); textEdit = handle != 0 ? new TextEdit(dialog, handle, true) : null; } return textEdit; } public PopupList getPopupList() { if (popupList == null) { int handle = getChildItemHandle(ITEM_POPUP); if (handle != 0) { // Pass on notification to this item, since we are not offering // listeners yet, and there is no other way to receive these // right now... // TODO: Consider adding support for listeners. popupList = new PopupList(dialog, handle, true) { protected void onNotify(Notifier notifier) { onNotify(notifier); } }; } } return popupList; } public boolean hasPopupList() { return type == ItemType.TEXT_EDIT_SCROLLING_POPUP || type == ItemType.TEXT_EDIT_POPUP; } protected Border getNativeMargin() { if (ScriptographerEngine.isMacintosh()) { // On Mac, edit fields appear to be vertically offset by 1px. // Correct using margins return new Border(-1, this instanceof SpinEdit ? -2 : -1, 1, -1); } else if (ScriptographerEngine.isWindows()) { // Popup-lists on Windows appear to need a 1px margin at the // bottom as they would overlap otherwise. if (hasPopupList() || isMultiline()) return new Border(0, 0, 1, 0); } return MARGIN_NONE; } protected Size getSizeCorrection() { if (ScriptographerEngine.isMacintosh()) { // This seems needed on Mac, as all TextEditItems appear 2px smaller // than they are told to. Also if it has a popup list, the popup // button gets cropped on the 2nd time bounds are set otherwise. return new Size(0, 2); } else if (ScriptographerEngine.isWindows()) { if (!hasPopupList() && !(this instanceof SpinEdit)) return new Size(0, 1); } return null; } protected void updateBounds(int x, int y, int width, int height, boolean sizeChanged) { super.updateBounds(x, y, width, height, sizeChanged); PopupList list = hasPopupList() ? getPopupList() : null; if (list != null) { Size size = list.getSize(); if (ScriptographerEngine.isMacintosh()) { // On the Mac, the height of popup list buttons are broken and // need to be corrected here. size.height = height; } else if (ScriptographerEngine.isWindows()) { // On Windows, the width of popup list items are set wrongly and // need to be corrected here. size.width = width; } list.setSize(size); } } }