/*
* Copyright 2011 Uwe Krueger.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mandelsoft.mand.tool;
import com.mandelsoft.mand.IllegalConfigurationException;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import com.mandelsoft.mand.Environment;
import com.mandelsoft.mand.MandelData;
import com.mandelsoft.io.AbstractFile;
import com.mandelsoft.mand.MandelName;
import com.mandelsoft.mand.QualifiedMandelName;
import com.mandelsoft.mand.Settings;
import com.mandelsoft.mand.cm.Colormap;
import com.mandelsoft.mand.cm.ColormapModel;
import com.mandelsoft.mand.image.MandelAreaImage;
import com.mandelsoft.mand.image.MandelImage;
import com.mandelsoft.mand.scan.MandelHandle;
import com.mandelsoft.mand.scan.MandelScanner;
import com.mandelsoft.mand.scan.MandelScannerListenerAdapter;
import com.mandelsoft.mand.tool.lists.MandelListsMenuFactory;
import com.mandelsoft.mand.util.MandelColormapCache;
import com.mandelsoft.mand.util.MandelList;
import com.mandelsoft.mand.util.MandelListFolder;
import com.mandelsoft.mand.util.MandelListFolderTree;
import com.mandelsoft.swing.WindowControlAction;
import java.awt.SplashScreen;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import javax.swing.DefaultListModel;
import javax.swing.ListModel;
/**
*
* @author Uwe Krueger
*/
public class ToolEnvironment extends Environment {
private JFileChooser fileChooser;
private InitialStartupFrame control;
private ToolControlAction toolControl;
private int windowcnt;
private boolean doexit=false;
private ColorListModel colors;
private TagListModel tags;
private MandelListFolderTreeModel tfavorites;
private MandelListFolderTreeModel memory;
private MandelListTableModel favorites;
private MandelListFolderTreeModel ttodos;
private MandelListTableModel todos;
private MandelListFolderTreeModel tlinks;
private AreasModel areas;
private NewRasterModel newrasters;
private UnseenRasterModel unseenrasters;
private MandelListTableModel variants;
private MandelListTableModel leafs;
private MandelListTableModel pending;
private MandelListTableModel unseenrefinements;
private MandelListTableModel refinerequests;
private ComposedMandelListFolderTreeModel lists;
private ColormapListModel colormaps;
private MandelListsMenuFactory listactions;
private ImageBaseModel imagebase_model;
private MandelColormapCache colormap_cache;
///////////////////////////////////////////////////////////////////////
// Tool control
///////////////////////////////////////////////////////////////////////
private class ToolControlAction extends WindowControlAction {
public ToolControlAction()
{ super(null,"Tool Control", new Creator());
}
}
private class Creator implements WindowControlAction.WindowCreator {
@Override
public Window createWindow(Window owner)
{
return new ToolControlFrame(ToolEnvironment.this);
}
}
///////////////////////////////////////////////////////////////////////
public ToolEnvironment(String[] args) throws IllegalConfigurationException
{
super("mandtool",args);
setup();
}
public ToolEnvironment(String[] args, File dir) throws IllegalConfigurationException
{
super("mandtool",args,dir);
setup();
}
public ToolEnvironment(String[] args, URL dir) throws IllegalConfigurationException
{
super("mandtool",args,dir);
setup();
}
private void setup()
{
ComposedMandelListFolderTreeModel userlists;
List<MandelListFolderTree> tmp;
int size=80;
String v=getProperty(Settings.COLORMAP_CACHE_SIZE);
if (v!=null) {
try {
size=Integer.parseInt(v);
if (size<=0) size=80;
}
catch (NumberFormatException nfe) {
System.out.println("illegal colormap cache size "+v+": "+nfe);
// keep default
}
}
colormap_cache=new MandelColormapCache(size);
Colormap cm=getDefaultColormap();
if (cm!=null) colormap_cache.lock(QualifiedMandelName.ROOT, cm);
refresh_pending=new HashMap<MandelListTableModel,Boolean>();
refresh_order=new ArrayList<MandelListTableModel>();
toolControl = new ToolControlAction();
listactions=new MandelListsMenuFactory(isReadonly());
if (!isReadonly())
fileChooser=new JFileChooser();
if (getColors()!=null) {
colors=new ColorListModel(getColors());
colors.setAutosave(!isReadonly());
}
if (getTags()!=null) {
tags=new TagListModel(getTags());
tags.setAutosave(!isReadonly());
}
if (getAreas()!=null) {
areas=new AreasModel();
}
if (getFavorites()!=null) {
tfavorites=getMandelListFolderTreeModel(getFavorites());
favorites=tfavorites.getMandelListModel(tfavorites.getRoot());
}
memory=new GeneralFolderTreeModel(getMemory(),true);
if (getTodos()!=null) {
ttodos=getMandelListFolderTreeModel(getTodos());
todos=ttodos.getMandelListModel(ttodos.getRoot());
}
if (getLinks()!=null) {
tlinks=new LinkFolderTreeModel(getLinks());
}
if (getNewRasters()!=null) newrasters=new NewRasterModel();
if (getUnseenRasters()!=null) unseenrasters=new UnseenRasterModel();
variants=new VariantsModel();
leafs=new LeafModel();
pending=new PendingModel();
// first refinement requests and then unseen unseenrefinements
// refresh order of listeners is important
if (getRefinementRequests()!=null) refinerequests=new RefinementRequestsModel();
if (getUnseenRefinements()!=null) unseenrefinements=new UnseenRefinementModel();
lists=new ComposedMandelListFolderTreeModel("lists",getAllScanner());
lists.setModifiable(!isReadonly());
if (unseenrasters!=null) lists.addListModel(unseenrasters, "unseen",
"list of image areas not yet seen");
lists.addFolderTreeModel(memory, memory.getRoot().getName());
lists.addListModel(variants, "variants");
if (tfavorites!=null) lists.addFolderTreeModel(tfavorites, "favorites");
if (ttodos!=null) lists.addFolderTreeModel(ttodos, "todo");
if (tlinks!=null) lists.addFolderTreeModel(tlinks, "links");
if (areas!=null) lists.addListModel(areas,"areas",
"list of marked area roots for backward navigation");
if (newrasters!=null) lists.addListModel(newrasters, "new",
"list of finished areas not yet examined");
if (leafs!=null) lists.addListModel(leafs, "leafs",
"list of areas without further sub areas");
if (pending!=null) lists.addListModel(pending, "pending",
"list of areas with pending sub area calculations");
tmp=getUserLists();
if (!tmp.isEmpty()) {
userlists=new ComposedMandelListFolderTreeModel("misc",getAllScanner());
for (MandelListFolderTree t:tmp) {
userlists.addFolderTreeModel(getMandelListFolderTreeModel(t), t.getRoot().getName());
}
lists.addFolderTreeModel(userlists,"misc");
}
colormaps=new ExtendedColormapListModel(getColormaps());
imagebase_model=new ImageBaseModel(this);
}
public ImageBaseModel getImagebaseModel()
{
return imagebase_model;
}
//////////////////////////////////////////////////////////////////////////
// link support
public void addLink(MandelName src, MandelName dst)
{ boolean done=false;
if (tlinks==null || isReadonly()) return;
done|=_addLink(src,dst);
done|=_addLink(dst,src);
if (done) handleAddLink(src,dst);
}
public void removeLink(MandelName src, MandelName dst)
{ boolean done=false;
if (tlinks==null || isReadonly()) return;
done|=_removeLink(src,dst);
done|=_removeLink(dst,src);
if (done) handleRemoveLink(src,dst);
}
public boolean _removeLink(MandelName src, MandelName dst)
{ boolean done=false;
if (tlinks==null||isReadonly()) return done;
MandelListFolder f=tlinks.getChild(tlinks.getRoot(), src.getName());
if (f!=null) {
MandelListModel m=tlinks.getMandelListModel(f);
QualifiedMandelName qn=new QualifiedMandelName(dst);
done=f.contains(qn);
m.remove(qn);
if (m.getList().isEmpty()) {
tlinks.removeFolder(f);
}
}
return done;
}
private boolean _addLink(MandelName src, MandelName dst)
{ boolean done=false;
if (tlinks==null || isReadonly()) return done;
MandelListFolder f=tlinks.getChild(tlinks.getRoot(), src.getName());
if (f==null) {
f=tlinks.insertFolder(src.getName(), tlinks.getRoot());
f.setThumbnailName(new QualifiedMandelName(src));
}
QualifiedMandelName qn=new QualifiedMandelName(dst);
done=!f.contains(qn);
tlinks.add(f, qn);
return done;
}
public MandelListModel getLinkModel(MandelName n)
{
if (tlinks==null) return null;
MandelListFolder f=tlinks.getChild(tlinks.getRoot(), n.getName());
if (f==null) return null;
return tlinks.getMandelListModel(f);
}
private Set<LinkListener> llisteners=new HashSet<LinkListener>();
public void addLinkListener(LinkListener h)
{
llisteners.add(h);
}
public void removeLinkListener(LinkListener h)
{
llisteners.remove(h);
}
private void handleAddLink(MandelName src, MandelName dst)
{
for (LinkListener h:llisteners) {
h.linkAdded(src,dst);
}
}
private void handleRemoveLink(MandelName src, MandelName dst)
{
for (LinkListener h:llisteners) {
h.linkRemoved(src,dst);
}
}
synchronized
public MandelColormapCache getColormapCache()
{
return colormap_cache;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
public WindowControlAction getToolControlAction()
{ return toolControl;
}
public MandelListsMenuFactory getListActions()
{
return listactions;
}
synchronized
public JFileChooser getFileChooser()
{
return fileChooser;
}
public MandelListFolderTreeModel getMandelListFolderTreeModel()
{
return lists;
}
public ColorListModel getColorsModel()
{
return colors;
}
public TagListModel getTagsModel()
{
return tags;
}
public MandelListTableModel getFavoritesModel()
{ return favorites;
}
public MandelListTableModel getTodosModel()
{ return todos;
}
public MandelListTableModel getMemoryModel()
{ return memory.getMandelListModel(memory.getRoot());
}
public MandelListTableModel getAreasModel()
{ return areas;
}
public MandelListTableModel getNewRastersModel()
{ return newrasters;
}
public MandelListTableModel getUnseenRastersModel()
{ return unseenrasters;
}
public MandelListTableModel getPendingModel()
{ return pending;
}
public MandelListTableModel getVariantsModel()
{ return variants;
}
public MandelListTableModel getUnseenRefinementsModel()
{ return unseenrefinements;
}
public MandelListTableModel getRefinementRequestsModel()
{ return refinerequests;
}
public ColormapListModel getColormapListModel()
{ return colormaps;
}
@Override
public boolean handleRasterSeen(AbstractFile f)
{
boolean b;
startUpdate();
try {
b=super.handleRasterSeen(f);
if (b && newrasters!=null) {
QualifiedMandelName name=QualifiedMandelName.create(f);
System.out.println("remove from new rasters "+f);
newrasters.setModifiable(true);
newrasters.remove(name);
newrasters.setModifiable(false);
}
}
finally {
finishUpdate();
}
return b;
}
@Override
protected void seenModified()
{
super.seenModified();
if (unseenrasters!=null) unseenrasters.fireTableDataChanged();
}
@Override
protected void unseenRefinementsModified()
{
super.unseenRefinementsModified();
if (unseenrefinements!=null) {
if (debug) System.out.println("unseen refinements modified");
unseenrefinements.fireTableDataChanged();
}
}
private DefaultListModel frames=new DefaultListModel();
public ListModel getMandelWindowsModel()
{
return frames;
}
private void setupFrame(JFrame frame)
{
new FrameListener(frame);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setVisible(true);
incrementWindowCount();
}
private class FrameListener extends WindowAdapter {
private JFrame frame;
public FrameListener(JFrame frame)
{
this.frame=frame;
if (frame instanceof MandelImageFrame) {
frames.addElement(frame);
}
frame.addWindowListener(this);
}
@Override
public void windowClosed(WindowEvent e)
{
decrementWindowCount();
if (control==e.getSource()) control=null;
frame.removeWindowListener(this);
frames.removeElement(e.getSource());
}
}
synchronized
public void createControlFrame()
{
if (control==null) setupFrame(control=new InitialStartupFrame(this));
}
synchronized
public void createMandelImageFrame(String n) throws IOException
{ File f=new File(n);
QualifiedMandelName name;
//System.out.println(f.getName()+": "+f);
if (f.getName().equals(n)) {
try {
name=QualifiedMandelName.create(f);
createMandelImageFrame(name);
return;
}
catch (IllegalArgumentException ex) {
throw new IOException(n+" is no mandel name.");
}
}
String base=f.getName();
try {
name=QualifiedMandelName.create(f);
}
catch (IllegalArgumentException ex) {
throw new IOException(n+" is no mandel name.");
}
MandelData md=new MandelData(f);
MandelImage img=getFactory().getImage(md);
if (img!=null) {
setupFrame(new MandelImageFrame(this, new MandelAreaImage(name,img)));
}
else {
throw new IOException(n+": no image.");
}
}
synchronized
public void createMandelImageFrame(MandelName mn) throws IOException
{
createMandelImageFrame(getMandelImage(mn));
}
synchronized
public void createMandelImageFrame(MandelAreaImage img) throws IOException
{
setupFrame(new MandelImageFrame(this, img));
}
synchronized
public void createMandelImageFrame(MandelAreaImage img, int maxx) throws IOException
{
setupFrame(new MandelImageFrame(this, img, maxx));
}
synchronized
public void createMandelImageFrame(QualifiedMandelName mn) throws IOException
{
MandelAreaImage img=getMandelImage(mn);
if (img!=null)
createMandelImageFrame(img);
}
synchronized
public void createMandelImageFrame(QualifiedMandelName mn,
ColormapModel cm) throws IOException
{
MandelAreaImage img=getMandelImage(mn,cm);
if (img!=null)
createMandelImageFrame(img);
}
synchronized
public void createMandelImageFrame(QualifiedMandelName mn, int maxx) throws IOException
{
MandelAreaImage img=getMandelImage(mn);
if (img!=null)
createMandelImageFrame(img,maxx);
}
synchronized
public void createMandelImageFrame(QualifiedMandelName mn, ColormapModel cm,
int maxx) throws IOException
{
MandelAreaImage img=getMandelImage(mn,cm);
if (img!=null)
createMandelImageFrame(img,maxx);
}
synchronized
public void createMandelImageFrame(File f) throws IOException
{ MandelImage img;
img=getFactory().getImage(f);
if (img!=null) {
QualifiedMandelName name=QualifiedMandelName.create(f);
if (name==null) name=QualifiedMandelName.create("unknown-unknown");
createMandelImageFrame(new MandelAreaImage(name,img));
}
}
synchronized
public void incrementWindowCount()
{ windowcnt++;
}
synchronized
public void decrementWindowCount()
{ windowcnt--;
if (windowcnt<=0 && doexit) System.exit(0);
}
synchronized
public void startup()
{
doexit=true;
SwingUtilities.invokeLater(new Runnable() {
public void run()
{
SplashScreen sp=SplashScreen.getSplashScreen();
if (sp!=null) sp.close();
if (getInitialFile()!=null) {
try {
createMandelImageFrame(getInitialFile());
}
catch (IOException io) {
System.err.println("cannot load "+getInitialFile()+": "+io);
System.exit(1);
}
}
else if (getImageDataScanner().getMandelHandle(getInitialName())!=null) {
try {
createMandelImageFrame(getInitialName());
}
catch (IOException ex) {
createControlFrame();
}
}
else {
createControlFrame();
}
}
});
}
// public MandelListTableModel getMandelListModel(MandelList l)
// {
// return new GeneralModel(l,!isReadonly());
// }
public MandelListFolderTreeModel getMandelListFolderTreeModel(MandelListFolderTree l)
{
return new GeneralFolderTreeModel(l,!isReadonly());
}
/////////////////////////////////////////////////////////////////////////
private class GeneralFolderTreeModel extends DefaultMandelListFolderTreeModel {
public GeneralFolderTreeModel(MandelListFolderTree tree, boolean mod)
{
super(tree, getAllScanner());
setModifiable(mod);
}
}
private class LinkFolderTreeModel extends DefaultMandelListFolderTreeModel {
private boolean mod;
public LinkFolderTreeModel(MandelListFolderTree tree)
{
super(tree, getAllScanner());
setModifiable(false);
this.mod=!isReadonly();
}
@Override
protected boolean isListModifiable(MandelListFolder f)
{
return mod;
}
}
public MandelListTableModel getMandelListModel(String path)
{
MandelListFolderTreeModel fm=getMandelListFolderTreeModel();
MandelListFolder f=fm.getRoot();
MandelListTableModel model;
String[] comps=path.split("/");
for (String comp :comps) {
if (debug) System.out.println("lookup "+comp);
if (!comp.isEmpty()) {
f=f.getSubFolder(comp);
if (f==null) break;
}
}
if (f==null) return null;
model=fm.getMandelListModel(f);
return model;
}
/////////////////////////////////////////////////////////////////////////
// mandel list model wrapper
/////////////////////////////////////////////////////////////////////////
private Map<MandelListTableModel,Boolean> refresh_pending;
private List<MandelListTableModel> refresh_order;
public void refresh(MandelListTableModel m, boolean soft)
{
if (!isInUpdate()) m.refresh(soft);
else {
Boolean v=refresh_pending.get(m);
if (v==null) {
refresh_pending.put(m,soft);
refresh_order.add(m);
}
else {
if (v.booleanValue() && !soft) {
refresh_pending.put(m,soft); // force hard reset
}
}
}
}
@Override
protected void handleUpdate()
{
super.handleUpdate();
while (!refresh_order.isEmpty()) {
MandelListTableModel m=refresh_order.get(0);
m.refresh(refresh_pending.get(m));
refresh_order.remove(0);
refresh_pending.remove(m);
}
imagebase_model.handleUpdate();
}
/////////////////////////////////////////////////////////////////////////
private class AreasModel extends DefaultMandelListTableModel {
public AreasModel()
{
super(getAreas(),getImageDataScanner());
setModifiable(!isReadonly());
}
}
//////////////////////////////////////////////////////////////////////////
public class AutoRefreshMandelListTableModel
extends DefaultMandelListTableModel {
private MandelScanner refresh;
public AutoRefreshMandelListTableModel(MandelList list, MandelScanner scanner)
{
this(list,scanner,scanner);
}
public AutoRefreshMandelListTableModel(MandelList list,
MandelScanner listscanner,
MandelScanner refreshscanner)
{
super(list,listscanner);
refresh=refreshscanner;
if (refresh!=null) {
refresh.addMandelScannerListener(new Listener());
}
}
private class Listener extends MandelScannerListenerAdapter {
@Override
public void addMandelFile(MandelScanner s, MandelHandle h)
{
handleEnvironmentChange();
}
@Override
public void removeMandelFile(MandelScanner s, MandelHandle h)
{
handleEnvironmentChange();
}
@Override
public void scannerChanged(MandelScanner s)
{
handleEnvironmentChange();
}
private void handleEnvironmentChange()
{
if (!isInUpdate()) {
if (debug) {
System.out.println("soft refresh for scanner refresh: "+
AutoRefreshMandelListTableModel.this);
}
//new Throwable().printStackTrace(System.out);
ToolEnvironment.this.refresh(AutoRefreshMandelListTableModel.this,true);
}
}
}
}
// private class GeneralModel extends DefaultMandelListTableModel {
//
// public GeneralModel(MandelList list, boolean mod)
// {
// super(list, getAllScanner());
// setModifiable(mod);
// }
// }
/////////////////////////////////////////////////////////////////////////
private class NewRasterModel extends AutoRefreshMandelListTableModel {
public NewRasterModel()
{
super(getNewRasters(),getNewRasterScanner());
}
}
/////////////////////////////////////////////////////////////////////////
private class UnseenRasterModel extends AutoRefreshMandelListTableModel {
public UnseenRasterModel()
{
super(getUnseenRasters(),getImageDataScanner());
addAction(new SyncNewAction());
}
private class SyncNewAction extends AbstractAction {
public SyncNewAction()
{
super("Sync New");
putValue(SHORT_DESCRIPTION,"Synchoronize with new images");
}
public void actionPerformed(ActionEvent e)
{
MandelList unseen=getList();
MandelList n=getNewRasters();
n.refresh(false);
ToolEnvironment.this.refresh(unseen);
unseen.retainAll(n);
try {
unseen.save();
}
catch (IOException ex) {
System.err.println("cannot write seen: "+ex);
}
fireTableDataChanged();
}
}
}
/////////////////////////////////////////////////////////////////////////
private class VariantsModel extends AutoRefreshMandelListTableModel {
public VariantsModel()
{
super(getVariants(),getImageDataScanner());
}
}
private class LeafModel extends AutoRefreshMandelListTableModel {
public LeafModel()
{
super(getLeafs(),getImageDataScanner(),getAllScanner());
}
}
private class PendingModel extends AutoRefreshMandelListTableModel {
public PendingModel()
{
super(getPending(),getImageDataScanner(),getAllScanner());
}
}
private class UnseenRefinementModel extends AutoRefreshMandelListTableModel {
public UnseenRefinementModel()
{
super(getUnseenRefinements(),getImageDataScanner());
}
}
private class RefinementRequestsModel extends AutoRefreshMandelListTableModel {
public RefinementRequestsModel()
{
super(getRefinementRequests(),getInfoScanner());
}
}
/////////////////////////////////////////////////////////////////////////
static public interface Listener {
void mandelListDeleted(MandelList list);
}
static public class ListenerAdapter implements Listener {
public void mandelListDeleted(MandelList list)
{
}
}
private Set<Listener> listeners=new HashSet<Listener>();
private boolean listenerActive;
synchronized
public void addEnvironmentListener(Listener l)
{
if (listenerActive) listeners=new HashSet<Listener>(listeners);
listeners.add(l);
}
synchronized
public void removeEnvironmentListener(Listener l)
{
if (listenerActive) listeners=new HashSet<Listener>(listeners);
listeners.remove(l);
}
synchronized
public void fireMandelListDeleted(MandelList list)
{
try {
listenerActive=true;
for (Listener l :listeners) l.mandelListDeleted(list);
}
finally {
listenerActive=false;
}
}
}