From b229e9aa590557d00740d3ee4f8907529bf1bafc Mon Sep 17 00:00:00 2001 From: fros4943 Date: Wed, 19 May 2010 17:32:53 +0000 Subject: [PATCH] rewrote project dialog, for easier import and management of cooja projects --- tools/cooja/java/se/sics/cooja/GUI.java | 10 +- .../dialogs/ProjectDirectoriesDialog.java | 908 +++++++++++++----- .../dialogs/ProjectDirectoryInputDialog.java | 148 --- 3 files changed, 647 insertions(+), 419 deletions(-) delete mode 100644 tools/cooja/java/se/sics/cooja/dialogs/ProjectDirectoryInputDialog.java diff --git a/tools/cooja/java/se/sics/cooja/GUI.java b/tools/cooja/java/se/sics/cooja/GUI.java index 9aa852a04..f1d0f8ff7 100644 --- a/tools/cooja/java/se/sics/cooja/GUI.java +++ b/tools/cooja/java/se/sics/cooja/GUI.java @@ -24,7 +24,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: GUI.java,v 1.168 2010/04/26 08:19:32 fros4943 Exp $ + * $Id: GUI.java,v 1.169 2010/05/19 17:32:54 fros4943 Exp $ */ package se.sics.cooja; @@ -419,7 +419,7 @@ public class GUI extends Observable { if (isVisualized()) { JOptionPane.showMessageDialog(GUI.getTopParentContainer(), "Default projects could not load, reconfigure project directories:" + - "\n\tMenu->Settings->Manage project directories" + + "\n\tMenu->Settings->COOJA projects" + "\n\nSee console for stack trace with more information.", "Project loading error", JOptionPane.ERROR_MESSAGE); } @@ -898,7 +898,7 @@ public class GUI extends Observable { menuItem.setToolTipText("Not available in applet version"); } - menuItem = new JMenuItem("Manage project directories"); + menuItem = new JMenuItem("COOJA projects"); menuItem.setActionCommand("manage projects"); menuItem.addActionListener(guiEventHandler); menu.add(menuItem); @@ -906,7 +906,7 @@ public class GUI extends Observable { menuItem.setEnabled(false); menuItem.setToolTipText("Not available in applet version"); } - + menuItem = new JMenuItem("Compiler configuration wizard"); menuItem.setActionCommand("configuration wizard"); menuItem.addActionListener(guiEventHandler); @@ -2878,7 +2878,7 @@ public class GUI extends Observable { if (isVisualized()) { JOptionPane.showMessageDialog(GUI.getTopParentContainer(), "Configured projects could not load, reconfigure project directories:" + - "\n\tMenu->Settings->Manage project directories" + + "\n\tMenu->Settings->COOJA projects" + "\n\nSee console for stack trace with more information.", "Project loading error", JOptionPane.ERROR_MESSAGE); } diff --git a/tools/cooja/java/se/sics/cooja/dialogs/ProjectDirectoriesDialog.java b/tools/cooja/java/se/sics/cooja/dialogs/ProjectDirectoriesDialog.java index ccf20cbfe..8998438ce 100644 --- a/tools/cooja/java/se/sics/cooja/dialogs/ProjectDirectoriesDialog.java +++ b/tools/cooja/java/se/sics/cooja/dialogs/ProjectDirectoriesDialog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, Swedish Institute of Computer Science. + * Copyright (c) 2010, Swedish Institute of Computer Science. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,24 +26,29 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ProjectDirectoriesDialog.java,v 1.12 2010/02/03 10:19:58 fros4943 Exp $ + * $Id: ProjectDirectoriesDialog.java,v 1.13 2010/05/19 17:32:53 fros4943 Exp $ */ package se.sics.cooja.dialogs; import java.awt.BorderLayout; import java.awt.Color; +import java.awt.Component; import java.awt.Container; import java.awt.Dialog; import java.awt.Dimension; +import java.awt.Font; import java.awt.Frame; +import java.awt.Graphics; import java.awt.GraphicsEnvironment; -import java.awt.List; import java.awt.Rectangle; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.io.File; +import java.io.FileFilter; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; @@ -52,13 +57,28 @@ import java.util.Enumeration; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; +import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JDialog; -import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.JTree; +import javax.swing.ListSelectionModel; +import javax.swing.UIManager; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.event.TreeModelListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreePath; import org.apache.log4j.Logger; @@ -66,31 +86,31 @@ import se.sics.cooja.GUI; import se.sics.cooja.ProjectConfig; /** - * This dialog allows a user to manage the project directory configurations. Project - * directories can be added, removed or reordered. The resulting - * configuration can also be viewed. - * - * This dialog reads from the external project configuration files in each project - * directory, as well as from any specified default configuration files. + * This dialog allows a user to manage COOJA projects: extensions to COOJA that + * provide new functionality such as radio mediums, plugins, and mote types. * * @author Fredrik Osterlind */ public class ProjectDirectoriesDialog extends JDialog { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1896348946753376556L; private static Logger logger = Logger.getLogger(ProjectDirectoriesDialog.class); private GUI gui; - private File[] projects = null; - private List projectsList = new List(); - + private JTable table = null; + private DirectoryTreePanel treePanel = null; + + private ArrayList currentProjects = new ArrayList(); + private File[] finalProjects = null; + /** - * Allows editing the project directories list by adding new, - * reordering or removing project directories. - * + * Shows a blocking configuration dialog. + * Returns a list of new COOJA project directories, or null if canceled by the user. + * * @param parent Parent container - * @param currentProjects Current project directories - * @return The new project directories, or null if dialog was aborted + * @param gui COOJA + * @param currentProjects Current project configuration + * @return New COOJA projects, or null */ public static File[] showDialog(Container parent, GUI gui, File[] currentProjects) { if (GUI.isVisualizedInApplet()) { @@ -98,284 +118,642 @@ public class ProjectDirectoriesDialog extends JDialog { } ProjectDirectoriesDialog dialog = new ProjectDirectoriesDialog((Window) parent, currentProjects); - dialog.gui = gui; dialog.setLocationRelativeTo(parent); - dialog.setVisible(true); - - return dialog.projects; + return dialog.finalProjects; } private ProjectDirectoriesDialog(Container parent, File[] projects) { super( parent instanceof Dialog?(Dialog)parent: parent instanceof Window?(Window)parent: - (Frame)parent, "Manage Project Directories", ModalityType.APPLICATION_MODAL); + (Frame)parent, "COOJA projects", ModalityType.APPLICATION_MODAL); - JPanel mainPane = new JPanel(); - mainPane.setLayout(new BoxLayout(mainPane, BoxLayout.Y_AXIS)); - JPanel smallPane; - JButton button; + table = new JTable(new AbstractTableModel() { + private static final long serialVersionUID = 591599455927509191L; + public int getColumnCount() { + return 2; + } + public int getRowCount() { + return currentProjects.size(); + } + public Object getValueAt(int rowIndex, int columnIndex) { + if (columnIndex == 0) { + return rowIndex+1; + } - // BOTTOM BUTTON PART - JPanel buttonPane = new JPanel(); - buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.X_AXIS)); - buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10)); + if (!currentProjects.get(rowIndex).exists()) { + return currentProjects.get(rowIndex) + " (directory not found)"; + } + if (!new File(currentProjects.get(rowIndex), GUI.PROJECT_CONFIG_FILENAME).exists()) { + return currentProjects.get(rowIndex) + " (no " + GUI.PROJECT_CONFIG_FILENAME + " found)"; + } - buttonPane.add(Box.createHorizontalGlue()); - - button = new JButton("Cancel"); - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - ProjectDirectoriesDialog.this.projects = null; - dispose(); + return currentProjects.get(rowIndex); } }); - buttonPane.add(button); - - buttonPane.add(Box.createRigidArea(new Dimension(10, 0))); - - button = new JButton("Save as default"); - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - Object[] options = { "Ok", "Cancel" }; - - String newDefaultProjectDirs = ""; - for (String directory : projectsList.getItems()) { - if (newDefaultProjectDirs != "") { - newDefaultProjectDirs += ";"; - } - - newDefaultProjectDirs += gui.createPortablePath(new File(directory)).getPath(); - } - newDefaultProjectDirs = newDefaultProjectDirs.replace('\\', '/'); - - String question = "External tools setting DEFAULT_PROJECTDIRS will change from:\n" - + GUI.getExternalToolsSetting("DEFAULT_PROJECTDIRS", "").replace(';', '\n') - + "\n\n to:\n\n" - + newDefaultProjectDirs.replace(';', '\n'); - String title = "Change external tools settings?"; - int answer = JOptionPane.showOptionDialog(ProjectDirectoriesDialog.this, question, title, - JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, - options, options[0]); - - if (answer != JOptionPane.YES_OPTION) { + table.setTableHeader(null); + table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + table.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + public void valueChanged(ListSelectionEvent e) { + if (table.getSelectedRow() < 0) { return; } - - GUI.setExternalToolsSetting("DEFAULT_PROJECTDIRS", newDefaultProjectDirs); + selectTreeProject(currentProjects.get(table.getSelectedRow())); } }); - buttonPane.add(button); - - buttonPane.add(Box.createRigidArea(new Dimension(10, 0))); - - button = new JButton("OK"); - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - ArrayList newProjects = new ArrayList(); - for (String directory : projectsList.getItems()) { - newProjects.add(new File(directory)); + table.getColumnModel().getColumn(0).setPreferredWidth(30); + table.getColumnModel().getColumn(0).setMaxWidth(30); + table.getColumnModel().getColumn(1).setCellRenderer(new DefaultTableCellRenderer() { + private static final long serialVersionUID = 7224219223448831880L; + public Component getTableCellRendererComponent(JTable table, + Object value, boolean isSelected, boolean hasFocus, int row, + int column) { + if (!new File(currentProjects.get(row), GUI.PROJECT_CONFIG_FILENAME).exists()) { + setBackground(Color.RED); + } else { + setBackground(Color.WHITE); } - ProjectDirectoriesDialog.this.projects = newProjects.toArray(new File[0]); - dispose(); + + return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, + row, column); } }); - buttonPane.add(button); - this.getRootPane().setDefaultButton(button); - - // LIST PART - JPanel listPane = new JPanel(); - listPane.setLayout(new BoxLayout(listPane, BoxLayout.X_AXIS)); - listPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); - - JPanel listPane2 = new JPanel(); - listPane2.setLayout(new BoxLayout(listPane2, BoxLayout.Y_AXIS)); - - listPane2.add(projectsList); - - listPane.add(listPane2); - - smallPane = new JPanel(); - smallPane.setLayout(new BoxLayout(smallPane, BoxLayout.Y_AXIS)); - - button = new JButton("Move up"); - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - int selectedIndex = projectsList.getSelectedIndex(); - if (selectedIndex <= 0) { - return; - } - - File file = new File(projectsList.getItem(selectedIndex)); - - removeProjectDir(selectedIndex); - addProjectDir(file, selectedIndex - 1); - projectsList.select(selectedIndex - 1); - } - }); - smallPane.add(button); - - button = new JButton("Move down"); - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - int selectedIndex = projectsList.getSelectedIndex(); - if (selectedIndex < 0) { - return; - } - if (selectedIndex >= projectsList.getItemCount() - 1) { - return; - } - - File file = new File(projectsList.getItem(selectedIndex)); - removeProjectDir(selectedIndex); - addProjectDir(file, selectedIndex + 1); - projectsList.select(selectedIndex + 1); - } - }); - smallPane.add(button); - - smallPane.add(Box.createRigidArea(new Dimension(10, 10))); - - button = new JButton("Remove"); - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - if (projectsList.getSelectedIndex() < 0) { - return; - } - - removeProjectDir(projectsList.getSelectedIndex()); - } - }); - smallPane.add(button); - - listPane.add(smallPane); - - // ADD/REMOVE PART - JPanel addRemovePane = new JPanel(); - addRemovePane.setLayout(new BoxLayout(addRemovePane, BoxLayout.X_AXIS)); - addRemovePane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10)); - - button = new JButton("View resulting config"); - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - ProjectConfig config; - try { - // Create default configuration - config = new ProjectConfig(true); - } catch (FileNotFoundException ex) { - logger.fatal("Could not find default project config file: " - + GUI.PROJECT_DEFAULT_CONFIG_FILENAME); - return; - } catch (IOException ex) { - logger.fatal("Error when reading default project config file: " - + GUI.PROJECT_DEFAULT_CONFIG_FILENAME); - return; - } - - // Add the project directory configurations - for (String projectDir : projectsList.getItems()) { - try { - config.appendProjectDir(new File(projectDir)); - } catch (Exception ex) { - logger.fatal("Error when merging configurations: " + ex); - return; - } - } - - // Show merged configuration - ConfigViewer.showDialog(ProjectDirectoriesDialog.this, config); - } - }); - addRemovePane.add(button); - - addRemovePane.add(Box.createRigidArea(new Dimension(10, 0))); - - button = new JButton("Enter path manually"); - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - ProjectDirectoryInputDialog pathDialog = new ProjectDirectoryInputDialog(ProjectDirectoriesDialog.this); - pathDialog.pack(); - - pathDialog.setLocationRelativeTo(ProjectDirectoriesDialog.this); - pathDialog.setVisible(true); - - File projectPath = pathDialog.getProjectDirectory(); - if (projectPath != null) { - addProjectDir(projectPath); - } - } - }); - addRemovePane.add(button); - - addRemovePane.add(Box.createRigidArea(new Dimension(10, 0))); - - button = new JButton("Browse"); - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - JFileChooser fc = new JFileChooser(); - fc.setCurrentDirectory(new java.io.File(GUI.getExternalToolsSetting("PATH_CONTIKI"))); - fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - fc.setDialogTitle("Select project directory"); - - if (fc.showOpenDialog(ProjectDirectoriesDialog.this) == JFileChooser.APPROVE_OPTION) { - File dir = fc.getSelectedFile(); - String selectedPath = null; - try { - selectedPath = dir.getCanonicalPath().replace('\\', '/'); - String contikiPath = - new File(GUI.getExternalToolsSetting("PATH_CONTIKI", null)). - getCanonicalPath().replace('\\', '/'); - if (contikiPath != null && selectedPath.startsWith(contikiPath)) { - selectedPath = selectedPath.replaceFirst( - contikiPath, GUI.getExternalToolsSetting("PATH_CONTIKI")); - } - addProjectDir(new File(selectedPath)); - } catch (IOException ex) { - logger.fatal("Error in path extraction: " + ex); - } - - } - } - }); - addRemovePane.add(button); - - // Add components - Container contentPane = getContentPane(); - mainPane.add(listPane); - mainPane.add(addRemovePane); - contentPane.add(mainPane, BorderLayout.CENTER); - contentPane.add(buttonPane, BorderLayout.SOUTH); + /* Add current projects */ for (File projectDir : projects) { addProjectDir(projectDir); } - pack(); - } - private void addProjectDir(File projectDir) { - addProjectDir(projectDir, projectsList.getItemCount()); - } + Box mainPane = Box.createVerticalBox(); + Box buttonPane = Box.createHorizontalBox(); + JPanel smallPane; + JButton button; - private void addProjectDir(File projectDir, int index) { - // Check that file exists, is a directory and contains the correct files - if (!projectDir.exists()) { - logger.fatal("Can't find project directory: " + projectDir); - return; - } - if (!projectDir.isDirectory()) { - logger.fatal("Specified path is not a directory: " + projectDir); - return; + /* Lower buttons */ + { + buttonPane.setBorder(BorderFactory.createEmptyBorder(0,3,3,3)); + buttonPane.add(Box.createHorizontalGlue()); + + button = new JButton("View merged config"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ProjectConfig config; + try { + config = new ProjectConfig(true); + } catch (FileNotFoundException ex) { + logger.fatal("Could not find default project config file: " + GUI.PROJECT_DEFAULT_CONFIG_FILENAME); + return; + } catch (IOException ex) { + logger.fatal("Error when reading default project config file: " + GUI.PROJECT_DEFAULT_CONFIG_FILENAME); + return; + } + + /* Merge configs */ + for (File project : getProjects()) { + try { + config.appendProjectDir(project); + } catch (Exception ex) { + logger.fatal("Error when merging configurations: " + ex); + return; + } + } + + ConfigViewer.showDialog(ProjectDirectoriesDialog.this, config); + } + }); + buttonPane.add(button); + buttonPane.add(Box.createRigidArea(new Dimension(10, 0))); + + button = new JButton("Cancel"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ProjectDirectoriesDialog.this.finalProjects = null; + dispose(); + } + }); + buttonPane.add(button); + + buttonPane.add(Box.createRigidArea(new Dimension(10, 0))); + + button = new JButton("Save as default"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + Object[] options = { "Ok", "Cancel" }; + + String newDefaultProjectDirs = ""; + for (File f: currentProjects) { + if (newDefaultProjectDirs != "") { + newDefaultProjectDirs += ";"; + } + + newDefaultProjectDirs += gui.createPortablePath(f).getPath(); + } + newDefaultProjectDirs = newDefaultProjectDirs.replace('\\', '/'); + + String question = "External tools setting DEFAULT_PROJECTDIRS will change from:\n" + + GUI.getExternalToolsSetting("DEFAULT_PROJECTDIRS", "").replace(';', '\n') + + "\n\n to:\n\n" + + newDefaultProjectDirs.replace(';', '\n'); + String title = "Change external tools settings?"; + int answer = JOptionPane.showOptionDialog(ProjectDirectoriesDialog.this, question, title, + JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, + options, options[0]); + + if (answer != JOptionPane.YES_OPTION) { + return; + } + + GUI.setExternalToolsSetting("DEFAULT_PROJECTDIRS", newDefaultProjectDirs); + } + }); + buttonPane.add(button); + + buttonPane.add(Box.createRigidArea(new Dimension(10, 0))); + + button = new JButton("OK"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ProjectDirectoriesDialog.this.finalProjects = currentProjects.toArray(new File[0]); + dispose(); + } + }); + buttonPane.add(button); + this.getRootPane().setDefaultButton(button); } - projectsList.add(projectDir.getPath(), index); + /* Center: Tree and list*/ + { + JPanel listPane = new JPanel(new BorderLayout()); + + listPane.add(BorderLayout.WEST, treePanel = new DirectoryTreePanel(this)); + listPane.add(BorderLayout.CENTER, new JScrollPane(table)); + + smallPane = new JPanel(new BorderLayout()); + Icon icon = UIManager.getLookAndFeelDefaults().getIcon("Table.ascendingSortIcon"); + if (icon == null) { + button = new JButton("Up"); + } else { + button = new JButton(icon); + } + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + int selectedIndex = table.getSelectedRow(); + if (selectedIndex <= 0) { + return; + } + File file = currentProjects.get(selectedIndex); + removeProjectDir(file); + addProjectDir(file, selectedIndex - 1); + table.getSelectionModel().setSelectionInterval(selectedIndex - 1, selectedIndex - 1); + } + }); + smallPane.add(BorderLayout.NORTH, button); + icon = UIManager.getLookAndFeelDefaults().getIcon("Table.descendingSortIcon"); + if (icon == null) { + button = new JButton("Down"); + } else { + button = new JButton(icon); + } + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + int selectedIndex = table.getSelectedRow(); + if (selectedIndex < 0) { + return; + } + if (selectedIndex >= currentProjects.size() - 1) { + return; + } + File file = currentProjects.get(selectedIndex); + removeProjectDir(file); + addProjectDir(file, selectedIndex + 1); + table.getSelectionModel().setSelectionInterval(selectedIndex + 1, selectedIndex + 1); + } + }); + smallPane.add(BorderLayout.SOUTH, button); + listPane.add(BorderLayout.EAST, smallPane); + mainPane.setBackground(Color.WHITE); + mainPane.add(listPane); + } + + JPanel topPanel = new JPanel(new BorderLayout()); + topPanel.add(BorderLayout.CENTER, new JLabel(" A COOJA project depends on a cooja.config file, and extends COOJA with radio mediums, mote types, plugins etc.")); + topPanel.setBackground(Color.WHITE); + getContentPane().add(BorderLayout.NORTH, topPanel); + getContentPane().add(BorderLayout.CENTER, mainPane); + getContentPane().add(BorderLayout.SOUTH, buttonPane); + setSize(700, 500); } - private void removeProjectDir(int index) { - projectsList.remove(index); + public File[] getProjects() { + return currentProjects.toArray(new File[0]); + } + protected void addProjectDir(File projectDir) { + currentProjects.add(projectDir); + ((AbstractTableModel)table.getModel()).fireTableDataChanged(); + } + protected void addProjectDir(File projectDir, int index) { + currentProjects.add(index, projectDir); + ((AbstractTableModel)table.getModel()).fireTableDataChanged(); + } + protected void removeProjectDir(int index) { + currentProjects.remove(index); + ((AbstractTableModel)table.getModel()).fireTableDataChanged(); + } + protected void removeProjectDir(File dir) { + currentProjects.remove(dir); + ((AbstractTableModel)table.getModel()).fireTableDataChanged(); + } + private int getProjectListIndex(File dir) { + return currentProjects.indexOf(dir); + } + public void selectListProject(File dir) { + int i = getProjectListIndex(dir); + if (i >= 0) { + table.getSelectionModel().setSelectionInterval(i, i); + } + } + public void selectTreeProject(File dir) { + treePanel.selectProject(dir); + } +} + +/** + * Shows a directory tree, and allows for selecting directories with a cooja.config file. + * + * @author Fredrik Osterlind + */ +class DirectoryTreePanel extends JPanel { + private static final long serialVersionUID = -6852893350326771136L; + private static Logger logger = Logger.getLogger(DirectoryTreePanel.class); + + private ProjectDirectoriesDialog parent; + private JTree tree; + private DefaultMutableTreeNode treeRoot; + public DirectoryTreePanel(ProjectDirectoriesDialog parent) { + super(new BorderLayout()); + this.parent = parent; + + /* Build directory tree */ + treeRoot = new DefaultMutableTreeNode("My Computer"); + tree = new JTree(treeRoot); + tree.setRootVisible(false); + tree.setShowsRootHandles(true); + tree.expandRow(0); + tree.setCellRenderer(new DefaultTreeCellRenderer() { + private static final long serialVersionUID = 280434957859560569L; + private Icon unselectedIcon = new CheckboxIcon(null); + private Icon selectedIcon = new CheckboxIcon(new Color(0, 255, 0, 128)); + private Icon errorIcon = new CheckboxIcon(new Color(255, 0, 0, 128)); + private Font boldFont = null; + private Font normalFont = null; + public Component getTreeCellRendererComponent(JTree tree, + Object value, boolean sel, boolean expanded, boolean leaf, + int row, boolean hasFocus) { + super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, + row, hasFocus); + if (value instanceof DefaultMutableTreeNode) { + value = ((DefaultMutableTreeNode) value).getUserObject(); + } + if (!(value instanceof ProjectDirectory)) { + return this; + } + ProjectDirectory td = (ProjectDirectory) value; + + if (boldFont == null) { + normalFont = getFont(); + boldFont = getFont().deriveFont( Font.BOLD ); + } + + /* Style */ + setFont(normalFont); + if (td.isProject()) { + if (td.containsConfig()) { + setIcon(selectedIcon); + } else { + /* Error: no cooja.config */ + setIcon(errorIcon); + setFont(boldFont); + } + } else if (td.containsConfig()) { + setIcon(unselectedIcon); + } else if (td.subtreeContainsProject()) { + setFont(boldFont); + } + + return this; + } + class CheckboxIcon implements Icon { + Icon icon = (Icon) UIManager.get("CheckBox.icon"); + Color color; + public CheckboxIcon(Color color) { + this.color = color; + } + public int getIconHeight() { + return icon.getIconHeight(); + } + public int getIconWidth() { + return icon.getIconWidth(); + } + public void paintIcon(Component c, Graphics g, int x, int y) { + icon.paintIcon(c, g, x, y); + if (color != null) { + g.setColor(color); + g.fillRect(x, y, icon.getIconWidth(), icon.getIconHeight()); + } + } + } + }); + tree.setModel(new COOJAProjectTreeModel(treeRoot)); + tree.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + TreePath selPath = tree.getPathForLocation(e.getX(), e.getY()); + if (selPath == null) { + return; + } + if (e.getClickCount() != 1) { + return; + } + Object o = selPath.getLastPathComponent(); + if (!(o instanceof DefaultMutableTreeNode)) { + return; + } + if (!(((DefaultMutableTreeNode) o).getUserObject() instanceof ProjectDirectory)) { + return; + } + ProjectDirectory pd = (ProjectDirectory) ((DefaultMutableTreeNode) o).getUserObject(); + Rectangle r = tree.getPathBounds(selPath); + int delta = e.getX() - r.x; + if (delta > 18 /* XXX Icon width */) { + return; + } + + if (pd.isProject()) { + /* Remove project */ + DirectoryTreePanel.this.parent.removeProjectDir(pd.dir); + DirectoryTreePanel.this.parent.repaint(); + } else if (pd.containsConfig()) { + /* Add project */ + DirectoryTreePanel.this.parent.addProjectDir(pd.dir); + DirectoryTreePanel.this.parent.repaint(); + } + } + }); + tree.addTreeSelectionListener(new TreeSelectionListener() { + public void valueChanged(TreeSelectionEvent e) { + TreePath selPath = e.getPath(); + if (selPath == null) { + return; + } + Object o = selPath.getLastPathComponent(); + if (!(o instanceof DefaultMutableTreeNode)) { + return; + } + if (!(((DefaultMutableTreeNode) o).getUserObject() instanceof ProjectDirectory)) { + return; + } + ProjectDirectory pd = (ProjectDirectory) ((DefaultMutableTreeNode) o).getUserObject(); + if (pd.isProject()) { + DirectoryTreePanel.this.parent.selectListProject(pd.dir); + } + } + }); + + /* Try expand current COOJA projects */ + for (File projectDir: parent.getProjects()) { + if (!projectDir.exists()) { + logger.fatal("Project directory not found: " + projectDir); + continue; + } + try { + String projectCanonical = projectDir.getCanonicalPath(); + TreePath tp = new TreePath(tree.getModel().getRoot()); + tp = buildTreePath(projectCanonical, treeRoot, tp, tree); + /*logger.info("Expanding: " + tp);*/ + if (tp != null) { + tree.expandPath(tp.getParentPath()); + } + } catch (IOException ex) { + logger.warn("Error when expanding projects: " + ex.getMessage()); + } + } + add(BorderLayout.CENTER, new JScrollPane(tree)); } + public void selectProject(File dir) { + /* Expand view */ + try { + String projectCanonical = dir.getCanonicalPath(); + TreePath tp = new TreePath(tree.getModel().getRoot()); + tp = buildTreePath(projectCanonical, treeRoot, tp, tree); + /*logger.info("Expanding: " + tp);*/ + if (tp != null) { + tree.setSelectionPath(tp); + tree.scrollPathToVisible(tp); + } + } catch (IOException ex) { + logger.warn("Error when expanding projects: " + ex.getMessage()); + } + } + + private static TreePath buildTreePath(String projectCanonical, DefaultMutableTreeNode parent, TreePath tp, JTree tree) + throws IOException { + /* Force filesystem listing */ + tree.getModel().getChildCount(parent); + + for (int i=0; i < tree.getModel().getChildCount(parent); i++) { + DefaultMutableTreeNode child = (DefaultMutableTreeNode) tree.getModel().getChild(parent, i); + Object userObject = child.getUserObject(); + if (!(userObject instanceof ProjectDirectory)) { + logger.fatal("Bad tree element: " + userObject.getClass()); + continue; + } + ProjectDirectory td = (ProjectDirectory) userObject; + String treeCanonical = td.dir.getCanonicalPath(); + + if (projectCanonical.startsWith(treeCanonical)) { + tp = tp.pathByAddingChild(child); + if (projectCanonical.equals(treeCanonical)) { + return tp; + } + + return buildTreePath(projectCanonical, child, tp, tree); + } + } + return null; + } + + private class ProjectDirectory { + File dir = null; + File[] subdirs = null; + + public ProjectDirectory(File file) { + this.dir = file; + } + + boolean isProject() { + for (File project: parent.getProjects()) { + if (project.equals(dir)) { + return true; + } + } + return false; + } + boolean containsConfig() { + return new File(dir, GUI.PROJECT_CONFIG_FILENAME).exists(); + } + boolean subtreeContainsProject() { + try { + String dirCanonical = dir.getCanonicalPath(); + for (File project: parent.getProjects()) { + if (!project.exists()) { + continue; + } + String projectCanonical = project.getCanonicalPath(); + if (projectCanonical.startsWith(dirCanonical)) { + return true; + } + } + } catch (IOException ex) { + } + return false; + } + public String toString() { + if (dir.getName() == null || dir.getName().equals("")) { + return dir.getAbsolutePath(); + } + return dir.getName(); + } + } + private class COOJAProjectTreeModel extends DefaultTreeModel { + private static final long serialVersionUID = -4673855124090194313L; + + private DefaultMutableTreeNode computerNode; + + public COOJAProjectTreeModel(DefaultMutableTreeNode computerNode) { + super(computerNode); + this.computerNode = computerNode; + + /* List roots */ + File[] devices = File.listRoots(); + if (devices == null) { + logger.fatal("Could not list filesystem"); + return; + } + for (File device: devices) { + DefaultMutableTreeNode deviceNode = new DefaultMutableTreeNode(new ProjectDirectory(device)); + computerNode.add(deviceNode); + } + } + public Object getRoot() { + return computerNode.getUserObject(); + } + public boolean isLeaf(Object node) { + if ((node instanceof DefaultMutableTreeNode)) { + node = ((DefaultMutableTreeNode)node).getUserObject(); + } + if (!(node instanceof ProjectDirectory)) { + /* Computer node */ + return false; + } + ProjectDirectory td = ((ProjectDirectory)node); + + return td.dir.isFile(); + } + public int getChildCount(Object parent) { + if ((parent instanceof DefaultMutableTreeNode)) { + parent = ((DefaultMutableTreeNode)parent).getUserObject(); + } + if (!(parent instanceof ProjectDirectory)) { + /* Computer node */ + return computerNode.getChildCount(); + } + ProjectDirectory td = ((ProjectDirectory)parent); + + File[] children; + if (td.subdirs != null) { + children = td.subdirs; + } else { + children = td.dir.listFiles(DIRECTORIES); + td.subdirs = children; + } + if (children == null) { + return 0; + } + return children.length; + } + public Object getChild(Object parent, int index) { + if ((parent instanceof DefaultMutableTreeNode)) { + parent = ((DefaultMutableTreeNode)parent).getUserObject(); + } + if (!(parent instanceof ProjectDirectory)) { + /* Computer node */ + return computerNode.getChildAt(index); + } + ProjectDirectory td = ((ProjectDirectory)parent); + + File[] children; + if (td.subdirs != null) { + children = td.subdirs; + } else { + children = td.dir.listFiles(DIRECTORIES); + td.subdirs = children; + } + if ((children == null) || (index >= children.length)) { + return null; + } + return new DefaultMutableTreeNode(new ProjectDirectory(children[index])); + } + public int getIndexOfChild(Object parent, Object child) { + if ((parent instanceof DefaultMutableTreeNode)) { + parent = ((DefaultMutableTreeNode)parent).getUserObject(); + } + if (!(parent instanceof ProjectDirectory)) { + /* Computer node */ + for(int i=0; i < computerNode.getChildCount(); i++) { + if (computerNode.getChildAt(i).equals(child)) { + return i; + } + } + } + ProjectDirectory td = ((ProjectDirectory)parent); + + File[] children; + if (td.subdirs != null) { + children = td.subdirs; + } else { + children = td.dir.listFiles(DIRECTORIES); + td.subdirs = children; + } + if (children == null) { + return -1; + } + if (child instanceof DefaultMutableTreeNode) { + child = ((DefaultMutableTreeNode)child).getUserObject(); + } + File subDir = ((ProjectDirectory)child).dir; + for(int i = 0; i < children.length; i++) { + if (subDir.equals(children[i])) { + return i; + } + } + return -1; + } + + public void valueForPathChanged(TreePath path, Object newvalue) {} + public void addTreeModelListener(TreeModelListener l) {} + public void removeTreeModelListener(TreeModelListener l) {} + + private final FileFilter DIRECTORIES = new FileFilter() { + public boolean accept(File file) { + if (file.isDirectory()) { + return true; + } + return false; + } + }; + } } /** @@ -491,7 +869,5 @@ class ConfigViewer extends JDialog { if (maxSize != null && (getSize().height > maxSize.height)) { setSize(getSize().width, maxSize.height); } - } - } diff --git a/tools/cooja/java/se/sics/cooja/dialogs/ProjectDirectoryInputDialog.java b/tools/cooja/java/se/sics/cooja/dialogs/ProjectDirectoryInputDialog.java deleted file mode 100644 index 487e12614..000000000 --- a/tools/cooja/java/se/sics/cooja/dialogs/ProjectDirectoryInputDialog.java +++ /dev/null @@ -1,148 +0,0 @@ -package se.sics.cooja.dialogs; - -import java.beans.*; -import java.io.File; -import java.awt.*; -import java.awt.event.*; -import javax.swing.*; -import javax.swing.event.*; -import org.apache.log4j.Logger; - -class ProjectDirectoryInputDialog extends JDialog implements ActionListener, PropertyChangeListener { - private static Logger logger = Logger.getLogger(ProjectDirectoryInputDialog.class); - - private File projectDirectoryPath = null; - private JTextField textField; - - private JOptionPane optionPane; - - private String buttonAdd = "Add"; - private String buttonCancel = "Cancel"; - - public ProjectDirectoryInputDialog(Window window) { - super(window, ModalityType.APPLICATION_MODAL); - setupDialog(); - } - public ProjectDirectoryInputDialog(Dialog dialog) { - super(dialog, ModalityType.APPLICATION_MODAL); - setupDialog(); - } - public ProjectDirectoryInputDialog(Frame frame) { - super(frame, ModalityType.APPLICATION_MODAL); - setupDialog(); - } - - public void setupDialog() { - setTitle("Enter path"); - - textField = new JTextField(10); - textField.getDocument().addDocumentListener(new DocumentListener() { - public void insertUpdate(DocumentEvent e) { - pathChanged(); - } - public void removeUpdate(DocumentEvent e) { - pathChanged(); - } - public void changedUpdate(DocumentEvent e) { - pathChanged(); - } - }); - - Object[] objects = {"Enter path to project directory", textField}; - Object[] options = {buttonAdd, buttonCancel}; - - optionPane = new JOptionPane(objects, - JOptionPane.QUESTION_MESSAGE, - JOptionPane.YES_NO_OPTION, - null, - options, - options[0]); - - setContentPane(optionPane); - - setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); - addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent we) { - optionPane.setValue(new Integer( - JOptionPane.CLOSED_OPTION)); - } - }); - - addComponentListener(new ComponentAdapter() { - public void componentShown(ComponentEvent ce) { - textField.requestFocusInWindow(); - } - }); - - textField.addActionListener(this); - optionPane.addPropertyChangeListener(this); - - SwingUtilities.invokeLater(new Runnable() { - public void run() { - pathChanged(); - } - }); - } - - public File getProjectDirectory() { - return projectDirectoryPath; - } - - private void pathChanged() { - String newPath = textField.getText(); - File projectDir = new File(newPath); - if (!projectDir.exists()) { - textField.setBackground(Color.RED); - textField.setToolTipText("Directory does not exist"); - return; - } - - if (!projectDir.isDirectory()) { - textField.setBackground(Color.RED); - textField.setToolTipText("Not a directory"); - return; - } - -// if (!new File(projectDir, GUI.PROJECT_CONFIG_FILENAME).exists()) { -// textField.setBackground(Color.LIGHT_GRAY); -// textField.setToolTipText("No configuration file found"); -// return; -// } - - textField.setBackground(Color.WHITE); - textField.setToolTipText(""); - } - - public void actionPerformed(ActionEvent e) { - optionPane.setValue(buttonAdd); - } - - public void propertyChange(PropertyChangeEvent e) { - String prop = e.getPropertyName(); - if (isVisible() && (e.getSource() == optionPane) - && (JOptionPane.VALUE_PROPERTY.equals(prop) || - JOptionPane.INPUT_VALUE_PROPERTY.equals(prop))) { - Object value = optionPane.getValue(); - - if (value == JOptionPane.UNINITIALIZED_VALUE) { - return; - } - - optionPane.setValue( - JOptionPane.UNINITIALIZED_VALUE); - - if (buttonAdd.equals(value)) { - projectDirectoryPath = new File(textField.getText()); - close(); - } else { - projectDirectoryPath = null; - close(); - } - } - } - - public void close() { - textField.setText(null); - setVisible(false); - } -}