/*
* Copyright 2010-2015 Institut Pasteur.
*
* This file is part of Icy.
*
* Icy 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 3 of the License, or
* (at your option) any later version.
*
* Icy 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 Icy. If not, see <http://www.gnu.org/licenses/>.
*/
package icy.common;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import icy.common.listener.ChangeListener;
import icy.system.thread.ThreadUtil;
/**
* Utility class to handle <code>Update</code> type event.
*
* @author stephane
*/
public class UpdateEventHandler
{
ChangeListener parent;
/**
* dispatch in AWT dispatch thread
*/
private boolean awtDispatch;
/**
* internal update counter
*/
private int updateCnt;
/**
* internal pending change events
*/
private final LinkedHashMap<CollapsibleEvent, CollapsibleEvent> pendingChanges;
/**
*
*/
public UpdateEventHandler(ChangeListener parent, boolean awtDispatch)
{
super();
this.parent = parent;
this.awtDispatch = awtDispatch;
updateCnt = 0;
pendingChanges = new LinkedHashMap<CollapsibleEvent, CollapsibleEvent>();
}
/**
*
*/
public UpdateEventHandler(ChangeListener parent)
{
this(parent, false);
}
/**
* @return the awtDispatch
*/
public boolean isAwtDispatch()
{
return awtDispatch;
}
/**
* @param awtDispatch
* the awtDispatch to set
*/
public void setAwtDispatch(boolean awtDispatch)
{
this.awtDispatch = awtDispatch;
}
public Collection<CollapsibleEvent> getPendingChanges()
{
return pendingChanges.values();
}
public void beginUpdate()
{
updateCnt++;
}
public void endUpdate()
{
updateCnt--;
if (updateCnt <= 0)
{
final List<CollapsibleEvent> events;
synchronized (pendingChanges)
{
events = new ArrayList<CollapsibleEvent>(pendingChanges.values());
pendingChanges.clear();
}
// dispatch all contained events (use copy to avoid concurrent changes)
for (CollapsibleEvent event : events)
dispatchOnChanged(event);
}
}
public boolean isUpdating()
{
return updateCnt > 0;
}
public boolean hasPendingChanges()
{
return !pendingChanges.isEmpty();
}
protected void addPendingChange(CollapsibleEvent change)
{
final CollapsibleEvent previousChange;
// TODO: can take sometime (select all on many ROI)
// TODO: check how fast is it now...
synchronized (pendingChanges)
{
// search in pending changes if we have an equivalent change
previousChange = pendingChanges.get(change);
// not already existing ? --> just add the new change
if (previousChange == null)
pendingChanges.put(change, change);
}
// found an equivalent previous change ? --> collapse the new change into the old one
if (previousChange != null)
previousChange.collapse(change);
}
public void changed(CollapsibleEvent event)
{
if (isUpdating())
addPendingChange(event);
else
dispatchOnChanged(event);
}
protected void dispatchOnChanged(CollapsibleEvent event)
{
final CollapsibleEvent e = event;
if (awtDispatch)
{
// dispatch on AWT Dispatch Thread now
ThreadUtil.invokeNow(new Runnable()
{
@Override
public void run()
{
parent.onChanged(e);
}
});
}
else
parent.onChanged(e);
}
}