package bibliothek.notes.view;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import bibliothek.gui.DockFrontend;
import bibliothek.gui.DockStation;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.event.DockableFocusEvent;
import bibliothek.gui.dock.event.DockableFocusListener;
import bibliothek.gui.dock.layout.DockableProperty;
import bibliothek.gui.dock.layout.PropertyTransformer;
import bibliothek.gui.dock.util.DockUtilities;
import bibliothek.notes.model.Note;
import bibliothek.notes.model.NoteModel;
import bibliothek.notes.model.NoteModelListener;
import bibliothek.notes.view.panels.NoteView;
import bibliothek.util.container.Tuple;
import bibliothek.util.xml.XElement;
/**
* Manages the connection between {@link Note}s and {@link NoteView}s. Contains
* various methods to show and hide views for specific <code>Note</code>s.
* @author Benjamin Sigg
*/
public class NoteViewManager{
/** link to the docking-frames, used to show and hide views */
private DockFrontend frontend;
/** the set of root-{@link DockStation}s */
private ViewManager manager;
/** the set of {@link Note}s */
private NoteModel model;
/** a map containing views for some {@link Note}s */
private Map<Note, NoteView> noteViews = new HashMap<Note, NoteView>();
/** the history of the {@link NoteView}s that were or are focused */
private LinkedList<NoteView> focusedViews = new LinkedList<NoteView>();
/** the location of various {@link Note}s when they were closed the last time */
private Map<Note, Tuple<DockStation, DockableProperty>> locations =
new HashMap<Note, Tuple<DockStation,DockableProperty>>();
/**
* Creates a new manager.
* @param frontend the link to the docking-frames
* @param manager the set of the root-{@link DockStation}s
* @param model the mutable set of {@link Note}s
*/
public NoteViewManager( DockFrontend frontend, ViewManager manager, NoteModel model ){
this.frontend = frontend;
this.manager = manager;
this.model = model;
model.addNoteModelListener( new NoteModelListener(){
public void noteAdded( NoteModel model, Note note ){
// ignore
}
public void noteRemoved( NoteModel model, Note note ){
hide( note );
locations.remove( note );
}
});
frontend.getController().addDockableFocusListener( new DockableFocusListener(){
public void dockableFocused( DockableFocusEvent event ) {
if( event.getNewFocusOwner() instanceof NoteView ){
NoteView view = (NoteView)event.getNewFocusOwner();
focusedViews.remove( view );
focusedViews.addFirst( view );
}
}
});
}
/**
* Closes the view that shows <code>note</code>. Stores the location
* of the view in order to open the view again at the same location.
* @param note the <code>Note</code> whose view should be closed
*/
public void hide( Note note ){
NoteView view = noteViews.remove( note );
if( view != null ){
DockStation root = DockUtilities.getRoot( view );
DockableProperty location = DockUtilities.getPropertyChain( root, view );
locations.put( note, new Tuple<DockStation, DockableProperty>( root, location ) );
DockStation parent = view.getDockParent();
parent.drag( view );
view.setNote( null );
focusedViews.remove( view );
}
}
/**
* Adds an additional view to the set of known views.
* @param view the additional view
*/
public void putExternal( NoteView view ){
noteViews.put( view.getNote(), view );
}
/**
* Shows a view for <code>note</code>. The view will be positioned at the
* same location the last view for <code>note</code> was shown. If this is
* the first time that the view is shown, it will be positioned at the same
* location as the last focused view.
* @param note the <code>Note</code> whose view should be shown
*/
public void show( Note note ){
Tuple<DockStation, DockableProperty> location = locations.remove( note );
if( location != null )
show( note, location.getA(), location.getB() );
else if( focusedViews.isEmpty() )
show( note, null );
else
show( note, focusedViews.getFirst() );
}
/**
* Opens a view for <code>note</code> at the same location as
* <code>location</code>.
* @param note the <code>Note</code> which will be shown
* @param location the preferred location of the new view, might be <code>null</code>
*/
public void show( Note note, Dockable location ){
if( location == null )
show( note, null, null );
else{
DockStation station = DockUtilities.getRoot( location );
DockableProperty property = DockUtilities.getPropertyChain( station, location );
show( note, station, property );
}
}
/**
* Shows a view for <code>note</code> at the given location as child
* of <code>root</code>.
* @param note the <code>Note</code> for which a view should be opened
* @param root the preferred parent, might be <code>null</code>
* @param location the preferred location, relative to <code>root</code>. Might
* be <code>null</code>.
*/
public void show( Note note, DockStation root, DockableProperty location ){
NoteView view = noteViews.get( note );
if( view == null ){
view = new NoteView( this, model );
view.setNote( note );
if( root == null || location == null ){
frontend.getDefaultStation().drop( view );
}
else{
if( !root.drop( view, location )){
frontend.getDefaultStation().drop( view );
}
}
noteViews.put( note, view );
}
frontend.getController().setFocusedDockable( view, false );
}
/**
* Writes the location of the views of the known {@link Note}s.
* @param out the stream to write into
* @throws IOException if this method can't write into <code>out</code>
*/
public void write( DataOutputStream out ) throws IOException{
PropertyTransformer transformer = new PropertyTransformer( frontend.getController() );
out.writeInt( locations.size() );
for( Map.Entry<Note, Tuple<DockStation, DockableProperty>> location : locations.entrySet() ){
out.writeUTF( location.getKey().getId() );
out.writeUTF( manager.getName( location.getValue().getA() ) );
transformer.write( location.getValue().getB(), out );
}
}
/**
* Reads the location of the views of all known <code>Note</code>s.
* @param in the stream to read from
* @throws IOException if <code>in</code> can't be read
*/
public void read( DataInputStream in ) throws IOException{
PropertyTransformer transformer = new PropertyTransformer( frontend.getController() );
int count = in.readInt();
for( int i = 0; i < count; i++ ){
Note note = model.getNote( in.readUTF() );
DockStation station = manager.getStation( in.readUTF() );
DockableProperty property = transformer.read( in );
if( note != null ){
locations.put( note, new Tuple<DockStation, DockableProperty>( station, property ) );
}
}
}
/**
* Writes the location of the views of the known {@link Note}s.
* @param element the xml-element to write into, the attributes of
* <code>element</code> will not be changed
*/
public void writeXML( XElement element ) throws IOException{
PropertyTransformer transformer = new PropertyTransformer( frontend.getController() );
for( Map.Entry<Note, Tuple<DockStation, DockableProperty>> location : locations.entrySet() ){
XElement xnote = element.addElement( "note" );
xnote.addString( "id", location.getKey().getId() );
xnote.addString( "station", manager.getName( location.getValue().getA() ) );
transformer.writeXML( location.getValue().getB(), xnote );
}
}
/**
* Reads the location of the views of all known <code>Note</code>s.
* @param element the xml-element to read from
*/
public void readXML( XElement element ){
PropertyTransformer transformer = new PropertyTransformer( frontend.getController() );
for( XElement xnote : element.getElements( "note" )){
Note note = model.getNote( xnote.getString( "id" ) );
DockStation station = manager.getStation( xnote.getString( "station" ) );
DockableProperty property = transformer.readXML( xnote );
if( note != null ){
locations.put( note, new Tuple<DockStation, DockableProperty>( station, property ) );
}
}
}
}