/* * 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 23.02.2005. */ package com.scriptographer.ai; import java.util.ArrayList; import com.scriptographer.ScriptographerEngine; import com.scriptographer.ScriptographerException; import com.scratchdisk.script.Callable; import com.scratchdisk.util.IntMap; import com.scratchdisk.util.SoftIntMap; import com.scriptographer.adm.Drawer; /** * @author lehni * * @jshide */ public class Annotator extends NativeObject { private boolean active; private static IntMap<Annotator> annotators = new IntMap<Annotator>(); private static ArrayList<Annotator> unusedAnnotators = null; // this is list of drawers that map viewports to created ADM Drawer objects: private static SoftIntMap<Drawer> drawers = new SoftIntMap<Drawer>(); private static int counter = 0; public Annotator() { // now see first whether there is an unusedEffect already: ArrayList unusedAnnotators = getUnusedAnnotators(); int index = unusedAnnotators.size() - 1; if (index >= 0) { Annotator annotator = (Annotator) unusedAnnotators.get(index); // found one, let's reuse it's handle and remove the old timer from // the list: handle = annotator.handle; annotator.handle = 0; unusedAnnotators.remove(index); } else { handle = nativeCreate("Scriptographer Annotator " + (counter++)); } if (handle == 0) throw new ScriptographerException("Unable to create Annotator."); active = false; annotators.put(handle, this); } /** * Called from the native environment. */ protected Annotator(int handle) { super(handle); } private native int nativeCreate(String name); /** * @param active if {@code true}, activates the annotator, otherwise * deactivates it */ public void setActive(boolean active) { if (nativeSetActive(handle, active)) { this.active = active; } } public boolean isActive() { return active; } private native boolean nativeSetActive(int handle, boolean active); /** * @jshide */ public void invalidate(int x, int y, int width, int height) { nativeInvalidate(handle, x, y, width, height); } public void invalidate(Rectangle rect) { // TODO: implement DocumentView and pass handle to it! nativeInvalidate(handle, (int) rect.x, (int) rect.y, (int) rect.width, (int) rect.height); } private native void nativeInvalidate(int viewHandle, int x, int y, int width, int height); public void dispose() { // see whether we're still linked: if (annotators.get(handle) == this) { // if so remove it and put it to the list of unused timers, for later // recycling annotators.remove(handle); getUnusedAnnotators().add(this); } } /** * @jshide */ public static void disposeAll() { // As remove() modifies the map, using an iterator is not possible here: Object[] annotators = Annotator.annotators.values().toArray(); for (int i = 0; i < annotators.length; i++) ((Annotator) annotators[i]).dispose(); // also clean up the port drawers: Object[] drawers = Annotator.drawers.values().toArray(); for (int i = 0; i < drawers.length; i++) ((Drawer) drawers[i]).dispose(); } private static ArrayList<Annotator> getUnusedAnnotators() { if (unusedAnnotators == null) unusedAnnotators = nativeGetAnnotators(); return unusedAnnotators; } private static native ArrayList<Annotator> nativeGetAnnotators(); private Callable onDraw = null; public void setOnDraw(Callable onDraw) { this.onDraw = onDraw; this.setActive(onDraw != null); } public Callable getOnDraw() { return onDraw; } protected void onDraw(Drawer drawer, DocumentView view) { if (onDraw != null) ScriptographerEngine.invoke(onDraw, this, drawer, view); } private Callable onInvalidate = null; public void setOnInvalidate(Callable onInvalidate) { this.onInvalidate = onInvalidate; } public Callable getOnInvalidate() { return onInvalidate; } protected void onInvalidate() { if (onInvalidate != null) ScriptographerEngine.invoke(onInvalidate, this); } /** * To be called from the native environment: */ private static void onDraw(int handle, int portHandle, int viewHandle, int docHandle) { Annotator annotator = getAnnotator(handle); if (annotator != null) { annotator.onDraw(createDrawer(portHandle), DocumentView.wrapHandle(viewHandle, Document.wrapHandle(docHandle))); } } private static void onInvalidate(int handle) { Annotator annotator = getAnnotator(handle); if (annotator != null) { annotator.onInvalidate(); } } private static Annotator getAnnotator(int handle) { return annotators.get(handle); } /** * Returns a Drawer for the passed portHandle. * The drawers are cashed and reused for the same port. * TODO: Remove dependency from adm.Drawer and introduce a new ui.Drawer * that makes the abstraction behind the scenes. Or: Use an interface called * ui.Drawer and have different implementations sharing that common * interface. * * @param portHandle */ private static Drawer createDrawer(int portHandle) { Drawer drawer = drawers.get(portHandle); if (drawer == null) { drawer = nativeCreateDrawer(portHandle); drawers.put(portHandle, drawer); } return drawer; } private static native Drawer nativeCreateDrawer(int portHandle); protected void finalize() { dispose(); } }