/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE 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. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ class ModulesPanel : public Component, private ListBoxModel, private ValueTree::Listener, private Button::Listener { public: ModulesPanel (Project& p) : project (p), modulesValueTree (p.getModules().state), header ("Modules", Icon (getIcons().modules, Colours::transparentBlack)), setCopyModeButton ("Set copy-mode for all modules..."), copyPathButton ("Set paths for all modules...") { listHeader = new ListBoxHeader ( { "Module", "Version", "Make Local Copy", "Paths" }, { 0.25f, 0.2f, 0.2f, 0.35f } ); list.setHeaderComponent (listHeader); list.setModel (this); list.setColour (ListBox::backgroundColourId, Colours::transparentBlack); addAndMakeVisible (list); list.updateContent(); list.setRowHeight (30); list.setMultipleSelectionEnabled (true); addAndMakeVisible (header); addAndMakeVisible (setCopyModeButton); addAndMakeVisible (copyPathButton); setCopyModeButton.addListener (this); setCopyModeButton.setTriggeredOnMouseDown (true); copyPathButton.addListener (this); copyPathButton.setTriggeredOnMouseDown (true); modulesValueTree.addListener (this); lookAndFeelChanged(); } void paint (Graphics& g) override { g.setColour (findColour (secondaryBackgroundColourId)); g.fillRect (getLocalBounds().reduced (12, 0)); } void resized() override { auto bounds = getLocalBounds().reduced (12, 0); header.setBounds (bounds.removeFromTop (40)); bounds.reduce (10, 0); list.setBounds (bounds.removeFromTop (list.getRowPosition (getNumRows() - 1, true).getBottom() + 20)); auto buttonRow = bounds.removeFromTop (35); setCopyModeButton.setBounds (buttonRow.removeFromLeft (jmin (200, bounds.getWidth() / 3))); buttonRow.removeFromLeft (8); copyPathButton.setBounds (buttonRow.removeFromLeft (jmin (200, bounds.getWidth() / 3))); } void parentSizeChanged() override { setSize (jmax (550, getParentWidth()), getParentHeight()); } int getNumRows() override { return project.getModules().getNumModules(); } void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected) override { ignoreUnused (height); auto bounds = Rectangle (0, 0, width, height); g.setColour (rowIsSelected ? findColour (defaultHighlightColourId) : findColour (rowNumber % 2 == 0 ? widgetBackgroundColourId : secondaryWidgetBackgroundColourId)); g.fillRect (bounds.withTrimmedBottom (1)); bounds.removeFromLeft (5); g.setColour (rowIsSelected ? findColour (defaultHighlightedTextColourId) : findColour (widgetTextColourId)); //====================================================================== const auto moduleID = project.getModules().getModuleID (rowNumber); g.drawFittedText (moduleID, bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (0) * width)), Justification::centredLeft, 1); //====================================================================== auto version = project.getModules().getModuleInfo (moduleID).getVersion(); if (version.isEmpty()) version = "?"; g.drawFittedText (version, bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (1) * width)), Justification::centredLeft, 1); //====================================================================== const auto copyLocally = project.getModules().shouldCopyModuleFilesLocally (moduleID).getValue() ? "Yes" : "No"; g.drawFittedText (copyLocally, bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (2) * width)), Justification::centredLeft, 1); //====================================================================== StringArray paths; for (Project::ExporterIterator exporter (project); exporter.next();) paths.addIfNotAlreadyThere (exporter->getPathForModuleString (moduleID).trim()); const auto pathText = paths.joinIntoString (", "); g.drawFittedText (pathText, bounds.removeFromLeft (roundToInt (listHeader->getProportionAtIndex (3) * width)), Justification::centredLeft, 1); } void listBoxItemDoubleClicked (int row, const MouseEvent&) override { const String moduleID (project.getModules().getModuleID (row)); if (moduleID.isNotEmpty()) if (ProjectContentComponent* pcc = findParentComponentOfClass()) pcc->showModule (moduleID); } void deleteKeyPressed (int row) override { project.getModules().removeModule (project.getModules().getModuleID (row)); } void buttonClicked (Button* b) override { if (b == &setCopyModeButton) showCopyModeMenu(); if (b == ©PathButton) showSetPathsMenu(); } void lookAndFeelChanged() override { setCopyModeButton.setColour (TextButton::buttonColourId, findColour (secondaryButtonBackgroundColourId)); copyPathButton.setColour (TextButton::buttonColourId, findColour (defaultButtonBackgroundColourId)); } private: enum { nameCol = 1, versionCol, copyCol, pathCol }; Project& project; ValueTree modulesValueTree; ContentViewHeader header; ListBox list; ListBoxHeader* listHeader; TextButton setCopyModeButton, copyPathButton; std::map modulePathClipboard; void valueTreePropertyChanged (ValueTree&, const Identifier&) override { itemChanged(); } void valueTreeChildAdded (ValueTree&, ValueTree&) override { itemChanged(); } void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { itemChanged(); } void valueTreeChildOrderChanged (ValueTree&, int, int) override { itemChanged(); } void valueTreeParentChanged (ValueTree&) override { itemChanged(); } void itemChanged() { list.updateContent(); resized(); repaint(); } void showCopyModeMenu() { PopupMenu m; m.addItem (1, "Set all modules to copy locally"); m.addItem (2, "Set all modules to not copy locally"); int res = m.showAt (&setCopyModeButton); if (res != 0) project.getModules().setLocalCopyModeForAllModules (res == 1); } void showSetPathsMenu() { enum { copyPathsToAllModulesID = 1, copyPathsID, pastePathsID }; auto& moduleList = project.getModules(); auto moduleToCopy = moduleList.getModuleID (list.getSelectedRow()); if (moduleToCopy.isNotEmpty()) { PopupMenu m; m.addItem (copyPathsToAllModulesID, "Copy the paths from the module '" + moduleToCopy + "' to all other modules"); m.addItem (copyPathsID, "Copy paths from selected module", list.getNumSelectedRows() == 1); m.addItem (pastePathsID, "Paste paths to selected modules", ! modulePathClipboard.empty()); int res = m.showAt (©PathButton); if (res == copyPathsToAllModulesID) { for (Project::ExporterIterator exporter (project); exporter.next();) { for (int i = 0; i < moduleList.getNumModules(); ++i) { auto modID = moduleList.getModuleID (i); if (modID != moduleToCopy) exporter->getPathForModuleValue (modID) = exporter->getPathForModuleValue (moduleToCopy).getValue(); } } } else if (res == copyPathsID) { modulePathClipboard.clear(); for (Project::ExporterIterator exporter (project); exporter.next();) modulePathClipboard[exporter->getName()] = exporter->getPathForModuleValue (moduleToCopy).getValue(); } else if (res == pastePathsID) { for (int selectionId = 0; selectionId < list.getNumSelectedRows(); ++selectionId) { auto rowNumber = list.getSelectedRow (selectionId); auto modID = moduleList.getModuleID (rowNumber); for (Project::ExporterIterator exporter (project); exporter.next();) exporter->getPathForModuleValue (modID) = modulePathClipboard[exporter->getName()]; } } list.repaint(); } else { PopupMenu m; m.addItem (1, "(Select a module in the list above to use this option)", false); m.showAt (©PathButton); } } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ModulesPanel) };