/*
 Tiamat - a role-playing game framework
 SequenceEditorFrame.java - main window for editing a Sequence
 
 Copyright  (C) 2006 Hugues Johnson
 
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
 the GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

package com.huguesjohnson.tiamat.tools;

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;

import javax.swing.BoxLayout;
import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.ScrollPaneConstants;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import com.huguesjohnson.ImageLoader;
import com.huguesjohnson.PathResolver;
import com.huguesjohnson.fileFilter.ImageFileFilter;
import com.huguesjohnson.fileFilter.MidiFileFilter;
import com.huguesjohnson.fileFilter.XmlFileFilter;
import com.huguesjohnson.serialization.XmlFormatter;
import com.huguesjohnson.tiamat.Event;
import com.huguesjohnson.tiamat.audio.MidiPlayer;
import com.huguesjohnson.tiamat.graphics.Sequence;
import com.huguesjohnson.tiamat.graphics.SequenceFrame;
import com.huguesjohnson.ui.FileBrowsePanel;

/** SequenceEditorFrame - main window for editing a Sequence
 * @author Hugues Johnson
 */
public class SequenceEditorFrame extends BaseEditorFrame{
	/* Determines if a de-serialized file is compatible with this class. See <a href="http://java.sun.com/j2se/1.5.0/docs/guide/serialization/spec/class.html">Java Object Serialization Specifications</a>. */
	private static final long serialVersionUID=-4447001794083788324L;

	//used to store items in the list
	private class ListEntry{
		public SequenceFrame frame;
		public String caption;
		public ListEntry(){ /* used for cloneEntry() */ }
		public ListEntry(String caption,SequenceFrame frame){
			this.caption=new String(caption);
			this.frame=frame;
		}
		public String toString(){ return(this.caption); }
		public ListEntry cloneEntry(){
			ListEntry clone=new ListEntry();
			clone.caption=new String(this.caption);
			clone.frame=new SequenceFrame();
			if(this.frame.getBgImagePath()!=null){ clone.frame.setBgImagePath(this.frame.getBgImagePath()); }
			if(this.frame.getBgMusicPath()!=null){ clone.frame.setBgMusicPath(this.frame.getBgMusicPath()); }
			if(this.frame.getText()!=null){ clone.frame.setText(this.frame.getText()); }
			return(clone);
		}
	}
	
	//ui components
	private FileBrowsePanel browsePanel;
	private JList frameList;
	private JButton addButton;
	private JButton removeButton;
	private JButton upButton;
	private JButton downButton;
	private JTextArea text;
	private JTextField bgMusicPath;
	private JButton bgMusicBrowse;
	private JToggleButton bgMusicPlay;
	private JLabel previewImageLabel;
	private JButton bgImageBrowse;
	private JTextField bgImagePath;
	private EventListDialog eventListDialog;
	
	//used to preview the background music
	private MidiPlayer player;
	
	//the preview image
	private BufferedImage previewImage;
	
	//it's so stupid that I need to manually track this
	private int currentFrameIndex;
	
	//the exit events for this sequence
	private Event[] exitEvents;
	
	public SequenceEditorFrame(){
		super("Sequence");
		this.currentFrameIndex=-1;
		this.onNew();
	}
	
	public SequenceEditorFrame(String filePath){
		super("Sequence");
		this.currentFrameIndex=-1;
		this.onNew();
		this.openSequence(filePath);
	}	
	
	protected void initComponents(){
		//set the layout
		this.panelContent.setLayout(new BoxLayout(this.panelContent,BoxLayout.Y_AXIS));
		
		//add the browse panel
		this.browsePanel=new FileBrowsePanel("Sequence File",new XmlFileFilter(),false);
		this.panelContent.add(this.browsePanel);
		
		//create the navigation panel
		JPanel navigationPanel=new JPanel();
		navigationPanel.setLayout(new BoxLayout(navigationPanel,BoxLayout.Y_AXIS));
		navigationPanel.setBorder(new TitledBorder("Frames"));
		this.frameList=new JList();
		this.frameList.setMinimumSize(new Dimension(120,240));
		this.frameList.setPreferredSize(new Dimension(120,240));
		this.frameList.addListSelectionListener(new ListSelectionListener(){
			public void valueChanged(ListSelectionEvent e){
				onListSelectionChanged(e);
			}
       	});		
		JScrollPane scrollPane=new JScrollPane(this.frameList);
		navigationPanel.add(scrollPane);
		JPanel buttonPanel=new JPanel();
		buttonPanel.setLayout(new BoxLayout(buttonPanel,BoxLayout.X_AXIS));
		JToolBar toolBar=new JToolBar();
        toolBar.setFloatable(false);
        toolBar.setRollover(true);
        this.addButton=new JButton();
        this.addButton.setIcon(new ImageIcon(this.getClass().getResource("/com/huguesjohnson/tiamat/tools/images/plus.gif")));
        this.addButton.setToolTipText("Add new frame");
        this.addButton.setEnabled(true);
        this.addButton.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent actionEvent){
                onAdd();
            }
       	});
		toolBar.add(this.addButton);
		this.removeButton=new JButton();
		this.removeButton.setIcon(new ImageIcon(this.getClass().getResource("/com/huguesjohnson/tiamat/tools/images/minus.gif")));
		this.removeButton.setToolTipText("Delete selected frame");
        this.removeButton.setEnabled(false);
		this.removeButton.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent actionEvent){
            	onRemove();
            }
		});
		toolBar.add(this.removeButton);
		toolBar.addSeparator();
		this.upButton=new JButton();
		this.upButton.setIcon(new ImageIcon(this.getClass().getResource("/com/huguesjohnson/tiamat/tools/images/up.gif")));
		this.upButton.setToolTipText("Move selected frame up");
		this.upButton.setEnabled(false);
		this.upButton.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent actionEvent){
                onUp();
            }
        });
		toolBar.add(this.upButton);
		this.downButton=new JButton();
		this.downButton.setIcon(new ImageIcon(this.getClass().getResource("/com/huguesjohnson/tiamat/tools/images/down.gif")));
		this.downButton.setToolTipText("Move selected frame down");
		this.downButton.setEnabled(false);
		this.downButton.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent actionEvent){
            	onDown();
            }
        });
		toolBar.add(this.downButton);
		buttonPanel.add(toolBar);
		navigationPanel.add(buttonPanel);
		
		//create the panel for the current frame
		JPanel currentFramePanel=new JPanel();
		currentFramePanel.setLayout(new BoxLayout(currentFramePanel,BoxLayout.Y_AXIS));
		//background image
		JPanel bgImagePanel=new JPanel();
		bgImagePanel.setLayout(new BoxLayout(bgImagePanel,BoxLayout.Y_AXIS));
		bgImagePanel.setBorder(new TitledBorder("Background Image"));
		//it took WAAAY too much time to set this crap up, once again I ponder porting this to VB6
	    this.previewImageLabel=new JLabel();
		JScrollPane jsp1=new JScrollPane(this.previewImageLabel);
		jsp1.setMinimumSize(new Dimension(160,120));
		jsp1.setPreferredSize(new Dimension(160,120));
		bgImagePanel.add(jsp1);
		JPanel bgImageFilePanel=new JPanel();
		bgImageFilePanel.setLayout(new BoxLayout(bgImageFilePanel,BoxLayout.X_AXIS));
		bgImageFilePanel.add(new JLabel("Path: "));
		this.bgImagePath=new JTextField();
		this.bgImagePath.setEditable(false);
		bgImageFilePanel.add(this.bgImagePath);
		this.bgImageBrowse=new JButton();
		this.bgImageBrowse.setIcon(new ImageIcon(this.getClass().getResource("/com/huguesjohnson/tiamat/tools/images/open.gif")));
		this.bgImageBrowse.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent actionEvent){
            	onBrowseBgImage();
            }
        });		
		this.bgImageBrowse.setToolTipText("Browse for a file..");
		bgImageFilePanel.add(this.bgImageBrowse);
		bgImagePanel.add(bgImageFilePanel);
		currentFramePanel.add(bgImagePanel);
		//background music
		JPanel bgMusicPanel=new JPanel();
		bgMusicPanel.setLayout(new BoxLayout(bgMusicPanel,BoxLayout.X_AXIS));
		bgMusicPanel.setBorder(new TitledBorder("Background Music"));
		bgMusicPanel.add(new JLabel("Path: "));
		this.bgMusicPath=new JTextField();
		this.bgMusicPath.setEditable(false);
		bgMusicPanel.add(this.bgMusicPath);
		this.bgMusicPlay=new JToggleButton();
		this.bgMusicPlay.setIcon(new ImageIcon(this.getClass().getResource("/com/huguesjohnson/tiamat/tools/images/play.gif")));
		this.bgMusicPlay.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent actionEvent){
            	onPlayBgMusic();
            }
        });	
		this.bgMusicPlay.setToolTipText("Preview");
		this.bgMusicPlay.setEnabled(false);
		bgMusicPanel.add(this.bgMusicPlay);
		this.bgMusicBrowse=new JButton();
		this.bgMusicBrowse.setIcon(new ImageIcon(this.getClass().getResource("/com/huguesjohnson/tiamat/tools/images/open.gif")));
		this.bgMusicBrowse.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent actionEvent){
            	onBrowseBgMusic();
            }
        });		
		this.bgMusicBrowse.setToolTipText("Browse for a file..");
		bgMusicPanel.add(this.bgMusicBrowse);
		currentFramePanel.add(bgMusicPanel);
		//text
		JPanel textPanel=new JPanel();
		textPanel.setLayout(new BoxLayout(textPanel,BoxLayout.Y_AXIS));
		textPanel.setBorder(new TitledBorder("Text"));
		this.text=new JTextArea();
		this.text.setLineWrap(true);
		this.text.setWrapStyleWord(true);
		this.text.setMinimumSize(new Dimension(160,900));
		this.text.setPreferredSize(new Dimension(160,900));
		JScrollPane textScrollPane=new JScrollPane();
		textScrollPane.setViewportView(this.text); 
		textScrollPane.setMinimumSize(new Dimension(160,96));
		textScrollPane.setPreferredSize(new Dimension(160,96));
		textScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
		textScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
		textPanel.add(textScrollPane);
		currentFramePanel.add(textPanel);
		
		//now add a split pane to hold the navigation and current frame panel
		JPanel panel=new JPanel();
		panel.setLayout(new BoxLayout(panel,BoxLayout.X_AXIS));
		JSplitPane splitPane=new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,navigationPanel,currentFramePanel);
		splitPane.setDividerLocation(150);
		panel.add(splitPane);
		this.panelContent.add(panel);

		//add buttons to hold preview and exit events
		JPanel customToolbarPanel=new JPanel();
		customToolbarPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
		JToolBar customToolbar=new JToolBar();
		customToolbar.setFloatable(false);
		JButton exitEventButton=new JButton();
		exitEventButton.setText("Edit Exit Events..");
		exitEventButton.setIcon(new ImageIcon(this.getClass().getResource("/com/huguesjohnson/tiamat/tools/images/edit.gif")));
		exitEventButton.setToolTipText("Edit the exit Events for this Sequence..");
		exitEventButton.setEnabled(true);
		exitEventButton.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent actionEvent){
            	showEventListDialog();
            }
		});
		customToolbar.add(exitEventButton);
		customToolbarPanel.add(customToolbar);
		this.panelContent.add(customToolbarPanel);		
		
		//pack
		this.pack();
	}
		
	public void onSave(){
		//get the frames
		DefaultListModel listModel=(DefaultListModel)this.frameList.getModel();
		int length=listModel.getSize();
		SequenceFrame[] frames=new SequenceFrame[length];
		for(int index=0;index<length;index++){
			frames[index]=((ListEntry)listModel.getElementAt(index)).frame;
		}
		//construct the sequence
		Sequence sequence;
		if(this.exitEvents==null){ 
			sequence=new Sequence(frames,new Event[0]);
		} else{
			sequence=new Sequence(frames,this.exitEvents);
		}
		
		//save
		XmlFormatter formatter=new XmlFormatter();
		try{
			formatter.saveTo(sequence,this.browsePanel.getFilePath());
		} catch(Exception x){
			x.printStackTrace();
			JOptionPane.showMessageDialog(this,"Error saving sequence:"+x.getMessage(),"Error",JOptionPane.ERROR_MESSAGE);
		}
	}

	protected void onSaveAs(){
		//save the currently selected frame
		this.saveSelectedFrame();
		//check if frames current have absolute paths (which they will if no path has been set)
		boolean updateRelativePaths=((this.browsePanel.getDirectory()==null)||(this.browsePanel.getDirectory().length()<1));
		//browse for a file
		if(this.browsePanel.browse()){
			String filePath=this.browsePanel.getFilePath();
			if(!filePath.toLowerCase().endsWith(".sequence.xml")){
				this.browsePanel.setFilePath(filePath+".sequence.xml");
			}
			String dirPath=this.browsePanel.getDirectory();
			//update the paths for every frame
			DefaultListModel listModel=(DefaultListModel)this.frameList.getModel();
			int size=listModel.size();
			for(int index=0;index<size;index++){
				ListEntry entry=(ListEntry)listModel.getElementAt(index);
				entry.frame.setAbsoluteDir(dirPath);
				if(updateRelativePaths){
					entry.frame.setBgImagePath(PathResolver.getRelativePath(dirPath,entry.frame.getBgImagePath()));
					entry.frame.setBgMusicPath(PathResolver.getRelativePath(dirPath,entry.frame.getBgMusicPath()));
				}
				listModel.setElementAt(entry,index);
			}
			this.onSave();
			this.enableSave();
			this.addRecentFile(this.browsePanel.getFilePath());
			//force a repaint of the current frame if paths changed
			if(updateRelativePaths){
				int selectedIndex=this.frameList.getSelectedIndex();
				ListEntry entry=(ListEntry)listModel.getElementAt(selectedIndex);
				this.bgImagePath.setText(entry.frame.getBgImagePath());
				this.bgMusicPath.setText(entry.frame.getBgMusicPath());
			}
		}
	}

	protected void onNew(){
		//clear the list
		this.frameList.setModel(new DefaultListModel());
		//clear paths
		this.browsePanel.setFilePath(null);
		this.bgImagePath.setText(null);
		this.bgMusicPath.setText(null);
		//disable buttons
		this.upButton.setEnabled(false);
		this.downButton.setEnabled(false);
		this.removeButton.setEnabled(false);
		if(this.bgMusicPlay.isSelected()){
			this.bgMusicPlay.setSelected(false);
			this.onPlayBgMusic();
		}
		this.bgMusicPlay.setEnabled(false);
		//everything else
		this.text.setText(null);
		this.previewImageLabel.setIcon(null);
		this.disableSave();
		//add the first frame
		this.onAdd();
	}

	protected void onOpen(){
		boolean success=true;
		String currentFilePath=new String(this.browsePanel.getFilePath());
		if(this.browsePanel.browse()){
			String newFilePath=new String(this.browsePanel.getFilePath());
			success=this.openSequence(newFilePath);
			//if the open operation failed set the file path back to the old one
			if(!success){
				this.browsePanel.setFilePath(currentFilePath);
			} else{
				this.addRecentFile(newFilePath);
			}
		}
	}

	protected void onRecentFile(String filePath){
		boolean success=true;
		String currentFilePath=new String(this.browsePanel.getFilePath());
		success=this.openSequence(filePath);
		// f the open operation failed set the file path back to the old one
		if(!success){
			this.browsePanel.setFilePath(currentFilePath);
		}
	}

	public void onViewXml(){
		String filePath="file:///"+this.browsePanel.getFilePath();
		this.viewXml(filePath);
	}
	
	public void onAdd(){ 
		DefaultListModel listModel=(DefaultListModel)this.frameList.getModel();
		int frameNumber=listModel.getSize()+1;
		ListEntry entry=new ListEntry("Frame "+frameNumber,new SequenceFrame());
		listModel.addElement(entry);
		this.frameList.setSelectedIndex(frameNumber-1);
	}
	
	public void onRemove(){
		this.currentFrameIndex=-1; //this will be updated when the selection change event fires after the remove
		//remove the selected item
		int removeIndex=this.frameList.getSelectedIndex();
		DefaultListModel listModel=(DefaultListModel)this.frameList.getModel();
		listModel.remove(removeIndex);
		//now update the captions
		int length=listModel.size();
		for(int index=removeIndex;index<length;index++){
			ListEntry entry=(ListEntry)listModel.getElementAt(index);
			entry.caption=new String("Frame "+(index+1));
		}
		if(removeIndex>=listModel.getSize()){ removeIndex--; }
		this.frameList.setSelectedIndex(removeIndex);
	}
	
	public void onUp(){
		//save the current frame
		this.saveSelectedFrame();
		//get the frames that need to be swapped
		int selectedIndex=this.frameList.getSelectedIndex();
		DefaultListModel listModel=(DefaultListModel)this.frameList.getModel();
		ListEntry selectedEntry=((ListEntry)listModel.getElementAt(selectedIndex)).cloneEntry();
		selectedEntry.caption="Frame "+(selectedIndex);
		ListEntry swapEntry=((ListEntry)listModel.getElementAt(selectedIndex-1)).cloneEntry();
		swapEntry.caption="Frame "+(selectedIndex+1);
		//swap the frames
		listModel.setElementAt(swapEntry,selectedIndex);
		listModel.setElementAt(selectedEntry,selectedIndex-1);
		//follow the frame that was moved up
		this.currentFrameIndex=-1;
		this.frameList.setSelectedIndex(selectedIndex-1);
	}

	public void onDown(){
		//save the current frame
		this.saveSelectedFrame();
		//get the frames that need to be swapped
		int selectedIndex=this.frameList.getSelectedIndex();
		DefaultListModel listModel=(DefaultListModel)this.frameList.getModel();
		ListEntry selectedEntry=((ListEntry)listModel.getElementAt(selectedIndex)).cloneEntry();
		selectedEntry.caption="Frame "+(selectedIndex+2);
		ListEntry swapEntry=((ListEntry)listModel.getElementAt(selectedIndex+1)).cloneEntry();
		swapEntry.caption="Frame "+(selectedIndex+1);
		//swap the frames
		listModel.setElementAt(swapEntry,selectedIndex);
		listModel.setElementAt(selectedEntry,selectedIndex+1);
		//follow the frame that was moved up
		this.currentFrameIndex=-1;
		this.frameList.setSelectedIndex(selectedIndex+1);
	}
	
	public void onBrowseBgImage(){
		JFileChooser fileChooser=new JFileChooser();
	    fileChooser.setDialogTitle("Open Background Image");
	    //do not allow multiple selections
	    fileChooser.setMultiSelectionEnabled(false);
	    //set file filters
	    fileChooser.setAcceptAllFileFilterUsed(false);
	    fileChooser.setFileFilter(new ImageFileFilter());
	    //set the current directory
        String lastPath=System.getProperty("last-image-open-path");
        if(lastPath==null){
            lastPath=System.getProperty("user.dir");
        }
        fileChooser.setCurrentDirectory(new File(lastPath));
        //show the file chooser
	    int returnValue=fileChooser.showOpenDialog(null);
	    //was 'OK' selected?
	    if(returnValue==JFileChooser.APPROVE_OPTION){
	    	String dir=fileChooser.getCurrentDirectory().getPath();
	    	if(!dir.endsWith(File.separator)){
	    		dir=dir+File.separator;
	    	}
            System.setProperty("last-image-open-path",dir);
            String name=fileChooser.getSelectedFile().getName();
            //update the text area
            String fullImagePath=dir+name;
            String sequenceFilePath=this.browsePanel.getFilePath();
            if((sequenceFilePath==null)||(sequenceFilePath.length()<1)){
            	this.bgImagePath.setText(fullImagePath);
            } else{
            	this.bgImagePath.setText(PathResolver.getRelativePath(sequenceFilePath,fullImagePath));
            }
           //draw the preview image
            this.drawPreviewImage(fullImagePath);
	    } 
	}
	
	public void onBrowseBgMusic(){
		JFileChooser fileChooser=new JFileChooser();
	    fileChooser.setDialogTitle("Open Background Music");
	    //do not allow multiple selections
	    fileChooser.setMultiSelectionEnabled(false);
	    //set file filters
	    fileChooser.setAcceptAllFileFilterUsed(false);
	    fileChooser.setFileFilter(new MidiFileFilter());
	    //set the current directory
        String lastPath=System.getProperty("last-music-open-path");
        if(lastPath==null){
            lastPath=System.getProperty("user.dir");
        }
        fileChooser.setCurrentDirectory(new File(lastPath));
        //show the file chooser
	    int returnValue=fileChooser.showOpenDialog(null);
	    //was 'OK' selected?
	    if(returnValue==JFileChooser.APPROVE_OPTION){
	    	String dir=fileChooser.getCurrentDirectory().getPath();
	    	if(!dir.endsWith(File.separator)){
	    		dir=dir+File.separator;
	    	}
            System.setProperty("last-music-open-path",dir);
            String name=fileChooser.getSelectedFile().getName();
            //update the text area
            String fullMusicPath=dir+name;
            String sequenceFilePath=this.browsePanel.getFilePath();
            if((sequenceFilePath==null)||(sequenceFilePath.length()<1)){
            	this.bgMusicPath.setText(fullMusicPath);
            } else{
            	this.bgMusicPath.setText(PathResolver.getRelativePath(sequenceFilePath,fullMusicPath));
            }
           //enable the play button
            this.bgMusicPlay.setEnabled(true);
	    } 

	}
	
	public void onPlayBgMusic(){
		if(this.bgMusicPlay.isSelected()){
			this.bgMusicPlay.setIcon(new ImageIcon(this.getClass().getResource("/com/huguesjohnson/tiamat/tools/images/stop.gif")));
			if(this.player==null){
				this.player=new MidiPlayer();
			}
			String fullMusicPath=this.bgMusicPath.getText();
            String sequenceFilePath=this.browsePanel.getFilePath();
            if((sequenceFilePath!=null)&&(sequenceFilePath.length()>1)){
            	fullMusicPath=PathResolver.getAbsolutePath(sequenceFilePath,fullMusicPath);
            }
			this.player.play(fullMusicPath,-1);
		} else{
			this.bgMusicPlay.setIcon(new ImageIcon(this.getClass().getResource("/com/huguesjohnson/tiamat/tools/images/play.gif")));
			if(this.player!=null){ //probably isn't possible
				this.player.stop();
			}
		}
	}
	
	public void onListSelectionChanged(ListSelectionEvent e){
		if(e.getValueIsAdjusting()==false){
			int selectedIndex=this.frameList.getSelectedIndex();
			if((selectedIndex==this.currentFrameIndex)){ return; }
			DefaultListModel listModel=(DefaultListModel)this.frameList.getModel();
			//save the previous frame
			if(this.currentFrameIndex>=0){
				this.saveSelectedFrame();
			}
			//load the new one
			if(selectedIndex>=0){
				SequenceFrame currentFrame=((ListEntry)listModel.get(selectedIndex)).frame;
				this.bgMusicPath.setText(currentFrame.getBgMusicPath());
				this.text.setText(currentFrame.getText());
				//load the image
				String imagePath=currentFrame.getBgImagePath();
				this.bgImagePath.setText(imagePath);
				if((imagePath==null)||(imagePath.length()<1)){
					this.previewImageLabel.setIcon(null);
				} else{
		            String sequenceFilePath=this.browsePanel.getFilePath();
		            if((sequenceFilePath==null)||(sequenceFilePath.length()<1)){
		            	this.drawPreviewImage(imagePath);
		            } else{
		            	this.drawPreviewImage(PathResolver.getAbsolutePath(sequenceFilePath,imagePath));
		            }
				}
			}
			//check which buttons should be enabled
			if(listModel.size()>1){ 
				this.removeButton.setEnabled(true); 
			} else{
				this.removeButton.setEnabled(false); 
			}
			this.upButton.setEnabled(selectedIndex>0);
			this.downButton.setEnabled(selectedIndex<(listModel.size()-1));
			//set the current frame index
			this.currentFrameIndex=selectedIndex;
		}
	}
	
	private void saveSelectedFrame(){
		if((this.currentFrameIndex>=0)&&(this.currentFrameIndex<this.frameList.getModel().getSize())){
			SequenceFrame frame=new SequenceFrame();
			frame.setBgImagePath(this.bgImagePath.getText());
			frame.setBgMusicPath(this.bgMusicPath.getText());
			frame.setText(this.text.getText());
			DefaultListModel listModel=(DefaultListModel)this.frameList.getModel();
			listModel.setElementAt(new ListEntry("Frame "+(this.currentFrameIndex+1),frame),this.currentFrameIndex);
		}
	}
	
	private void drawPreviewImage(String imagePath){
		this.previewImage=ImageLoader.loadBufferedImage(imagePath);
		this.drawPreviewImage();
	}
	
	private void drawPreviewImage(){
		if(this.previewImage!=null){
            ImageIcon icon=new ImageIcon(this.previewImage);
            this.previewImageLabel.setIcon(icon);			
		}
	}
	
	@SuppressWarnings("serial")
	public void showEventListDialog(){
		if(this.eventListDialog==null){
			this.eventListDialog=new EventListDialog(this){
				public void onOk(){ onEventListDialogOk(); }
				public void onCancel(){ onEventListDialogCancel(); }
			};
		}
		if(this.exitEvents!=null){
			this.eventListDialog.setEvents(this.exitEvents);
		}
		this.eventListDialog.setVisible(true);
	}
	
	public void onEventListDialogOk(){
		this.exitEvents=this.eventListDialog.getEvents();
		this.eventListDialog.setVisible(false);
	}
	
	public void onEventListDialogCancel(){
		this.eventListDialog.setVisible(false);
	}
	
	private boolean openSequence(String filePath){
		boolean success=true;
		String currentFilePath=new String(this.browsePanel.getFilePath());
		//update the sequence file panel if needed
		if(!currentFilePath.equals(filePath)){
			this.browsePanel.setFilePath(filePath);
		}
		//try loading the sequence
		try{
			XmlFormatter formatter=new XmlFormatter();
			Sequence sequence=(Sequence)formatter.loadFrom(filePath);
			//load the sequence into the navigation
			int length=sequence.getFrameCount();
			DefaultListModel listModel=new DefaultListModel();
			listModel.setSize(length);
			for(int index=0;index<length;index++){
				ListEntry entry=new ListEntry();
				entry.frame=sequence.getFrame(index);
				entry.caption=new String("Frame "+(index+1));
				listModel.set(index,entry);
			}
			/* this next line is a workaround to a bug (in swing?) 
			where the new model isn't being applied in place of the new one, 
			some weird merge thing happens instead */
			this.frameList.setModel(new DefaultListModel());
			this.frameList.setModel(listModel);
			//copy the exit events
			this.exitEvents=sequence.getExitEvents();
			//select the first item
			if(length>0){
				this.frameList.setSelectedIndex(0);
			}
		} catch(Exception x){
			x.printStackTrace();
			success=false;
		}
		return(success);
	}	
}