001/*
002 * SVG Salamander
003 * Copyright (c) 2004, Mark McKay
004 * All rights reserved.
005 *
006 * Redistribution and use in source and binary forms, with or 
007 * without modification, are permitted provided that the following
008 * conditions are met:
009 *
010 *   - Redistributions of source code must retain the above 
011 *     copyright notice, this list of conditions and the following
012 *     disclaimer.
013 *   - Redistributions in binary form must reproduce the above
014 *     copyright notice, this list of conditions and the following
015 *     disclaimer in the documentation and/or other materials 
016 *     provided with the distribution.
017 *
018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
019 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
020 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
021 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
022 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
023 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
025 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
026 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
027 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
028 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
029 * OF THE POSSIBILITY OF SUCH DAMAGE. 
030 * 
031 * Mark McKay can be contacted at mark@kitfox.com.  Salamander and other
032 * projects can be found at http://www.kitfox.com
033 *
034 * Created on April 3, 2004, 5:28 PM
035 */
036
037package com.kitfox.svg.app;
038
039
040import com.kitfox.svg.SVGConst;
041import com.kitfox.svg.SVGDiagram;
042import com.kitfox.svg.SVGDisplayPanel;
043import com.kitfox.svg.SVGElement;
044import com.kitfox.svg.SVGException;
045import com.kitfox.svg.SVGUniverse;
046import java.awt.Color;
047import java.awt.event.MouseAdapter;
048import java.awt.event.MouseEvent;
049import java.awt.geom.Point2D;
050import java.io.File;
051import java.io.InputStream;
052import java.net.URI;
053import java.net.URL;
054import java.net.URLEncoder;
055import java.security.AccessControlException;
056import java.util.ArrayList;
057import java.util.List;
058import java.util.Vector;
059import java.util.logging.Level;
060import java.util.logging.Logger;
061import java.util.regex.Matcher;
062import java.util.regex.Pattern;
063import javax.swing.JFileChooser;
064import javax.swing.JOptionPane;
065
066/**
067 * @author Mark McKay
068 * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
069 */
070public class SVGPlayer extends javax.swing.JFrame
071{
072    public static final long serialVersionUID = 1;
073
074    SVGDisplayPanel svgDisplayPanel = new SVGDisplayPanel();
075
076    final PlayerDialog playerDialog;
077    
078    SVGUniverse universe;
079    
080    /** FileChooser for running in trusted environments */
081    final JFileChooser fileChooser;
082    {
083//        fileChooser = new JFileChooser(new File("."));
084        JFileChooser fc = null;
085        try
086        {
087            fc = new JFileChooser();
088            fc.setFileFilter(
089                new javax.swing.filechooser.FileFilter() {
090                    final Matcher matchLevelFile = Pattern.compile(".*\\.svg[z]?").matcher("");
091
092                    public boolean accept(File file)
093                    {
094                        if (file.isDirectory()) return true;
095
096                        matchLevelFile.reset(file.getName());
097                        return matchLevelFile.matches();
098                    }
099
100                    public String getDescription() { return "SVG file (*.svg, *.svgz)"; }
101                }
102            );
103        }
104        catch (AccessControlException ex)
105        {
106            //Do not create file chooser if webstart refuses permissions
107        }
108        fileChooser = fc;
109    }
110
111    /** Backup file service for opening files in WebStart situations */
112    /*
113    final FileOpenService fileOpenService;
114    {
115        try 
116        { 
117            fileOpenService = (FileOpenService)ServiceManager.lookup("javax.jnlp.FileOpenService"); 
118        } 
119        catch (UnavailableServiceException e) 
120        { 
121            fileOpenService = null; 
122        } 
123    }
124     */
125    
126    /** Creates new form SVGViewer */
127    public SVGPlayer() {
128        initComponents();
129
130        setSize(800, 600);
131
132        svgDisplayPanel.setBgColor(Color.white);
133        svgDisplayPanel.addMouseListener(new MouseAdapter()
134        {
135            public void mouseClicked(MouseEvent evt)
136            {
137                SVGDiagram diagram = svgDisplayPanel.getDiagram();
138                if (diagram == null) return;
139                
140                System.out.println("Picking at cursor (" + evt.getX() + ", " + evt.getY() + ")");
141                try
142                {
143                    List paths = diagram.pick(new Point2D.Float(evt.getX(), evt.getY()), null);
144                    for (int i = 0; i < paths.size(); i++)
145                    {
146                        ArrayList path = (ArrayList)paths.get(i);
147                        System.out.println(pathToString(path));
148                    }
149                }
150                catch (SVGException ex)
151                {
152                    Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, 
153                        "Could not pick", ex);
154                }
155            }
156        }
157        );
158        
159        svgDisplayPanel.setPreferredSize(getSize());
160        scrollPane_svgArea.setViewportView(svgDisplayPanel);
161        
162        playerDialog = new PlayerDialog(this);
163    }
164    
165    private String pathToString(List path)
166    {
167        if (path.size() == 0) return "";
168        
169        StringBuffer sb = new StringBuffer();
170        sb.append(path.get(0));
171        for (int i = 1; i < path.size(); i++)
172        {
173            sb.append("/");
174            sb.append(((SVGElement)path.get(i)).getId());
175        }
176        return sb.toString();
177    }
178    
179    public void updateTime(double curTime)
180    {
181        try
182        {
183            if (universe != null)
184            {
185                universe.setCurTime(curTime);
186                universe.updateTime();
187    //            svgDisplayPanel.updateTime(curTime);
188                repaint();
189            }
190        }
191        catch (Exception e)
192        {
193            Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e);
194        }
195    }
196
197    private void loadURL(URL url)
198    {
199        boolean verbose = cmCheck_verbose.isSelected();
200
201        universe = new SVGUniverse();
202        universe.setVerbose(verbose);
203        SVGDiagram diagram = null;
204
205        if (!CheckBoxMenuItem_anonInputStream.isSelected())
206        {
207            //Load from a disk with a valid URL
208            URI uri = universe.loadSVG(url);
209
210            if (verbose) System.err.println(uri.toString());
211
212            diagram = universe.getDiagram(uri);
213        }
214        else
215        {
216            //Load from a stream with no particular valid URL
217            try
218            {
219                InputStream is = url.openStream();
220                URI uri = universe.loadSVG(is, "defaultName");
221
222                if (verbose) System.err.println(uri.toString());
223
224                diagram = universe.getDiagram(uri);
225            }
226            catch (Exception e)
227            {
228                Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e);
229            }
230        }
231
232        svgDisplayPanel.setDiagram(diagram);
233        repaint();
234    }
235    
236    /** This method is called from within the constructor to
237     * initialize the form.
238     * WARNING: Do NOT modify this code. The content of this method is
239     * always regenerated by the Form Editor.
240     */
241    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
242    private void initComponents()
243    {
244        scrollPane_svgArea = new javax.swing.JScrollPane();
245        jMenuBar1 = new javax.swing.JMenuBar();
246        menu_file = new javax.swing.JMenu();
247        cm_loadFile = new javax.swing.JMenuItem();
248        cm_loadUrl = new javax.swing.JMenuItem();
249        menu_window = new javax.swing.JMenu();
250        cm_player = new javax.swing.JMenuItem();
251        jSeparator2 = new javax.swing.JSeparator();
252        cm_800x600 = new javax.swing.JMenuItem();
253        CheckBoxMenuItem_anonInputStream = new javax.swing.JCheckBoxMenuItem();
254        cmCheck_verbose = new javax.swing.JCheckBoxMenuItem();
255        menu_help = new javax.swing.JMenu();
256        cm_about = new javax.swing.JMenuItem();
257
258        setTitle("SVG Player - Salamander Project");
259        addWindowListener(new java.awt.event.WindowAdapter()
260        {
261            public void windowClosing(java.awt.event.WindowEvent evt)
262            {
263                exitForm(evt);
264            }
265        });
266
267        getContentPane().add(scrollPane_svgArea, java.awt.BorderLayout.CENTER);
268
269        menu_file.setMnemonic('f');
270        menu_file.setText("File");
271        cm_loadFile.setMnemonic('l');
272        cm_loadFile.setText("Load File...");
273        cm_loadFile.addActionListener(new java.awt.event.ActionListener()
274        {
275            public void actionPerformed(java.awt.event.ActionEvent evt)
276            {
277                cm_loadFileActionPerformed(evt);
278            }
279        });
280
281        menu_file.add(cm_loadFile);
282
283        cm_loadUrl.setText("Load URL...");
284        cm_loadUrl.addActionListener(new java.awt.event.ActionListener()
285        {
286            public void actionPerformed(java.awt.event.ActionEvent evt)
287            {
288                cm_loadUrlActionPerformed(evt);
289            }
290        });
291
292        menu_file.add(cm_loadUrl);
293
294        jMenuBar1.add(menu_file);
295
296        menu_window.setText("Window");
297        cm_player.setText("Player");
298        cm_player.addActionListener(new java.awt.event.ActionListener()
299        {
300            public void actionPerformed(java.awt.event.ActionEvent evt)
301            {
302                cm_playerActionPerformed(evt);
303            }
304        });
305
306        menu_window.add(cm_player);
307
308        menu_window.add(jSeparator2);
309
310        cm_800x600.setText("800 x 600");
311        cm_800x600.addActionListener(new java.awt.event.ActionListener()
312        {
313            public void actionPerformed(java.awt.event.ActionEvent evt)
314            {
315                cm_800x600ActionPerformed(evt);
316            }
317        });
318
319        menu_window.add(cm_800x600);
320
321        CheckBoxMenuItem_anonInputStream.setText("Anonymous Input Stream");
322        menu_window.add(CheckBoxMenuItem_anonInputStream);
323
324        cmCheck_verbose.setText("Verbose");
325        cmCheck_verbose.addActionListener(new java.awt.event.ActionListener()
326        {
327            public void actionPerformed(java.awt.event.ActionEvent evt)
328            {
329                cmCheck_verboseActionPerformed(evt);
330            }
331        });
332
333        menu_window.add(cmCheck_verbose);
334
335        jMenuBar1.add(menu_window);
336
337        menu_help.setText("Help");
338        cm_about.setText("About...");
339        cm_about.addActionListener(new java.awt.event.ActionListener()
340        {
341            public void actionPerformed(java.awt.event.ActionEvent evt)
342            {
343                cm_aboutActionPerformed(evt);
344            }
345        });
346
347        menu_help.add(cm_about);
348
349        jMenuBar1.add(menu_help);
350
351        setJMenuBar(jMenuBar1);
352
353        pack();
354    }// </editor-fold>//GEN-END:initComponents
355
356    private void cm_loadUrlActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_cm_loadUrlActionPerformed
357    {//GEN-HEADEREND:event_cm_loadUrlActionPerformed
358        String urlStrn = JOptionPane.showInputDialog(this, "Enter URL of SVG file");
359        if (urlStrn == null) return;
360        
361        try
362        {
363            URL url = new URL(URLEncoder.encode(urlStrn, "UTF-8"));
364            loadURL(url);
365        }
366        catch (Exception e)
367        {
368            Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e);
369        }
370
371    }//GEN-LAST:event_cm_loadUrlActionPerformed
372
373    private void cmCheck_verboseActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_cmCheck_verboseActionPerformed
374    {//GEN-HEADEREND:event_cmCheck_verboseActionPerformed
375// TODO add your handling code here:
376    }//GEN-LAST:event_cmCheck_verboseActionPerformed
377
378    private void cm_playerActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_cm_playerActionPerformed
379    {//GEN-HEADEREND:event_cm_playerActionPerformed
380        playerDialog.setVisible(true);
381    }//GEN-LAST:event_cm_playerActionPerformed
382
383    private void cm_aboutActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_cm_aboutActionPerformed
384    {//GEN-HEADEREND:event_cm_aboutActionPerformed
385        VersionDialog dia = new VersionDialog(this, true, cmCheck_verbose.isSelected());
386        dia.setVisible(true);
387//        JOptionPane.showMessageDialog(this, "Salamander SVG - Created by Mark McKay\nhttp://www.kitfox.com");
388    }//GEN-LAST:event_cm_aboutActionPerformed
389
390    private void cm_800x600ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cm_800x600ActionPerformed
391        setSize(800, 600);
392    }//GEN-LAST:event_cm_800x600ActionPerformed
393    
394    private void cm_loadFileActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_cm_loadFileActionPerformed
395    {//GEN-HEADEREND:event_cm_loadFileActionPerformed
396        boolean verbose = cmCheck_verbose.isSelected();
397        
398        try
399        {
400            int retVal = fileChooser.showOpenDialog(this);
401            if (retVal == JFileChooser.APPROVE_OPTION)
402            {
403                File chosenFile = fileChooser.getSelectedFile();
404
405                URL url = chosenFile.toURI().toURL();
406
407                loadURL(url);
408            }
409        }
410        catch (Exception e)
411        {
412            Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e);
413        }
414
415    }//GEN-LAST:event_cm_loadFileActionPerformed
416
417    /** Exit the Application */
418    private void exitForm(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_exitForm
419        System.exit(0);
420    }//GEN-LAST:event_exitForm
421
422    /**
423     * @param args the command line arguments
424     */
425    public static void main(String args[]) {
426        new SVGPlayer().setVisible(true);
427    }
428
429    public void updateTime(double curTime, double timeStep, int playState)
430    {
431    }
432    
433    // Variables declaration - do not modify//GEN-BEGIN:variables
434    private javax.swing.JCheckBoxMenuItem CheckBoxMenuItem_anonInputStream;
435    private javax.swing.JCheckBoxMenuItem cmCheck_verbose;
436    private javax.swing.JMenuItem cm_800x600;
437    private javax.swing.JMenuItem cm_about;
438    private javax.swing.JMenuItem cm_loadFile;
439    private javax.swing.JMenuItem cm_loadUrl;
440    private javax.swing.JMenuItem cm_player;
441    private javax.swing.JMenuBar jMenuBar1;
442    private javax.swing.JSeparator jSeparator2;
443    private javax.swing.JMenu menu_file;
444    private javax.swing.JMenu menu_help;
445    private javax.swing.JMenu menu_window;
446    private javax.swing.JScrollPane scrollPane_svgArea;
447    // End of variables declaration//GEN-END:variables
448
449}