1 package ch
.cyberduck
.ui
;
4 * ch.cyberduck.ui.ListPanel.java
11 * Copyright (c) 2003 David Kocher. All rights reserved.
12 * http://icu.unizh.ch/~dkocher/
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * Bug fixes, suggestions and comments should be sent to:
25 * dkocher@cyberduck.ch
29 import javax
.swing
.event
.ListSelectionEvent
;
30 import javax
.swing
.event
.ListSelectionListener
;
32 import java
.awt
.event
.*;
33 import java
.util
.Observable
;
34 import java
.util
.Observer
;
35 import java
.util
.Vector
;
37 import ch
.cyberduck
.Cyberduck
;
38 import ch
.cyberduck
.CyberduckMenu
;
39 import ch
.cyberduck
.Preferences
;
40 import ch
.cyberduck
.connection
.Bookmark
;
41 import ch
.cyberduck
.connection
.Path
;
42 import ch
.cyberduck
.connection
.TransferAction
;
43 import ch
.cyberduck
.ui
.action
.ActionMap
;
44 import ch
.cyberduck
.ui
.common
.GUIFactory
;
45 import ch
.cyberduck
.ui
.model
.ListTableModel
;
50 public class ListPanel
extends JPanel
implements Observer
{
52 private ListTable listingTable
;
53 private ListTableModel listingTableModel
;
56 * The currently selected bookmark
58 private Bookmark selected
;
60 private JScrollPane pane
;
61 private JComboBox comboBox
;
62 private ComboPathModel comboModel
;
63 private ActionListener comboActionListener
;
64 private ItemListener comboItemListener
;
66 private Action backAction
;
67 private Action upAction
;
68 private Action refreshAction
;
69 private Action newDirectoryAction
;
70 private Action renameAction
;
71 private Action deleteAction
;
72 private Action downloadAction
;
73 private Action permissionAction
;
74 private Action abortAction
;
75 private Action disconnectAction
;
82 public void update(Observable o
, Object arg
) {
83 this.listingTable
.update(o
, arg
);
84 if(arg
.equals(Bookmark
.SELECTION
) || arg
.equals(Bookmark
.LIST
)) {
85 this.selected
= (Bookmark
)o
;
86 boolean valid
= false;
88 valid
= selected
.isValid();
89 valid
= selected
.getProtocol().equals(ch
.cyberduck
.connection
.Session
.FTP
);
90 valid
= selected
.isListing() && selected
.getListing() != null;
92 backAction
.setEnabled(valid
);
93 upAction
.setEnabled(valid
);
94 refreshAction
.setEnabled(valid
);
95 newDirectoryAction
.setEnabled(valid
);
96 abortAction
.setEnabled(valid
);
97 disconnectAction
.setEnabled(valid
);
99 downloadAction
.setEnabled(false);
100 renameAction
.setEnabled(false);
101 deleteAction
.setEnabled(false);
102 permissionAction
.setEnabled(false);
104 if(selected
.isListing()) {
105 Path cwd
= selected
.getCurrentPath();
107 //@workaround because changing the model of JComboBox fires the ActionListener
108 this.comboBox
.removeItemListener(comboItemListener
);
109 this.comboModel
.removeAll();
110 int depth
= cwd
.getPathDepth();
111 for(int i
= 0; i
<= depth
; i
++) {
112 this.comboModel
.addElement(cwd
.getPathFragment(i
));
114 // selected the last added item
115 this.comboModel
.setSelectedItem(comboModel
.getElementAt(comboModel
.getSize()-1));
116 this.comboBox
.addItemListener(comboItemListener
);
122 public Dimension
getPreferredSize() {
123 Cyberduck
.DEBUG("[ListPanel] getMinimumSize()");
124 return listingTable
.getPreferredScrollableViewportSize();
128 public Dimension
getMinimumSize() {
129 Dimension d
= new Dimension(listingTable
.getPreferredScrollableViewportSize().width
, 300);
130 Cyberduck
.DEBUG("[ListPanel] getMinimumSize():" + d
.toString());
136 * Initialize the graphical user interface; the table and associated TableModel
138 private void init() {
139 Cyberduck
.DEBUG("[ListPanel] init()");
140 listingTable
= new ListTable();
141 listingTableModel
= (ListTableModel
)listingTable
.getModel();
142 this.setLayout(new BorderLayout());
144 backAction
= new BackAction();
145 upAction
= new UpAction();
146 refreshAction
= new RefreshAction();
147 abortAction
= new AbortAction();
148 downloadAction
= new ListingDownloadAction();
149 newDirectoryAction
= new NewDirectoryAction();
150 renameAction
= new RenameAction();
151 deleteAction
= new DeleteAction();
152 permissionAction
= new PermissionAction();
153 disconnectAction
= new DisconnectAction();
155 this.updateFtpMenu();
157 ListSelectionModel selectionModel
= listingTable
.getSelectionModel();
158 selectionModel
.addListSelectionListener(new ListSelectionListener() {
159 public void valueChanged(ListSelectionEvent e
) {
160 if(!e
.getValueIsAdjusting()) {
161 //Cyberduck.DEBUG(e.paramString());
162 ListSelectionModel lsm
= (ListSelectionModel
)e
.getSource();
163 boolean selection
= !(lsm
.isSelectionEmpty());
165 downloadAction
.setEnabled(selection
);
166 renameAction
.setEnabled(selection
);
167 deleteAction
.setEnabled(selection
);
168 permissionAction
.setEnabled(selection
);
177 pane
= new JScrollPane(listingTable
);
178 pane
.setVerticalScrollBarPolicy(JScrollPane
.VERTICAL_SCROLLBAR_ALWAYS
);
179 pane
.setHorizontalScrollBarPolicy(JScrollPane
.HORIZONTAL_SCROLLBAR_AS_NEEDED
);
182 final javax
.swing
.JPopupMenu popupMenu
= new javax
.swing
.JPopupMenu();
183 popupMenu
.add(downloadAction
);
184 popupMenu
.addSeparator();
185 popupMenu
.add(newDirectoryAction
);
186 popupMenu
.add(renameAction
);
187 popupMenu
.add(deleteAction
);
188 popupMenu
.add(permissionAction
);
190 listingTable
.addMouseListener(new java
.awt
.event
.MouseAdapter() {
191 public void mousePressed(java
.awt
.event
.MouseEvent e
) {
194 public void mouseReleased(java
.awt
.event
.MouseEvent e
) {
197 private void maybeShowPopup(java
.awt
.event
.MouseEvent e
) {
198 if (e
.isPopupTrigger()) {
199 popupMenu
.show(e
.getComponent(), e
.getX(), e
.getY());
206 this.add(new NavigationPanel(), BorderLayout
.NORTH
);
207 this.add(pane
, BorderLayout
.CENTER
);
208 this.add(new ButtonPanel(), BorderLayout
.SOUTH
);
211 private void updateFtpMenu() {
212 final JMenuItem
[] ftpMenuItems
= {
213 CyberduckMenu
.instance().menuItemBuilder(newDirectoryAction
),
214 CyberduckMenu
.instance().menuItemBuilder(renameAction
),
215 CyberduckMenu
.instance().menuItemBuilder(deleteAction
),
216 CyberduckMenu
.instance().menuItemBuilder(permissionAction
),
218 CyberduckMenu
.instance().menuItemBuilder(disconnectAction
)
221 JMenu ftpMenu
= CyberduckMenu
.instance().getMenu(CyberduckMenu
.FTP_MENU
);
223 for(int i
= 0; i
< ftpMenuItems
.length
; i
++) {
224 ftpMenu
.add(ftpMenuItems
[i
]);
228 private class ButtonPanel
extends JPanel
{
229 public ButtonPanel() {
231 this.setLayout(new FlowLayout(FlowLayout
.LEFT
));
232 JButton downloadButton
= GUIFactory
.buttonBuilder(GUIFactory
.FONT_SMALL
, downloadAction
);
233 downloadButton
.setIcon(new ImageIcon("ToolBarUI"));
234 JButton newDirectoryButton
= GUIFactory
.buttonBuilder(GUIFactory
.FONT_SMALL
, newDirectoryAction
);
235 newDirectoryButton
.setIcon(new ImageIcon("ToolBarUI"));
236 JButton renameButton
= GUIFactory
.buttonBuilder(GUIFactory
.FONT_SMALL
, renameAction
);
237 renameButton
.setIcon(new ImageIcon("ToolBarUI"));
238 JButton deleteButton
= GUIFactory
.buttonBuilder(GUIFactory
.FONT_SMALL
, deleteAction
);
239 deleteButton
.setIcon(new ImageIcon("ToolBarUI"));
240 JButton permissionButton
= GUIFactory
.buttonBuilder(GUIFactory
.FONT_SMALL
, permissionAction
);
241 permissionButton
.setIcon(new ImageIcon("ToolBarUI"));
242 JButton disconnectButton
= GUIFactory
.buttonBuilder(GUIFactory
.FONT_SMALL
, disconnectAction
);
243 disconnectButton
.setIcon(new ImageIcon("ToolBarUI"));
245 this.add(downloadButton
);
246 this.add(newDirectoryButton
);
247 this.add(renameButton
);
248 this.add(deleteButton
);
249 this.add(permissionButton
);
250 this.add(disconnectButton
);
254 private class NavigationPanel
extends JPanel
{
255 public NavigationPanel() {
257 this.setLayout(new FlowLayout(FlowLayout
.LEFT
));
258 this.add(GUIFactory
.buttonBuilder(backAction
));
259 this.add(GUIFactory
.buttonBuilder(upAction
));
260 this.add(GUIFactory
.buttonBuilder(refreshAction
));
261 //directory selection
262 comboBox
= new JComboBox();
263 comboBox
.setEditable(false);
264 comboBox
.setModel(comboModel
= new ComboPathModel());
265 comboBox
.setRenderer(new ComboPathRenderer());
266 comboBox
.addItemListener(comboItemListener
= new ItemListener() {
267 public void itemStateChanged(ItemEvent e
) {
268 if(e
.getStateChange() == ItemEvent
.SELECTED
) {
269 Cyberduck
.DEBUG(e
.paramString());
270 Path p
= (Path
)e
.getItem();
271 selected
.transfer(new TransferAction(TransferAction
.LIST
, p
));
277 this.add(GUIFactory
.buttonBuilder(abortAction
));
281 // ************************************************************************************************************
283 private class ComboPathRenderer
extends DefaultListCellRenderer
{
284 public Component
getListCellRendererComponent(JList list
, Object value
, int index
, boolean isSelected
, boolean cellHasFocus
) {
285 super.getListCellRendererComponent(list
, value
, index
, isSelected
, cellHasFocus
);
286 if(value
instanceof Path
) {
287 Path p
= (Path
)value
;
288 if(p
.getPathDepth() > 0) {
289 this.setIcon(new IndentIcon(GUIFactory
.FOLDER_ICON
, p
.getPathDepth()));
290 this.setText(p
.getName());
293 this.setIcon(new IndentIcon(GUIFactory
.HARDDRIVE_ICON
, p
.getPathDepth()));
294 this.setText(selected
.getHost());
301 private class IndentIcon
implements Icon
{
305 public IndentIcon(Icon i
, int d
) {
309 public void paintIcon(Component c
, Graphics g
, int x
, int y
) {
311 icon
.paintIcon(c
, g
, 10 + x
+ depth
* 10, y
);
313 public int getIconWidth() {
314 return 10 + depth
* 10 + (icon
!= null ? icon
.getIconWidth() : 0);
316 public int getIconHeight() {
317 return (icon
!= null ? icon
.getIconHeight() : 1);
321 // ************************************************************************************************************
323 private class ComboPathModel
extends AbstractListModel
implements MutableComboBoxModel
{
324 Object selected
= null;
325 Vector paths
= new Vector();
327 public int getSize() {
331 public void addElement(Object o
) {
332 // Cyberduck.DEBUG("[ComboPathModel] addElement(" + o + ")");
334 // this.fireIntervalAdded(this, this.getSize() - 1, this.getSize() - 1);
336 public void insertElementAt(Object o
, int index
) {
337 Cyberduck
.DEBUG("[ComboPathModel] insertElementAt(" + o
+ "," + index
+ ")");
339 this.fireIntervalAdded(this, index
, index
);
341 public void removeElement(Object o
) {
342 Cyberduck
.DEBUG("[ComboPathModel] removeElement(" + o
+ ")");
343 paths
.removeElement(o
);
344 this.fireContentsChanged(this, 0, this.getSize());
346 public void removeElementAt(int index
) {
347 Cyberduck
.DEBUG("[ComboPathModel] removeElementAt(" + index
+ ")");
348 paths
.removeElementAt(index
);
349 this.fireIntervalRemoved(this, index
, index
);
351 public void removeAll() {
353 this.fireContentsChanged(this, 0, this.getSize());
355 public boolean isEmpty() {
356 return paths
.isEmpty();
358 public Object
getElementAt(int index
) {
359 // Cyberduck.DEBUG("[ComboPathModel] getElementAt(" + index + ")");
360 return paths
.elementAt(index
);//.getPath();
362 public void setSelectedItem(Object item
) {
363 // Cyberduck.DEBUG("[ComboPathModel] setSelectedItem(" + item + ")");
364 this.selected
= item
;
365 this.fireContentsChanged(this, 0, this.getSize());
366 // comboBox.addActionListener(comboActionListener);
369 public Object
getSelectedItem() {
370 // Cyberduck.DEBUG("[ComboPathModel] getSelectedItem()");
371 if(selected
instanceof java
.lang
.String
)
372 return new Path((String
)selected
);
373 return this.selected
;
378 // ************************************************************************************************************
380 private class UpAction
extends AbstractAction
{
382 // super("List parent");
383 this.putValue(SHORT_DESCRIPTION
, "List parent directory");
384 this.putValue(SMALL_ICON
, Cyberduck
.getIcon(Cyberduck
.getResource(this.getClass(), "up_small.gif")));
385 this.setEnabled(true);
386 //ActionMap.instance().put(this.getValue(NAME), this);
388 public void actionPerformed(ActionEvent ae
) {
389 Cyberduck
.DEBUG(ae
.paramString());
390 selected
.transfer(new TransferAction(TransferAction
.LIST
, selected
.getCurrentPath().getParent()));
394 private class BackAction
extends AbstractAction
{
395 public BackAction() {
397 this.putValue(SHORT_DESCRIPTION
, "List previous directory");
398 this.putValue(SMALL_ICON
, Cyberduck
.getIcon(Cyberduck
.getResource(this.getClass(), "back_small.gif")));
399 this.setEnabled(true);
400 //ActionMap.instance().put(this.getValue(NAME), this);
402 public void actionPerformed(ActionEvent ae
) {
403 Cyberduck
.DEBUG(ae
.paramString());
404 selected
.transfer(new TransferAction(TransferAction
.LIST
, selected
.getPreviousPath()));
408 private class RefreshAction
extends AbstractAction
{
409 public RefreshAction() {
411 this.putValue(SHORT_DESCRIPTION
, "Refresh directory listing");
412 this.putValue(SMALL_ICON
, Cyberduck
.getIcon(Cyberduck
.getResource(this.getClass(), "refresh_small.gif")));
413 this.setEnabled(true);
414 //ActionMap.instance().put(this.getValue(NAME), this);
416 public void actionPerformed(ActionEvent ae
) {
417 Cyberduck
.DEBUG(ae
.paramString());
418 selected
.transfer(new TransferAction(TransferAction
.LIST
, comboModel
.getElementAt(comboModel
.getSize() - 1)));
422 private class AbortAction
extends AbstractAction
{
423 public AbortAction() {
425 this.putValue(SHORT_DESCRIPTION
, "Abort last action");
426 this.putValue(SMALL_ICON
, Cyberduck
.getIcon(Cyberduck
.getResource(this.getClass(), "stop_small.gif")));
427 this.setEnabled(true);
428 //ActionMap.instance().put(this.getValue(NAME), this);
430 public void actionPerformed(ActionEvent ae
) {
431 Cyberduck
.DEBUG(ae
.paramString());
432 //selected.transfer(new TransferAction(TransferAction.ABORT));
433 selected
.status
.setCanceled(true);
434 //((Action)ActionMap.instance().get("Stop")).actionPerformed(new ActionEvent(selected, ae.getID(), ae.getActionCommand()));
438 private class NewDirectoryAction
extends AbstractAction
{
439 public NewDirectoryAction() {
441 this.putValue(SHORT_DESCRIPTION
, "Create new directory");
442 //this.putValue(SMALL_ICON, GUIFactory.NEW_FOLDER_ICON);
443 this.setEnabled(false);
444 //ActionMap.instance().put(this.getValue(NAME), this);
446 public void actionPerformed(ActionEvent ae
) {
447 Cyberduck
.DEBUG(ae
.paramString());
448 Object input
= JOptionPane
.showInputDialog(
450 "Name of the new folder:",
452 JOptionPane
.QUESTION_MESSAGE
,
458 if(input
instanceof String
) {
459 selected
.transfer(new TransferAction(TransferAction
.MKDIR
, (String
)input
));
465 private class DeleteAction
extends AbstractAction
{
466 public DeleteAction() {
468 this.putValue(SHORT_DESCRIPTION
, "Delete file or folder");
469 this.setEnabled(false);
470 //ActionMap.instance().put(this.getValue(NAME), this);
472 public void actionPerformed(ActionEvent ae
) {
473 Cyberduck
.DEBUG(ae
.paramString());
474 if(listingTable
.getSelectedRowCount() < 1 )
477 int[] rows
= listingTable
.getSelectedRows();
478 StringBuffer candidates
= new StringBuffer();
479 for (int i
= 0; i
< rows
.length
; i
++) {
480 candidates
.append("\t"+listingTableModel
.getEntry(rows
[i
])+"\n");
482 candidates
.append("\t... and "+(rows
.length
- i
)+" more files.");
486 String sep
= System
.getProperty("line.separator");
487 int input
= JOptionPane
.showConfirmDialog(null,
488 "Do you really want to delete all selected files?"+sep
+candidates
.toString()+sep
+"You cannot undo this action.",
490 JOptionPane
.YES_NO_OPTION
,
491 JOptionPane
.QUESTION_MESSAGE
493 if(input
== JOptionPane
.YES_OPTION
) {
495 Cyberduck
.DEBUG("[ListPanel] Deleting " + rows
.length
+ " files");
496 for(int i
= 0; i
< rows
.length
; i
++) {
497 delete
= (Path
)listingTableModel
.getEntry(rows
[i
]);
498 selected
.transfer(new TransferAction(TransferAction
.DELE
, delete
), true);
500 selected
.startQueue();
506 private class RenameAction
extends AbstractAction
{
507 public RenameAction() {
509 this.putValue(SHORT_DESCRIPTION
, "Rename file or folder");
510 this.setEnabled(false);
511 //ActionMap.instance().put(this.getValue(NAME), this);
513 public void actionPerformed(ActionEvent ae
) {
514 Cyberduck
.DEBUG(ae
.paramString());
515 if(listingTable
.getSelectedRowCount() > 1)
517 if(listingTable
.getSelectedRowCount() < 1)
520 Path from
= (Path
)listingTableModel
.getEntry(listingTable
.getSelectedRow());
521 Object input
= JOptionPane
.showInputDialog(null,
522 "Rename '" + from
.getName() + "' to:",
524 JOptionPane
.QUESTION_MESSAGE
,
530 if(input
instanceof String
) {
531 selected
.transfer(new TransferAction(TransferAction
.RNFR
, from
, new Path(from
.getParent().getPath(), (String
)input
)));
538 private class DisconnectAction
extends AbstractAction
{
539 public DisconnectAction() {
541 this.putValue(SHORT_DESCRIPTION
, "Disconnect from remote host");
542 this.setEnabled(false);
543 //ActionMap.instance().put(this.getValue(NAME), this);
545 public void actionPerformed(ActionEvent ae
) {
546 Cyberduck
.DEBUG(ae
.paramString());
547 selected
.transfer(new TransferAction(TransferAction
.QUIT
));
551 private class ListingDownloadAction
extends AbstractAction
{
552 public ListingDownloadAction() {
554 this.putValue(SHORT_DESCRIPTION
, "Download selected file or folder");
555 this.setEnabled(false);
556 //ActionMap.instance().put(this.getValue(NAME), this);
558 public void actionPerformed(ActionEvent ae
) {
559 Cyberduck
.DEBUG(ae
.paramString());
560 if(listingTable
.getSelectedRowCount() < 1 )
563 Bookmark t
= selected
.copy();
564 Path p
= (Path
)listingTableModel
.getEntry(listingTable
.getSelectedRow());
565 t
.setServerPath(p
.toString());
566 t
.setLocalPath(new java
.io
.File(Preferences
.instance().getProperty("download.path"), p
.getName()));
567 ((Action
)(ActionMap
.instance().get("New Bookmark"))).actionPerformed(new ActionEvent(t
, ActionEvent
.ACTION_PERFORMED
, "New Bookmark"));
568 t
.transfer(new TransferAction(TransferAction
.GET
));
574 * Show permission dialog
575 * @see ch.cyberduck.ui.PermissionDialog
577 private class PermissionAction
extends AbstractAction
{
578 public PermissionAction() {
579 super("Set Permissions");
580 this.putValue(SHORT_DESCRIPTION
, "Edit file permissions");
581 this.putValue(ACCELERATOR_KEY
, KeyStroke
.getKeyStroke(KeyEvent
.VK_I
, GUIFactory
.MENU_MASK
));
582 this.setEnabled(false);
583 //ActionMap.instance().put(this.getValue(NAME), this);
585 public void actionPerformed(ActionEvent ae
) {
586 Cyberduck
.DEBUG(ae
.paramString());
587 if(listingTable
.getSelectedRowCount() < 1 )
590 Path file
= (Path
)listingTableModel
.getEntry(listingTable
.getSelectedRow());
591 javax
.swing
.JDialog d
= new PermissionDialog(selected
, file
);