// // CabViewer.java // S.Fraize July 2002 // import java.io.*; import java.util.*; import java.awt.*; import java.awt.event.*; import java.text.*; import javax.swing.*; import javax.swing.table.*; import javax.swing.tree.*; import javax.swing.event.*; import org.okip.service.filing.api.*; public class CabViewer extends JFrame implements ActionListener { JTree jtree; JTable jtable; CabTreeNode rootNode; AbstractTableModel displayedModel; public CabViewer() { super("OKI Cabinet Viewer"); debug("constructing CabViewer"); this.setDefaultCloseOperation(EXIT_ON_CLOSE); this.setSize(new Dimension(800,640)); this.setLocation(100,100); this.addMenus(); this.setVisible(true);// show something right away rootNode = new CabTreeNode("CabViewer(invisible root node)"); rootNode.setDisplayName("CabViewer"); /* * create the JTree */ this.jtree = new JTree(rootNode, true); this.jtree.setRootVisible(false); this.jtree.setExpandsSelectedPaths(true);//this have effect? addJTreeListeners(); /* * create the JTable -- and we need a table-column model for that */ CabinetTableModel cabinetDataModel = getDefaultCabinetTableModel(); TableColumnModel columnModel = cabinetDataModel.getTableColumnModel(); columnModel.setColumnMargin(10);// this makes for ugly breaks in line-selection indicator tho... this.jtable = new JTable(cabinetDataModel, columnModel); this.jtable.setGridColor(Color.lightGray); addTableRenderers(); /* * create the JSplitPane & subcomponents with scrollers */ JSplitPane splitPane = new JSplitPane(); JScrollPane leftScroller = new JScrollPane(this.jtree); JScrollPane rightScroller = new JScrollPane(this.jtable); splitPane.setResizeWeight(0.25); // 25% space to the left component splitPane.setContinuousLayout(true); splitPane.setOneTouchExpandable(true); splitPane.setLeftComponent(leftScroller); splitPane.setRightComponent(rightScroller); Container pane = getContentPane(); pane.setLayout(new BorderLayout()); //pane.add(getToolBar(), BorderLayout.NORTH); JLabel statusBar = new JLabel("Status messages"); pane.add(splitPane, BorderLayout.CENTER); //pane.add(statusBar, BorderLayout.SOUTH); this.constructing = false; } public void setDisplayedModel(AbstractTableModel tm) { this.displayedModel = tm; jtable.setModel(this.displayedModel); } /* * Refresh the currently visible table. * todo: also refresh the tree in case * a directory was added. */ public void refresh() { ((CabinetTableModel)this.displayedModel).refresh(); } public void addRootCabinet(Cabinet cabinet) { CabTreeNode ctn = new CabTreeNode(cabinet); try { ctn.setDisplayName(cabinet.getPath()); } catch (FilingException e) { errout(e); } rootNode.add(ctn); ((DefaultTreeModel)jtree.getModel()).reload(rootNode); } public void expandRootEntry(int n) { jtree.expandPath(jtree.getPathForRow(n)); } CabinetTableModel defaultTableModel; CabinetTableModel getDefaultCabinetTableModel() { if (defaultTableModel == null) defaultTableModel = new CabinetTableModel(null); return defaultTableModel; } private JToolBar getToolBar() { // cache this! JToolBar tb = new JToolBar(); //JButton tbQuit = new JButton("Quit"); JButton tbClose = new JButton("Close"); JButton tbRefresh = new JButton("Refresh"); //tbQuit.addActionListener(this); tbClose.addActionListener(this);// will pick up "Close" action from menu tbRefresh.addActionListener(this); //tbQuit.setAction(); //tb.add(tbQuit); tb.add(tbClose); tb.add(tbRefresh); tb.setBorderPainted(false); tb.setFloatable(false); return tb; } protected void addJTreeListeners() { jtree.addTreeSelectionListener (new TreeSelectionListener() { public void valueChanged(TreeSelectionEvent e) { CabTreeNode cabNode = (CabTreeNode) jtree.getLastSelectedPathComponent(); if (cabNode == null) return; debug("tree node selected: " + cabNode); setDisplayedModel(cabNode.getDataModel()); } }); // could put these on the the class instead of inline jtree.addTreeWillExpandListener (new TreeWillExpandListener() { public void treeWillExpand(TreeExpansionEvent e) { TreePath path = e.getPath(); debug("tree expanding at path: " + path); CabTreeNode cabNode = (CabTreeNode) path.getLastPathComponent(); if (cabNode == null) return; debug("tree expanding at node: " + cabNode); jtree.setSelectionPath(path); setDisplayedModel(cabNode.getDataModel()); } public void treeWillCollapse(TreeExpansionEvent e) {} }); } protected void addTableRenderers() { // Set some renderers (defined in-line) for the CabinetEntry & Date classes. // Could also set these up by column instead of class in CabinetTableModel.createTableColumnModel(); // It would probably be cleaner to do it there. jtable.setDefaultRenderer(CabinetEntry.class, new DefaultTableCellRenderer() { // in-line class definition // An example custom renderer -- if the CabinetEntry is unwriteable, make it red, // if a directory (Cabinet vs. ByteStore) make it blue and add a "/", if // emacs backup file, gray it out. public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (value instanceof CabinetEntry) { CabinetEntry ce = (CabinetEntry) value; try { String label = ce.getName(); if (ce.isCabinet()) label += "/"; if (!ce.canRead()) setForeground(Color.lightGray); else if (label.endsWith("~")) setForeground(Color.gray); //else if (!ce.canWrite()) // setForeground(Color.red); else if (ce.isCabinet()) setForeground(Color.blue); else setForeground(Color.black); value = label; } catch (FilingException e) { errout(e); value = "<"+e.getMessage()+">"; } } return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); } }); jtable.setDefaultRenderer(Date.class, new DefaultTableCellRenderer() { // in-line class definition DateFormat dateFormatter = new SimpleDateFormat("MMM dd HH:mm:ss zzz yyyy"); { //setToolTipText("Last modification time"); setHorizontalAlignment(SwingConstants.RIGHT); } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (value instanceof Date) { Date d = (Date) value; value = dateFormatter.format(d); } // else, it may be an empty string -- no date info available return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); } }); } private boolean constructing = true; private Font font = new Font("serif", Font.ITALIC+Font.BOLD, 48); public void paint(Graphics g) { super.paint(g); if (constructing) { g.setColor(Color.lightGray); g.setFont(font); g.drawString("OKI FILING DEMO", getWidth()/4, getHeight()/2); } } protected void _processEvent(AWTEvent e) { debug(e.toString()); super.processEvent(e); } // Declarations for menus static JMenuBar mainMenuBar = null; static JMenu fileMenu = null; private static JMenuBar getMainMenuBar() { if (mainMenuBar == null) mainMenuBar = new JMenuBar(); return mainMenuBar; } private static JMenu getFileMenu() { if (fileMenu == null) fileMenu = new JMenu("File"); return fileMenu; } protected JMenuItem miRefresh; protected JMenuItem miNew; protected JMenuItem miOpen; protected JMenuItem miClose; protected JMenuItem miSave; protected JMenuItem miSaveAs; static final JMenu editMenu = new JMenu("Edit"); protected JMenuItem miUndo; protected JMenuItem miCut; protected JMenuItem miCopy; protected JMenuItem miPaste; protected JMenuItem miClear; protected JMenuItem miSelectAll; public void addFileMenuItems() { getFileMenu(); miRefresh = new JMenuItem ("Refresh"); miRefresh.setAccelerator(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F5, 0)); miRefresh.setAccelerator(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_R, java.awt.Event.META_MASK)); fileMenu.add(miRefresh).setEnabled(true); miRefresh.addActionListener(this); miNew = new JMenuItem ("New"); miNew.setAccelerator(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_N, java.awt.Event.META_MASK)); fileMenu.add(miNew).setEnabled(false); miNew.addActionListener(this); miOpen = new JMenuItem ("Open..."); miOpen.setAccelerator(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_O, java.awt.Event.META_MASK)); fileMenu.add(miOpen).setEnabled(false); miOpen.addActionListener(this); miClose = new JMenuItem ("Close"); miClose.setAccelerator(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_W, java.awt.Event.META_MASK)); fileMenu.add(miClose).setEnabled(true); miClose.addActionListener(this); miSave = new JMenuItem ("Save"); miSave.setAccelerator(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, java.awt.Event.META_MASK)); fileMenu.add(miSave).setEnabled(false); miSave.addActionListener(this); miSaveAs = new JMenuItem ("Save As..."); fileMenu.add(miSaveAs).setEnabled(false); miSaveAs.addActionListener(this); getMainMenuBar().add(fileMenu); } public void addEditMenuItems() { miUndo = new JMenuItem("Undo"); miUndo.setAccelerator(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Z, java.awt.Event.META_MASK)); editMenu.add(miUndo).setEnabled(false); miUndo.addActionListener(this); editMenu.addSeparator(); miCut = new JMenuItem("Cut"); miCut.setAccelerator(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_X, java.awt.Event.META_MASK)); editMenu.add(miCut).setEnabled(false); miCut.addActionListener(this); miCopy = new JMenuItem("Copy"); miCopy.setAccelerator(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_C, java.awt.Event.META_MASK)); editMenu.add(miCopy).setEnabled(false); miCopy.addActionListener(this); miPaste = new JMenuItem("Paste"); miPaste.setAccelerator(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_V, java.awt.Event.META_MASK)); editMenu.add(miPaste).setEnabled(false); miPaste.addActionListener(this); miClear = new JMenuItem("Clear"); editMenu.add(miClear).setEnabled(true); miClear.addActionListener(this); editMenu.addSeparator(); miSelectAll = new JMenuItem("Select All"); miSelectAll.setAccelerator(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_A, java.awt.Event.META_MASK)); editMenu.add(miSelectAll).setEnabled(true); miSelectAll.addActionListener(this); getMainMenuBar().add(editMenu); } public void addMenus() { addFileMenuItems(); addEditMenuItems(); //mainMenuBar.add(new JLabel(" Hello World ", JLabel.RIGHT)); //mainMenuBar.add(new JButton("Spin")); setJMenuBar (mainMenuBar); } // ActionListener interface (for menus) public void actionPerformed(ActionEvent newEvent) { if (newEvent.getActionCommand().equals(miNew.getActionCommand())) doNew(); else if (newEvent.getActionCommand().equals(miRefresh.getActionCommand())) doRefresh(); else if (newEvent.getActionCommand().equals(miOpen.getActionCommand())) doOpen(); else if (newEvent.getActionCommand().equals(miClose.getActionCommand())) doClose(); else if (newEvent.getActionCommand().equals(miSave.getActionCommand())) doSave(); else if (newEvent.getActionCommand().equals(miSaveAs.getActionCommand())) doSaveAs(); else if (newEvent.getActionCommand().equals(miUndo.getActionCommand())) doUndo(); else if (newEvent.getActionCommand().equals(miCut.getActionCommand())) doCut(); else if (newEvent.getActionCommand().equals(miCopy.getActionCommand())) doCopy(); else if (newEvent.getActionCommand().equals(miPaste.getActionCommand())) doPaste(); else if (newEvent.getActionCommand().equals(miClear.getActionCommand())) doClear(); else if (newEvent.getActionCommand().equals(miSelectAll.getActionCommand())) doSelectAll(); else debug("actionPerformed: " + newEvent.toString()); } public void doRefresh() { this.refresh(); } public void doNew() {} public void doOpen() { debug("doOpen"); } public void doClose() { debug("doClose"); System.exit(0); } public void doSave() {} public void doSaveAs() {} public void doUndo() {} public void doCut() { debug("cut row " + jtable.getSelectedRow()); } public void doCopy() {} public void doPaste() {} public void doClear() { jtable.clearSelection(); } public void doSelectAll() { jtable.selectAll(); } class CabTreeNode extends DefaultMutableTreeNode { AbstractTableModel dataModel; String displayName; CabTreeNode(Cabinet cabinet) { super(cabinet); } private CabTreeNode(String s) { super(s); setDisplayName(s); // for constructing the invisible root node } public void setDisplayName(String name) { this.displayName = name; } private Cabinet getCabinet() { if (getUserObject() instanceof Cabinet) return (Cabinet) getUserObject(); return null; } AbstractTableModel getDataModel() { if (this.dataModel == null) { debug("CabTreeNode: creating new CabinetTableModel for " + getCabinet()); this.dataModel = new CabinetTableModel(getCabinet()); expandTree(); } return this.dataModel; } private void expandTree() { /* * BUILD OUT THE TREE * (Create new cab CabTreeNodes for any child cabinets found) * We assume that if they want the data model, it's an appropriate * time to expand deeper in to the tree structure. */ Iterator i; try { i = getCabinet().entries(); } catch (FilingException e) { errout(e); return; } while (i.hasNext()) { CabinetEntry ce = (CabinetEntry) i.next(); try { if (ce.isCabinet()) this.add(new CabTreeNode((Cabinet)ce)); } catch (FilingException e) { errout(e); } } } /* * toString is very functional here -- it's * used to produce the text that the tree widget displays. */ public String toString() { if (displayName != null) return displayName; try { CabinetEntry ce = (CabinetEntry) userObject; return ce.getName(); } catch (Exception e) { errout(e); return userObject.getClass().toString(); } } } class CabinetTableModel extends AbstractTableModel { final int COL_NAME = 0; final int COL_SIZE = 1; final int COL_MODTIME = 2; final int COL_PERMISSIONS = 3; final int COL_OWNER = 4; Cabinet cabinet; ArrayList entries; // int columnCount = 0; CabinetTableModel(Cabinet c) { this.entries = new ArrayList(); if (c != null) this.loadModelFromCabinet(c); } /* * At the moment, getTableColumnModel and it's helper addTablecolumn * could really be methods on CabViewer (or static inner classes * if that were allowed), tho we're keeping them here and dynamic * in case we end up using different TableColumnModels for specific * cabinet types (or, perhaps, just via configuration preferences -- * e.g., remembering a custom view on a particular cabinet). */ TableColumnModel columnModel; public TableColumnModel getTableColumnModel() { if (columnModel != null) return columnModel; columnModel = new DefaultTableColumnModel(); addTableColumn(COL_NAME, "File Name", 1000); addTableColumn(COL_SIZE, "Size", 200); addTableColumn(COL_MODTIME, "Modified", 700); addTableColumn(COL_PERMISSIONS, "Access", 150); //addTableColumn(COL_OWNER, "Owner", 500); return columnModel; } private void addTableColumn(int index, String name, int width) { TableColumn tc = new TableColumn(index, width); tc.setHeaderValue(name); // columnCount++; columnModel.addColumn(tc); } public Class getColumnClass(int col) { if (col == COL_NAME) return CabinetEntry.class; if (col == COL_SIZE) return Integer.class; // this will cause align-right if (col == COL_MODTIME) return Date.class; return super.getColumnClass(col); } public int getColumnCount() { return 0;/*columnCount;*/ } public int getRowCount() { return entries.size(); } public Object getValueAt(int row, int col) { CabinetEntry ce = (CabinetEntry) entries.get(row); try { return getColumnData(ce, col); } catch (FilingException e) { return e; } } private Object getColumnData(CabinetEntry ce, int col) throws FilingException { boolean isBS = ce.isByteStore(); boolean isCAB = ce.isCabinet(); ByteStore bs = isBS ? (ByteStore) ce : null; Object data = null; try { if (col == COL_NAME) { data = ce; } else if (col == COL_SIZE) { if (isBS) data = new Long(bs.length()); else data = ""; } else if (col == COL_MODTIME) { if (isBS) data = new Date(bs.getLastModifiedTime()); else data = ""; } else if (col == COL_PERMISSIONS) { String s = " "; s += isCAB ? 'd' : '-'; s += ce.canRead() ? 'r' : '-'; s += ce.canWrite() ? 'w' : '-'; data = s; } else if (col == COL_OWNER) { if (isBS) data = bs.getOwner(); } } catch (Exception e) { errout(e.toString()); data = "<" + e.getMessage() + ">"; } return data == null ? "-" : data; } public void loadModelFromCabinet(Cabinet c) { this.cabinet = c; this.entries.clear(); Iterator i; try { i = this.cabinet.entries(); } catch (FilingException e) { errout(e); return; } while (i.hasNext()) { CabinetEntry ce = (CabinetEntry) i.next(); entries.add(ce); debug("CabinetTableModel added " + ce); } fireTableDataChanged(); } public void refresh() { if (cabinet instanceof Refreshable) { try { ((Refreshable)cabinet).refresh(); } catch (Exception e) { errout(e); } } loadModelFromCabinet(cabinet); } }; //------------------------------------------------------------------ // end of inner classes //------------------------------------------------------------------ private static void out(String s) { System.out.print(s); } private static void outln(String s) { System.out.println("CabViewer: " + s); } private static void errout(String s) { System.err.println("CabViewer: " + s); } private static void errout(Exception e) { if (e instanceof org.okip.service.shared.api.Exception) ((org.okip.service.shared.api.Exception)e).printChainedTrace(); else { errout(e.toString()); e.printStackTrace(); } } static void debug(String s) { if (A_DEBUG) System.out.println("CabViewer: " + s); } static boolean A_DEBUG = false; static boolean A_VERBOSE = false; public static void main(String args[]) { CabUtil.parseCommandLine(args); ArrayList dirs = new ArrayList(); for (int i = 0; i < args.length; i++) { String a = args[i]; if (a.equals("-debug")) { A_DEBUG = !A_DEBUG; if (A_DEBUG) A_VERBOSE = true; } else if (a.equals("-verbose")) A_VERBOSE = !A_VERBOSE; else if (!a.startsWith("-")) dirs.add(a); } try { CabViewer cv = new CabViewer(); if (dirs.size() == 0) dirs.add("."); int found = 0; Iterator i = dirs.iterator(); while (i.hasNext()) { Cabinet cabinet = CabUtil.getCabinetFromDirectory((String)i.next()); if (cabinet != null) { cv.addRootCabinet(cabinet); found++; } } if (found <= 0) System.exit(1); cv.expandRootEntry(0); // open the 1st cabinet cv.setVisible(true); } catch (Exception e) { errout(e); System.exit(2); } } }