KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openlaszlo > compiler > SWFWriter


1 /*****************************************************************************
2  * SWFWriter.java
3  * ****************************************************************************/

4
5 /* J_LZ_COPYRIGHT_BEGIN *******************************************************
6 * Copyright 2001-2004 Laszlo Systems, Inc. All Rights Reserved. *
7 * Use is subject to license terms. *
8 * J_LZ_COPYRIGHT_END *********************************************************/

9
10 package org.openlaszlo.compiler;
11
12 import org.openlaszlo.sc.ScriptCompiler;
13 import org.openlaszlo.server.LPS;
14 import org.openlaszlo.utils.ChainedException;
15 import org.openlaszlo.utils.FileUtils;
16 import org.openlaszlo.utils.ListFormat;
17 import org.openlaszlo.iv.flash.api.*;
18 import org.openlaszlo.iv.flash.api.action.*;
19 import org.openlaszlo.iv.flash.api.button.*;
20 import org.openlaszlo.iv.flash.api.image.*;
21 import org.openlaszlo.iv.flash.api.sound.*;
22 import org.openlaszlo.iv.flash.api.shape.*;
23 import org.openlaszlo.iv.flash.api.text.*;
24 import org.openlaszlo.iv.flash.util.*;
25 import org.openlaszlo.iv.flash.cache.*;
26
27 import org.openlaszlo.compiler.CompilationEnvironment;
28
29 import org.openlaszlo.media.*;
30
31 import java.io.*;
32 import java.util.*;
33 import java.lang.Math JavaDoc;
34 import java.lang.Character JavaDoc;
35
36 import org.jdom.Element;
37
38 // jgen 1.4
39
import java.awt.geom.Rectangle2D JavaDoc;
40
41 import org.apache.log4j.*;
42
43 /** Accumulates code, XML, and assets to a SWF object file.
44  *
45  * Once there's a second target, the methods here can be lifted into
46  * an API that SWFWriter implements. Rationale for not doing this
47  * now: It's easier to generalize from more than one instance, and
48  * having to change signatures twice for every refactoring is an
49  * unnecessary burden during early development.
50  *
51  * Make heavy use of JGenerator API.
52  *
53  * Properties documented in Compiler.getProperties.
54  */

55 class SWFWriter {
56     /** Stream to write to. */
57     private OutputStream mStream = null;
58     /** Movie being constructed. */
59     private SWFFile mFlashFile;
60     /** Fonts being collected. */
61     private FontsCollector mFontsCollector = new FontsCollector();
62     private FontsCollector mPreloaderFontsCollector = new FontsCollector();
63     private FontManager mFontManager = new FontManager();
64     /** True iff close() has been called. */
65     private boolean mCloseCalled = false;
66
67     private final CompilationEnvironment mEnv;
68
69     /** Set of resoures we're importing into the output SWF */
70     private final HashSet mMultiFrameResourceSet = new HashSet();
71
72     /** Constant */
73     private final int TWIP = 20;
74     
75     /** Total number of frames in the movie **/
76     private int mLastFrame = 0;
77     
78     /** Unique name supply for clip/js names */
79     protected SymbolGenerator mNameSupply = new SymbolGenerator("$LZ");
80
81     /** Properties */
82     private Properties mProperties;
83
84     /** Input text fontinfo map */
85     private final TreeMap mInputTextSet = new TreeMap();
86
87     /** Index of font table in the first frame */
88     private int mFontTableIndex = -1;
89
90     /** Index of resource table in the first frame */
91     private int mResourceTableIndex = -1;
92
93     /** Default font */
94     private Font mDefaultFont = null;
95     private String JavaDoc mDefaultFontName = null;
96     private String JavaDoc mDefaultFontFileName = null;
97     private String JavaDoc mDefaultBoldFontFileName = null;
98     private String JavaDoc mDefaultItalicFontFileName = null;
99     private String JavaDoc mDefaultBoldItalicFontFileName = null;
100     // TODO: [2003-12-08 bloch] need logic to set this to true
101
private boolean mDefaultFontUsedForMeasurement = false;
102
103     /** Flash format version */
104     private int mFlashVersion = 6;
105
106     /** frame rate of movie */
107     private int mFrameRate = 30;
108
109     /** Leading for text and input text */
110     private int mTextLeading = 2;
111
112     /** media cache for transcoding */
113     private CompilerMediaCache mCache = null;
114
115     /** <String,Resource> maps resource files to the Resources
116      * definition in the swf file. */

117     protected Map mResourceMap = new HashMap();
118     protected Map mClickResourceMap = new HashMap();
119     protected Map mMultiFrameResourceMap = new HashMap();
120
121     private Map mDeviceFontTable = new HashMap();
122
123     /** Logger */
124     private static Logger mLogger = org.apache.log4j.Logger.getLogger(SWFWriter.class);
125
126     /** Height for generated advance (width) table */
127     public static final int DEFAULT_SIZE = 8;
128
129     /**
130      * Initialize jgenerator
131      */

132     static {
133         org.openlaszlo.iv.flash.util.Util.init(LPS.getConfigDirectory());
134     }
135
136     /**
137      * Compiled ExpiryProgram
138      */

139     private static Program mExpiryProgram = null;
140
141     /** Canvas Height */
142     private int mHeight = 0;
143
144     /** Canvas Width */
145     private int mWidth = 0;
146
147     /** Width of the "root" text output object. Defaults to canvas width. */
148     private int mMaxTextWidth = 0;
149
150
151     /** Height of the "root" text output object. Defaults to canvas height. */
152     private int mMaxTextHeight = 0;
153
154     /** Has the preloader been added? */
155     private boolean mPreloaderAdded = false;
156
157     /** InfoXML */
158     private Element mInfo = new Element("swf");
159
160     /** Logger for jgenerator */
161     /**
162      * Initializes a SWFWriter with an OutputStream to which a new SWF
163      * will be written when <code>SWFWriter.close()</code> is called.
164      *
165      * @param stream A <code>java.io.OutputStream</code> that the
166      * movie will be written to.
167      * @param props list of properties
168      * @param cache media cache
169      * @param importLibrary If true, the compiler will add in the LaszloLibrary.
170      */

171     SWFWriter(Properties props, OutputStream stream,
172               CompilerMediaCache cache,
173               boolean importLibrary,
174               CompilationEnvironment env) {
175         this.mProperties = props;
176         this.mCache = cache;
177         this.mStream = stream;
178         this.mEnv = env;
179
180         String JavaDoc s;
181
182         s = mProperties.getProperty("swf.frame.rate", "30");
183         mFrameRate = Integer.parseInt(s);
184
185         s = mProperties.getProperty("swf.text.leading", "2");
186         mTextLeading = Integer.parseInt(s);
187
188         mFlashVersion = env.getSWFVersionInt();
189
190         try {
191             if (!importLibrary) {
192                 mFlashFile = new SWFFile(props);
193                 mFlashFile.setVersion(mFlashVersion);
194                 mFlashFile.setMainScript(new Script(1));
195             }
196
197         } catch (CompilationError e) {
198             throw new ChainedException(e);
199         }
200     }
201
202     public void importBaseLibrary(String JavaDoc library, CompilationEnvironment env) {
203
204         try {
205             File f = env.resolve(library, "");
206             mFlashFile = new SWFFile(f.getAbsolutePath(), mProperties);
207             mFlashFile.setVersion(mFlashVersion);
208             // Set the frame rate (shifted)
209
mFlashFile.setFrameRate(mFrameRate << 8);
210             
211             // mFlashFile.printContent(System.out);
212

213             if (LPS.isInternalBuild()) {
214                 long lfcModTime = f.lastModified();
215                 List newerFiles = new Vector(); // List<File>
216
List sourceDirs = new Vector(); // List<File>
217
sourceDirs.add(f.getParentFile());
218                 while (!sourceDirs.isEmpty()) {
219                     File ff = (File) sourceDirs.get(0);
220                     sourceDirs.remove(0);
221                     if (ff.isDirectory()) {
222                         sourceDirs.addAll(Arrays.asList(ff.listFiles()));
223                     } else if (ff.isFile() && ff.getName().endsWith(".as")) {
224                         long modTime = ff.lastModified();
225                         if (modTime > lfcModTime)
226                             newerFiles.add(ff.getName());
227                     }
228                 }
229                 if (!newerFiles.isEmpty()) {
230                     env.warn(f.getName() + " is older than " +
231                              new ListFormat().format(newerFiles));
232                 }
233             }
234         } catch (FileNotFoundException e) {
235             throw new ChainedException(e);
236         }
237     }
238     
239     FontManager getFontManager() {
240         return mFontManager;
241     }
242
243     public boolean isDeviceFont(String JavaDoc face) {
244         return (mDeviceFontTable.get(face) != null);
245     }
246
247     public void setDeviceFont(String JavaDoc face) {
248         mDeviceFontTable.put(face,"true");
249     }
250
251     public void setFontManager(FontManager fm) {
252         this.mFontManager = fm;
253     }
254
255     void addPreloaderScript(String JavaDoc script) {
256         if (mPreloaderAdded != true)
257             throw new RuntimeException JavaDoc("Preloader not added yet.");
258
259         addScript(script, 0);
260     }
261         
262     void addPreloader(CompilationEnvironment env) {
263         if (mPreloaderAdded == true)
264             throw new RuntimeException JavaDoc("Preloader already added.");
265
266         // TODO: [2004-03-04 bloch] maybe someday we have different versions of
267
// preloader.lzl (e.g. debug, profile).
268
File f;
269         try {
270             f = env.resolve("lzpreloader.lzl", "");
271         } catch (FileNotFoundException e) {
272             throw new ChainedException(e);
273         }
274         mFlashFile.addPreloaderFrame(f.getAbsolutePath());
275         mLastFrame = 1;
276
277         mPreloaderAdded = true;
278     }
279
280     /**
281      * Sets the canvas for the movie
282      *
283      * @param canvas
284      *
285      */

286     void setCanvas(Canvas canvas, String JavaDoc canvasConstructor) {
287         Rectangle2D JavaDoc r = new Rectangle2D.Double JavaDoc(
288             0, 0,
289             (canvas.getWidth()*TWIP),
290             (canvas.getHeight()*TWIP));
291
292         mFlashFile.setFrameSize(r);
293
294         int bgc = canvas.getBGColor();
295         int red = (bgc >> 16) & 0xff;
296         int green = (bgc >> 8) & 0xff;
297         int blue = (bgc) & 0xff;
298         Color c = new Color(red, green, blue);
299         SetBackgroundColor setbgc = new SetBackgroundColor(c);
300         mFlashFile.getMainScript().setBackgroundColor(setbgc);
301         
302         // NOTE: disable constant pool when compiling canvas constructor
303
// so that build id etc... are easy for qa to pick out.
304
Properties props = (Properties)mProperties.clone();
305         props.setProperty("disableConstantPool", "1");
306         byte[] action = ScriptCompiler.compileToByteArray(canvasConstructor, props);
307         Program program = new Program(action, 0, action.length);
308         mLogger.debug(" Adding a program of " + action.length + " bytes.");
309         addProgram(program);
310
311         // Set width and height properties for preloader...
312
mWidth = canvas.getWidth();
313         mHeight = canvas.getHeight();
314
315         mMaxTextWidth = canvas.getMaxTextWidth();
316         // If zero, default to canvas width
317
if (mMaxTextWidth == 0) {
318             mMaxTextWidth = mWidth;
319         }
320
321         mMaxTextHeight = canvas.getMaxTextHeight();
322         // If zero, default to canvas height
323
if (mMaxTextHeight == 0) {
324             mMaxTextHeight = mHeight;
325         }
326
327         // Get default font info
328
FontInfo fontInfo = canvas.getFontInfo();
329
330         mDefaultFontName = canvas.defaultFont;
331         mDefaultFontFileName = canvas.defaultFontFilename;
332         mDefaultBoldFontFileName = canvas.defaultBoldFontFilename;
333         mDefaultItalicFontFileName = canvas.defaultItalicFontFilename;
334         mDefaultBoldItalicFontFileName = canvas.defaultBoldItalicFontFilename;
335
336         Frame frame = mFlashFile.getMainScript().getFrameAt(mLastFrame);
337
338         // Make a bogus object for us to replace with the font and resource table
339
// actionscript at the end of compilation.
340
mFontTableIndex = frame.size();
341         frame.addFlashObject(new SetBackgroundColor(new Color(0, 0, 0)));
342         mResourceTableIndex = frame.size();
343         frame.addFlashObject(new SetBackgroundColor(new Color(0, 0, 0)));
344
345         mEnv.getCanvas().addInfo(mInfo);
346     }
347
348
349     /** Get default fonts and stuff from canvas; used for snippet compilation */
350     void setCanvasDefaults(Canvas canvas, CompilerMediaCache mc) {
351         Rectangle2D JavaDoc r = new Rectangle2D.Double JavaDoc(
352             0, 0,
353             (canvas.getWidth()*TWIP),
354             (canvas.getHeight()*TWIP));
355
356         mFlashFile.setFrameSize(r);
357         this.mCache = mc;
358         mWidth = canvas.getWidth();
359         mHeight = canvas.getHeight();
360         // Get default font info
361
FontInfo fontInfo = canvas.getFontInfo();
362         mDefaultFontName = canvas.defaultFont;
363         mDefaultFontFileName = canvas.defaultFontFilename;
364         mDefaultBoldFontFileName = canvas.defaultBoldFontFilename;
365         mDefaultItalicFontFileName = canvas.defaultItalicFontFilename;
366         mDefaultBoldItalicFontFileName = canvas.defaultBoldItalicFontFilename;
367     }
368
369
370     /** Create a program from the given script */
371     Program program(String JavaDoc script) {
372          byte[] action = ScriptCompiler.compileToByteArray(script, mProperties);
373          return new Program(action, 0, action.length);
374     }
375
376     /** Compiles the specified script to bytecodes
377      * and add its bytecodes to the current frame in this movie.
378      *
379      * @param script the script to be compiled
380      * @return the number of bytes
381      */

382     int addScript(String JavaDoc script) {
383          byte[] action = ScriptCompiler.compileToByteArray(script, mProperties);
384          Program program = new Program(action, 0, action.length);
385          mLogger.debug(" Adding a program of " + action.length + " bytes.");
386          addProgram(program);
387          return action.length;
388     }
389
390     /** Compiles the specified script to bytecodes
391      * and add its bytecodes to the current frame in this movie.
392      *
393      * @param script the script to be compiled
394      * @param offset of frame to add to
395      */

396     private void addScript(String JavaDoc script, int offset) {
397          byte[] action = ScriptCompiler.compileToByteArray(script, mProperties);
398          Program program = new Program(action, 0, action.length);
399          mLogger.debug(" Adding a program of " + action.length + " bytes.");
400          addProgram(program, offset);
401     }
402
403     /**
404      * Adds the program to the next frame
405      *
406      * @param program to be added
407      */

408     void addProgram(Program program) {
409          Frame frame = mFlashFile.getMainScript().getFrameAt(mLastFrame);
410          frame.addFlashObject(new DoAction(program));
411     }
412     
413     /**
414      * Adds the program to the specified frame
415      *
416      * @param program to be added
417      * @param offset of frame to add to
418      */

419     private void addProgram(Program program, int offset) {
420          Frame frame = mFlashFile.getMainScript().getFrameAt(offset);
421          frame.addFlashObject(new DoAction(program));
422     }
423
424
425     class ImportResourceError extends CompilationError {
426
427         ImportResourceError(String JavaDoc fileName, CompilationEnvironment env) {
428             super("Can't import " + stripBaseName(fileName, env));
429         }
430         ImportResourceError(String JavaDoc fileName, String JavaDoc type, CompilationEnvironment env) {
431             super("Can't import " + type + " " + stripBaseName(fileName, env));
432         }
433         ImportResourceError(String JavaDoc fileName, Exception JavaDoc e, CompilationEnvironment env) {
434             super("Can't import " + stripBaseName(fileName, env) + ": " + e.getMessage());
435         }
436         ImportResourceError(String JavaDoc fileName, Exception JavaDoc e, String JavaDoc type, CompilationEnvironment env) {
437             super("Can't import " + type + " " + stripBaseName(fileName, env) + ": " + e.getMessage());
438         }
439
440     }
441
442     public static String JavaDoc stripBaseName (String JavaDoc fileName, CompilationEnvironment env) {
443         try {
444             fileName = (new File(fileName)).getCanonicalFile().toString();
445         } catch (java.io.IOException JavaDoc e) {
446         }
447         String JavaDoc base = env.getErrorHandler().fileBase;
448         if (base == null || "".equals(base)) {
449             return fileName;
450         }
451         base = (new File(base)).getAbsolutePath() + File.separator;
452         if (base != null) {
453             int i = 1;
454             // Find longest common substring
455
while (i < base.length() && fileName.startsWith(base.substring(0, i))) { i++; }
456             // remove base string prefix
457
return fileName.substring(i-1);
458         } else {
459             return fileName;
460         }
461     }
462
463    /** Find a resource for importing into a movie and return a flashdef.
464      * Doesn't includes stop action.
465      *
466      * @param fileName file name of the resource
467      * @param name name of the resource
468      */

469     private Resource getMultiFrameResource(String JavaDoc fileName, String JavaDoc name, int fNum)
470         throws ImportResourceError
471     {
472         Resource res = (Resource)mMultiFrameResourceMap.get(fileName);
473         if (res != null) {
474             return res;
475         }
476
477         res = getResource(fileName, "$" + name + "_lzf_" + (fNum+1), false);
478
479         mMultiFrameResourceMap.put(fileName, res);
480         return res;
481     }
482
483    /** Find a resource for importing into a movie and return a flashdef.
484      * Includes stop action.
485      *
486      * @param fileName file name of the resource
487      * @param name name of the resource
488      */

489     private Resource getResource(String JavaDoc fileName, String JavaDoc name)
490         throws ImportResourceError
491     {
492         return getResource(fileName, name, true);
493     }
494
495    /** Find a resource for importing into a movie and return a flashdef.
496      *
497      * @param name name of the resource
498      * @param fileName file name of the resource
499      * @param stop include stop action if true
500      */

501     private Resource getResource(String JavaDoc fileName, String JavaDoc name, boolean stop)
502         throws ImportResourceError
503     {
504         try {
505             String JavaDoc inputMimeType = MimeType.fromExtension(fileName);
506             if (!Transcoder.canTranscode(inputMimeType, MimeType.SWF)
507                 && !inputMimeType.equals(MimeType.SWF)) {
508                 inputMimeType = Transcoder.guessSupportedMimeTypeFromContent(fileName);
509                 if (inputMimeType == null || inputMimeType.equals("")) {
510                     throw new ImportResourceError(fileName, new Exception JavaDoc("bad mime type"), mEnv);
511                 }
512             }
513             // No need to get these from the cache since they don't need to be
514
// transcoded and we usually keep the cmcache on disk.
515
if (inputMimeType.equals(MimeType.SWF)) {
516         
517                 long fileSize = FileUtils.getSize(new File(fileName));
518     
519                 Element elt = new Element("resource");
520                     elt.setAttribute("name", name);
521                     elt.setAttribute("mime-type", inputMimeType);
522                     elt.setAttribute("source", fileName);
523                     elt.setAttribute("filesize", "" + fileSize);
524                 mInfo.addContent(elt);
525     
526                 return importSWF(fileName, name, false);
527             }
528
529             // TODO: [2002-12-3 bloch] use cache for mp3s; for now we're skipping it
530
// arguably, this is a fixme
531
if (inputMimeType.equals(MimeType.MP3) ||
532                 inputMimeType.equals(MimeType.XMP3)) {
533                 return importMP3(fileName, name);
534             }
535     
536             File inputFile = new File(fileName);
537             File outputFile = mCache.transcode(inputFile, inputMimeType, MimeType.SWF);
538             mLogger.debug("importing: " + fileName + " as " + name +
539                           " from cache; size: " + outputFile.length());
540
541             long fileSize = FileUtils.getSize(outputFile);
542
543             Element elt = new Element("resource");
544                 elt.setAttribute("name", name);
545                 elt.setAttribute("mime-type", inputMimeType);
546                 elt.setAttribute("source", fileName);
547                 elt.setAttribute("filesize", "" + fileSize);
548             mInfo.addContent(elt);
549
550             return importSWF(outputFile.getPath(), name, stop);
551         } catch (Exception JavaDoc e) {
552             mLogger.error("Can't get resource " + fileName);
553             throw new ImportResourceError(fileName, e, mEnv);
554         }
555
556     }
557
558     /** Import a resource file into the preloader movie.
559      * Using a name that already exists clobbers the
560      * old resource (for now).
561      *
562      * @param fileName file name of the resource
563      * @param name name of the MovieClip/Sprite
564      * @throws CompilationError
565      */

566     public void importPreloadResource(String JavaDoc fileName, String JavaDoc name)
567         throws ImportResourceError
568     {
569         if (name.equals(""))
570             name = createName();
571         importResource(fileName, name, 0, mPreloaderFontsCollector);
572     }
573
574     /** Import a multiframe resource into the current movie. Using a
575      * name that already exists clobbers the old resource (for now).
576      */

577     public void importPreloadResource(List sources, String JavaDoc name, File parent)
578         throws ImportResourceError
579     {
580         if (name.equals(""))
581             name = createName();
582         importResource(sources, name, parent, 0, mPreloaderFontsCollector);
583     }
584
585     /** Import a resource file into the current movie.
586      * Using a name that already exists clobbers the
587      * old resource (for now).
588      *
589      * @param fileName file name of the resource
590      * @param name name of the MovieClip/Sprite
591      * @throws CompilationError
592      */

593     public void importResource(String JavaDoc fileName, String JavaDoc name)
594         throws ImportResourceError
595     {
596         importResource(fileName, name, -1);
597     }
598
599     /** Import a resource file into the current movie.
600      * Using a name that already exists clobbers the
601      * old resource (for now).
602      *
603      * @param fileName file name of the resource
604      * @param name name of the MovieClip/Sprite
605      * @param frameNum frame offset to add to
606      * @throws CompilationError
607      */

608     public void importResource(String JavaDoc fileName, String JavaDoc name, int frameNum)
609         throws CompilationError
610     {
611         importResource(fileName, name, frameNum, null);
612     }
613
614
615     /** Import a resource file into the current movie.
616      * Using a name that already exists clobbers the
617      * old resource (for now).
618      *
619      * @param fileName file name of the resource
620      * @param name name of the MovieClip/Sprite
621      * @param frameNum frame offset to add to
622      * @param fontsCollector fonts collector for resource (used by preloader)
623      * @throws CompilationError
624      */

625     public void importResource(String JavaDoc fileName, String JavaDoc name, int frameNum,
626                                FontsCollector fontsCollector)
627         throws CompilationError
628     {
629         mLogger.debug(" Importing resource " + name);
630
631         try {
632             fileName = new File(fileName).getCanonicalPath();
633         } catch (java.io.IOException JavaDoc e) {
634             throw new ImportResourceError(fileName, e, mEnv);
635         }
636         FlashDef def = null;
637
638         Resource res = (Resource)mResourceMap.get(fileName);
639         boolean oldRes = (res != null);
640         if (!oldRes) {
641             // Get the resource and put in the map
642
res = getResource(fileName, name);
643             mResourceMap.put(fileName, res);
644
645             def = res.getFlashDef();
646             if (fontsCollector != null) def.collectFonts(fontsCollector);
647             mFlashFile.addDefToLibrary(name, def);
648             def.setName(name);
649         
650         } else {
651             def = res.getFlashDef();
652             if (fontsCollector != null) def.collectFonts(fontsCollector);
653             // Add an element with 0 size, since it's already there.
654
Element elt = new Element("resource");
655                 elt.setAttribute("name", name);
656                 // elt.setAttribute("mime-type", MimeType.MP3);
657
elt.setAttribute("source", fileName);
658                 elt.setAttribute("filesize", "0");
659             mInfo.addContent(elt);
660         }
661         
662     
663         ExportAssets ea = new ExportAssets();
664         ea.addAsset(name, def);
665         Timeline timeline = mFlashFile.getMainScript().getTimeline();
666         if (frameNum == -1) {
667             frameNum = timeline.getFrameCount() - 1;
668         }
669         Frame frame = timeline.getFrameAt(frameNum);
670         frame.addFlashObject(ea);
671     }
672     
673     /** Imports file, if it has not previously been imported, and
674      * returns in any case the name of the clip that refers to it.
675      * File should refer to a graphical asset. */

676     public String JavaDoc importResource(File file)
677     {
678         Resource res;
679
680         try {
681             file = file.getCanonicalFile();
682             res = (Resource) mResourceMap.get(file.getCanonicalPath());
683         } catch (java.io.IOException JavaDoc e) {
684             throw new CompilationError(e);
685         }
686         if (res == null) {
687             String JavaDoc clipName = createName();
688             importResource(file.getPath(), clipName);
689             return clipName;
690         } else {
691             return res.getName();
692         }
693     }
694
695     /** Imports this resource, if it has not previously been imported, as
696      * resource that can be used as a click region, and returns in any
697      * case the name of the clip that refers to it.
698      */

699     public String JavaDoc importClickResource(File file) throws ImportResourceError
700     {
701         String JavaDoc fileName;
702         try {
703             fileName = file.getCanonicalPath();
704         } catch (java.io.IOException JavaDoc e) {
705             throw new CompilationError(e);
706         }
707
708         String JavaDoc name = (String JavaDoc) mClickResourceMap.get(fileName);
709
710         if (name == null) {
711
712             Button2 but = new Button2();
713
714             FlashDef def;
715             Rectangle2D JavaDoc bounds;
716
717             // FIXME: [2004-06-29 bloch]
718
// For each instance in the first frame, add a button record.
719
// Get bounds for entire clip; should only get bounds for first frame!
720
// Should only allow swf resources as click resources.
721
try {
722                 Script script = FlashFile.parse(fileName).getMainScript();
723                 Frame frame = script.getFrameAt(0);
724                 bounds = script.getBounds();
725                 for(int i = 0; i < frame.size(); i++) {
726                     FlashObject o = (FlashObject)frame.getFlashObjectAt(i);
727                     if (o instanceof Instance) {
728                         Instance inst = (Instance)o;
729                         CXForm cxform = inst.cxform;
730                         if (cxform == null) {
731                             cxform = CXForm.newIdentity(false);
732                         }
733                         java.awt.geom.AffineTransform JavaDoc matrix = inst.matrix;
734                         if (matrix == null) {
735                             matrix = new java.awt.geom.AffineTransform JavaDoc();
736                         }
737                          
738                         but.addButtonRecord(new ButtonRecord(ButtonRecord.HitTest,
739                                                  inst.def, 1, matrix, cxform));
740                     }
741                 }
742             } catch (Exception JavaDoc e) {
743                 throw new ImportResourceError(fileName, e, mEnv);
744             }
745
746             // TODO: [2004-06029 bloch] When we merge into lps-intl2, there should
747
// be some code sharing between this and SWFFile
748
but.addActionCondition(ActionCondition.onPress(program(
749                 "_root.LzModeManager.handleMouseButton( myView, 'onmousedown')")));
750             but.addActionCondition(ActionCondition.onRelease(program(
751                 "_root.LzModeManager.handleMouseButton( myView, 'onmouseup');" +
752                 "_root.LzModeManager.handleMouseEvent( myView, 'onclick')")));
753             but.addActionCondition(ActionCondition.onReleaseOutside(program(
754                 "_root.LzModeManager.handleMouseButton( myView, 'onmouseup');" +
755                 "_root.LzModeManager.handleMouseEvent( myView, 'onmouseupoutside')")));
756             but.addActionCondition(ActionCondition.onRollOver(program(
757                 "_root.LzModeManager.handleMouseEvent( myView, 'onmouseover')")));
758             but.addActionCondition(ActionCondition.onRollOut(program(
759                 "_root.LzModeManager.handleMouseEvent( myView, 'onmouseout')")));
760             but.addActionCondition(ActionCondition.onDragOut(program(
761                 "_root.LzModeManager.handleMouseEvent( myView, 'onmouseout');" +
762                 "_root.LzModeManager.handleMouseEvent( myView, 'onmousedragout')")));
763             but.addActionCondition(ActionCondition.onDragOver(program(
764                 "_root.LzModeManager.handleMouseEvent( myView, 'onmouseover');" +
765                 "_root.LzModeManager.handleMouseEvent( myView, 'onmousedragin')")));
766             
767             name = createName();
768
769             // Scale the movieclip to 100x100 for use by LFC.
770
Script movieClip = new Script(1);
771             Frame f = movieClip.getFrameAt(0);
772             double sx = 100.0 * TWIP / (double)bounds.getWidth();
773             double sy = 100.0 * TWIP / (double)bounds.getHeight();
774             java.awt.geom.AffineTransform JavaDoc matrix = java.awt.geom.AffineTransform.getScaleInstance(sx, sy);
775             f.addInstance(but, 1, matrix, null);
776
777             mFlashFile.addDefToLibrary(name, movieClip);
778             movieClip.setName(name);
779             ExportAssets ea = new ExportAssets();
780             ea.addAsset(name, movieClip);
781             Timeline timeline = mFlashFile.getMainScript().getTimeline();
782             Frame frame = timeline.getFrameAt(timeline.getFrameCount() - 1);
783             frame.addFlashObject(ea);
784
785             mClickResourceMap.put(fileName, name);
786         }
787
788         return name;
789     }
790
791     /** Import a multiframe resource into the current movie. Using a
792      * name that already exists clobbers the old resource (for now).
793      *
794      * @param sources file names of the resources
795      * @param name name of the MovieClip/Sprite
796      * @param parent parent's File object
797      */

798     public void importResource(List sources, String JavaDoc name, File parent)
799     {
800         importResource(sources, name, parent, -1);
801     }
802
803     /** Import a multiframe resource into the current movie. Using a
804      * name that already exists clobbers the old resource (for now).
805      *
806      * @param sources file names of the resources
807      * @param name name of the MovieClip/Sprite
808      * @param parent parent's File object
809      * @param frameNum frame offset to add to
810      */

811     public void importResource(List sources, String JavaDoc name, File parent, int frameNum)
812     {
813         importResource(sources, name, parent, frameNum, null);
814     }
815
816
817     /** Import a multiframe resource into the current movie. Using a
818      * name that already exists clobbers the old resource (for now).
819      *
820      * @param sources file names of the resources
821      * @param name name of the MovieClip/Sprite
822      * @param parent parent's File object
823      * @param frameNum frame offset to add to
824      * @param fontsCollector fonts collector for resource (used by preloader)
825      */

826     public void importResource(List sources, String JavaDoc name, File parent, int frameNum,
827                                FontsCollector fontsCollector)
828     {
829         Script out = new Script(1);
830         String JavaDoc fileName = null;
831         mLogger.debug("Including multiple resources as " + name);
832         int width = 0;
833         int height = 0;
834         int fNum = 0;
835         for (Iterator e = sources.iterator() ; e.hasNext() ;) {
836             fileName = (String JavaDoc)e.next();
837