diff --git a/diylc/diylc-swing/src/org/diylc/images/IconLoader.java b/diylc/diylc-swing/src/org/diylc/images/IconLoader.java index 52d6030d..dec8d92b 100644 --- a/diylc/diylc-swing/src/org/diylc/images/IconLoader.java +++ b/diylc/diylc-swing/src/org/diylc/images/IconLoader.java @@ -36,7 +36,7 @@ public enum IconLoader { "cloud_edit.png"), CloudUpload("cloud_upload.png"), NavLeftBlue("nav_left_blue.png"), NavRightBlue( "nav_right_blue.png"), MissingImage("missing_image.png"), CloudBg("cloud_bg.png"), CloudBig("cloud_big.png"), CloudWait( "cloud_wait.png"), Spinning("spinning.gif"), Megaphone("megaphone.png"), Download("download.png"), Eye("eye.png"), Messages( - "messages.png"); + "messages.png"), SearchBox("search-box.png"), Screwdriver("screwdriver.png"), Hammer("hammer.png"); protected String name; diff --git a/diylc/diylc-swing/src/org/diylc/images/hammer.png b/diylc/diylc-swing/src/org/diylc/images/hammer.png new file mode 100644 index 00000000..2d55c7ac Binary files /dev/null and b/diylc/diylc-swing/src/org/diylc/images/hammer.png differ diff --git a/diylc/diylc-swing/src/org/diylc/images/screwdriver.png b/diylc/diylc-swing/src/org/diylc/images/screwdriver.png new file mode 100644 index 00000000..cc0f4f9a Binary files /dev/null and b/diylc/diylc-swing/src/org/diylc/images/screwdriver.png differ diff --git a/diylc/diylc-swing/src/org/diylc/images/search-box.png b/diylc/diylc-swing/src/org/diylc/images/search-box.png new file mode 100644 index 00000000..822f9454 Binary files /dev/null and b/diylc/diylc-swing/src/org/diylc/images/search-box.png differ diff --git a/diylc/diylc-swing/src/org/diylc/swing/ActionFactory.java b/diylc/diylc-swing/src/org/diylc/swing/ActionFactory.java index a332d7fa..4f79c19c 100644 --- a/diylc/diylc-swing/src/org/diylc/swing/ActionFactory.java +++ b/diylc/diylc-swing/src/org/diylc/swing/ActionFactory.java @@ -27,6 +27,7 @@ import org.diylc.images.IconLoader; import org.diylc.presenter.Presenter; import org.diylc.swing.gui.DialogFactory; import org.diylc.swing.gui.editor.PropertyEditorDialog; +import org.diylc.swing.plugins.config.ConfigPlugin; import org.diylc.swing.plugins.edit.ComponentTransferable; import org.diylc.swing.plugins.file.BomDialog; import org.diylc.swing.plugins.file.FileFilterEnum; @@ -160,6 +161,10 @@ public class ActionFactory { return new ThemeAction(plugInPort, theme); } + public ComponentBrowserAction createComponentBrowserAction(String browserType) { + return new ComponentBrowserAction(browserType); + } + public RenumberAction createRenumberAction(IPlugInPort plugInPort, boolean xAxisFirst) { return new RenumberAction(plugInPort, xAxisFirst); } @@ -1005,6 +1010,30 @@ public class ActionFactory { } } + public static class ComponentBrowserAction extends AbstractAction { + + private static final long serialVersionUID = 1L; + + private String browserType; + + public ComponentBrowserAction(String browserType) { + super(); + this.browserType = browserType; + putValue(AbstractAction.NAME, browserType); + putValue(IView.RADIO_BUTTON_GROUP_KEY, "componentBrowser"); + + putValue(AbstractAction.SELECTED_KEY, + browserType.equals(ConfigurationManager.getInstance().readString(ConfigPlugin.COMPONENT_BROWSER, ConfigPlugin.SEARCHABLE_TREE))); + } + + @Override + public void actionPerformed(ActionEvent e) { + LOG.info(getValue(AbstractAction.NAME) + " triggered"); + ConfigurationManager.getInstance().writeValue(ConfigPlugin.COMPONENT_BROWSER, browserType); + } + } + + public static class RenumberAction extends AbstractAction { private static final long serialVersionUID = 1L; diff --git a/diylc/diylc-swing/src/org/diylc/swing/gui/MainFrame.java b/diylc/diylc-swing/src/org/diylc/swing/gui/MainFrame.java index 1211c961..dbe24056 100644 --- a/diylc/diylc-swing/src/org/diylc/swing/gui/MainFrame.java +++ b/diylc/diylc-swing/src/org/diylc/swing/gui/MainFrame.java @@ -59,6 +59,7 @@ import org.diylc.swing.plugins.help.HelpMenuPlugin; import org.diylc.swing.plugins.layers.LayersMenuPlugin; import org.diylc.swing.plugins.statusbar.StatusBar; import org.diylc.swing.plugins.toolbox.ToolBox; +import org.diylc.swing.plugins.tree.ComponentTree; import org.diylc.swingframework.ButtonDialog; public class MainFrame extends JFrame implements ISwingUI { @@ -95,6 +96,7 @@ public class MainFrame extends JFrame implements ISwingUI { this.presenter = new Presenter(this); presenter.installPlugin(new ToolBox(this)); + presenter.installPlugin(new ComponentTree(this)); presenter.installPlugin(new FileMenuPlugin(this)); presenter.installPlugin(new EditMenuPlugin(this)); presenter.installPlugin(new ConfigPlugin(this)); diff --git a/diylc/diylc-swing/src/org/diylc/swing/plugins/config/ConfigPlugin.java b/diylc/diylc-swing/src/org/diylc/swing/plugins/config/ConfigPlugin.java index d12b4952..e169d434 100644 --- a/diylc/diylc-swing/src/org/diylc/swing/plugins/config/ConfigPlugin.java +++ b/diylc/diylc-swing/src/org/diylc/swing/plugins/config/ConfigPlugin.java @@ -29,6 +29,10 @@ public class ConfigPlugin implements IPlugIn { private static final String CONFIG_MENU = "Config"; private static final String THEME_MENU = "Theme"; + private static final String COMPONENT_BROWSER_MENU = "Toolbox"; + public static final String COMPONENT_BROWSER = "componentBrowser"; + public static final String SEARCHABLE_TREE = "Searchable Tree"; + public static final String TABBED_TOOLBAR = "Tabbed Toolbar"; private ISwingUI swingUI; @@ -43,8 +47,8 @@ public class ConfigPlugin implements IPlugIn { ActionFactory.getInstance() .createConfigAction(plugInPort, "Anti-Aliasing", IPlugInPort.ANTI_ALIASING_KEY, true), CONFIG_MENU); swingUI.injectMenuAction( - ActionFactory.getInstance() - .createConfigAction(plugInPort, "Auto-Create Pads", SolderPadAutoCreator.AUTO_PADS_KEY, false), CONFIG_MENU); + ActionFactory.getInstance().createConfigAction(plugInPort, "Auto-Create Pads", + SolderPadAutoCreator.AUTO_PADS_KEY, false), CONFIG_MENU); swingUI.injectMenuAction( ActionFactory.getInstance().createConfigAction(plugInPort, "Auto-Edit Mode", IPlugInPort.AUTO_EDIT_KEY, true), CONFIG_MENU); @@ -87,6 +91,13 @@ public class ConfigPlugin implements IPlugIn { } } } + + swingUI.injectSubmenu(COMPONENT_BROWSER_MENU, IconLoader.Hammer.getIcon(), CONFIG_MENU); + + swingUI.injectMenuAction(ActionFactory.getInstance().createComponentBrowserAction(SEARCHABLE_TREE), + COMPONENT_BROWSER_MENU); + swingUI.injectMenuAction(ActionFactory.getInstance().createComponentBrowserAction(TABBED_TOOLBAR), + COMPONENT_BROWSER_MENU); } @Override diff --git a/diylc/diylc-swing/src/org/diylc/swing/plugins/toolbox/ToolBox.java b/diylc/diylc-swing/src/org/diylc/swing/plugins/toolbox/ToolBox.java index 7ce86532..e6c3d32f 100644 --- a/diylc/diylc-swing/src/org/diylc/swing/plugins/toolbox/ToolBox.java +++ b/diylc/diylc-swing/src/org/diylc/swing/plugins/toolbox/ToolBox.java @@ -5,11 +5,14 @@ import java.util.EnumSet; import javax.swing.SwingConstants; import org.apache.log4j.Logger; +import org.diylc.appframework.miscutils.ConfigurationManager; +import org.diylc.appframework.miscutils.IConfigListener; import org.diylc.common.BadPositionException; import org.diylc.common.EventType; import org.diylc.common.IPlugIn; import org.diylc.common.IPlugInPort; import org.diylc.swing.ISwingUI; +import org.diylc.swing.plugins.config.ConfigPlugin; import org.diylc.swing.plugins.statusbar.StatusBar; @@ -34,6 +37,18 @@ public class ToolBox implements IPlugIn { } catch (BadPositionException e) { LOG.error("Could not install the toolbox", e); } + ConfigurationManager.getInstance().addConfigListener(ConfigPlugin.COMPONENT_BROWSER, new IConfigListener() { + + @Override + public void valueChanged(String key, Object value) { + getComponentTabbedPane().setVisible( + ConfigPlugin.COMPONENT_BROWSER.equals(key) && ConfigPlugin.TABBED_TOOLBAR.equals(value)); + } + }); + + getComponentTabbedPane().setVisible( + ConfigurationManager.getInstance().readString(ConfigPlugin.COMPONENT_BROWSER, ConfigPlugin.SEARCHABLE_TREE) + .equals(ConfigPlugin.TABBED_TOOLBAR)); } public ComponentTabbedPane getComponentTabbedPane() { diff --git a/diylc/diylc-swing/src/org/diylc/swing/plugins/tree/ComponentTree.java b/diylc/diylc-swing/src/org/diylc/swing/plugins/tree/ComponentTree.java new file mode 100644 index 00000000..6b4565fc --- /dev/null +++ b/diylc/diylc-swing/src/org/diylc/swing/plugins/tree/ComponentTree.java @@ -0,0 +1,72 @@ +package org.diylc.swing.plugins.tree; + +import java.util.EnumSet; + +import javax.swing.SwingConstants; + +import org.apache.log4j.Logger; +import org.diylc.appframework.miscutils.ConfigurationManager; +import org.diylc.appframework.miscutils.IConfigListener; +import org.diylc.common.BadPositionException; +import org.diylc.common.EventType; +import org.diylc.common.IPlugIn; +import org.diylc.common.IPlugInPort; +import org.diylc.swing.ISwingUI; +import org.diylc.swing.plugins.config.ConfigPlugin; +import org.diylc.swing.plugins.statusbar.StatusBar; + + +public class ComponentTree implements IPlugIn { + + private static final Logger LOG = Logger.getLogger(StatusBar.class); + + private ISwingUI swingUI; + private IPlugInPort plugInPort; + + private TreePanel treePanel; + + public ComponentTree(ISwingUI swingUI) { + this.swingUI = swingUI; + } + + @Override + public void connect(IPlugInPort plugInPort) { + this.plugInPort = plugInPort; + try { + swingUI.injectGUIComponent(getTreePanel(), SwingConstants.LEFT); + } catch (BadPositionException e) { + LOG.error("Could not install the component tree", e); + } + ConfigurationManager.getInstance().addConfigListener(ConfigPlugin.COMPONENT_BROWSER, new IConfigListener() { + + @Override + public void valueChanged(String key, Object value) { + getTreePanel().setVisible( + ConfigPlugin.COMPONENT_BROWSER.equals(key) && ConfigPlugin.SEARCHABLE_TREE.equals(value)); + } + }); + + getTreePanel().setVisible( + ConfigurationManager.getInstance().readString(ConfigPlugin.COMPONENT_BROWSER, ConfigPlugin.SEARCHABLE_TREE) + .equals(ConfigPlugin.SEARCHABLE_TREE)); + } + + public TreePanel getTreePanel() { + if (treePanel == null) { + treePanel = new TreePanel(plugInPort); + } + return treePanel; + } + + @Override + public EnumSet getSubscribedEventTypes() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void processMessage(EventType eventType, Object... params) { + // TODO Auto-generated method stub + + } +} diff --git a/diylc/diylc-swing/src/org/diylc/swing/plugins/tree/TreePanel.java b/diylc/diylc-swing/src/org/diylc/swing/plugins/tree/TreePanel.java new file mode 100644 index 00000000..8a258347 --- /dev/null +++ b/diylc/diylc-swing/src/org/diylc/swing/plugins/tree/TreePanel.java @@ -0,0 +1,282 @@ +package org.diylc.swing.plugins.tree; + +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import javax.swing.Icon; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.JTree; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreePath; + +import org.diylc.common.ComponentType; +import org.diylc.common.IPlugInPort; +import org.diylc.images.IconLoader; + +public class TreePanel extends JPanel { + + private static final long serialVersionUID = 1L; + + private DefaultTreeModel treeModel; + private JTree tree; + private JTextField searchField; + + private IPlugInPort plugInPort; + + public TreePanel(IPlugInPort plugInPort) { + this.plugInPort = plugInPort; + setLayout(new GridBagLayout()); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.weighty = 0; + + add(getSearchField(), gbc); + + gbc.gridy++; + gbc.fill = GridBagConstraints.BOTH; + gbc.weighty = 1; + gbc.weightx = 1; + + JScrollPane scroll = new JScrollPane(getTree()); + scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + add(scroll, gbc); + + setPreferredSize(new Dimension(240, 200)); + } + + public DefaultTreeModel getTreeModel() { + if (treeModel == null) { + final DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("Components", true); + Map> componentTypes = plugInPort.getComponentTypes(); + List categories = new ArrayList(componentTypes.keySet()); + Collections.sort(categories); + for (String category : categories) { + final DefaultMutableTreeNode categoryNode = new DefaultMutableTreeNode(new Payload(category)); + rootNode.add(categoryNode); + for (ComponentType type : componentTypes.get(category)) { + final DefaultMutableTreeNode componentNode = new DefaultMutableTreeNode(new Payload(type), false); + categoryNode.add(componentNode); + } + } + treeModel = new DefaultTreeModel(rootNode); + } + return treeModel; + } + + public JTree getTree() { + if (tree == null) { + + + tree = new JTree(getTreeModel()); + tree.setRootVisible(false); + tree.setCellRenderer(new ComponentCellRenderer()); + tree.setRowHeight(0); + tree.addTreeSelectionListener(new TreeSelectionListener() { + + @Override + public void valueChanged(TreeSelectionEvent e) { + Object c = e.getPath().getLastPathComponent(); + if (c != null && c.getClass().equals(DefaultMutableTreeNode.class)) { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) c; + Object obj = node.getUserObject(); + if (obj != null && obj.getClass().equals(Payload.class)) { + Payload payload = (Payload) obj; + plugInPort.setNewComponentTypeSlot(payload.getComponentType(), null); + } + } + } + }); + } + return tree; + } + + public JTextField getSearchField() { + if (searchField == null) { + searchField = new JTextField() { + + private static final long serialVersionUID = 1L; + + @Override + public void paint(Graphics g) { + super.paint(g); + + Graphics2D g2d = (Graphics2D) g; + g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f)); + Icon icon = IconLoader.SearchBox.getIcon(); + icon.paintIcon(searchField, g2d, searchField.getWidth() - 18, 3); + + if (searchField.getText().trim().length() == 0 && !searchField.hasFocus()) { + g2d.setColor(Color.gray); + g2d.setFont(searchField.getFont()); + g2d.drawString("Search", 4, 3 + searchField.getFont().getSize()); + } + } + }; + + searchField.addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + searchField.repaint(); + } + + @Override + public void focusLost(FocusEvent e) { + searchField.repaint(); + } + }); + + searchField.getDocument().addDocumentListener(new DocumentListener() { + + public void changedUpdate(DocumentEvent e) { + process(); + } + + public void removeUpdate(DocumentEvent e) { + process(); + } + + public void insertUpdate(DocumentEvent e) { + process(); + } + + public void process() { + String text = searchField.getText(); + DefaultTreeModel model = getTreeModel(); + DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode) model.getRoot(); + for (int i = 0; i < model.getChildCount(rootNode); i++) { + int visibleCount = 0; + DefaultMutableTreeNode categoryNode = (DefaultMutableTreeNode) model.getChild(rootNode, i); + for (int j = 0; j < model.getChildCount(categoryNode); j++) { + DefaultMutableTreeNode componentNode = (DefaultMutableTreeNode) model.getChild(categoryNode, j); + Object obj = componentNode.getUserObject(); + if (obj != null && obj.getClass().equals(Payload.class)) { + Payload payload = (Payload) obj; + boolean visible = false; + if (text.trim().length() == 0) { + visible = true; + } else { + ComponentType type = payload.getComponentType(); + visible = + type.getName().toLowerCase().contains(text.toLowerCase()) + || type.getDescription().toLowerCase().contains(text.toLowerCase()) + || type.getCategory().toLowerCase().contains(text.toLowerCase()); + } + if (visible != payload.isVisible()) { + payload.setVisible(visible); + model.nodeStructureChanged(componentNode); + } + if (visible) + visibleCount++; + } + } + + Object obj = categoryNode.getUserObject(); + if (obj != null && obj.getClass().equals(Payload.class)) { + Payload payload = (Payload) obj; + boolean visible = visibleCount > 0; + if (visible != payload.isVisible()) { + payload.setVisible(visible); + model.nodeStructureChanged(rootNode); + } + if (visible && text.trim().length() > 0) { + tree.expandPath(new TreePath(categoryNode.getPath())); + } + } + } + + getTree().invalidate(); + } + }); + } + return searchField; + } + + public class ComponentCellRenderer extends DefaultTreeCellRenderer { + + private static final long serialVersionUID = 1L; + + @Override + public Component getTreeCellRendererComponent(final JTree tree, final Object value, final boolean selected, + final boolean expanded, final boolean leaf, final int row, final boolean hasFocus) { + + super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); + + Object obj = ((DefaultMutableTreeNode) value).getUserObject(); + if (obj != null && obj.getClass().equals(Payload.class)) { + Payload type = (Payload) obj; + if (type.getComponentType() == null) { + if (type.isVisible()) + setPreferredSize(new Dimension(240, 20)); + else + setPreferredSize(new Dimension(0, 0)); + } else { + setIcon(type.getComponentType().getIcon()); + if (type.isVisible()) + setPreferredSize(new Dimension(240, 32)); + else + setPreferredSize(new Dimension(0, 0)); + } + } + + return this; + } + } + + public class Payload { + private ComponentType componentType; + private String category; + private boolean isVisible; + + public Payload(ComponentType componentType) { + super(); + this.componentType = componentType; + this.isVisible = true; + } + + public Payload(String category) { + super(); + this.category = category; + this.isVisible = true; + } + + public ComponentType getComponentType() { + return componentType; + } + + public boolean isVisible() { + return isVisible; + } + + public void setVisible(boolean isVisible) { + this.isVisible = isVisible; + } + + @Override + public String toString() { + return componentType == null ? category : componentType.getName(); + } + } +}