Browse Source

Improvements to PluginListComponent - it's now a sortable table rather than a list.

tags/2021-05-28
jules 12 years ago
parent
commit
7a11730317
5 changed files with 184 additions and 143 deletions
  1. +12
    -7
      modules/juce_audio_processors/scanning/juce_KnownPluginList.cpp
  2. +4
    -3
      modules/juce_audio_processors/scanning/juce_KnownPluginList.h
  3. +141
    -98
      modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp
  4. +14
    -22
      modules/juce_audio_processors/scanning/juce_PluginListComponent.h
  5. +13
    -13
      modules/juce_gui_basics/widgets/juce_TableListBox.h

+ 12
- 7
modules/juce_audio_processors/scanning/juce_KnownPluginList.cpp View File

@@ -241,7 +241,8 @@ void KnownPluginList::clearBlacklistedFiles()
//==============================================================================
struct PluginSorter
{
PluginSorter (KnownPluginList::SortMethod sortMethod) noexcept : method (sortMethod) {}
PluginSorter (KnownPluginList::SortMethod sortMethod, bool forwards) noexcept
: method (sortMethod), direction (forwards ? 1 : -1) {}
int compareElements (const PluginDescription* const first,
const PluginDescription* const second) const
@@ -252,6 +253,7 @@ struct PluginSorter
{
case KnownPluginList::sortByCategory: diff = first->category.compareLexicographically (second->category); break;
case KnownPluginList::sortByManufacturer: diff = first->manufacturerName.compareLexicographically (second->manufacturerName); break;
case KnownPluginList::sortByFormat: diff = first->pluginFormatName.compare (second->pluginFormatName); break;
case KnownPluginList::sortByFileSystemLocation: diff = lastPathPart (first->fileOrIdentifier).compare (lastPathPart (second->fileOrIdentifier)); break;
default: break;
}
@@ -259,7 +261,7 @@ struct PluginSorter
if (diff == 0)
diff = first->name.compareLexicographically (second->name);
return diff;
return diff * direction;
}
private:
@@ -268,14 +270,17 @@ private:
return path.replaceCharacter ('\\', '/').upToLastOccurrenceOf ("/", false, false);
}
KnownPluginList::SortMethod method;
const KnownPluginList::SortMethod method;
const int direction;
JUCE_DECLARE_NON_COPYABLE (PluginSorter)
};
void KnownPluginList::sort (const SortMethod method)
void KnownPluginList::sort (const SortMethod method, bool forwards)
{
if (method != defaultOrder)
{
PluginSorter sorter (method);
PluginSorter sorter (method, forwards);
types.sort (sorter, true);
sendChangeMessage();
@@ -477,7 +482,7 @@ KnownPluginList::PluginTree* KnownPluginList::createTree (const SortMethod sortM
Array <PluginDescription*> sorted;
{
PluginSorter sorter (sortMethod);
PluginSorter sorter (sortMethod, true);
for (int i = 0; i < types.size(); ++i)
sorted.addSorted (sorter, types.getUnchecked(i));
@@ -485,7 +490,7 @@ KnownPluginList::PluginTree* KnownPluginList::createTree (const SortMethod sortM
PluginTree* tree = new PluginTree();
if (sortMethod == sortByCategory || sortMethod == sortByManufacturer)
if (sortMethod == sortByCategory || sortMethod == sortByManufacturer || sortMethod == sortByFormat)
{
PluginTreeUtils::buildTreeByCategory (*tree, sorted, sortMethod);
}


+ 4
- 3
modules/juce_audio_processors/scanning/juce_KnownPluginList.h View File

@@ -132,6 +132,7 @@ public:
sortAlphabetically,
sortByCategory,
sortByManufacturer,
sortByFormat,
sortByFileSystemLocation
};
@@ -143,7 +144,7 @@ public:
Use getIndexChosenByMenu() to find out the type that was chosen.
*/
void addToMenu (PopupMenu& menu, const SortMethod sortMethod) const;
void addToMenu (PopupMenu& menu, SortMethod sortMethod) const;
/** Converts a menu item index that has been chosen into its index in this list.
Returns -1 if it's not an ID that was used.
@@ -153,7 +154,7 @@ public:
//==============================================================================
/** Sorts the list. */
void sort (const SortMethod method);
void sort (SortMethod method, bool forwards);
//==============================================================================
/** Creates some XML that can be used to store the state of this list. */
@@ -195,7 +196,7 @@ public:
private:
//==============================================================================
OwnedArray <PluginDescription> types;
OwnedArray<PluginDescription> types;
StringArray blacklist;
ScopedPointer<CustomScanner> scanner;
CriticalSection scanLock;


+ 141
- 98
modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp View File

@@ -22,10 +22,114 @@
==============================================================================
*/
PluginListComponent::PluginListComponent (AudioPluginFormatManager& manager,
KnownPluginList& listToEdit,
const File& deadMansPedal,
PropertiesFile* const props)
class PluginListComponent::TableModel : public TableListBoxModel
{
public:
TableModel (PluginListComponent& c, KnownPluginList& l) : owner (c), list (l) {}
int getNumRows() override
{
return list.getNumTypes() + list.getBlacklistedFiles().size();
}
void paintRowBackground (Graphics& g, int /*rowNumber*/, int /*width*/, int /*height*/, bool rowIsSelected) override
{
if (rowIsSelected)
g.fillAll (owner.findColour (TextEditor::highlightColourId));
}
enum
{
nameCol = 1,
typeCol = 2,
categoryCol = 3,
manufacturerCol = 4,
descCol = 5
};
void paintCell (Graphics& g, int row, int columnId, int width, int height, bool /*rowIsSelected*/) override
{
String text;
bool isBlacklisted = row >= list.getNumTypes();
if (isBlacklisted)
{
if (columnId == nameCol)
text = list.getBlacklistedFiles() [row - list.getNumTypes()];
else if (columnId == descCol)
text = TRANS("Deactivated after failing to initialise correctly");
}
else if (const PluginDescription* const desc = list.getType (row))
{
switch (columnId)
{
case nameCol: text = desc->name; break;
case typeCol: text = desc->pluginFormatName; break;
case categoryCol: text = desc->category.isNotEmpty() ? desc->category : "-"; break;
case manufacturerCol: text = desc->manufacturerName; break;
case descCol: text = getPluginDescription (*desc); break;
default: jassertfalse; break;
}
}
if (text.isNotEmpty())
{
g.setColour (isBlacklisted ? Colours::red
: columnId == nameCol ? Colours::black
: Colours::grey);
g.setFont (Font (height * 0.7f, Font::bold));
g.drawFittedText (text, 4.0f, 0.0f, width - 6.0f, height, Justification::centredLeft, 1, 0.9f);
}
}
void deleteKeyPressed (int lastRowSelected) override
{
removePluginItem (list, lastRowSelected);
}
void sortOrderChanged (int newSortColumnId, bool isForwards) override
{
switch (newSortColumnId)
{
case nameCol: list.sort (KnownPluginList::sortAlphabetically, isForwards); break;
case typeCol: list.sort (KnownPluginList::sortByFormat, isForwards); break;
case categoryCol: list.sort (KnownPluginList::sortByCategory, isForwards); break;
case manufacturerCol: list.sort (KnownPluginList::sortByManufacturer, isForwards); break;
case descCol: break;
default: jassertfalse; break;
}
}
static void removePluginItem (KnownPluginList& list, int index)
{
if (index < list.getNumTypes())
list.removeType (index);
else
list.removeFromBlacklist (list.getBlacklistedFiles() [index - list.getNumTypes()]);
}
static String getPluginDescription (const PluginDescription& desc)
{
StringArray items;
if (desc.descriptiveName != desc.name)
items.add (desc.descriptiveName);
items.add (desc.version);
items.removeEmptyStrings();
return items.joinIntoString (" - ");
}
PluginListComponent& owner;
KnownPluginList& list;
};
//==============================================================================
PluginListComponent::PluginListComponent (AudioPluginFormatManager& manager, KnownPluginList& listToEdit,
const File& deadMansPedal, PropertiesFile* const props)
: formatManager (manager),
list (listToEdit),
deadMansPedalFile (deadMansPedal),
@@ -33,8 +137,20 @@ PluginListComponent::PluginListComponent (AudioPluginFormatManager& manager,
propertiesToUse (props),
numThreads (0)
{
listBox.setModel (this);
addAndMakeVisible (&listBox);
tableModel = new TableModel (*this, listToEdit);
TableHeaderComponent& header = table.getHeader();
header.addColumn (TRANS("Name"), TableModel::nameCol, 200, 100, 700, TableHeaderComponent::defaultFlags | TableHeaderComponent::sortedForwards);
header.addColumn (TRANS("Format"), TableModel::typeCol, 80, 80, 80, TableHeaderComponent::notResizable);
header.addColumn (TRANS("Category"), TableModel::categoryCol, 100, 100, 200);
header.addColumn (TRANS("Manufacturer"), TableModel::manufacturerCol, 200, 100, 300);
header.addColumn (TRANS("Description"), TableModel::descCol, 300, 100, 500, TableHeaderComponent::notSortable);
table.setHeaderHeight (22);
table.setRowHeight (20);
table.setModel (tableModel);
addAndMakeVisible (&table);
addAndMakeVisible (&optionsButton);
optionsButton.addListener (this);
@@ -66,9 +182,13 @@ void PluginListComponent::setNumberOfThreadsForScanning (int num)
void PluginListComponent::resized()
{
listBox.setBounds (0, 0, getWidth(), getHeight() - 30);
Rectangle<int> r (getLocalBounds().reduced (2));
optionsButton.setBounds (r.removeFromBottom (24));
optionsButton.changeWidthToFitText (24);
optionsButton.setTopLeftPosition (0, getHeight() - 28);
r.removeFromBottom (3);
table.setBounds (r);
}
void PluginListComponent::changeListenerCallback (ChangeBroadcaster*)
@@ -78,89 +198,22 @@ void PluginListComponent::changeListenerCallback (ChangeBroadcaster*)
void PluginListComponent::updateList()
{
listBox.updateContent();
listBox.repaint();
}
int PluginListComponent::getNumRows()
{
return list.getNumTypes() + list.getBlacklistedFiles().size();
}
void PluginListComponent::paintListBoxItem (int row, Graphics& g, int width, int height, bool rowIsSelected)
{
if (rowIsSelected)
g.fillAll (findColour (TextEditor::highlightColourId));
String name, desc;
bool isBlacklisted = false;
if (row >= list.getNumTypes())
{
isBlacklisted = true;
name = list.getBlacklistedFiles() [row - list.getNumTypes()];
desc = TRANS("Deactivated after failing to initialise correctly");
}
else if (const PluginDescription* const pd = list.getType (row))
{
name = pd->name;
desc << pd->pluginFormatName
<< (pd->isInstrument ? " instrument" : " effect")
<< " - " << pd->numInputChannels << (pd->numInputChannels == 1 ? " in" : " ins")
<< " / " << pd->numOutputChannels << (pd->numOutputChannels == 1 ? " out" : " outs");
if (pd->manufacturerName.isNotEmpty()) desc << " - " << pd->manufacturerName;
if (pd->version.isNotEmpty()) desc << " - " << pd->version;
if (pd->category.isNotEmpty()) desc << " - category: '" << pd->category << '\'';
}
if (name.isNotEmpty())
{
GlyphArrangement ga;
ga.addCurtailedLineOfText (Font (height * 0.7f, Font::bold),
name, 8.0f, height * 0.8f, width - 10.0f, true);
g.setColour (isBlacklisted ? Colours::red : Colours::black);
ga.draw (g);
const Rectangle<float> bb (ga.getBoundingBox (0, -1, false));
ga.clear();
ga.addCurtailedLineOfText (Font (height * 0.6f), desc,
jmax (bb.getRight() + 10.0f, width / 3.0f), height * 0.8f,
width - bb.getRight() - 12.0f, true);
g.setColour (isBlacklisted ? Colours::red : Colours::grey);
ga.draw (g);
}
}
static void removePluginItem (KnownPluginList& list, int index)
{
if (index < list.getNumTypes())
list.removeType (index);
else
list.removeFromBlacklist (list.getBlacklistedFiles() [index - list.getNumTypes()]);
}
void PluginListComponent::deleteKeyPressed (int lastRowSelected)
{
removePluginItem (list, lastRowSelected);
table.updateContent();
table.repaint();
}
void PluginListComponent::removeSelected()
{
const SparseSet <int> selected (listBox.getSelectedRows());
const SparseSet<int> selected (table.getSelectedRows());
for (int i = list.getNumTypes(); --i >= 0;)
if (selected.contains (i))
removePluginItem (list, i);
TableModel::removePluginItem (list, i);
}
bool PluginListComponent::canShowSelectedFolder() const
{
if (const PluginDescription* const desc = list.getType (listBox.getSelectedRow()))
if (const PluginDescription* const desc = list.getType (table.getSelectedRow()))
return File::createFileWithoutCheckingPath (desc->fileOrIdentifier).exists();
return false;
@@ -169,7 +222,7 @@ bool PluginListComponent::canShowSelectedFolder() const
void PluginListComponent::showSelectedFolder()
{
if (canShowSelectedFolder())
if (const PluginDescription* const desc = list.getType (listBox.getSelectedRow()))
if (const PluginDescription* const desc = list.getType (table.getSelectedRow()))
File (desc->fileOrIdentifier).getParentDirectory().startAsProcess();
}
@@ -192,12 +245,9 @@ void PluginListComponent::optionsMenuCallback (int result)
{
case 0: break;
case 1: list.clear(); break;
case 2: list.sort (KnownPluginList::sortAlphabetically); break;
case 3: list.sort (KnownPluginList::sortByCategory); break;
case 4: list.sort (KnownPluginList::sortByManufacturer); break;
case 5: removeSelected(); break;
case 6: showSelectedFolder(); break;
case 7: removeMissingPlugins(); break;
case 2: removeSelected(); break;
case 3: showSelectedFolder(); break;
case 4: removeMissingPlugins(); break;
default:
if (AudioPluginFormat* format = formatManager.getFormat (result - 10))
@@ -213,13 +263,9 @@ void PluginListComponent::buttonClicked (Button* button)
{
PopupMenu menu;
menu.addItem (1, TRANS("Clear list"));
menu.addItem (5, TRANS("Remove selected plug-in from list"), listBox.getNumSelectedRows() > 0);
menu.addItem (6, TRANS("Show folder containing selected plug-in"), canShowSelectedFolder());
menu.addItem (7, TRANS("Remove any plug-ins whose files no longer exist"));
menu.addSeparator();
menu.addItem (2, TRANS("Sort alphabetically"));
menu.addItem (3, TRANS("Sort by category"));
menu.addItem (4, TRANS("Sort by manufacturer"));
menu.addItem (2, TRANS("Remove selected plug-in from list"), table.getNumSelectedRows() > 0);
menu.addItem (3, TRANS("Show folder containing selected plug-in"), canShowSelectedFolder());
menu.addItem (4, TRANS("Remove any plug-ins whose files no longer exist"));
menu.addSeparator();
for (int i = 0; i < formatManager.getNumFormats(); ++i)
@@ -262,10 +308,7 @@ void PluginListComponent::setLastSearchPath (PropertiesFile& properties, AudioPl
class PluginListComponent::Scanner : private Timer
{
public:
Scanner (PluginListComponent& plc,
AudioPluginFormat& format,
PropertiesFile* properties,
int threads)
Scanner (PluginListComponent& plc, AudioPluginFormat& format, PropertiesFile* properties, int threads)
: owner (plc), formatToScan (format), propertiesToUse (properties),
pathChooserWindow (TRANS("Select folders to scan..."), String::empty, AlertWindow::NoIcon),
progressWindow (TRANS("Scanning for plug-ins..."),


+ 14
- 22
modules/juce_audio_processors/scanning/juce_PluginListComponent.h View File

@@ -33,7 +33,6 @@
*/
class JUCE_API PluginListComponent : public Component,
public FileDragAndDropTarget,
private ListBoxModel,
private ChangeListener,
private ButtonListener // (can't use Button::Listener due to idiotic VC2005 bug)
{
@@ -53,7 +52,7 @@ public:
/** Destructor. */
~PluginListComponent();
/** Changes the text in the panel's button. */
/** Changes the text in the panel's options button. */
void setOptionsButtonText (const String& newText);
/** Sets how many threads to simultaneously scan for plugins.
@@ -62,42 +61,32 @@ public:
void setNumberOfThreadsForScanning (int numThreads);
/** Returns the last search path stored in a given properties file for the specified format. */
static FileSearchPath getLastSearchPath (PropertiesFile& properties, AudioPluginFormat& format);
static FileSearchPath getLastSearchPath (PropertiesFile&, AudioPluginFormat&);
/** Stores a search path in a properties file for the given format. */
static void setLastSearchPath (PropertiesFile& properties, AudioPluginFormat& format,
const FileSearchPath& newPath);
static void setLastSearchPath (PropertiesFile&, AudioPluginFormat&, const FileSearchPath&);
/** Triggers an asynchronous scan for the given format. */
void scanFor (AudioPluginFormat& format);
void scanFor (AudioPluginFormat&);
/** Returns true if there's currently a scan in progress. */
bool isScanning() const noexcept;
//==============================================================================
/** @internal */
void resized() override;
/** @internal */
bool isInterestedInFileDrag (const StringArray&) override;
/** @internal */
void filesDropped (const StringArray&, int, int) override;
/** @internal */
int getNumRows() override;
/** @internal */
void paintListBoxItem (int row, Graphics&, int width, int height, bool rowIsSelected) override;
/** @internal */
void deleteKeyPressed (int lastRowSelected) override;
private:
//==============================================================================
AudioPluginFormatManager& formatManager;
KnownPluginList& list;
File deadMansPedalFile;
ListBox listBox;
TableListBox table;
TextButton optionsButton;
PropertiesFile* propertiesToUse;
int numThreads;
class TableModel;
friend class TableModel;
friend struct ContainerDeletePolicy<TableModel>;
ScopedPointer<TableModel> tableModel;
class Scanner;
friend class Scanner;
friend struct ContainerDeletePolicy<Scanner>;
@@ -107,11 +96,14 @@ private:
static void optionsMenuStaticCallback (int, PluginListComponent*);
void optionsMenuCallback (int);
void updateList();
void removeSelected();
void showSelectedFolder();
bool canShowSelectedFolder() const;
void removeSelected();
void removeMissingPlugins();
void resized() override;
bool isInterestedInFileDrag (const StringArray&) override;
void filesDropped (const StringArray&, int, int) override;
void buttonClicked (Button*) override;
void changeListenerCallback (ChangeBroadcaster*) override;


+ 13
- 13
modules/juce_gui_basics/widgets/juce_TableListBox.h View File

@@ -291,31 +291,31 @@ public:
//==============================================================================
/** @internal */
int getNumRows();
int getNumRows() override;
/** @internal */
void paintListBoxItem (int, Graphics&, int, int, bool);
void paintListBoxItem (int, Graphics&, int, int, bool) override;
/** @internal */
Component* refreshComponentForRow (int rowNumber, bool isRowSelected, Component* existingComponentToUpdate);
Component* refreshComponentForRow (int rowNumber, bool isRowSelected, Component* existingComponentToUpdate) override;
/** @internal */
void selectedRowsChanged (int lastRowSelected);
void selectedRowsChanged (int lastRowSelected) override;
/** @internal */
void deleteKeyPressed (int currentSelectedRow);
void deleteKeyPressed (int currentSelectedRow) override;
/** @internal */
void returnKeyPressed (int currentSelectedRow);
void returnKeyPressed (int currentSelectedRow) override;
/** @internal */
void backgroundClicked();
void backgroundClicked() override;
/** @internal */
void listWasScrolled();
void listWasScrolled() override;
/** @internal */
void tableColumnsChanged (TableHeaderComponent*);
void tableColumnsChanged (TableHeaderComponent*) override;
/** @internal */
void tableColumnsResized (TableHeaderComponent*);
void tableColumnsResized (TableHeaderComponent*) override;
/** @internal */
void tableSortOrderChanged (TableHeaderComponent*);
void tableSortOrderChanged (TableHeaderComponent*) override;
/** @internal */
void tableColumnDraggingChanged (TableHeaderComponent*, int);
void tableColumnDraggingChanged (TableHeaderComponent*, int) override;
/** @internal */
void resized();
void resized() override;
private:


Loading…
Cancel
Save