public ContributionTab(ContributionType type, ContributionManagerDialog dialog) {
    this.contribType = type;
    this.contribDialog = dialog;

    /*
    if (type == ContributionType.MODE) {
      title = Language.text("contrib.manager_title.mode");
    } else if (type == ContributionType.TOOL) {
      title = Language.text("contrib.manager_title.tool");
    } else if (type == ContributionType.LIBRARY) {
      title = Language.text("contrib.manager_title.library");
    } else if (type == ContributionType.EXAMPLES) {
      title = Language.text("contrib.manager_title.examples");
    }
    */

    filter =
        new Contribution.Filter() {
          public boolean matches(Contribution contrib) {
            return contrib.getType() == contribType;
          }
        };

    contribListing = ContributionListing.getInstance();
    statusPanel = new StatusPanel(650, this);
    contributionListPanel = new ListPanel(this, filter);
    contribListing.addListener(contributionListPanel);
  }
  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 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(ContributionManagerDialog.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);
 }
/**
 * This class is the main Contribution Manager Dialog. It contains all the contributions tab and the
 * update tab.
 */
public class ContributionManagerDialog {
  static final String ANY_CATEGORY = Language.text("contrib.all");
  static final int TAB_WIDTH = 100;
  static final int TAB_HEIGHT = 34;
  static final int AUTHOR_WIDTH = 240;
  static final int STATUS_WIDTH = 66;
  static final int FILTER_WIDTH = 180;
  static final int ICON_WIDTH = 50;

  static final String title = "Manager";

  JFrame dialog;
  JTabbedPane tabbedPane;
  JButton restartButton;

  // the calling editor, so updates can be applied
  Editor editor;

  ContributionTab toolsContributionTab;
  ContributionTab librariesContributionTab;
  ContributionTab examplesContributionTab;
  ContributionTab modesContributionTab;
  UpdateContributionTab updatesContributionTab;
  JLabel numberLabel;

  ContributionListing contributionListing = ContributionListing.getInstance();

  private JLabel[] tabLabels;
  private JPanel updateTabPanel;
  private JLabel updateTabLabel;

  static Font font;

  public ContributionManagerDialog() {
    font = Toolkit.getSansFont(14, Font.PLAIN);
    numberLabel = new JLabel(Toolkit.getLibIconX("manager/notification"), SwingConstants.CENTER);
    librariesContributionTab = new ContributionTab(ContributionType.LIBRARY, this);
    modesContributionTab = new ContributionTab(ContributionType.MODE, this);
    toolsContributionTab = new ContributionTab(ContributionType.TOOL, this);
    examplesContributionTab = new ContributionTab(ContributionType.EXAMPLES, this);
    updatesContributionTab = new UpdateContributionTab(null, this);
  }

  /*
  public boolean hasUpdates(Base base) {
    return librariesContributionTab.hasUpdates(base)
      || modesContributionTab.hasUpdates(base)
      || toolsContributionTab.hasUpdates(base)
      || examplesContributionTab.hasUpdates(base);
  }
   */

  public void showFrame(Editor editor, ContributionType contributionType) {
    this.editor = editor;

    // Calculating index to switch to the required tab
    int index = getIndex(contributionType);
    if (dialog == null) {
      makeFrame(editor);
      // done before as downloadAndUpdateContributionListing()
      // requires the current selected tab
      tabbedPane.setSelectedIndex(index);
      downloadAndUpdateContributionListing(editor.getBase());
      if (index != 4) {
        Component selected = tabbedPane.getTabComponentAt(tabbedPane.getSelectedIndex());
        selected.setBackground(new Color(0xe0fffd));
        selected.setForeground(Color.BLACK);
      } else {
        updateTabPanel.setBackground(new Color(0xe0fffd));
        updateTabLabel.setForeground(Color.BLACK);
      }
    }
    tabbedPane.setSelectedIndex(index);
    dialog.setVisible(true);
  }

  int getIndex(ContributionType contributionType) {
    int index;
    if (contributionType == ContributionType.LIBRARY) {
      index = 0;
    } else if (contributionType == ContributionType.MODE) {
      index = 1;
    } else if (contributionType == ContributionType.TOOL) {
      index = 2;
    } else if (contributionType == ContributionType.EXAMPLES) {
      index = 3;
    } else {
      index = 4;
    }
    return index;
  }

  ContributionTab getTab(ContributionType contributionType) {
    if (contributionType == ContributionType.LIBRARY) {
      return librariesContributionTab;
    } else if (contributionType == ContributionType.MODE) {
      return modesContributionTab;
    } else if (contributionType == ContributionType.TOOL) {
      return toolsContributionTab;
    } else if (contributionType == ContributionType.EXAMPLES) {
      return examplesContributionTab;
    }
    return updatesContributionTab;
  }

  private void makeFrame(final Editor editor) {
    dialog = new JFrame(title);
    dialog.setMinimumSize(new Dimension(750, 500));
    tabbedPane = new JTabbedPane();

    makeAndShowTab(false, true);

    tabbedPane.addTab("Libraries", null, librariesContributionTab.panel, "Libraries");
    tabbedPane.setMnemonicAt(0, KeyEvent.VK_1);

    tabbedPane.addTab("Modes", null, modesContributionTab.panel, "Modes");
    tabbedPane.setMnemonicAt(1, KeyEvent.VK_2);

    tabbedPane.addTab("Tools", null, toolsContributionTab.panel, "Tools");
    tabbedPane.setMnemonicAt(2, KeyEvent.VK_3);

    tabbedPane.addTab("Examples", null, examplesContributionTab.panel, "Examples");
    tabbedPane.setMnemonicAt(3, KeyEvent.VK_4);

    tabbedPane.addTab("Updates", null, updatesContributionTab.panel, "Updates");
    tabbedPane.setMnemonicAt(4, KeyEvent.VK_5);

    tabbedPane.setUI(new SpacedTabbedPaneUI());
    tabbedPane.setBackground(new Color(0x132638));
    tabbedPane.setOpaque(true);

    for (int i = 0; i < 5; i++) {
      tabbedPane.setToolTipTextAt(i, null);
    }

    makeAndSetTabComponents();

    tabbedPane.addChangeListener(
        new ChangeListener() {

          @Override
          public void stateChanged(ChangeEvent e) {
            for (int i = 0; i < 4; i++) {
              tabLabels[i].setBackground(new Color(0x2d4251));
              tabLabels[i].setForeground(Color.WHITE);
            }
            updateTabPanel.setBackground(new Color(0x2d4251));
            updateTabLabel.setForeground(Color.WHITE);
            int currentIndex = tabbedPane.getSelectedIndex();
            if (currentIndex != 4) {
              tabbedPane
                  .getTabComponentAt(tabbedPane.getSelectedIndex())
                  .setBackground(new Color(0xe0fffd));
              tabbedPane
                  .getTabComponentAt(tabbedPane.getSelectedIndex())
                  .setForeground(Color.BLACK);
            } else {
              updateTabPanel.setBackground(new Color(0xe0fffd));
              updateTabLabel.setForeground(Color.BLACK);
            }
            getActiveTab().contributionListPanel.scrollPane.requestFocusInWindow();
            //        // When the tab is changed update status to the current selected panel
            //        ContributionPanel currentPanel = getActiveTab().contributionListPanel
            //          .getSelectedPanel();
            //        if (currentPanel != null) {
            //          getActiveTab().contributionListPanel.setSelectedPanel(currentPanel);
            //        }
          }
        });

    //    tabbedPane.setSize(450, 400);
    setLayout();

    restartButton = new JButton(Language.text("contrib.restart"));
    restartButton.setVisible(false);
    restartButton.addActionListener(
        new ActionListener() {

          @Override
          public void actionPerformed(ActionEvent arg0) {

            Iterator<Editor> iter = editor.getBase().getEditors().iterator();
            while (iter.hasNext()) {
              Editor ed = iter.next();
              if (ed.getSketch().isModified()) {
                int option =
                    Messages.showYesNoQuestion(
                        editor,
                        title,
                        Language.text("contrib.unsaved_changes"),
                        Language.text("contrib.unsaved_changes.prompt"));

                if (option == JOptionPane.NO_OPTION) return;
                else break;
              }
            }

            // Thanks to http://stackoverflow.com/a/4160543
            StringBuilder cmd = new StringBuilder();
            cmd.append(
                System.getProperty("java.home")
                    + File.separator
                    + "bin"
                    + File.separator
                    + "java ");
            for (String jvmArg : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
              cmd.append(jvmArg + " ");
            }
            cmd.append("-cp ")
                .append(ManagementFactory.getRuntimeMXBean().getClassPath())
                .append(" ");
            cmd.append(Base.class.getName());

            try {
              Runtime.getRuntime().exec(cmd.toString());
              System.exit(0);
            } catch (IOException e) {
              e.printStackTrace();
            }
          }
        });

    Toolkit.setIcon(dialog);
    registerDisposeListeners();

    dialog.pack();
    dialog.setLocationRelativeTo(null);
  }

  private void makeAndSetTabComponents() {
    final String[] tabTitles = {"Libraries", "Modes", "Tools", "Examples", "Updates"};
    tabLabels = new JLabel[4];

    for (int i = 0; i < 4; i++) {
      final int temp = i;
      tabLabels[i] =
          new JLabel(tabTitles[i]) {
            @Override
            protected void paintComponent(Graphics g) {
              g.setClip(
                  Toolkit.createRoundRect(
                      0, 0, getWidth(), getHeight(), temp == 0 ? 6 : 0, temp == 3 ? 6 : 0, 0, 0));
              super.paintComponent(g);
            }
          };
      tabLabels[i].setForeground(Color.WHITE);
      tabLabels[i].setBackground(new Color(0x2d4251));
      tabLabels[i].setOpaque(true);
      tabLabels[i].setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));
      tabLabels[i].setPreferredSize(new Dimension(TAB_WIDTH, TAB_HEIGHT));
      tabLabels[i].setHorizontalAlignment(SwingConstants.CENTER);
      tabLabels[i].setFont(Toolkit.getSansFont(14, Font.BOLD));
      tabbedPane.setTabComponentAt(i, tabLabels[i]);
    }

    updateTabPanel =
        new JPanel() {
          @Override
          protected void paintComponent(Graphics g) {
            g.setClip(Toolkit.createRoundRect(0, 0, getWidth(), getHeight(), 6, 6, 0, 0));
            super.paintComponent(g);
          }
        };
    ;
    updateTabLabel = new JLabel("Updates");
    updateTabLabel.setFont(Toolkit.getSansFont(14, Font.BOLD));
    numberLabel.setVerticalTextPosition(SwingConstants.CENTER);
    numberLabel.setHorizontalTextPosition(SwingConstants.CENTER);
    numberLabel.setFont(Toolkit.getSansFont(14, Font.BOLD));
    numberLabel.setForeground(Color.WHITE);
    updateTabPanel.setOpaque(true);
    updateTabPanel.setBackground(new Color(0x2d4251));
    updateTabLabel.setForeground(Color.WHITE);
    updateTabPanel.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));
    updateTabPanel.setPreferredSize(new Dimension(TAB_WIDTH, TAB_HEIGHT));
    tabbedPane.setTabComponentAt(4, updateTabPanel);

    GroupLayout tabLayout = new GroupLayout(updateTabPanel);
    tabLayout.setAutoCreateGaps(true);
    updateTabPanel.setLayout(tabLayout);
    tabLayout.setHorizontalGroup(
        tabLayout
            .createSequentialGroup()
            .addPreferredGap(
                LayoutStyle.ComponentPlacement.RELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)
            .addComponent(updateTabLabel)
            .addComponent(numberLabel)
            .addPreferredGap(
                LayoutStyle.ComponentPlacement.RELATED,
                GroupLayout.PREFERRED_SIZE,
                Short.MAX_VALUE));
    tabLayout.setVerticalGroup(
        tabLayout
            .createParallelGroup(GroupLayout.Alignment.CENTER)
            .addComponent(numberLabel)
            .addComponent(updateTabLabel));

    numberLabel.setVisible(false);
  }

  // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

  public class SpacedTabbedPaneUI extends BasicTabbedPaneUI {

    @Override
    protected void installDefaults() {
      UIManager.put("TabbedPane.selected", Color.BLACK);
      UIManager.put("TabbedPane.tabsOverlapBorder", true);
      super.installDefaults();
      tabInsets = new Insets(0, 0, 0, 0);
      contentBorderInsets = new Insets(0, 0, 0, 0);
      tabAreaInsets = new Insets(0, 0, 0, 0);
      selectedTabPadInsets = new Insets(0, 0, 0, 0);
    }

    @Override
    protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) {
      return 1;
    }

    @Override
    protected void paintTabBackground(
        Graphics g,
        int tabPlacement,
        int tabIndex,
        int x,
        int y,
        int w,
        int h,
        boolean isSelected) {}

    @Override
    protected void paintTabBorder(
        Graphics g,
        int tabPlacement,
        int tabIndex,
        int x,
        int y,
        int w,
        int h,
        boolean isSelected) {}

    @Override
    protected void paintFocusIndicator(
        Graphics g,
        int tabPlacement,
        Rectangle[] rects,
        int tabIndex,
        Rectangle iconRect,
        Rectangle textRect,
        boolean isSelected) {}

    @Override
    protected LayoutManager createLayoutManager() {
      return new BasicTabbedPaneUI.TabbedPaneLayout() {

        /*
        @Override
        public void addLayoutComponent(String name, Component comp) {
          super.addLayoutComponent(name, comp);
        }
        */

        @Override
        protected void calculateTabRects(int tabPlacement, int tabCount) {
          super.calculateTabRects(tabPlacement, tabCount);
          rects[0].x -= 2;
          rects[1].x -= 1;
          rects[2].x -= 1;
          rects[3].x -= 1;
          rects[4].x = tabbedPane.getWidth() - rects[4].width + 1;
        }
      };
    }
  }

  // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

  private void setLayout() {
    GroupLayout layout = new GroupLayout(dialog.getContentPane());
    dialog.getContentPane().setLayout(layout);
    dialog.setResizable(true);
    layout.setAutoCreateContainerGaps(true);
    layout.setHorizontalGroup(layout.createParallelGroup().addComponent(tabbedPane));
    layout.setVerticalGroup(layout.createParallelGroup().addComponent(tabbedPane));
    layout.setHonorsVisibility(tabbedPane, true);
    // TODO set color here
    dialog.getContentPane().setBackground(new Color(0x132638));
    dialog.validate();
    dialog.repaint();
  }

  /** Close the window after an OK or Cancel. */
  protected void disposeFrame() {
    dialog.dispose();
    editor = null;
  }

  private void registerDisposeListeners() {
    dialog.addWindowListener(
        new WindowAdapter() {
          public void windowClosing(WindowEvent e) {
            disposeFrame();
          }
        });
    // handle window closing commands for ctrl/cmd-W or hitting ESC.
    Toolkit.registerWindowCloseKeys(
        dialog.getRootPane(),
        new ActionListener() {
          public void actionPerformed(ActionEvent actionEvent) {
            disposeFrame();
          }
        });

    dialog
        .getContentPane()
        .addKeyListener(
            new KeyAdapter() {
              public void keyPressed(KeyEvent e) {
                // System.out.println(e);
                KeyStroke wc = Toolkit.WINDOW_CLOSE_KEYSTROKE;
                if ((e.getKeyCode() == KeyEvent.VK_ESCAPE)
                    || (KeyStroke.getKeyStrokeForEvent(e).equals(wc))) {
                  disposeFrame();
                }
              }
            });
  }

  void downloadAndUpdateContributionListing(Base base) {
    // activeTab is required now but should be removed
    // as there is only one instance of contribListing and it should be present in this class
    final ContributionTab activeTab = getActiveTab();

    ContribProgressMonitor progress =
        new ContribProgressBar(activeTab.progressBar) {

          @Override
          public void startTask(String name, int maxValue) {
            super.startTask(name, maxValue);
            progressBar.setVisible(true);
            progressBar.setString(null);
          }

          @Override
          public void setProgress(int value) {
            super.setProgress(value);
            //        int percent = 100 * value / this.max;
            progressBar.setValue(value);
          }

          @Override
          public void finishedAction() {
            progressBar.setVisible(false);
            activeTab.updateContributionListing();
            activeTab.updateCategoryChooser();

            if (error) {
              exception.printStackTrace();
              makeAndShowTab(true, false);
            } else {
              makeAndShowTab(false, false);
            }
          }
        };
    activeTab.contribListing.downloadAvailableList(base, progress);
  }

  void makeAndShowTab(boolean activateErrorPanel, boolean isLoading) {
    librariesContributionTab.showFrame(editor, activateErrorPanel, isLoading);
    modesContributionTab.showFrame(editor, activateErrorPanel, isLoading);
    toolsContributionTab.showFrame(editor, activateErrorPanel, isLoading);
    examplesContributionTab.showFrame(editor, activateErrorPanel, isLoading);
    updatesContributionTab.showFrame(editor, activateErrorPanel, isLoading);
  }

  ContributionTab getActiveTab() {
    switch (tabbedPane.getSelectedIndex()) {
      case 0:
        return librariesContributionTab;
      case 1:
        return modesContributionTab;
      case 2:
        return toolsContributionTab;
      case 3:
        return examplesContributionTab;
      default:
        return updatesContributionTab;
    }
  }
}