001    /*
002     Copyright (C) 2003 Adam Olsen
003     This program is free software; you can redistribute it and/or modify
004     it under the terms of the GNU General Public License as published by
005     the Free Software Foundation; either version 1, or (at your option)
006     any later version.
007     This program is distributed in the hope that it will be useful,
008     but WITHOUT ANY WARRANTY; without even the implied warranty of
009     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
010     GNU General Public License for more details.
011     You should have received a copy of the GNU General Public License
012     along with this program; if not, write to the Free Software
013     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
014     */
015    package com.valhalla.jbother;
016    
017    import com.valhalla.gui.*;
018    import javax.swing.*;
019    
020    import java.awt.*;
021    import java.awt.event.*;
022    import javax.swing.table.*;
023    import java.util.*;
024    import javax.swing.*;
025    import java.beans.*;
026    import org.jivesoftware.smackx.filetransfer.*;
027    
028    /**
029     *
030     * @author synic
031     */
032    public class FileProgressDialog extends JFrame {
033    
034        private static FileProgressDialog instance = null;
035        private JPanel main;
036        private JButton clear = new JButton("Clean Up");
037        private JButton close = new JButton("Close");
038        private JButton cancel = new JButton("Cancel");
039        private Component glue = Box.createVerticalGlue();
040        private TableModel model = new TableModel();
041        private JTable table = new JTable(model);
042        java.text.NumberFormat nf = java.text.NumberFormat.getInstance();
043        java.text.NumberFormat nf2 = java.text.NumberFormat.getInstance();
044    
045    
046        /** Creates a new instance of FileProgressDialog */
047        private FileProgressDialog() {
048            super("File Transfer Manager");
049    
050            setIconImage(Standard.getImage("frameicon.png"));
051    
052            main = (JPanel)getContentPane();
053            main.setLayout(new BorderLayout());
054            main.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
055            nf.setMaximumFractionDigits(1);
056            nf.setMinimumFractionDigits(1);
057            JScrollPane scroll = new JScrollPane(table);
058            table.setBackground(Color.WHITE);
059            scroll.getViewport().setBackground(Color.WHITE);
060    
061            main.add(scroll, BorderLayout.CENTER);
062            table.setShowGrid(false);
063            table.setShowHorizontalLines(true);
064            table.setTableHeader(null);
065    
066            // get the height for each row
067            Window w = new Window(this);
068            w.setLayout(new BorderLayout());
069            TransferPanel tp = new TransferPanel(null,"", "",66, 0);
070            tp.bar.setValue(100);
071            w.add(tp, BorderLayout.CENTER);
072            w.pack();
073    
074            table.setRowHeight(w.getHeight() + 18);
075            w.dispose();
076    
077            TableColumn col = table.getColumnModel().getColumn(0);
078            col.setResizable(false);
079            col.setPreferredWidth(28);
080            col.setMaxWidth(28);
081            col = table.getColumnModel().getColumn(1);
082            col.setPreferredWidth(376);
083            table.setDefaultRenderer(TransferPanel.class, new CellRend());
084            table.setDefaultRenderer(IconLabel.class, new IconRend());
085    
086            JPanel buttons = new JPanel();
087            buttons.setLayout(new BoxLayout(buttons, BoxLayout.X_AXIS));
088            buttons.add(Box.createHorizontalGlue());
089            buttons.add(cancel);
090            buttons.add(clear);
091            buttons.add(close);
092            buttons.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
093            close.addActionListener(new ActionListener()
094            {
095                public void actionPerformed(ActionEvent e)
096                {
097                    setVisible(false);
098                }
099            });
100    
101            clear.addActionListener(new ActionListener()
102            {
103                public void actionPerformed(ActionEvent e)
104                {
105                    cleanup();
106                }
107            } );
108    
109            cancel.addActionListener(new ActionListener()
110            {
111                public void actionPerformed(ActionEvent e)
112                {
113                    int row = table.getSelectedRow();
114                    if(row != -1)
115                    {
116                        TransferPanel panel = (TransferPanel)model.getValueAt(row, 1);
117                        if(!panel.getDone()) panel.cancel();
118                    }
119                }
120            } );
121    
122            main.add(buttons, BorderLayout.SOUTH);
123            pack();
124            setSize(new Dimension(450, 250));
125            Standard.cascadePlacement(this);
126        }
127    
128        private void cleanup()
129        {
130            ArrayList items = model.getItems();
131            for( int i = 0; i < items.size(); i++ )
132            {
133                TransferPanel panel = (TransferPanel)items.get(i);
134                if(panel.getDone())
135                {
136                    model.remove(panel);
137                    i--;
138                }
139            }
140        }
141    
142        public static FileProgressDialog getInstance()
143        {
144            if(instance == null) instance = new FileProgressDialog();
145            return instance;
146        }
147    
148        public void addFile(FileTransfer transfer, String jid, String name, int type, long size)
149        {
150            setVisible(true);
151            new TransferPanel(transfer, jid, name, type, size);
152        }
153    
154        class TransferPanel extends JPanel
155        {
156            JProgressBar bar = new JProgressBar(0, 100);
157            private String fileName = null;
158            private JLabel percent = new JLabel("0%");
159            private int status = 3;
160            private int type = 0;
161            private IconLabel l = new IconLabel(this);
162            private FileTransfer transfer;
163            private String name;
164            private long size;
165            private long current = 0;
166            private long lastSize = 0;
167            private long last = 0;
168            private javax.swing.Timer rateTimer = new javax.swing.Timer(1000, new RateHandler());
169            private double rate = 0;
170            private Vector avg = new Vector();
171            private int counter = 0;
172            private boolean popShown = false;
173            private String tooltip = null;
174            private String jid, from;
175    
176            public TransferPanel(FileTransfer transfer, String jid, String name, int type, long size)
177            {
178                this.type = type;
179                this.transfer = transfer;
180                this.jid = jid;
181                this.from = ConnectorThread.getInstance().getConnection().getUser();
182                this.size = size;
183                setLayout(new BorderLayout());
184                setBackground(Color.WHITE);
185                setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
186                this.name = name;
187    
188                JLabel n = new JLabel(name);
189                n.setToolTipText(getTooltip());
190                l.setToolTipText(getTooltip());
191                setToolTipText(getTooltip());
192                add(n, BorderLayout.NORTH);
193    
194                bar.setToolTipText(getTooltip());
195                percent.setToolTipText(getTooltip());
196    
197                add(bar, BorderLayout.CENTER);
198                add(percent, BorderLayout.SOUTH);
199    
200                this.fileName = name;
201                if(type != 66) model.add(this);
202                startMonitor();
203            }
204    
205            public String getTooltip()
206            {
207                if(transfer == null) return "";
208                if(tooltip == null)
209                {
210                    StringBuffer buff = new StringBuffer();
211                    buff.append("<html><table><tr><td><b>File: </b></td><td>");
212    
213                    String loc = name;
214                    if(type == 1) loc = transfer.getFileName();
215                    buff.append(loc).append("</td></tr>");
216                    if(type == 0)
217                    {
218                        buff.append("<tr><td><b>To: </b></td><td>").append(jid).append("</td></tr>");
219                    }
220                    else {
221                        buff.append("<tr><td><b>From: </b></td><td>").append(from).append("</td></tr>");
222                    }
223    
224                    String desc = ""; //transfer.getDescription();
225                    if(desc.equals("")) desc = "None given";
226                    buff.append("<tr><td><b>Description:</b></td><td>").append(desc).append("</td></tr>");
227                    buff.append("</table></html>\n");
228                    tooltip = buff.toString();
229                }
230                return tooltip;
231            }
232    
233            class RateHandler implements ActionListener
234            {
235                public void actionPerformed(ActionEvent e)
236                {
237                    double diff = current - last;
238                    last = current;
239    
240                    avg.add(0, new Double(diff));
241                    if(avg.size() > 5) avg.setSize(5);
242    
243                    if(avg.size() < 5) rate = diff / 1024.0;
244                    else {
245                        double value = 0;
246                        for(int i = 0; i < avg.size(); i++)
247                        {
248                            Double d = (Double)avg.get(i);
249                            value += d.doubleValue();
250                        }
251    
252                        rate = (value / avg.size()) / 1024.0;
253                    }
254                }
255            }
256    
257            public IconLabel getLabel()
258            {
259                return l;
260            }
261    
262            public int getStatus()
263            {
264                return status;
265            }
266    
267            public boolean getDone()
268            {
269                return transfer.isDone();
270            }
271    
272            public void cancel() 
273            {   
274                transfer.cancel();
275                rateTimer.stop();
276                status = 4; 
277            }
278    
279            public void setSelected(boolean selected)
280            {
281                if(selected) setBackground(table.getSelectionBackground());
282                else setBackground(Color.WHITE);
283            }
284    
285            class MonitorThread extends Thread
286            {
287                FileTransfer transfer;
288                public MonitorThread(FileTransfer transfer)
289                {
290                    this.transfer = transfer;
291                }
292                
293                public void run()
294                {
295                    while( !transfer.isDone() )
296                    {
297                        current = transfer.getAmountWritten();
298                        try {
299                            Thread.sleep(500);
300                        }
301                        catch(Exception ex) { }
302                        if( transfer.getStatus().equals(FileTransfer.Status.ERROR) )
303                        {
304                            status = 4;
305                        }
306    
307                        SwingUtilities.invokeLater(new Runnable() {
308                            public void run() {
309                                int val = (int)(transfer.getProgress() * 100);
310                                bar.setValue(val);
311                                table.repaint();
312                                table.validate();
313                                percent.setText(val + "%");
314                            }
315                        } );
316                    }
317    
318                }
319            }
320            
321            public void startMonitor()
322            {
323                if( transfer == null ) return;
324                if( !rateTimer.isRunning()) rateTimer.start();
325                status = type;
326                new MonitorThread(transfer).start();
327            }
328    
329            public void update(final String event, final int percent, final long bytes)
330            {
331                if(!rateTimer.isRunning()) rateTimer.start();
332    
333                if(event.equals("Transferring")) status = type;
334                else if(event.equals("Done")) status = 5;
335                else if(event.equals("Error") || event.equals("Cancelled")) status = 4;
336    
337                if( status > 1 ) rateTimer.stop();
338    
339                if(counter++ < 5 && status == type) return;
340                counter = 0;
341    
342                SwingUtilities.invokeLater(new Runnable()
343                {
344                   public void run()
345                   {
346                       long b = bytes;
347                       if(bytes <= 0)
348                       {
349                           b = lastSize;
350                       }
351                       else {
352                           bar.setValue(percent);
353                       }
354                       lastSize = b;
355                       current = b;
356                       String transferred = nf2.format(b / 1024) + "k";
357                       String total = nf2.format(size / 1024) + "k";
358    
359                       TransferPanel.this.percent.setText(percent + "% - " + event + " - " +
360                                transferred + "/" + total + " - " +   nf.format(rate) + "KB/s");
361                       table.repaint();
362    
363                       if(status == 5 && !popShown)
364                       {
365                           popShown = true;
366                           NotificationPopup.showSingleton(FileProgressDialog.this, "Transfer Complete", transfer.getFileName(), TransferPanel.this);
367                       }
368                   }
369                });
370            }
371        }
372    
373        class CellRend extends JPanel implements TableCellRenderer
374        {
375    
376            public Component getTableCellRendererComponent(
377                                JTable table, Object color,
378                                boolean isSelected, boolean hasFocus,
379                                int row, int column) {
380    
381                TransferPanel panel = (TransferPanel)color;
382                setToolTipText(panel.getTooltip());
383                panel.setSelected(isSelected);
384                panel.validate();
385                return (JPanel)color;
386            }
387        }
388    
389        class IconLabel extends JLabel
390        {
391            private ImageIcon up = Standard.getIcon("images/buttons/Up24.gif");
392            private ImageIcon down = Standard.getIcon("images/buttons/Down24.gif");
393            private ImageIcon error = Standard.getIcon("images/buttons/Error24.gif");
394            private ImageIcon wait = Standard.getIcon("images/buttons/Wait24.gif");
395            private ImageIcon done = Standard.getIcon("images/buttons/Done24.gif");
396    
397            private TransferPanel panel;
398    
399            public IconLabel(TransferPanel panel)
400            {
401                setPreferredSize(new Dimension(28, 28));
402                setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
403                this.panel = panel;
404                setOpaque(true);
405            }
406    
407            public void updateIcon()
408            {
409                int status = panel.getStatus();
410    
411                switch(status)
412                {
413                    case 0:
414                        setIcon(up);
415                        break;
416                    case 1:
417                        setIcon(down);
418                        break;
419                    case 4:
420                        setIcon(error);
421                        break;
422                    case 5:
423                        setIcon(done);
424                        break;
425                    default:
426                        setIcon(wait);
427                        break;
428                }
429            }
430    
431            public void setSelected(boolean selected)
432            {
433                if(selected) setBackground(table.getSelectionBackground());
434                else setBackground(Color.WHITE);
435            }
436    
437        }
438    
439        class IconRend extends JPanel implements TableCellRenderer
440        {
441            public Component getTableCellRendererComponent(
442                                JTable table, Object color,
443                                boolean isSelected, boolean hasFocus,
444                                int row, int column) {
445    
446                IconLabel panel = (IconLabel)color;
447                panel.setSelected(isSelected);
448    
449                panel.setSelected(isSelected);
450                panel.updateIcon();
451                panel.validate();
452                return (JLabel)panel;
453            }
454        }
455    
456        class TableModel extends AbstractTableModel
457        {
458            private ArrayList items = new ArrayList();
459    
460            public synchronized void add(TransferPanel panel)
461            {
462                items.add(0, panel);
463                fireTableRowsInserted(items.size(), items.size());
464            }
465    
466            public ArrayList getItems() { return items; }
467    
468            public int getColumnCount() { return 2; }
469    
470            public int getRowCount() { return items.size(); }
471    
472            public Object getValueAt(int row, int col)
473            {
474                TransferPanel panel = (TransferPanel)items.get(row);
475                if(col == 0) return panel.getLabel();
476                else return panel;
477            }
478    
479            public Class getColumnClass(int c)
480            {
481                if(c == 0) return IconLabel.class;
482                else return TransferPanel.class;
483            }
484    
485            public synchronized void remove(TransferPanel panel)
486            {
487                int index = items.indexOf(panel);
488                if(index != -1) items.remove(index);
489                fireTableRowsDeleted(items.size(), items.size());
490            }
491    
492    
493        }
494    }