/*
* 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.gui.main;
import icy.gui.frame.progress.ProgressFrame;
import icy.gui.frame.progress.TaskFrame;
import icy.main.Icy;
import icy.system.thread.ThreadUtil;
import java.awt.Dimension;
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
/**
* Manage the TaskFrame to display them on right of the window.
*
* @author Fabrice de Chaumont & Stephane Dallongeville
*/
public class TaskFrameManager implements Runnable
{
private static Dimension getDesktopSize()
{
final MainFrame mainFrame = Icy.getMainInterface().getMainFrame();
if (mainFrame != null)
// get bottom right border location
return mainFrame.getDesktopSize();
return null;
}
private static class TaskFrameInfo
{
final TaskFrame frame;
Point position;
long showDelay;
long hideDelay;
boolean visible;
public TaskFrameInfo(TaskFrame frame, Point position, long showDelay, long hideDelay)
{
super();
this.frame = frame;
this.position = position;
this.showDelay = showDelay;
this.hideDelay = hideDelay;
visible = false;
}
public boolean canHide()
{
return hideDelay < 0;
}
public boolean canShow()
{
return showDelay < 0;
}
}
final Thread animThread;
List<TaskFrameInfo> taskFrameInfos;
long lastUpdateTime;
/**
*
*/
public TaskFrameManager()
{
super();
taskFrameInfos = new ArrayList<TaskFrameInfo>();
animThread = new Thread(this, "TaskFrame manager");
lastUpdateTime = System.currentTimeMillis();
}
// we have to separate init as thread call the getMainInterface() method
public void init()
{
animThread.start();
}
public void addTaskWindow(final TaskFrame tFrame, final long showDelay, final long hideDelay)
{
final Dimension desktopSize = getDesktopSize();
if ((desktopSize != null) && !tFrame.canRemove())
{
// get bottom right border location
final Point pos = new Point(desktopSize.width + 10, desktopSize.height);
final TaskFrameInfo frameInfo = new TaskFrameInfo(tFrame, pos, showDelay, hideDelay);
synchronized (taskFrameInfos)
{
taskFrameInfos.add(frameInfo);
}
}
}
public void addTaskWindow(final TaskFrame tFrame)
{
// we use a different default value for progress frame
if (tFrame instanceof ProgressFrame)
addTaskWindow(tFrame, 0L, 1000L);
else
addTaskWindow(tFrame, 0L, 0L);
}
@Override
public void run()
{
while (true)
{
long currentTime = System.currentTimeMillis();
long deltaTime = currentTime - lastUpdateTime;
lastUpdateTime = currentTime;
animateFrames(deltaTime);
// sleep a bit
ThreadUtil.sleep(20);
}
}
void animateFrames(long delta)
{
// get bottom right border location
final Dimension desktopSize = getDesktopSize();
// not yet initialized
if (desktopSize == null)
return;
List<TaskFrameInfo> list;
// create temporary copy of the frame list
synchronized (taskFrameInfos)
{
list = new ArrayList<TaskFrameInfo>(taskFrameInfos);
}
// process frames which will be closed
for (int i = list.size() - 1; i >= 0; i--)
{
final TaskFrameInfo info = list.get(i);
final TaskFrame frame = info.frame;
info.showDelay -= delta;
// close order
if (frame.canRemove())
info.hideDelay -= delta;
// frame need to be removed ?
if (info.canHide())
{
// frame hidden ?
if (!info.visible || ((info.position.x >= desktopSize.width)))
{
// remove it from list
list.remove(i);
// and close it definitely
frame.internalClose();
}
}
}
// calculate top Y position
float currentY = desktopSize.height;
int ind;
for (ind = list.size() - 1; ind >= 0; ind--)
{
final TaskFrameInfo info = list.get(ind);
if (info.canShow())
{
final int h = info.frame.getHeight();
currentY -= h;
// outside screen --> interrupt
if (currentY < 0)
{
currentY += h;
break;
}
}
}
// need to remove frame outside screen
if (ind != -1)
{
for (int i = 0; i <= ind; i++)
{
final TaskFrameInfo info = list.get(i);
// close the frame definitely
info.frame.internalClose();
}
// remove frames from list
list = new ArrayList<TaskFrameInfo>(list.subList(ind + 1, list.size()));
}
// calculate and update all frames position
for (TaskFrameInfo info : list)
{
final TaskFrame frame = info.frame;
if (info.canShow())
{
int targetX;
// find X target position
if (info.canHide())
targetX = desktopSize.width + 20;
else
targetX = desktopSize.width - frame.getWidth();
final Point targetPos = new Point(targetX, (int) currentY);
final Point curPos = info.position;
float vectX = (targetPos.x - curPos.x) / 10f;
if (vectX != 0f)
{
// we want at least 1 or -1
if (Math.abs(vectX) < 1f)
{
if (vectX < 0)
vectX = -1f;
else
vectX = 1f;
}
}
float vectY = (targetPos.y - curPos.y) / 10f;
if (vectY != 0f)
{
// we want at least 1 or -1
if (Math.abs(vectY) < 1f)
{
if (vectY < 0)
vectY = -1f;
else
vectY = 1f;
}
}
// define new position
final Point newPos = new Point((int) (curPos.x + vectX), (int) (curPos.y + vectY));
// set Y when starting the scroll
if (curPos.x > desktopSize.width)
newPos.y = targetPos.y;
// update position
info.position = newPos;
// avoid repaint on JDesktopPane if position did not changed
if (frame.isInternalized())
{
if (!frame.getLocationInternal().equals(newPos))
frame.setLocationInternal(newPos);
}
else
{
if (!frame.getLocationExternal().equals(newPos))
frame.setLocationExternal(newPos);
}
// frame need to be displayed now ?
if (info.canShow() && !info.visible)
{
// do set visible before adding to desktop pane so the frame does not take focus
frame.setVisible(true);
frame.addToDesktopPane();
frame.toFront();
info.visible = true;
}
currentY += frame.getHeight();
}
}
// update global list
taskFrameInfos = list;
}
}