/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* Part of the Processing project - http://processing.org Copyright (c) 2012-15 The Processing Foundation Copyright (c) 2011-12 Ben Fry and Casey Reas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package processing.app.contrib; import java.awt.Color; import java.awt.Cursor; import java.awt.Dimension; import java.awt.event.*; import java.util.*; import javax.swing.*; import javax.swing.event.*; import processing.app.*; import processing.app.ui.Editor; import processing.app.ui.Toolkit; public class ContributionTab extends JPanel { static final String ANY_CATEGORY = Language.text("contrib.all"); static final int FILTER_WIDTH = Toolkit.zoom(180); ContributionType contribType; ManagerFrame contribDialog; Contribution.Filter filter; JComboBox<String> categoryChooser; ListPanel contributionListPanel; StatusPanel statusPanel; FilterField filterField; // TODO: remove or initialize restartButton //JButton restartButton; JLabel categoryLabel; JLabel loaderLabel; JPanel errorPanel; JTextPane errorMessage; JButton tryAgainButton; JButton closeButton; // the calling editor, so updates can be applied Editor editor; String category; ContributionListing contribListing; JProgressBar progressBar; public ContributionTab() { } public ContributionTab(ManagerFrame dialog, ContributionType type) { this.contribDialog = dialog; this.contribType = type; filter = new Contribution.Filter() { public boolean matches(Contribution contrib) { return contrib.getType() == contribType; } }; contribListing = ContributionListing.getInstance(); statusPanel = new StatusPanel(this, 650); contributionListPanel = new ListPanel(this, filter); contribListing.addListener(contributionListPanel); } // public boolean hasUpdates(Base base) { // return contribListing.hasUpdates(base); // } public void showFrame(final Editor editor, boolean error, boolean loading) { this.editor = editor; setLayout(error, loading); contributionListPanel.setVisible(!loading); loaderLabel.setVisible(loading); errorPanel.setVisible(error); validate(); repaint(); } protected void setLayout(boolean activateErrorPanel, boolean isLoading) { if (progressBar == null) { progressBar = new JProgressBar(); progressBar.setVisible(false); createComponents(); buildErrorPanel(); loaderLabel = new JLabel(Toolkit.getLibIcon("manager/loader.gif")); loaderLabel.setOpaque(false); loaderLabel.setBackground(Color.WHITE); } int scrollBarWidth = contributionListPanel.scrollPane.getVerticalScrollBar().getPreferredSize().width; GroupLayout layout = new GroupLayout(this); setLayout(layout); // layout.setAutoCreateContainerGaps(true); // layout.setAutoCreateGaps(true); layout.setHorizontalGroup(layout .createParallelGroup(GroupLayout.Alignment.CENTER) .addGroup(layout .createSequentialGroup() .addGap(ManagerFrame.STATUS_WIDTH) .addComponent(filterField, FILTER_WIDTH, FILTER_WIDTH, FILTER_WIDTH) // .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) .addComponent(categoryChooser, ManagerFrame.AUTHOR_WIDTH, ManagerFrame.AUTHOR_WIDTH, ManagerFrame.AUTHOR_WIDTH) .addGap(scrollBarWidth)).addComponent(loaderLabel) .addComponent(contributionListPanel).addComponent(errorPanel) .addComponent(statusPanel)); layout.setVerticalGroup(layout .createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER) .addComponent(categoryChooser) .addComponent(filterField)) .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER) .addComponent(loaderLabel) .addComponent(contributionListPanel)) .addComponent(errorPanel) .addComponent(statusPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)); layout.linkSize(SwingConstants.VERTICAL, categoryChooser, filterField); // these will occupy space even if not visible layout.setHonorsVisibility(contributionListPanel, false); layout.setHonorsVisibility(categoryChooser, false); setBackground(Color.WHITE); setBorder(null); } private void createComponents() { categoryLabel = new JLabel(Language.text("contrib.category")); categoryChooser = new JComboBox<String>(); categoryChooser.setMaximumRowCount(20); categoryChooser.setFont(ManagerFrame.NORMAL_PLAIN); updateCategoryChooser(); categoryChooser.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { category = (String) categoryChooser.getSelectedItem(); if (ManagerFrame.ANY_CATEGORY.equals(category)) { category = null; } filterLibraries(category, filterField.filters); contributionListPanel.updateColors(); } }); filterField = new FilterField(); // TODO: initialize restartButton, whatever it is // restartButton = ??? } protected void buildErrorPanel() { errorPanel = new JPanel(); GroupLayout layout = new GroupLayout(errorPanel); layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(true); errorPanel.setLayout(layout); errorMessage = new JTextPane(); errorMessage.setEditable(false); errorMessage.setContentType("text/html"); errorMessage.setText("<html><body><center>Could not connect to the Processing server.<br>" + "Contributions cannot be installed or updated without an Internet connection.<br>" + "Please verify your network connection again, then try connecting again.</center></body></html>"); DetailPanel.setTextStyle(errorMessage, "1em"); Dimension dim = new Dimension(550, 60); errorMessage.setMaximumSize(dim); errorMessage.setMinimumSize(dim); errorMessage.setOpaque(false); /* StyledDocument doc = errorMessage.getStyledDocument(); SimpleAttributeSet center = new SimpleAttributeSet(); StyleConstants.setAlignment(center, StyleConstants.ALIGN_CENTER); doc.setParagraphAttributes(0, doc.getLength(), center, false); */ closeButton = Toolkit.createIconButton("manager/close"); closeButton.setContentAreaFilled(false); closeButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { contribDialog.makeAndShowTab(false, false); } }); tryAgainButton = new JButton("Try Again"); tryAgainButton.setFont(ManagerFrame.NORMAL_PLAIN); tryAgainButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { contribDialog.makeAndShowTab(false, true); contribDialog.downloadAndUpdateContributionListing(editor.getBase()); } }); layout.setHorizontalGroup(layout.createSequentialGroup() .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) .addGroup(layout .createParallelGroup(GroupLayout.Alignment.CENTER) .addComponent(errorMessage) .addComponent(tryAgainButton, StatusPanel.BUTTON_WIDTH, StatusPanel.BUTTON_WIDTH, StatusPanel.BUTTON_WIDTH)) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) .addComponent(closeButton)); layout.setVerticalGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup().addComponent(errorMessage) .addComponent(closeButton)).addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED).addComponent(tryAgainButton)); errorPanel.setBackground(Color.PINK); errorPanel.validate(); } protected void updateCategoryChooser() { if (categoryChooser != null) { ArrayList<String> categories; categoryChooser.removeAllItems(); categories = new ArrayList<String>(contribListing.getCategories(filter)); // for (int i = 0; i < categories.size(); i++) { // System.out.println(i + " category: " + categories.get(i)); // } Collections.sort(categories); // categories.add(0, ContributionManagerDialog.ANY_CATEGORY); boolean categoriesFound = false; categoryChooser.addItem(ManagerFrame.ANY_CATEGORY); for (String s : categories) { categoryChooser.addItem(s); if (!s.equals(Contribution.UNKNOWN_CATEGORY)) { categoriesFound = true; } } categoryChooser.setVisible(categoriesFound); } } protected void filterLibraries(String category, List<String> filters) { List<Contribution> filteredLibraries = contribListing.getFilteredLibraryList(category, filters); contributionListPanel.filterLibraries(filteredLibraries); } protected void updateContributionListing() { if (editor != null) { List<Contribution> contributions = new ArrayList<Contribution>(); List<Library> libraries = new ArrayList<Library>(editor.getMode().contribLibraries); // Only add core libraries that are installed in the sketchbook // https://github.com/processing/processing/issues/3688 //libraries.addAll(editor.getMode().coreLibraries); final String sketchbookPath = Base.getSketchbookLibrariesFolder().getAbsolutePath(); for (Library lib : editor.getMode().coreLibraries) { if (lib.getLibraryPath().startsWith(sketchbookPath)) { libraries.add(lib); } } contributions.addAll(libraries); Base base = editor.getBase(); List<ToolContribution> tools = base.getToolContribs(); contributions.addAll(tools); List<ModeContribution> modes = base.getModeContribs(); contributions.addAll(modes); List<ExamplesContribution> examples = base.getExampleContribs(); contributions.addAll(examples); // ArrayList<LibraryCompilation> compilations = LibraryCompilation.list(libraries); // // // Remove libraries from the list that are part of a compilations // for (LibraryCompilation compilation : compilations) { // Iterator<Library> it = libraries.iterator(); // while (it.hasNext()) { // Library current = it.next(); // if (compilation.getFolder().equals(current.getFolder().getParentFile())) { // it.remove(); // } // } // } contribListing.updateInstalledList(contributions); } } protected void setFilterText(String filter) { if (filter == null || filter.isEmpty()) { filterField.setText(""); } else { filterField.setText(filter); } filterField.applyFilter(); } class FilterField extends JTextField { List<String> filters; public FilterField () { super(""); JLabel filterLabel = new JLabel("Filter"); filterLabel.setFont(ManagerFrame.NORMAL_PLAIN); filterLabel.setOpaque(false); setFont(ManagerFrame.NORMAL_PLAIN); filterLabel.setIcon(Toolkit.getLibIconX("manager/search")); JButton removeFilter = Toolkit.createIconButton("manager/remove"); removeFilter.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 2)); removeFilter.setBorderPainted(false); removeFilter.setContentAreaFilled(false); removeFilter.setCursor(Cursor.getDefaultCursor()); removeFilter.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { setText(""); filterField.requestFocusInWindow(); } }); //searchIcon = new ImageIcon(java.awt.Toolkit.getDefaultToolkit().getImage("NSImage://NSComputerTemplate")); setOpaque(false); GroupLayout fl = new GroupLayout(this); setLayout(fl); fl.setHorizontalGroup(fl .createSequentialGroup() .addComponent(filterLabel) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) .addComponent(removeFilter)); fl.setVerticalGroup(fl.createSequentialGroup() .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) .addGroup(fl.createParallelGroup() .addComponent(filterLabel) .addComponent(removeFilter)) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)); removeFilter.setVisible(false); filters = new ArrayList<String>(); addFocusListener(new FocusListener() { public void focusLost(FocusEvent focusEvent) { if (getText().isEmpty()) { filterLabel.setVisible(true); } } public void focusGained(FocusEvent focusEvent) { filterLabel.setVisible(false); } }); getDocument().addDocumentListener(new DocumentListener() { public void removeUpdate(DocumentEvent e) { removeFilter.setVisible(!getText().isEmpty()); applyFilter(); } public void insertUpdate(DocumentEvent e) { removeFilter.setVisible(!getText().isEmpty()); applyFilter(); } public void changedUpdate(DocumentEvent e) { removeFilter.setVisible(!getText().isEmpty()); applyFilter(); } }); } public void applyFilter() { String filter = getText(); filter = filter.toLowerCase(); // Replace anything but 0-9, a-z, or : with a space filter = filter.replaceAll("[^\\x30-\\x39^\\x61-\\x7a^\\x3a]", " "); filters = Arrays.asList(filter.split(" ")); filterLibraries(category, filters); contributionListPanel.updateColors(); } } // public boolean hasAlreadyBeenOpened() { // return panel != null; // } public void updateStatusPanel(DetailPanel contributionPanel) { statusPanel.update(contributionPanel); } protected void updateAll() { Collection<DetailPanel> collection = contributionListPanel.panelByContribution.values(); for (DetailPanel detailPanel : collection) { detailPanel.update(); // Refreshing the ContributionUpdateTab's status icons contributionListPanel.updatePanelOrdering(contributionListPanel .panelByContribution.keySet()); } } protected boolean hasUpdates() { return contributionListPanel.getRowCount() > 0; } }