001/*
002 * Copyright 2010-2015 Institut Pasteur.
003 * 
004 * This file is part of Icy.
005 * 
006 * Icy is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 * 
011 * Icy is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 * 
016 * You should have received a copy of the GNU General Public License
017 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
018 */
019package icy.gui.main;
020
021import icy.gui.frame.progress.ProgressFrame;
022import icy.gui.frame.progress.TaskFrame;
023import icy.main.Icy;
024import icy.system.thread.ThreadUtil;
025
026import java.awt.Dimension;
027import java.awt.Point;
028import java.util.ArrayList;
029import java.util.List;
030
031/**
032 * Manage the TaskFrame to display them on right of the window.
033 * 
034 * @author Fabrice de Chaumont & Stephane Dallongeville
035 */
036public class TaskFrameManager implements Runnable
037{
038    private static Dimension getDesktopSize()
039    {
040        final MainFrame mainFrame = Icy.getMainInterface().getMainFrame();
041
042        if (mainFrame != null)
043            // get bottom right border location
044            return mainFrame.getDesktopSize();
045
046        return null;
047    }
048
049    private static class TaskFrameInfo
050    {
051        final TaskFrame frame;
052        Point position;
053        long showDelay;
054        long hideDelay;
055        boolean visible;
056
057        public TaskFrameInfo(TaskFrame frame, Point position, long showDelay, long hideDelay)
058        {
059            super();
060
061            this.frame = frame;
062            this.position = position;
063            this.showDelay = showDelay;
064            this.hideDelay = hideDelay;
065            visible = false;
066        }
067
068        public boolean canHide()
069        {
070            return hideDelay < 0;
071        }
072
073        public boolean canShow()
074        {
075            return showDelay < 0;
076        }
077    }
078
079    final Thread animThread;
080    List<TaskFrameInfo> taskFrameInfos;
081    long lastUpdateTime;
082
083    /**
084     * 
085     */
086    public TaskFrameManager()
087    {
088        super();
089
090        taskFrameInfos = new ArrayList<TaskFrameInfo>();
091        animThread = new Thread(this, "TaskFrame manager");
092        lastUpdateTime = System.currentTimeMillis();
093    }
094
095    // we have to separate init as thread call the getMainInterface() method
096    public void init()
097    {
098        animThread.start();
099    }
100
101    public void addTaskWindow(final TaskFrame tFrame, final long showDelay, final long hideDelay)
102    {
103        final Dimension desktopSize = getDesktopSize();
104
105        if ((desktopSize != null) && !tFrame.canRemove())
106        {
107            // get bottom right border location
108            final Point pos = new Point(desktopSize.width + 10, desktopSize.height);
109            final TaskFrameInfo frameInfo = new TaskFrameInfo(tFrame, pos, showDelay, hideDelay);
110
111            synchronized (taskFrameInfos)
112            {
113                taskFrameInfos.add(frameInfo);
114            }
115        }
116    }
117
118    public void addTaskWindow(final TaskFrame tFrame)
119    {
120        // we use a different default value for progress frame
121        if (tFrame instanceof ProgressFrame)
122            addTaskWindow(tFrame, 0L, 1000L);
123        else
124            addTaskWindow(tFrame, 0L, 0L);
125    }
126
127    @Override
128    public void run()
129    {
130        while (true)
131        {
132            long currentTime = System.currentTimeMillis();
133            long deltaTime = currentTime - lastUpdateTime;
134            lastUpdateTime = currentTime;
135
136            animateFrames(deltaTime);
137
138            // sleep a bit
139            ThreadUtil.sleep(20);
140        }
141    }
142
143    void animateFrames(long delta)
144    {
145        // get bottom right border location
146        final Dimension desktopSize = getDesktopSize();
147        // not yet initialized
148        if (desktopSize == null)
149            return;
150
151        List<TaskFrameInfo> list;
152
153        // create temporary copy of the frame list
154        synchronized (taskFrameInfos)
155        {
156            list = new ArrayList<TaskFrameInfo>(taskFrameInfos);
157        }
158
159        // process frames which will be closed
160        for (int i = list.size() - 1; i >= 0; i--)
161        {
162            final TaskFrameInfo info = list.get(i);
163            final TaskFrame frame = info.frame;
164
165            info.showDelay -= delta;
166            // close order
167            if (frame.canRemove())
168                info.hideDelay -= delta;
169
170            // frame need to be removed ?
171            if (info.canHide())
172            {
173                // frame hidden ?
174                if (!info.visible || ((info.position.x >= desktopSize.width)))
175                {
176                    // remove it from list
177                    list.remove(i);
178                    // and close it definitely
179                    frame.internalClose();
180                }
181            }
182        }
183
184        // calculate top Y position
185        float currentY = desktopSize.height;
186        int ind;
187        for (ind = list.size() - 1; ind >= 0; ind--)
188        {
189            final TaskFrameInfo info = list.get(ind);
190
191            if (info.canShow())
192            {
193                final int h = info.frame.getHeight();
194
195                currentY -= h;
196                // outside screen --> interrupt
197                if (currentY < 0)
198                {
199                    currentY += h;
200                    break;
201                }
202            }
203        }
204
205        // need to remove frame outside screen
206        if (ind != -1)
207        {
208            for (int i = 0; i <= ind; i++)
209            {
210                final TaskFrameInfo info = list.get(i);
211                // close the frame definitely
212                info.frame.internalClose();
213            }
214
215            // remove frames from list
216            list = new ArrayList<TaskFrameInfo>(list.subList(ind + 1, list.size()));
217        }
218
219        // calculate and update all frames position
220        for (TaskFrameInfo info : list)
221        {
222            final TaskFrame frame = info.frame;
223
224            if (info.canShow())
225            {
226                int targetX;
227
228                // find X target position
229                if (info.canHide())
230                    targetX = desktopSize.width + 20;
231                else
232                    targetX = desktopSize.width - frame.getWidth();
233
234                final Point targetPos = new Point(targetX, (int) currentY);
235                final Point curPos = info.position;
236
237                float vectX = (targetPos.x - curPos.x) / 10f;
238                if (vectX != 0f)
239                {
240                    // we want at least 1 or -1
241                    if (Math.abs(vectX) < 1f)
242                    {
243                        if (vectX < 0)
244                            vectX = -1f;
245                        else
246                            vectX = 1f;
247                    }
248                }
249                float vectY = (targetPos.y - curPos.y) / 10f;
250                if (vectY != 0f)
251                {
252                    // we want at least 1 or -1
253                    if (Math.abs(vectY) < 1f)
254                    {
255                        if (vectY < 0)
256                            vectY = -1f;
257                        else
258                            vectY = 1f;
259                    }
260                }
261
262                // define new position
263                final Point newPos = new Point((int) (curPos.x + vectX), (int) (curPos.y + vectY));
264
265                // set Y when starting the scroll
266                if (curPos.x > desktopSize.width)
267                    newPos.y = targetPos.y;
268
269                // update position
270                info.position = newPos;
271
272                // avoid repaint on JDesktopPane if position did not changed
273                if (frame.isInternalized())
274                {
275                    if (!frame.getLocationInternal().equals(newPos))
276                        frame.setLocationInternal(newPos);
277                }
278                else
279                {
280                    if (!frame.getLocationExternal().equals(newPos))
281                        frame.setLocationExternal(newPos);
282                }
283
284                // frame need to be displayed now ?
285                if (info.canShow() && !info.visible)
286                {
287                    // do set visible before adding to desktop pane so the frame does not take focus
288                    frame.setVisible(true);
289                    frame.addToDesktopPane();
290                    frame.toFront();
291                    info.visible = true;
292                }
293
294                currentY += frame.getHeight();
295            }
296        }
297
298        // update global list
299        taskFrameInfos = list;
300    }
301}