Juce 1.34


#1

Ok, got another version out there for you. Lots of little changes and fixes, some of which will probably require you to tweak your code a bit - but it’s all for the best!

* a bunch of changes to continue improving the expressiveness and consistency of listener classes, (and moving away from generic listeners like ChangeListener):
* new class: LabelListener class now replaces Label's use of ChangeListeners
* new class: ComboBoxListener for ComboBoxes, replacing the old use of ActionListener
* new class: ScrollBarListener for ScrollBars, replacing the old use of ChangeListener
* new class: KeyboardFocusTraverser, to take the logic of keyboard focus traversal out of the component class.
* removed the Component::setFocusOrder method - instead, a KeyboardFocusTraverser object now decides the focus order (and can be overridden to support custom behaviours)
* new class: ApplicationCommandManagerListener - this is used to listen for commands being invoked, and for changes to the status of commands. The Button class now uses this so that when a button is linked to a command, it enables itself only when the command is active, and flashes when it's invoked.
* new class: FocusChangeListener - this can be registered with the Desktop class to receive callbacks whenever the focused component changes
* new class: FilenameComponentListener - for getting events from FilenameComponents, replacing the use of ActionListener
* new class: BooleanPropertyComponent - a property component with a toggle button in it
* some fixes to DLL builds on windows
* couple of additions to the MidiKeyboardComponent class
* fix for large menus not scrolling correctly
* got rid of Component::getMouseX() and getMouseY() - this functionality is already available in Desktop::getMousePosition(), so not needed here as well
* replaced the Component::getMouseXRelative() and getMouseYRelative() method with a single method getMouseXYRelative() that returns both co-ordinates at once (this is a more efficient way of doing things)
* added new methods Component::relativePositionToGlobal, globalPositionToRelative and relativePositionToOtherComponent for converting co-ordinates to and from screen co-ords. These replace the old getXRelativeTo() method.
* new class: MagnifierComponent, which magnifies or shrinks any component that you put inside it
* added colour swatches to the ColourSelectorComponent
* Jucer: literal text strings can now contain special strings which are treated as c++ code - anything inside a pair of %% characters counts as c++, so %%getName()%% gets translated into the name of the component; %%getButtonText()%% into getButtonText(), and these are concatenated with the rest of the string.
* Jucer: Button documents now have a list of the various over/down/toggled states for which you want to design paint routines, and any combination of these can be enabled
* Jucer: you can now drag-and-drop a Jucer .cpp file into a component's layout window, and it will add it as a Jucer component
* Jucer: highlighted object borders can now go beyond the edges of the component, making it easier to edit comps that are slightly off-screen or aligned with the edges of the parent comp
* Jucer: new command to bring any items that are off the edges of the screen back into the middle
* Jucer: ComboBoxes and Labels now create callback methods
* Jucer: Zoom mode! As well as the zoom in/out commands on the menus and keyboard, you can use the mouse-wheel with ctrl or alt held down to zoom.
* Jucer: Holding down the space bar now lets you scroll around the component
* Jucer: ability to group paint elements together to treat them as a single entity

#2

Excellent. Only had 6 errors concerning new GetMouseRelativeXY method, was easy to fix.

Only 1 problem: the following code worked fine before:


void COrangeWindow::mouseDown(const MouseEvent& e)
{
...

// file -> audio device selector
		if(m_result==5)
		{
			// create audio device selector component
			AudioDeviceSelectorComponent audio_device_manager_selector(audio_device_manager,2,2,2,2,true);

			// set size
			audio_device_manager_selector.setSize(500,300);

			// and show it in a DialogWindow...
			DialogWindow::showModalDialog(T("Audio Settings"),&audio_device_manager_selector,this,Colours::lightgrey,true);

			// create xml with audio settings state
			XmlElement* pxml_adm_state=audio_device_manager.createStateXml();
			pxml_adm_state->writeToFile(File(str_app_dir+"/orange_audio_manager_state.xml"),"");

			// delete xml object with audio settings state
			delete pxml_adm_state;
		}
...

}

Tested Windows XP, AMD Athlon X2.

The problem is within the DialogWindow::showModalDialog call wich seems to fail right after attempting to run the dialog (it appears in lower taskbar) but the dialog doesnt show and freezes the application.

COrangeWindow is a Component derived class, and the parent is a DocumentWindow derived object.


#3

Oh, dammit - the same thing happens in the demo app. Ok - it’s a bug in AudioDeviceSelectorComponent - here’s some new code for it:

[code]/*

This file is part of the JUCE library - "Jules’ Utility Class Extensions"
Copyright 2004-6 by Raw Material Software ltd.


JUCE can be redistributed and/or modified under the terms of the
GNU General Public License, as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later version.

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.

You should have received a copy of the GNU General Public License
along with JUCE; if not, visit www.gnu.org/licenses or write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA


If you’d like to release a closed-source product which uses JUCE, commercial
licenses are also available: visit www.rawmaterialsoftware.com/juce for
more information.

==============================================================================
*/

BEGIN_JUCE_NAMESPACE

//==============================================================================
class MidiInputListBox : public SimpleListBox,
public SimpleListBoxModel
{
public:
//==============================================================================
MidiInputListBox (AudioDeviceManager& deviceManager_)
: SimpleListBox (T(“midi”), 0),
deviceManager (deviceManager_)
{
devices = MidiInput::getDevices();

    setModel (this);
    setOutlineColour (getLookAndFeel().comboBoxOutline, 1);
}

~MidiInputListBox()
{
}

int getNumRows()
{
    return devices.size();
}

void paintListBoxItem (int row,
                       Graphics& g,
                       int width, int height,
                       bool rowIsSelected)
{
    if (row >= 0 && row < devices.size())
    {
        if (rowIsSelected)
            g.fillAll (getLookAndFeel().textEditorHighlight.withMultipliedAlpha (0.3f));

        const String device (devices [row]);

        const bool enabled = deviceManager.isMidiInputEnabled (device);
        const int x = getTickX();
        const int tickW = height - height / 4;

        getLookAndFeel().drawTickBox (g, x - tickW, (height - tickW) / 2, tickW, tickW,
                                      enabled, true, true, false);

        g.setFont (height * 0.6f);
        g.setColour (Colours::black.withAlpha (enabled ? 1.0f : 0.6f));
        g.drawText (device, x, 0, width - x - 2, height, Justification::centredLeft, true);
    }
}

void listBoxItemClicked (int row, const MouseEvent& e)
{
    selectRow (row);

    if (e.x < getTickX())
        flipEnablement (row);
}

void listBoxItemDoubleClicked (int row, const MouseEvent&)
{
    flipEnablement (row);
}

void paint (Graphics& g)
{
    SimpleListBox::paint (g);

    if (getNumRows() == 0)
    {
        g.setColour (Colours::grey);
        g.setFont (13.0f);
        g.drawText (TRANS("(no midi inputs available)"),
                    0, 0, getWidth(), getHeight() / 2, Justification::centred, true);
    }
}

//==============================================================================
juce_UseDebuggingNewOperator

private:
AudioDeviceManager& deviceManager;
StringArray devices;

void flipEnablement (const int row)
{
    if (row >= 0 && row < devices.size())
    {
        const String device (devices [row]);

        deviceManager.setMidiInputEnabled (device, ! deviceManager.isMidiInputEnabled (device));
    }
}

int getTickX() const throw()
{
    return getRowHeight() + 5;
}

MidiInputListBox (const MidiInputListBox&);
const MidiInputListBox& operator= (const MidiInputListBox&);

};

//==============================================================================
AudioDeviceSelectorComponent::AudioDeviceSelectorComponent (AudioDeviceManager& deviceManager_,
const int minInputChannels_,
const int maxInputChannels_,
const int minOutputChannels_,
const int maxOutputChannels_,
const bool showMidiOptions_)
: deviceManager (deviceManager_),
minOutputChannels (minOutputChannels_),
maxOutputChannels (maxOutputChannels_),
minInputChannels (minInputChannels_),
maxInputChannels (maxInputChannels_),
showMidiOptions (showMidiOptions_),
sampleRateDropDown (0),
inputChansDropDown (0),
inputsLabel (0),
outputChansDropDown (0),
outputsLabel (0),
sampleRateLabel (0),
bufferSizeDropDown (0),
bufferSizeLabel (0),
launchUIButton (0)
{
jassert (minOutputChannels >= 0 && minOutputChannels <= maxOutputChannels);
jassert (minInputChannels >= 0 && minInputChannels <= maxInputChannels);

audioDeviceDropDown = new ComboBox (T("device"));
deviceManager_.addDeviceNamesToComboBox (*audioDeviceDropDown);
audioDeviceDropDown->setSelectedId (-1);

if (deviceManager_.getCurrentAudioDeviceName().isNotEmpty())
    audioDeviceDropDown->setText (deviceManager_.getCurrentAudioDeviceName());

audioDeviceDropDown->addListener (this);
addAndMakeVisible (audioDeviceDropDown);

Label* label = new Label (T("l1"), TRANS ("audio device:"));
label->attachToComponent (audioDeviceDropDown, true);

if (showMidiOptions)
{
    addAndMakeVisible (midiInputsList = new MidiInputListBox (deviceManager));

    midiInputsLabel = new Label (T("lm"), TRANS ("active midi inputs:"));
    midiInputsLabel->setJustificationType (Justification::topRight);
    midiInputsLabel->attachToComponent (midiInputsList, true);
}
else
{
    midiInputsList = 0;
    midiInputsLabel = 0;
}

deviceManager_.addChangeListener (this);
changeListenerCallback (0);

}

AudioDeviceSelectorComponent::~AudioDeviceSelectorComponent()
{
deviceManager.removeChangeListener (this);
deleteAllChildren();
}

void AudioDeviceSelectorComponent::resized()
{
int lx = proportionOfWidth (0.35f);
int w = proportionOfWidth (0.55f);
int y = 15;
const int h = 24;
const int dh = 30;

audioDeviceDropDown->setBounds (lx, y, w, h);
y += dh;

if (sampleRateDropDown != 0)
{
    sampleRateDropDown->setBounds (lx, y, w, h);
    y += dh;
}

if (bufferSizeDropDown != 0)
{
    bufferSizeDropDown->setBounds (lx, y, w, h);
    y += dh;
}

if (outputChansDropDown != 0)
{
    outputChansDropDown->setBounds (lx, y, w, h);
    y += dh;
}

if (inputChansDropDown != 0)
{
    inputChansDropDown->setBounds (lx, y, w, h);
    y += dh;
}

if (launchUIButton != 0)
{
    launchUIButton->setBounds (lx, y, 150, h);
    ((TextButton*) launchUIButton)->changeWidthToFitText();
    y += dh;
}

if (midiInputsList != 0)
{
    midiInputsList->setBounds (lx, y, w, jmin (h * 4, getHeight() - y - 2));
    y += dh;
}

}

void AudioDeviceSelectorComponent::buttonClicked (Button*)
{
AudioIODevice* const device = deviceManager.getCurrentAudioDevice();

if (device != 0 && device->hasControlPanel())
{
    const String lastDevice (device->getName());

    if (device->showControlPanel())
    {
        deviceManager.setAudioDevice (String::empty);
        deviceManager.setAudioDevice (lastDevice);
    }

    getTopLevelComponent()->toFront (true);
}

}

void AudioDeviceSelectorComponent::comboBoxChanged (ComboBox* comboBoxThatHasChanged)
{
AudioIODevice* const audioDevice = deviceManager.getCurrentAudioDevice();

if (comboBoxThatHasChanged == audioDeviceDropDown)
{
    if (audioDeviceDropDown->getSelectedId() < 0)
    {
        deviceManager.setAudioDevice (String::empty);
    }
    else
    {
        String error (deviceManager.setAudioDevice (audioDeviceDropDown->getText()));

        if (error.isNotEmpty())
        {
            AlertWindow::showMessageBox (AlertWindow::WarningIcon,
                                         T("Error while opening \"")
                                            + audioDeviceDropDown->getText()
                                            + T("\""),
                                         error);
        }
    }

    if (deviceManager.getCurrentAudioDeviceName().isNotEmpty())
        audioDeviceDropDown->setText (deviceManager.getCurrentAudioDeviceName());
    else
        audioDeviceDropDown->setSelectedId (-1);
}
else if (audioDevice != 0)
{
    if (bufferSizeDropDown != 0 && comboBoxThatHasChanged == bufferSizeDropDown)
    {
        if (bufferSizeDropDown->getSelectedId() > 0)
            deviceManager.setAudioDevice (audioDevice->getName(),
                                          bufferSizeDropDown->getSelectedId(),
                                          audioDevice->getCurrentSampleRate());
    }
    else if (sampleRateDropDown != 0 && comboBoxThatHasChanged == sampleRateDropDown)
    {
        if (sampleRateDropDown->getSelectedId() > 0)
            deviceManager.setAudioDevice (audioDevice->getName(),
                                          audioDevice->getCurrentBufferSizeSamples(),
                                          sampleRateDropDown->getSelectedId());
    }
    else if (outputChansDropDown != 0 && comboBoxThatHasChanged == outputChansDropDown)
    {
        const int bit = (outputChansDropDown->getSelectedId() - 1) * maxOutputChannels;

        BitArray chans;
        for (int i = 0; i < maxOutputChannels; ++i)
            chans.setBit (bit + i);

        deviceManager.setAudioDevice (audioDevice->getName(),
                                      audioDevice->getCurrentBufferSizeSamples(),
                                      audioDevice->getCurrentSampleRate(),
                                      0, &chans);
    }
    else if (inputChansDropDown != 0 && comboBoxThatHasChanged == inputChansDropDown)
    {
        const int bit = (inputChansDropDown->getSelectedId() - 1) * maxInputChannels;

        BitArray chans;
        for (int i = 0; i < maxInputChannels; ++i)
            chans.setBit (bit + i);

        deviceManager.setAudioDevice (audioDevice->getName(),
                                      audioDevice->getCurrentBufferSizeSamples(),
                                      audioDevice->getCurrentSampleRate(),
                                      &chans, 0);
    }
}

}

void AudioDeviceSelectorComponent::changeListenerCallback (void*)
{
deleteAndZero (sampleRateDropDown);
deleteAndZero (inputChansDropDown);
deleteAndZero (inputsLabel);
deleteAndZero (outputChansDropDown);
deleteAndZero (outputsLabel);
deleteAndZero (sampleRateLabel);
deleteAndZero (bufferSizeDropDown);
deleteAndZero (bufferSizeLabel);
deleteAndZero (launchUIButton);

AudioIODevice* const currentDevice = deviceManager.getCurrentAudioDevice();

if (currentDevice != 0)
{
    // sample rate
    addAndMakeVisible (sampleRateDropDown = new ComboBox (T("samplerate")));
    sampleRateLabel = new Label (T("l2"), TRANS ("sample rate:"));
    sampleRateLabel->attachToComponent (sampleRateDropDown, true);

    const int numRates = currentDevice->getNumSampleRates();

    int i;
    for (i = 0; i < numRates; ++i)
    {
        const int rate = roundDoubleToInt (currentDevice->getSampleRate (i));
        sampleRateDropDown->addItem (String (rate) + T(" Hz"), rate);
    }

    const double currentRate = currentDevice->getCurrentSampleRate();
    sampleRateDropDown->setSelectedId (roundDoubleToInt (currentRate), true);
    sampleRateDropDown->addListener (this);

    // buffer size
    addAndMakeVisible (bufferSizeDropDown = new ComboBox (T("buffersize")));
    bufferSizeLabel = new Label (T("l2"), TRANS ("audio buffer size:"));
    bufferSizeLabel->attachToComponent (bufferSizeDropDown, true);

    const int numBufferSizes = currentDevice->getNumBufferSizesAvailable();

    for (i = 0; i < numBufferSizes; ++i)
    {
        const int bs = currentDevice->getBufferSizeSamples (i);
        bufferSizeDropDown->addItem (String (bs)
                                      + T(" samples (")
                                      + String (bs * 1000.0 / currentRate, 1)
                                      + T(" ms)"),
                                     bs);
    }

    bufferSizeDropDown->setSelectedId (currentDevice->getCurrentBufferSizeSamples(), true);
    bufferSizeDropDown->addListener (this);

    // output chans
    if (maxOutputChannels > 0)
    {
        addAndMakeVisible (outputChansDropDown = new ComboBox (T("outs")));
        outputsLabel = new Label (T("l3"), TRANS ("output channels:"));
        outputsLabel->attachToComponent (outputChansDropDown, true);

        StringArray outs (currentDevice->getOutputChannelNames());

        if (outs.size() == 0)
        {
            outputChansDropDown->addItem (TRANS ("<< none available >>"), -1);
        }
        else
        {
            if (minOutputChannels == 0)
            {
                outputChansDropDown->addItem (T("<< none >>"), -1);
                outputChansDropDown->addSeparator();
            }

            for (int i = 0; i < outs.size() / maxOutputChannels; ++i)
            {
                String name;

                for (int j = 0; j < maxOutputChannels; ++j)
                {
                    name += outs [i * maxOutputChannels + j];

                    if (j < maxOutputChannels - 1)
                        name += T(" & ");
                }

                outputChansDropDown->addItem (name, i + 1);
            }
        }

        const int lowestBit = deviceManager.getOutputChannels().findNextSetBit (0);
        outputChansDropDown->setSelectedId ((lowestBit < 0) ? -1 : lowestBit / maxOutputChannels + 1, true);
        outputChansDropDown->addListener (this);
    }

    // input chans
    if (maxInputChannels > 0)
    {
        addAndMakeVisible (inputChansDropDown = new ComboBox (T("ins")));
        inputsLabel = new Label (T("l4"), TRANS("input channels:"));
        inputsLabel->attachToComponent (inputChansDropDown, true);

        StringArray ins (currentDevice->getInputChannelNames());

        if (ins.size() == 0)
        {
            inputChansDropDown->addItem (T("<< none available >>"), -1);
        }
        else
        {
            if (minInputChannels == 0)
            {
                inputChansDropDown->addItem (T("<< none >>"), -1);
                inputChansDropDown->addSeparator();
            }

            for (int i = 0; i < ins.size() / maxInputChannels; ++i)
            {
                String name;

                for (int j = 0; j < maxInputChannels; ++j)
                {
                    name += ins [i * maxInputChannels + j];

                    if (j < maxInputChannels - 1)
                        name += T(" & ");
                }

                inputChansDropDown->addItem (name, i + 1);
            }
        }

        const int lowestBit = deviceManager.getInputChannels().findNextSetBit (0);
        inputChansDropDown->setSelectedId ((lowestBit < 0) ? -1 : lowestBit / maxInputChannels + 1, true);
        inputChansDropDown->addListener (this);
    }

    if (currentDevice->hasControlPanel())
    {
        addAndMakeVisible (launchUIButton = new TextButton (T("show this device's control panel"),
                                                            T("opens the device's own control panel")));

        launchUIButton->addButtonListener (this);
    }

    if (midiInputsList != 0)
        midiInputsList->updateContent();
}
else
{
    audioDeviceDropDown->setSelectedId (-1);
}

resized();

}

END_JUCE_NAMESPACE
[/code]

Silly mistake, really. I’ll patch the new version to fix this, I think.


#4

This new code solved it.

Perfect.


#5

neat-o!

good job i had my final final presentation today!! ! :smiley: :smiley: :smiley:

altho i’ve got to go to alton towers tomorrow so no coding all day for me… :wink:


#6

whau, really good news. the application command listener class is a must have for components, the same for the whole command targets approach. only a thing is strange. i have a menu attached to a window, and a toolbar child of the content component. i made the menu to listen to the global command manager. now if i trigger a button in the toolbar component (which is a toggle one, with down state) i can see a flash in the menu name where the same command is present inside it. for buttons that are not toggles is working correctly. other thing, if you don’t attach a command manager to the setMenuBar function, the menu names are not shown the first time the window opens, you must click on some of them to make’em visible.
anyway i’ve not inspected too much the new classes but i think i’ll have to. now the property components could come in hand here :wink:


#7

ah - interesting point about the menus without command managers, I guess it’ll need a tweak in MenuBarComponent:

void MenuBarComponent::setModel (MenuBarModel* const newModel) { if (model != newModel) { model = newModel; repaint(); refreshMenuItems(); } }

Just to make sure it updates its list. Sorry 'bout that - all my test menus were using command managers…


#8

hey good!
other thing with menus and commands and buttons:
i think you should tweak this:

[code]void Button::applicationCommandListChanged()
{
if (commandManagerToUse != 0)
{
ApplicationCommandInfo info (0);
ApplicationCommandTarget* target =
commandManagerToUse->getTargetForCommand (commandID, info);

    setEnabled (target != 0
                    && (info.flags & ApplicationCommandInfo::isDisabled) == 0);

if (clickTogglesState)
    setToggleState (target != 0
                    && (info.flags & ApplicationCommandInfo::isTicked) == 0, false);
}

}[/code]

:smiley:


#9

Hmm - yes, that’s a good idea, actually.


#10

mmmh no, is not working as expected… maybe setState (buttonDown) ?
i think you can understand what i mean. no even setState buttonDown is not good. cause is triggering a sendStateMessage… mmmmhh


#11

(though I think you mean (info.flags & ApplicationCommandInfo::isTicked) != 0)


#12

oooups… yeah :wink:


#13

anyway is not working as expected. i mean the menu is ticked correctly when the command is triggered from the button, but the button is never changing to down state when triggering the command from the menu…


#14

it would be nice if there was a version of containsWholeWord that returned the location of the word.


#15

Yeah, that’d be a good one. I’ll probably throw that in there.


#16