diff --git a/app/src/processing/app/LibraryListPanel.java b/app/src/processing/app/LibraryListPanel.java index 277e24c77..7d3559399 100644 --- a/app/src/processing/app/LibraryListPanel.java +++ b/app/src/processing/app/LibraryListPanel.java @@ -26,18 +26,17 @@ import java.util.*; import java.util.List; import javax.swing.*; +import javax.swing.event.*; import javax.swing.text.*; import java.awt.event.*; import java.awt.font.*; import java.awt.*; -import java.io.File; -import java.net.MalformedURLException; -import java.net.URL; +import java.net.*; import java.text.*; import processing.app.LibraryListing.LibraryInfo; -import processing.app.LibraryManager.LibraryInstaller; +import processing.app.LibraryListing.LibraryInfo.Author; public class LibraryListPanel extends JPanel implements Scrollable { @@ -49,7 +48,13 @@ public class LibraryListPanel extends JPanel implements Scrollable { public LibraryListPanel(LibraryManager libraryManager) { super(); - preferredViewPositionListener = null; + preferredViewPositionListener = new PreferredViewPositionListener() { + + public void handlePreferredLocation(Point p) { + } + + }; + this.libraryManager = libraryManager; libraries = libraryManager.getLibraryListing(null); @@ -98,71 +103,6 @@ public class LibraryListPanel extends JPanel implements Scrollable { for (LibraryInfo lib : hiddenLibraries) { libPanelsByInfo.get(lib).setVisible(false); } - - updateLibraryListSizeAndPosition(0, false, null); - } - - /** - * Updates the widths of all library panels in this library list. - */ - public void setWidth(int newWidth) { - for (Component c : getComponents()) { - if (c instanceof LibraryPanel) { - ((LibraryPanel) c).updateSize(newWidth); - } - } - - updateLibraryListSizeAndPosition(0, true, null); - } - - /** - * Updates the width and height of this library list based on the sizes of the - * library panes it contains. - * - * @param obtainedSpace - * the height of the space obtained from the closing of the previous - * panel - * @param obtainedSpaceIsAbove - * true if the panel that was closed was above the panel which was - * just opened - * @param expandedPanel - * the panel that was expanded, or null if none - */ - private void updateLibraryListSizeAndPosition(int obtainedSpace, - boolean obtainedSpaceIsAbove, - LibraryPanel expandedPanel) { - - int height = 0; - int width = 0; - -// int selectedTop = 0, selectedBottom = 0; -// if (expandedPanel != null) { -// selectedTop = expandedPanel.getLocation().y; -// selectedBottom = selectedTop + expandedPanel.getHeight(); -// } - - for (Component c : getComponents()) { - if (c.isVisible()) { - if (c instanceof LibraryPanel) { - Dimension d = c.getPreferredSize(); - if (d.width > width) { - width = d.width; - } - height += d.height; - } - } - } - - Rectangle r = getVisibleRect(); - if (obtainedSpaceIsAbove && preferredViewPositionListener != null) { - - Point p = new Point(r.x, r.y); - p.y -= obtainedSpace; - - preferredViewPositionListener.handlePreferredLocation(p); - } - - setPreferredSize(new Dimension(width, height)); } /** @@ -247,13 +187,14 @@ public class LibraryListPanel extends JPanel implements Scrollable { * as a contained. This assumed that both word wrap and wrap-style word are * enabled for the JTextArea. */ + @SuppressWarnings("unused") private static int calculateHeight(JTextArea textArea, int width) { Font font = textArea.getFont(); FontMetrics fontMetrics = textArea.getFontMetrics(font); int lineHeight = fontMetrics.getAscent() + fontMetrics.getDescent(); return lineHeight * lineCount(textArea, width); } - + public Dimension getPreferredScrollableViewportSize() { return getPreferredSize(); } @@ -293,10 +234,12 @@ public class LibraryListPanel extends JPanel implements Scrollable { int nextHeight = height + d.height; if (direction > 0) { + // scrolling down if (nextHeight > bottomOfScrollArea) { return nextHeight - bottomOfScrollArea; } } else { + // scrolling up if (nextHeight > visibleRect.y) { if (visibleRect.y != height) { return visibleRect.y - height; @@ -321,7 +264,7 @@ public class LibraryListPanel extends JPanel implements Scrollable { } public boolean getScrollableTracksViewportWidth() { - return false; + return true; } public void setPreferredViewPositionListener(PreferredViewPositionListener preferredViewPositionListener) { @@ -335,79 +278,110 @@ public class LibraryListPanel extends JPanel implements Scrollable { private static final int BUTTON_WIDTH = 100; - final String unclickedCardId = "unclicked"; - - final String clickedCardId = "clicked"; - final int topAndBottomBorder = 2; LibraryInfo libInfo; JPanel headerPanel; -// JTextArea briefText; - - JPanel infoPanel; - - JPanel unclickedCard; - - JPanel clickedCard; - - JTextArea description; + JProgressBar installProgressBar; + + JTextPane authorLabel; + + JTextPane descriptionText; JButton installOrRemove; boolean isInfoShown; + + String authorsWithLinks = ""; + + String authorsWithouthLinks = ""; + + private HyperlinkListener hyperlinkOpener; private LibraryPanel(LibraryInfo libInfo) { this.libInfo = libInfo; setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); - + + generateAuthorStrings(); + hyperlinkOpener = new HyperlinkListener() { + + public void hyperlinkUpdate(HyperlinkEvent e) { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + Base.openURL(e.getURL().toString()); + } + } + }; + setBorder(BorderFactory.createEmptyBorder(topAndBottomBorder, 2, topAndBottomBorder, 2)); - configureHeaderPane(); - configureInfoPane(); + addPaneComponents(); + addProgressBarAndButton(); setFocusable(true); setShowInfo(false); updateColors(); - updateLibraryListSizeAndPosition(0, true, null); - addMouseListener(new MouseAdapter() { - public void mouseClicked(MouseEvent e) { - - int obtainedSpace = 0; - boolean obtainedSpaceIsAbove = true; + MouseAdapter expandPanelMouseListener = new MouseAdapter() { + public void mousePressed(MouseEvent e) { for (Component c : LibraryListPanel.this.getComponents()) { - if (c == LibraryPanel.this) { - obtainedSpaceIsAbove = false; - } else if (c instanceof LibraryPanel) { + if (c instanceof LibraryPanel) { LibraryPanel lp = (LibraryPanel) c; if (lp.isInfoShown) { - obtainedSpace = lp.description.getSize().height; lp.setShowInfo(false); break; } } } - + setShowInfo(true); updateColors(); - updateLibraryListSizeAndPosition(obtainedSpace, obtainedSpaceIsAbove, - LibraryPanel.this); - requestFocusInWindow(); + getParent().requestFocusInWindow(); } - }); + }; + + addMouseListener(expandPanelMouseListener); + authorLabel.addMouseListener(expandPanelMouseListener); + descriptionText.addMouseListener(expandPanelMouseListener); } + private void generateAuthorStrings() { + if (!libInfo.authors.isEmpty()) { + authorsWithLinks = "by "; + authorsWithouthLinks = "by "; + + for (int i = 0; i < libInfo.authors.size(); i++) { + Author author = libInfo.authors.get(i); + authorsWithLinks += "" + author.name + ""; + authorsWithouthLinks += author.name; + if (i + 2 < libInfo.authors.size()) { + authorsWithLinks += ", "; + authorsWithouthLinks += ", "; + } else if (i + 2 == libInfo.authors.size()) { + if (libInfo.authors.size() > 2) { + authorsWithLinks += ", and "; + authorsWithouthLinks += ", and "; + } else { + authorsWithLinks += " and "; + authorsWithouthLinks += " and "; + } + } + } + + authorsWithLinks += ""; + authorsWithouthLinks += ""; + } + } + /** * Create the widgets for the header panel which is visible when the library * panel is not clicked */ - private void configureHeaderPane() { + private void addPaneComponents() { headerPanel = new JPanel(); headerPanel.setFocusable(true); headerPanel.setOpaque(false); @@ -416,65 +390,94 @@ public class LibraryListPanel extends JPanel implements Scrollable { GridBagConstraints c = new GridBagConstraints(); c.gridx = 0; c.gridy = 0; - c.weightx = 1; c.anchor = GridBagConstraints.WEST; JLabel nameLabel = new JLabel(libInfo.name); + Font font = nameLabel.getFont(); + font = font.deriveFont(font.getStyle() | Font.BOLD); + nameLabel.setFont(font); headerPanel.add(nameLabel, c); + c = new GridBagConstraints(); + c.gridx = 1; + c.gridy = 0; + c.weightx = 1; + c.fill = GridBagConstraints.HORIZONTAL; + c.anchor = GridBagConstraints.EAST; + authorLabel = new JTextPane(); + authorLabel.setContentType("text/html"); + authorLabel.setText(authorsWithouthLinks); + authorLabel.setHighlighter(null); + authorLabel.setOpaque(false); + authorLabel.setEditable(false); + authorLabel.addHyperlinkListener(hyperlinkOpener); + headerPanel.add(authorLabel, c); + +// c = new GridBagConstraints(); +// c.gridx = 2; +// c.gridy = 0; +// c.anchor = GridBagConstraints.NORTHEAST; +// JLabel categoryLabel = new JLabel(libInfo.categoryName + " "); +// font = categoryLabel.getFont(); +// font = font.deriveFont(font.getSize() * 0.7f); +// categoryLabel.setFont(font); +// headerPanel.add(categoryLabel, c); + c = new GridBagConstraints(); c.gridx = 0; c.gridy = 1; c.weightx = 1; - c.fill = GridBagConstraints.HORIZONTAL; + c.gridwidth = 3; + c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.WEST; -// briefText = new JTextArea(libInfo.brief); -// briefText.setHighlighter(null); -// briefText.setOpaque(false); -// briefText.setEditable(false); -// briefText.setLineWrap(true); -// briefText.setWrapStyleWord(true); -// Font font = briefText.getFont(); -// font = font.deriveFont(font.getSize() * 0.85f); -// briefText.setFont(font); -// headerPanel.add(briefText, c); - - c = new GridBagConstraints(); - c.gridx = 1; - c.gridy = 1; - c.anchor = GridBagConstraints.EAST; - headerPanel.add(Box.createRigidArea(new Dimension(BUTTON_WIDTH, 1)), c); + + descriptionText = new JTextPane(); + descriptionText.setContentType("text/html"); + descriptionText.setText("" + libInfo.description + ""); + descriptionText.setHighlighter(null); + descriptionText.setOpaque(false); + descriptionText.setEditable(false); + descriptionText.setMargin(new Insets(0, 0, 10, 5)); + descriptionText.addHyperlinkListener(hyperlinkOpener); + headerPanel.add(descriptionText, c); add(headerPanel); } - - /** - * Create the widgets for the info panel which is visible when the library - * panel is clicked - */ - private void configureInfoPane() { - infoPanel = new JPanel(); - infoPanel.setLayout(new BoxLayout(infoPanel, BoxLayout.Y_AXIS)); - infoPanel.setLayout(new CardLayout()); - - unclickedCard = new JPanel(); - clickedCard = new JPanel(); - - infoPanel.setOpaque(false); - unclickedCard.setOpaque(false); - clickedCard.setOpaque(false); - - infoPanel.setFocusable(true); - unclickedCard.setFocusable(true); - clickedCard.setFocusable(true); - - description = new JTextArea(libInfo.description); + + public void addProgressBarAndButton() { + GridBagConstraints c = new GridBagConstraints(); + c.gridx = 3; + c.gridy = 0; + c.weighty = 1; + c.gridheight = 2; + c.fill = GridBagConstraints.VERTICAL; + c.anchor = GridBagConstraints.NORTH; + JPanel rightPane = new JPanel(); + rightPane.setOpaque(false); + rightPane.setLayout(new BoxLayout(rightPane, BoxLayout.Y_AXIS)); + headerPanel.add(rightPane, c); + + installProgressBar = new JProgressBar(); + installProgressBar.setString(""); + installProgressBar.setStringPainted(true); + installProgressBar.setVisible(false); + Dimension d = installProgressBar.getPreferredSize(); + d.width = BUTTON_WIDTH; + installProgressBar.setPreferredSize(d); + installProgressBar.setMaximumSize(d); + installProgressBar.setMinimumSize(d); + installProgressBar.setOpaque(false); + rightPane.add(installProgressBar); + installProgressBar.setAlignmentX(CENTER_ALIGNMENT); + + rightPane.add(Box.createVerticalGlue()); + installOrRemove = new JButton(); if (libInfo.isInstalled) { } else { installOrRemove.setText("Install"); ActionListener installLibAction = new ActionListener() { - + public void actionPerformed(ActionEvent arg) { try { URL url = new URL(libInfo.link); @@ -490,85 +493,47 @@ public class LibraryListPanel extends JPanel implements Scrollable { }; installOrRemove.addActionListener(installLibAction); } - - clickedCard.setLayout(new BoxLayout(clickedCard, BoxLayout.X_AXIS)); - - description.setHighlighter(null); - description.setOpaque(false); - description.setEditable(false); - description.setLineWrap(true); - description.setWrapStyleWord(true); - Font font = this.description.getFont(); - font = font.deriveFont(font.getSize() * 0.9f); - description.setFont(font); - - clickedCard.add(description); - clickedCard.add(Box.createHorizontalGlue()); - clickedCard.add(installOrRemove); - - installOrRemove.setAlignmentY(Component.BOTTOM_ALIGNMENT); - description.setAlignmentY(Component.BOTTOM_ALIGNMENT); - Dimension installButtonDimensions = installOrRemove.getPreferredSize(); installButtonDimensions.width = BUTTON_WIDTH; installOrRemove.setPreferredSize(installButtonDimensions); - - infoPanel.add(unclickedCard, unclickedCardId); - infoPanel.add(clickedCard, clickedCardId); - - add(infoPanel); + installOrRemove.setMaximumSize(installButtonDimensions); + installOrRemove.setMinimumSize(installButtonDimensions); + installOrRemove.setOpaque(false); + rightPane.add(installOrRemove); + installOrRemove.setAlignmentX(CENTER_ALIGNMENT); + + // Set the minimum size of this pane to be the sum of the height of the + // progress bar and install button + d = installProgressBar.getPreferredSize(); + Dimension d2 = installOrRemove.getPreferredSize(); + d.width = BUTTON_WIDTH; + d.height = d.height+d2.height; + rightPane.setMinimumSize(d); + rightPane.setPreferredSize(d); } - /** - * Turns on/off the info panel which contains a brief description of the - * library and a button to Install/Remove a library. - */ public void setShowInfo(boolean doShow) { isInfoShown = doShow; - CardLayout cardLayout = (CardLayout) infoPanel.getLayout(); - - if (isInfoShown) { - cardLayout.show(infoPanel, clickedCardId); + installOrRemove.setVisible(doShow); + if (doShow) { + authorLabel.setText(authorsWithLinks); } else { - cardLayout.show(infoPanel, unclickedCardId); + authorLabel.setText(authorsWithouthLinks); } - - int width = getSize().width; - updateSize(width > 0 ? width : 100); } /** * Updates the sizes of components in this panel given width as a constraint */ public void updateSize(int width) { - if (isInfoShown) { - Dimension textDimentions = description.getPreferredSize(); - textDimentions.width = width - installOrRemove.getPreferredSize().width; - textDimentions.height = calculateHeight(description, textDimentions.width); - - description.setMaximumSize(textDimentions); - description.setMinimumSize(textDimentions); - description.setPreferredSize(textDimentions); - description.setSize(textDimentions); - } - - Dimension d; - if (isInfoShown) { - d = headerPanel.getPreferredSize(); - d.width = width; - d.height += clickedCard.getPreferredSize().height + 2 - * topAndBottomBorder; - } else { - d = headerPanel.getPreferredSize(); - d.width = width; - d.height += 2 * topAndBottomBorder; - } + Dimension d = headerPanel.getPreferredSize(); + d.width = width; setMaximumSize(d); setMinimumSize(d); setPreferredSize(d); setSize(d); - + revalidate(); } diff --git a/app/src/processing/app/LibraryListing.java b/app/src/processing/app/LibraryListing.java index e2a05801c..ea3afe4f1 100644 --- a/app/src/processing/app/LibraryListing.java +++ b/app/src/processing/app/LibraryListing.java @@ -93,7 +93,7 @@ public class LibraryListing { } private boolean matches(LibraryInfo libInfo, String filter) { - filter = "\\p{Alnum}*" + filter.toLowerCase() + "\\p{Alnum}*"; + filter = ".*" + filter.toLowerCase() + ".*"; if (filter.isEmpty()) { return true; @@ -104,9 +104,11 @@ public class LibraryListing { return true; } } - return libInfo.name.toLowerCase().matches(filter) + + return libInfo.description.toLowerCase().matches(filter) + || libInfo.categoryName.toLowerCase().matches(filter) || libInfo.name.toLowerCase().matches(filter); - + } public static class LibraryListFetcher { @@ -175,7 +177,7 @@ public class LibraryListing { public String versionId; public String link; - boolean isInstalled; + boolean isInstalled = false; public String brief; @@ -271,9 +273,15 @@ public class LibraryListing { currentLibInfo.authors.add(currentAuthor); currentAuthor = null; doingAuthor = false; + } else if (doingDescription) { - currentLibInfo.description = new String(ch, start, length).trim(); - doingDescription = false; + String str = new String(ch, start, length); + if (currentLibInfo.description == null) { + currentLibInfo.description = str; + } else { + currentLibInfo.description += str; + } + } } @@ -291,6 +299,9 @@ public class LibraryListing { } currentLibInfo = null; + } else if ("description".equals(qName)) { + currentLibInfo.description = currentLibInfo.description.trim(); + doingDescription = false; } } diff --git a/app/src/processing/app/LibraryManager.java b/app/src/processing/app/LibraryManager.java index 555430f2a..8dc2e77a9 100644 --- a/app/src/processing/app/LibraryManager.java +++ b/app/src/processing/app/LibraryManager.java @@ -269,15 +269,6 @@ public class LibraryManager { pane.add(scrollPane, c); scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); - scrollPane.getViewport().addChangeListener(new ChangeListener() { - - public void stateChanged(ChangeEvent ce) { - - int width = scrollPane.getViewportBorderBounds().width; - libraryListPane.setWidth(width); - } - }); - libraryListPane.setPreferredViewPositionListener(new PreferredViewPositionListener() { public void handlePreferredLocation(Point p) { @@ -314,19 +305,7 @@ public class LibraryManager { } }); - c = new GridBagConstraints(); - c.fill = GridBagConstraints.HORIZONTAL; - c.gridx = 0; - c.gridy = 3; - c.weightx = 1; - c.gridwidth = 2; - installProgressBar = new JProgressBar(); - installProgressBar.setString(""); - installProgressBar.setStringPainted(true); - installProgressBar.setVisible(false); - pane.add(installProgressBar, c); - - dialog.setMinimumSize(new Dimension(400, 400)); + dialog.setMinimumSize(new Dimension(650, 400)); } private void registerDisposeListeners() { @@ -690,7 +669,11 @@ public class LibraryManager { FileDownloader fileDownloader; public LibraryInstaller(FileDownloader downloader, ProgressMonitor pm) { - progressMonitor = pm; + if (pm == null) { + progressMonitor = new NullProgressMonitor(); + } else { + progressMonitor = pm; + } fileDownloader = downloader; }